From a3a959d75ae86c6ecbc6968e6b0a3ab62f2c0f7c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 11 Oct 2013 05:27:33 +0200 Subject: [PATCH 0001/1144] first commit of GNU LGPL3 text and the readme file of the latest matlab version of PILOT. --- LICENSE.txt | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README | 13 +++++ 2 files changed, 178 insertions(+) create mode 100644 LICENSE.txt create mode 100644 README diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..755013bb --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README b/README new file mode 100644 index 00000000..9270ebea --- /dev/null +++ b/README @@ -0,0 +1,13 @@ +PILOT + +version: 5.2 + +PIcking and LOcalisation Tool + +author: L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff +======= +developer: S. Wehling-Benatelli, L. Kueperkoch, M. Bischoff, M. Rische +others: A. Bruestle, T. Meier + +February 2012 + From 91cd80bdb568d9413bcbae63e4286056cc2fd265 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 11 Oct 2013 06:01:12 +0200 Subject: [PATCH 0002/1144] changed the README text to match the context --- README | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/README b/README index 9270ebea..21540e84 100644 --- a/README +++ b/README @@ -1,13 +1,30 @@ -PILOT +PyLoT -version: 5.2 +version: 0.1 -PIcking and LOcalisation Tool +The Python picking and Localisation Tool -author: L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff +This python library contains a graphical user interfaces for picking +seismic phases. This software needs to install ObsPy (see +http://github.com/obspy/obspy/wiki) and of the Qt4 libraries first. + +PILOT has been developed in Mathworks' MatLab. In order to distribute +PILOT without facing portability problems it has been decided to re- +develop the software package in Python. The great work of the ObsPy +group allows easy handling of a bunch of seismic data and PyLoT will +benefit a lot compared to the former MatLab version. + +The development of PyLoT is part of the joint research projekt MAGS2. + +author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT) ======= -developer: S. Wehling-Benatelli, L. Kueperkoch, M. Bischoff, M. Rische +developer(s): S. Wehling-Benatelli, L. Kueperkoch, M. Bischoff, M. Rische others: A. Bruestle, T. Meier -February 2012 +release notes: +============== + + + +October 2013 From 6c363d34ffa1e08c9ff16ebc4cd65911e4fc1f38 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 8 Nov 2013 10:33:33 +0100 Subject: [PATCH 0003/1144] updated contacts in README --- README | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/README b/README index 21540e84..abbea32b 100644 --- a/README +++ b/README @@ -5,21 +5,26 @@ version: 0.1 The Python picking and Localisation Tool This python library contains a graphical user interfaces for picking -seismic phases. This software needs to install ObsPy (see -http://github.com/obspy/obspy/wiki) and of the Qt4 libraries first. +seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki) +and the Qt4 libraries to be installed first. PILOT has been developed in Mathworks' MatLab. In order to distribute -PILOT without facing portability problems it has been decided to re- +PILOT without facing portability problems, it has been decided to re- develop the software package in Python. The great work of the ObsPy group allows easy handling of a bunch of seismic data and PyLoT will benefit a lot compared to the former MatLab version. -The development of PyLoT is part of the joint research projekt MAGS2. +The development of PyLoT is part of the joint research project MAGS2. -author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT) -======= -developer(s): S. Wehling-Benatelli, L. Kueperkoch, M. Bischoff, M. Rische -others: A. Bruestle, T. Meier +staff: +====== + +original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT) + +developer(s): S. Wehling-Benatelli, L. Kueperkoch, K. Olbert, M. Bischoff, + C. Wollin, M. Rische + +others: A. Bruestle, T. Meier, W. Friederich release notes: ============== From 5d4084ac8a0dfa4dac67ecc77ed2383847cfaa0d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 8 Nov 2013 14:31:09 +0100 Subject: [PATCH 0004/1144] no message --- pylot/__init__.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 pylot/__init__.py diff --git a/pylot/__init__.py b/pylot/__init__.py new file mode 100644 index 00000000..1bd431cd --- /dev/null +++ b/pylot/__init__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +#-------------------------------------------------------- +# Purpose: Convience imports for PyLoT +# +''' +PyLoT - the Python picking and Localization Tool + +The Python picking and Localisation Tool + +This python library contains a graphical user interfaces for picking +seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki) +and the Qt4 libraries to be installed first. + +PILOT has been developed in Mathworks' MatLab. In order to distribute +PILOT without facing portability problems, it has been decided to re- +develop the software package in Python. The great work of the ObsPy +group allows easy handling of a bunch of seismic data and PyLoT will +benefit a lot compared to the former MatLab version. + +The development of PyLoT is part of the joint research project MAGS2. + +:copyright: + The PyLoT-Development Team +:license: + GNU Lesser General Public License, Version 3 + (http://www.gnu.org/copyleft/lesser.html) +''' + +import os.path as osp +from obspy.core import read From e92c00af48a63d0a65c2c4ba072970e9859eac5b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 8 Nov 2013 15:05:39 +0100 Subject: [PATCH 0005/1144] initial commit for the first blank files preparing git spare initialization on remote system --- pylot/QtPyLoT | 0 pylot/QtPyLoT.py | 0 pylot/__init__.py | 5 ++++- 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 pylot/QtPyLoT create mode 100644 pylot/QtPyLoT.py diff --git a/pylot/QtPyLoT b/pylot/QtPyLoT new file mode 100644 index 00000000..e69de29b diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py new file mode 100644 index 00000000..e69de29b diff --git a/pylot/__init__.py b/pylot/__init__.py index 1bd431cd..a4f85e15 100644 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -27,4 +27,7 @@ The development of PyLoT is part of the joint research project MAGS2. ''' import os.path as osp -from obspy.core import read +from obspy.core.utcdatetime import UTCDateTime +from obspy.core.util.attribdict import AttribDict +from obspy.core.trace import Stats, Trace +from obspy.core.stream import Stream, read From a484709bc4660f7db2bd387c47c94ccab3bcc326 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 8 Nov 2013 15:08:04 +0100 Subject: [PATCH 0006/1144] removing file created by accident --- pylot/QtPyLoT | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 pylot/QtPyLoT diff --git a/pylot/QtPyLoT b/pylot/QtPyLoT deleted file mode 100644 index e69de29b..00000000 From bf37a6546de8a5a355ffb86c1c6d6ef3e6294988 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 Nov 2013 06:02:43 +0100 Subject: [PATCH 0007/1144] =?UTF-8?q?Started=20to=20build=20the=20basic=20?= =?UTF-8?q?structure.=20Most=20classes=20will=20inherit=20functionality=20?= =?UTF-8?q?of=20the=20ObsPy=20Core=20classes,=20but=20slightly=20redefined?= =?UTF-8?q?=20to=20fit=20PyLoTs=E2=80=99=20purposes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pylot/__init__.py | 4 ++-- pylot/core/__init__.py | 1 + pylot/core/stream.py | 16 ++++++++++++++++ pylot/core/trace.py | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 pylot/core/__init__.py create mode 100644 pylot/core/stream.py create mode 100644 pylot/core/trace.py diff --git a/pylot/__init__.py b/pylot/__init__.py index a4f85e15..5f716ae7 100644 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -29,5 +29,5 @@ The development of PyLoT is part of the joint research project MAGS2. import os.path as osp from obspy.core.utcdatetime import UTCDateTime from obspy.core.util.attribdict import AttribDict -from obspy.core.trace import Stats, Trace -from obspy.core.stream import Stream, read +from pylot.core.trace import Stats, Trace +from pylot.core.stream import Stream, read diff --git a/pylot/core/__init__.py b/pylot/core/__init__.py new file mode 100644 index 00000000..4287ca86 --- /dev/null +++ b/pylot/core/__init__.py @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/pylot/core/stream.py b/pylot/core/stream.py new file mode 100644 index 00000000..d8e06f65 --- /dev/null +++ b/pylot/core/stream.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Nov 8 15:13:13 2013 + +@author: sebastianw +""" + +from obspy.core import Stream as Obspystream + + +class Stream(Obspystream): + pass + + +def read(): + pass diff --git a/pylot/core/trace.py b/pylot/core/trace.py new file mode 100644 index 00000000..71c68259 --- /dev/null +++ b/pylot/core/trace.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Nov 8 15:12:43 2013 + +@author: sebastianw +""" + +from obspy.core import Trace as Obspytrace +from obspy.core.util import AttribDict + + +class Stats(AttribDict): + pass + + +class Trace(Obspytrace): + pass From 8bf51c9315814422f20c9bc7e48783f156a1d0fa Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Dec 2013 13:25:05 +0100 Subject: [PATCH 0008/1144] added QtProject files. Testing the capabilities of QtDesigner for our purposes. Maybe it is better to design the GUI directly via PyQt/PySide instead of utilizing the QtDesigner and translate the *.ui file to *.py scripts. From 336572bfc3fd74a8099110fd0b249f0189080c77 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Dec 2013 13:28:13 +0100 Subject: [PATCH 0009/1144] stage all unstaged files From 33d10300f5fc9bafdc2d018d6942d1cd4bb1c317 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Dec 2013 13:31:14 +0100 Subject: [PATCH 0010/1144] commit staged changes --- README | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README b/README index abbea32b..01953a71 100644 --- a/README +++ b/README @@ -8,9 +8,9 @@ This python library contains a graphical user interfaces for picking seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki) and the Qt4 libraries to be installed first. -PILOT has been developed in Mathworks' MatLab. In order to distribute -PILOT without facing portability problems, it has been decided to re- -develop the software package in Python. The great work of the ObsPy +PILOT has originally been developed in Mathworks' MatLab. In order to +distribute PILOT without facing portability problems, it has been decided +to redevelop the software package in Python. The great work of the ObsPy group allows easy handling of a bunch of seismic data and PyLoT will benefit a lot compared to the former MatLab version. From 32a1c8bcd3ee25c117125a0acf0bd4fb78a53b5a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Dec 2013 13:34:01 +0100 Subject: [PATCH 0011/1144] commit master --- pylot/ui/PyLoT_main/PyLoT_main.pro | 20 ++++++++++ pylot/ui/PyLoT_main/main.cpp | 11 ++++++ pylot/ui/PyLoT_main/qt_pylot.cpp | 14 +++++++ pylot/ui/PyLoT_main/qt_pylot.h | 22 +++++++++++ pylot/ui/PyLoT_main/qt_pylot.ui | 62 ++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 pylot/ui/PyLoT_main/PyLoT_main.pro create mode 100644 pylot/ui/PyLoT_main/main.cpp create mode 100644 pylot/ui/PyLoT_main/qt_pylot.cpp create mode 100644 pylot/ui/PyLoT_main/qt_pylot.h create mode 100644 pylot/ui/PyLoT_main/qt_pylot.ui diff --git a/pylot/ui/PyLoT_main/PyLoT_main.pro b/pylot/ui/PyLoT_main/PyLoT_main.pro new file mode 100644 index 00000000..e119b23a --- /dev/null +++ b/pylot/ui/PyLoT_main/PyLoT_main.pro @@ -0,0 +1,20 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2013-12-09T13:16:21 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = PyLoT_main +TEMPLATE = app + + +SOURCES += main.cpp\ + qt_pylot.cpp + +HEADERS += qt_pylot.h + +FORMS += qt_pylot.ui diff --git a/pylot/ui/PyLoT_main/main.cpp b/pylot/ui/PyLoT_main/main.cpp new file mode 100644 index 00000000..49f1e337 --- /dev/null +++ b/pylot/ui/PyLoT_main/main.cpp @@ -0,0 +1,11 @@ +#include "qt_pylot.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + Qt_PyLoT w; + w.show(); + + return a.exec(); +} diff --git a/pylot/ui/PyLoT_main/qt_pylot.cpp b/pylot/ui/PyLoT_main/qt_pylot.cpp new file mode 100644 index 00000000..967482a0 --- /dev/null +++ b/pylot/ui/PyLoT_main/qt_pylot.cpp @@ -0,0 +1,14 @@ +#include "qt_pylot.h" +#include "ui_qt_pylot.h" + +Qt_PyLoT::Qt_PyLoT(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::Qt_PyLoT) +{ + ui->setupUi(this); +} + +Qt_PyLoT::~Qt_PyLoT() +{ + delete ui; +} diff --git a/pylot/ui/PyLoT_main/qt_pylot.h b/pylot/ui/PyLoT_main/qt_pylot.h new file mode 100644 index 00000000..5384d4ac --- /dev/null +++ b/pylot/ui/PyLoT_main/qt_pylot.h @@ -0,0 +1,22 @@ +#ifndef QT_PYLOT_H +#define QT_PYLOT_H + +#include + +namespace Ui { +class Qt_PyLoT; +} + +class Qt_PyLoT : public QMainWindow +{ + Q_OBJECT + +public: + explicit Qt_PyLoT(QWidget *parent = 0); + ~Qt_PyLoT(); + +private: + Ui::Qt_PyLoT *ui; +}; + +#endif // QT_PYLOT_H diff --git a/pylot/ui/PyLoT_main/qt_pylot.ui b/pylot/ui/PyLoT_main/qt_pylot.ui new file mode 100644 index 00000000..0b0b0da2 --- /dev/null +++ b/pylot/ui/PyLoT_main/qt_pylot.ui @@ -0,0 +1,62 @@ + + + Qt_PyLoT + + + + 0 + 0 + 1024 + 768 + + + + Qt_PyLoT + + + + + + 170 + 30 + 841 + 641 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + 1024 + 22 + + + + + + TopToolBarArea + + + false + + + + + + File + + + + + + + From e05c3d56bcba0e031d0479ad136637642f4ccfa4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 9 Jan 2014 10:43:40 +0100 Subject: [PATCH 0012/1144] initialized software versioning similar to obspy --- pylot/QtPyLoT.py | 53 +++++++++++++++++ pylot/RELEASE_VERSION | 1 + pylot/core/util/__init__.py | 1 + pylot/core/util/version.py | 111 ++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) mode change 100644 => 100755 pylot/QtPyLoT.py create mode 100644 pylot/RELEASE_VERSION create mode 100644 pylot/core/util/__init__.py create mode 100644 pylot/core/util/version.py diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py old mode 100644 new mode 100755 index e69de29b..9b8414bd --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -0,0 +1,53 @@ +# Main program: QtPyLoT.py +# +# +# +# +# +# +# +# +# +# +# +# + +import os +import platform +import sys +from PyQt4.QtCore import * +from PyQt4.QtGui import * +import helpform +from pylot.core.util import _getVersionString + +# Version information +__version__ = _getVersionString() + +# Creating a Qt application +pylot_app = QApplication(sys.argv) + +pylot_main = QWidget() +pylot_main.setWindowTitle('TestWindow') + +ok_btn = QPushButton('OK', pylot_main) + +@pyqtSlot() +def on_click(): + print('clicked') + +@pyqtSlot() +def on_press(): + print('pressed') + +@pyqtSlot() +def on_release(): + print('released') + +# Connect signals to slots +ok_btn.clicked.connect(on_click) +ok_btn.pressed.connect(on_press) +ok_btn.pressed.connect(on_release) + +# Show window and run the app +pylot_main.show() +pylot_app.exec_() diff --git a/pylot/RELEASE_VERSION b/pylot/RELEASE_VERSION new file mode 100644 index 00000000..58aa1fbf --- /dev/null +++ b/pylot/RELEASE_VERSION @@ -0,0 +1 @@ +0.1a1 diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py new file mode 100644 index 00000000..2d4a37e4 --- /dev/null +++ b/pylot/core/util/__init__.py @@ -0,0 +1 @@ +from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/version.py b/pylot/core/util/version.py new file mode 100644 index 00000000..5c87cccf --- /dev/null +++ b/pylot/core/util/version.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# Author: Douglas Creager +# This file is placed into the public domain. + +# Calculates the current version number. If possible, this is the +# output of “git describe”, modified to conform to the versioning +# scheme that setuptools uses. If “git describe” returns an error +# (most likely because we're in an unpacked copy of a release tarball, +# rather than in a git working copy), then we fall back on reading the +# contents of the RELEASE-VERSION file. +# +# To use this script, simply import it your setup.py file, and use the +# results of get_git_version() as your package version: +# +# from version import * +# +# setup( +# version=get_git_version(), +# . +# . +# . +# ) +# +# This will automatically update the RELEASE-VERSION file, if +# necessary. Note that the RELEASE-VERSION file should *not* be +# checked into git; please add it to your top-level .gitignore file. +# +# You'll probably want to distribute the RELEASE-VERSION file in your +# sdist tarballs; to do this, just create a MANIFEST.in file that +# contains the following line: +# +# include RELEASE-VERSION + +__all__ = ("get_git_version") + +# NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time) +import os +import inspect +from subprocess import Popen, PIPE +# NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time) + +script_dir = os.path.abspath(os.path.dirname(inspect.getfile( + inspect.currentframe()))) +PYLOT_ROOT = os.path.abspath(os.path.join(script_dir, os.pardir, + os.pardir, os.pardir)) +VERSION_FILE = os.path.join(PYLOT_ROOT, "pylot", "RELEASE-VERSION") + + +def call_git_describe(abbrev=4): + try: + p = Popen(['git', 'rev-parse', '--show-toplevel'], + cwd=PYLOT_ROOT, stdout=PIPE, stderr=PIPE) + p.stderr.close() + path = p.stdout.readlines()[0].strip() + except: + return None + if os.path.normpath(path) != PYLOT_ROOT: + return None + try: + p = Popen(['git', 'describe', '--dirty', '--abbrev=%d' % abbrev, + '--always'], + cwd=PYLOT_ROOT, stdout=PIPE, stderr=PIPE) + p.stderr.close() + line = p.stdout.readlines()[0] + # (this line prevents official releases) + if "-" not in line and "." not in line: + line = "0.0.0-g%s" % line + return line.strip() + except: + return None + + +def read_release_version(): + try: + version = open(VERSION_FILE, "r").readlines()[0] + return version.strip() + except: + return None + + +def write_release_version(version): + open(VERSION_FILE, "w").write("%s\n" % version) + + +def get_git_version(abbrev=4): + # Read in the version that's currently in RELEASE-VERSION. + release_version = read_release_version() + + # First try to get the current version using “git describe”. + version = call_git_describe(abbrev) + + # If that doesn't work, fall back on the value that's in + # RELEASE-VERSION. + if version is None: + version = release_version + + # If we still don't have anything, that's an error. + if version is None: + return '0.0.0-tar/zipball' + + # If the current version is different from what's in the + # RELEASE-VERSION file, update the file to be current. + if version != release_version: + write_release_version(version) + + # Finally, return the current version. + return version + + +if __name__ == "__main__": + print get_git_version() \ No newline at end of file From dcc38817202fe7af5b836e480703562fbada08a8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 10 Jan 2014 05:45:03 +0100 Subject: [PATCH 0013/1144] permissions repaired and QtPyLoT.py started with some MainWindow stuff --- pylot/QtPyLoT.py | 8 ++++++++ pylot/RELEASE-VERSION | 1 + pylot/RELEASE_VERSION | 1 - pylot/__init__.py | 0 pylot/core/__init__.py | 0 pylot/core/stream.py | 0 pylot/core/trace.py | 0 pylot/core/util/__init__.py | 0 pylot/core/util/version.py | 0 9 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 pylot/RELEASE-VERSION delete mode 100644 pylot/RELEASE_VERSION mode change 100644 => 100755 pylot/__init__.py mode change 100644 => 100755 pylot/core/__init__.py mode change 100644 => 100755 pylot/core/stream.py mode change 100644 => 100755 pylot/core/trace.py mode change 100644 => 100755 pylot/core/util/__init__.py mode change 100644 => 100755 pylot/core/util/version.py diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 9b8414bd..4c04629c 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -23,6 +23,14 @@ from pylot.core.util import _getVersionString # Version information __version__ = _getVersionString() +class MainWindow(QMainWindow): + + def __init__(self, parent=None): + super(MainWindow, self).__init__(parent) + + self.Stream = None + + # Creating a Qt application pylot_app = QApplication(sys.argv) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION new file mode 100644 index 00000000..b7161198 --- /dev/null +++ b/pylot/RELEASE-VERSION @@ -0,0 +1 @@ +0.1a1 \ No newline at end of file diff --git a/pylot/RELEASE_VERSION b/pylot/RELEASE_VERSION deleted file mode 100644 index 58aa1fbf..00000000 --- a/pylot/RELEASE_VERSION +++ /dev/null @@ -1 +0,0 @@ -0.1a1 diff --git a/pylot/__init__.py b/pylot/__init__.py old mode 100644 new mode 100755 diff --git a/pylot/core/__init__.py b/pylot/core/__init__.py old mode 100644 new mode 100755 diff --git a/pylot/core/stream.py b/pylot/core/stream.py old mode 100644 new mode 100755 diff --git a/pylot/core/trace.py b/pylot/core/trace.py old mode 100644 new mode 100755 diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py old mode 100644 new mode 100755 diff --git a/pylot/core/util/version.py b/pylot/core/util/version.py old mode 100644 new mode 100755 From aaf04a13d5001973fc259590845a46e20098c578 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Fri, 24 Jan 2014 14:31:57 +0100 Subject: [PATCH 0014/1144] started to write initialization methods for Dialogs, Windows and other Widgets --- pylot/QtPyLoT.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 4c04629c..3c658ee1 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -28,8 +28,33 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) - self.Stream = None + filterDockWidget = QDockWidget("Filter Options", self) + + + class PickWindow(QDialog): + def __init__(self, station=None, parent=None): + super(PickWindow, self).__init__(parent) + + filterDockWidget = FilterOptionsDock() + +class PropertiesWindow(QDialog): + + def __init__(self, parent=None): + super(PropertiesWindow, self).__init__(parent) + +class FilterOptionsDock(QDockWidget): + + def __init__(self, filter=None): + super(FilterOptionsDock, self).__init__() + + if filter is not None and filter isinstance(FilterOptions): + for key, value in filter.iteritems(): + +class FilterOptions(): + + def __init__(): + # Creating a Qt application pylot_app = QApplication(sys.argv) From 0c1e64895b4d79093774f3798189bae667433dd1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 28 Jan 2014 12:07:23 +0100 Subject: [PATCH 0015/1144] added collection class FilterOptions for filter option handling (container class in order to keep GUI up-to-date) --- pylot/QtPyLoT.py | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 3c658ee1..a743592e 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -45,16 +45,46 @@ class PropertiesWindow(QDialog): class FilterOptionsDock(QDockWidget): - def __init__(self, filter=None): + def __init__(self, filterOptions=None): super(FilterOptionsDock, self).__init__() - if filter is not None and filter isinstance(FilterOptions): - for key, value in filter.iteritems(): + if filterOptions and not isinstance(filterOptions, FilterOptions): + try: + fOptions = FilterOptions(filterOptions) + except e: + raise OptionsError, '%s' % e -class FilterOptions(): +class FilterOptions(object): - def __init__(): - + def __init__(self, filtertype=None, freq=None, order=None): + self.__filterInformation = {} + self._setfilterType(filtertype) + self._setFreq(freq) + self._setOrder(order) + + def _getFreq(self): + return self.__filterInformation['freq'] + + def _setFreq(self, freq): + self.__filterInformation['freq'] = freq + + def _getOrder(self): + return self.__filterInformation['order'] + + def _setOrder(self, order): + self.__filterInformation['order'] = order + + def _getFilterType(self): + return self.__filterInformation['filtertype'] + + def _setFilterType(self, filtertype): + self.__filterInformation['filtertype'] = filtertype + + filterType = property(fget=_getFilterType, fset=_setFilterType) + order = property(fget=_getOrder, fset=_setOrder) + freq = property(fget=_getFreq, fset=_setFreq) + +class OptionsError(Exception): pass # Creating a Qt application pylot_app = QApplication(sys.argv) From 25351d38274ae7a0f9a8889dd8207ac9a0d6c6cf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 30 Jan 2014 13:11:44 +0100 Subject: [PATCH 0016/1144] changes made during workshop --- pylot/QtPyLoT.py | 9 +++++---- pylot/core/stream.py | 3 --- pylot/core/trace.py | 1 + 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index a743592e..de2e0e4b 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -86,11 +86,12 @@ class FilterOptions(object): class OptionsError(Exception): pass -# Creating a Qt application -pylot_app = QApplication(sys.argv) +if __name__ == '__main__': + ##Creating a Qt application + pylot_app = QApplication(sys.argv) -pylot_main = QWidget() -pylot_main.setWindowTitle('TestWindow') + pylot_main = MainWindow() + pylot_main.setWindowTitle('TestWindow') ok_btn = QPushButton('OK', pylot_main) diff --git a/pylot/core/stream.py b/pylot/core/stream.py index d8e06f65..fcfc41b0 100755 --- a/pylot/core/stream.py +++ b/pylot/core/stream.py @@ -11,6 +11,3 @@ from obspy.core import Stream as Obspystream class Stream(Obspystream): pass - -def read(): - pass diff --git a/pylot/core/trace.py b/pylot/core/trace.py index 71c68259..8f961d30 100755 --- a/pylot/core/trace.py +++ b/pylot/core/trace.py @@ -15,3 +15,4 @@ class Stats(AttribDict): class Trace(Obspytrace): pass + \ No newline at end of file From d016a80a7247eddd94ec6f3de41bca0b12669e50 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Mon, 3 Feb 2014 12:51:23 +0100 Subject: [PATCH 0017/1144] classes for I/O started, QtPyLoT.py cleaned --- pylot/QtPyLoT.py | 111 +++++++++++++------------------- pylot/core/readdata/__init__.py | 0 pylot/core/readdata/types.py | 14 ++++ 3 files changed, 60 insertions(+), 65 deletions(-) create mode 100644 pylot/core/readdata/__init__.py create mode 100644 pylot/core/readdata/types.py diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index de2e0e4b..1036fcdd 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -33,57 +33,57 @@ class MainWindow(QMainWindow): class PickWindow(QDialog): - def __init__(self, station=None, parent=None): - super(PickWindow, self).__init__(parent) - - filterDockWidget = FilterOptionsDock() + def __init__(self, station=None, parent=None): + super(PickWindow, self).__init__(parent) + + filterDockWidget = FilterOptionsDock() class PropertiesWindow(QDialog): - - def __init__(self, parent=None): - super(PropertiesWindow, self).__init__(parent) + + def __init__(self, parent=None): + super(PropertiesWindow, self).__init__(parent) class FilterOptionsDock(QDockWidget): - - def __init__(self, filterOptions=None): - super(FilterOptionsDock, self).__init__() - - if filterOptions and not isinstance(filterOptions, FilterOptions): - try: - fOptions = FilterOptions(filterOptions) - except e: - raise OptionsError, '%s' % e + + def __init__(self, filterOptions=None): + super(FilterOptionsDock, self).__init__() + + if filterOptions and not isinstance(filterOptions, FilterOptions): + try: + fOptions = FilterOptions(filterOptions) + except e: + raise OptionsError, '%s' % e class FilterOptions(object): - - def __init__(self, filtertype=None, freq=None, order=None): - self.__filterInformation = {} - self._setfilterType(filtertype) - self._setFreq(freq) - self._setOrder(order) + + def __init__(self, filtertype=None, freq=None, order=None): + self.__filterInformation = {} + self._setfilterType(filtertype) + self._setFreq(freq) + self._setOrder(order) - def _getFreq(self): - return self.__filterInformation['freq'] - - def _setFreq(self, freq): - self.__filterInformation['freq'] = freq + def _getFreq(self): + return self.__filterInformation['freq'] + + def _setFreq(self, freq): + self.__filterInformation['freq'] = freq - def _getOrder(self): - return self.__filterInformation['order'] - - def _setOrder(self, order): - self.__filterInformation['order'] = order - - def _getFilterType(self): - return self.__filterInformation['filtertype'] - - def _setFilterType(self, filtertype): - self.__filterInformation['filtertype'] = filtertype - - filterType = property(fget=_getFilterType, fset=_setFilterType) - order = property(fget=_getOrder, fset=_setOrder) - freq = property(fget=_getFreq, fset=_setFreq) + def _getOrder(self): + return self.__filterInformation['order'] + + def _setOrder(self, order): + self.__filterInformation['order'] = order + + def _getFilterType(self): + return self.__filterInformation['filtertype'] + def _setFilterType(self, filtertype): + self.__filterInformation['filtertype'] = filtertype + + filterType = property(fget=_getFilterType, fset=_setFilterType) + order = property(fget=_getOrder, fset=_setOrder) + freq = property(fget=_getFreq, fset=_setFreq) + class OptionsError(Exception): pass if __name__ == '__main__': @@ -91,27 +91,8 @@ if __name__ == '__main__': pylot_app = QApplication(sys.argv) pylot_main = MainWindow() - pylot_main.setWindowTitle('TestWindow') + pylot_main.setWindowTitle('PyLoT-The Picking and Localization Tool') -ok_btn = QPushButton('OK', pylot_main) - -@pyqtSlot() -def on_click(): - print('clicked') - -@pyqtSlot() -def on_press(): - print('pressed') - -@pyqtSlot() -def on_release(): - print('released') - -# Connect signals to slots -ok_btn.clicked.connect(on_click) -ok_btn.pressed.connect(on_press) -ok_btn.pressed.connect(on_release) - -# Show window and run the app -pylot_main.show() -pylot_app.exec_() + # Show window and run the app + pylot_main.show() + pylot_app.exec_() diff --git a/pylot/core/readdata/__init__.py b/pylot/core/readdata/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pylot/core/readdata/types.py b/pylot/core/readdata/types.py new file mode 100644 index 00000000..eed500c7 --- /dev/null +++ b/pylot/core/readdata/types.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# +# Provide user the opportunity to read arbitrary organized database +# types. This means e.g. seiscomp data structure (SDS) or event based +# EGELADOS structure. +# + +class GenericDataBase(object): + def __init__(self, root=None, kwargs**): + pass + + +class SeiscompDataStructure(GenericDataBase): + pass From 738f280abe9506e58b1e767b7093a039921e09a3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 7 Feb 2014 05:40:49 +0100 Subject: [PATCH 0018/1144] started to implement a filter Widget --- pylot/QtPyLoT.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 1036fcdd..34b5375a 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -28,8 +28,11 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) - filterDockWidget = QDockWidget("Filter Options", self) - + filterDefaults + self.filterOptions = FilterOptions(filterDefaults) + + filterDockWidget = FilterOptionsDock("Filter Options", self) + class PickWindow(QDialog): @@ -45,7 +48,7 @@ class PropertiesWindow(QDialog): class FilterOptionsDock(QDockWidget): - def __init__(self, filterOptions=None): + def __init__(self, titleString="Filter options", filterOptions=None): super(FilterOptionsDock, self).__init__() if filterOptions and not isinstance(filterOptions, FilterOptions): From 1a4d306a40ee8f326a0841d73157f793b735b306 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 7 Feb 2014 05:42:31 +0100 Subject: [PATCH 0019/1144] initialized readinput module; there will be type classes to handle different input data types, e.g. inputs for automatic Picking, inputs for correlation detection, input for reference Picking ... --- pylot/core/readinput/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pylot/core/readinput/__init__.py diff --git a/pylot/core/readinput/__init__.py b/pylot/core/readinput/__init__.py new file mode 100644 index 00000000..e69de29b From a4f948fd765a85cb84eef67639c8cc11db81a0d1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Tue, 11 Feb 2014 13:21:05 +0100 Subject: [PATCH 0020/1144] modified initialization method of GenericDataBase class --- pylot/core/readdata/types.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pylot/core/readdata/types.py b/pylot/core/readdata/types.py index eed500c7..811d8f6f 100644 --- a/pylot/core/readdata/types.py +++ b/pylot/core/readdata/types.py @@ -5,9 +5,19 @@ # EGELADOS structure. # +import os + + class GenericDataBase(object): - def __init__(self, root=None, kwargs**): - pass + ''' + GenericDataBase type holds all information about the current data- + base working on. + ''' + def __init__(self, stexp=None, **kwargs): + structExpression = os.path.split(stexp) + self.dataBaseDict = kwargs + + class SeiscompDataStructure(GenericDataBase): From 4aa785626762bc3a2b8a1fff22d2df39b6907f3c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 12 Feb 2014 14:18:12 +0100 Subject: [PATCH 0021/1144] started initialization of SDS data organization scheme --- pylot/core/readdata/types.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pylot/core/readdata/types.py b/pylot/core/readdata/types.py index 811d8f6f..bef1ba45 100644 --- a/pylot/core/readdata/types.py +++ b/pylot/core/readdata/types.py @@ -14,11 +14,36 @@ class GenericDataBase(object): base working on. ''' def __init__(self, stexp=None, **kwargs): + dbRegExp = { + structExpression = os.path.split(stexp) - self.dataBaseDict = kwargs + self.dataBaseDict = kwargs class SeiscompDataStructure(GenericDataBase): - pass + # Data type options + typeOptions = {'waveform':'D', #Waveform data + 'detect':'E', #Detection data + 'log':'L', #Log data + 'timing':'T', #Timing data + 'calib':'C', #Calibration data + 'resp':'R', #Response data + 'opaque':'O' #Opaque data + } + # SDS fields' default values + # definitions from + # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS + sdsFields = {'SDSdir':['data','SDS'], #base directory list + 'YEAR':'1970', #4 digits + 'NET':'XX', #up to 8 characters + 'STA':'XXXX', #up to 8 characters + 'CHAN':'HHZ', #up to 8 characters + 'TYPE':typeOption['waveform'], #1 character + 'LOC':'', #up to 8 characters + 'DAY':'001' #3 digit day of year + } + def __init__(self, **kwargs): + + From 34c1f9111ba0bc3a88c94236555e6adace44f1bf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 13 Feb 2014 14:28:41 +0100 Subject: [PATCH 0022/1144] initial AutoPickParameter class import. Attributes are not callable at the moment (implementation pending). --- pylot/core/readinput/types.py | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 pylot/core/readinput/types.py diff --git a/pylot/core/readinput/types.py b/pylot/core/readinput/types.py new file mode 100644 index 00000000..6c426472 --- /dev/null +++ b/pylot/core/readinput/types.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on Thu Feb 13 09:58:45 2014 + +@author: sebastianw +""" + + +class AutoPickParameters(object): + ''' + AutoPickParameters is a parameter type object capable to read and/or write + parameter ASCII and binary files. Parameters are given by example: + phl S #phaselabel + ff1 0.1 #freqmin + ff2 0.5 #freqmax + tdet 6.875 #det-window_(s)_for_ar + tpred 2.5 #pred-window_(s)_for_ar + order 4 #order_of_ar + fnoise 0 #noise_level_for_ar + suppp 7 #envelopecoeff + tolt 300 #(s)time_int_around_the_arrival_time_of_the_phase_to_analyze + f1tpwt 4 #propfact_minfreq_secondtaper + pickwindow 9 #length_of_pick_window + w1 1 #length_of_smoothing_window + w2 0.37 #cf(i-1)*(1+peps)_for_local_min + w3 0.25 #cf(i-1)*(1+peps)_for_local_min + tslope 0.8;2 #slope_det_window_loc_glob + aerr 30;60 #adjusted_error_slope_fitting_loc_glob + tsn 20;5;20;10 #length_signal_window_S/N + proPh Sn #nextprominentphase + ''' + + def __init__(self, fn): + ''' + Initialize parameter object: + + read content of an ASCII file an form a type consistent dictionary + contain all parameters. + ''' + + parFileCont = {} + try: + inputFile = open(fn, 'r') + lines = inputFile.readlines() + for line in lines: + parspl = line.split('\t')[:2] + parFileCont[parspl[0]] = parspl[1] + for key, value in parFileCont.iteritems(): + try: + val = int(value) + except: + try: + val = float(value) + except: + if value.find(';') > 0: + vallist = value.strip().split(';') + val = [] + for val0 in vallist: + val0 = float(val0) + val.append(val0) + else: + val = str(value.strip()) + parFileCont[key] = val + except e: + raise 'ParameterError:\n %s' % e + finally: + parFileCont = None + self.__parameter = parFileCont + + def __str__(self): + pass + + def __repr__(self): + reprString = '' + reprString += 'Automated picking parameter:\n\n' + if self.__parameter is not None: + for key, value in self.__parameter.iteritems(): + reprString += '%s:\t\t%s' % (key, value) + else: + reprString += 'Empty parameter dictionary.' From 5a093ed736e48b90f18d9d06fb1959a6194fc905 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 15 Feb 2014 08:09:55 +0100 Subject: [PATCH 0023/1144] AutoPickParamter class modified; not working at the moment (!) --- pylot/core/readinput/types.py | 88 +++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/pylot/core/readinput/types.py b/pylot/core/readinput/types.py index 6c426472..65dbecf6 100644 --- a/pylot/core/readinput/types.py +++ b/pylot/core/readinput/types.py @@ -7,28 +7,28 @@ Created on Thu Feb 13 09:58:45 2014 """ -class AutoPickParameters(object): +class AutoPickParameter(object): ''' AutoPickParameters is a parameter type object capable to read and/or write parameter ASCII and binary files. Parameters are given by example: - phl S #phaselabel - ff1 0.1 #freqmin - ff2 0.5 #freqmax - tdet 6.875 #det-window_(s)_for_ar - tpred 2.5 #pred-window_(s)_for_ar - order 4 #order_of_ar - fnoise 0 #noise_level_for_ar - suppp 7 #envelopecoeff - tolt 300 #(s)time_int_around_the_arrival_time_of_the_phase_to_analyze - f1tpwt 4 #propfact_minfreq_secondtaper - pickwindow 9 #length_of_pick_window - w1 1 #length_of_smoothing_window - w2 0.37 #cf(i-1)*(1+peps)_for_local_min - w3 0.25 #cf(i-1)*(1+peps)_for_local_min - tslope 0.8;2 #slope_det_window_loc_glob - aerr 30;60 #adjusted_error_slope_fitting_loc_glob - tsn 20;5;20;10 #length_signal_window_S/N - proPh Sn #nextprominentphase + phl S # phaselabel + ff1 0.1 # freqmin + ff2 0.5 # freqmax + tdet 6.875 # det-window_(s)_for_ar + tpred 2.5 # pred-window_(s)_for_ar + order 4 # order_of_ar + fnoise 0 # noise_level_for_ar + suppp 7 # envelopecoeff + tolt 300 # (s)time around arrival time + f1tpwt 4 # propfact_minfreq_secondtaper + pickwindow 9 # length_of_pick_window + w1 1 # length_of_smoothing_window + w2 0.37 # cf(i-1)*(1+peps)_for_local_min + w3 0.25 # cf(i-1)*(1+peps)_for_local_min + tslope 0.8;2 # slope_det_window_loc_glob + aerr 30;60 # adjusted_error_slope_fitting_loc_glob + tsn 20;5;20;10 # length_signal_window_S/N + proPh Sn # nextprominentphase ''' def __init__(self, fn): @@ -38,10 +38,10 @@ class AutoPickParameters(object): read content of an ASCII file an form a type consistent dictionary contain all parameters. ''' - + self.filename = fn parFileCont = {} try: - inputFile = open(fn, 'r') + inputFile = open(self.filename, 'r') lines = inputFile.readlines() for line in lines: parspl = line.split('\t')[:2] @@ -62,20 +62,50 @@ class AutoPickParameters(object): else: val = str(value.strip()) parFileCont[key] = val - except e: + except Exception, e: raise 'ParameterError:\n %s' % e finally: parFileCont = None self.__parameter = parFileCont def __str__(self): - pass - - def __repr__(self): - reprString = '' - reprString += 'Automated picking parameter:\n\n' + string = '' + string += 'Automated picking parameter:\n\n' if self.__parameter is not None: for key, value in self.__parameter.iteritems(): - reprString += '%s:\t\t%s' % (key, value) + string += '%s:\t\t%s' % (key, value) else: - reprString += 'Empty parameter dictionary.' + string += 'Empty parameter dictionary.' + return string + + def __repr__(self): + return 'AutoPickParameter(%s)' % self.filename + + def __nonzero__(self): + return self.__parameter + + def getParam(self, *args): + try: + for param in args: + try: + return self.__parameter[param] + except KeyError, e: + raise 'ParameterError:\n %s' % e + except TypeError: + try: + return self.__parameter[param] + except KeyError, e: + raise 'ParameterError:\n %s' % e + + def setParam(self, **kwargs): + for param, value in kwargs: + try: + self.__parameter[param] = value + except KeyError, e: + raise 'ParameterError:\n %s' % e + finally: + self.__str__() + + +class ParameterError(Exception): + pass From f9d8173b2fa6770bde7efba02ed10d95a63cd095 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 18 Feb 2014 09:33:36 +0100 Subject: [PATCH 0024/1144] class AutoPickParameter is now working; work on ticket #119 not finished yet (closure pending) --- pylot/core/readinput/types.py | 76 ++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/pylot/core/readinput/types.py b/pylot/core/readinput/types.py index 65dbecf6..61894a52 100644 --- a/pylot/core/readinput/types.py +++ b/pylot/core/readinput/types.py @@ -10,7 +10,10 @@ Created on Thu Feb 13 09:58:45 2014 class AutoPickParameter(object): ''' AutoPickParameters is a parameter type object capable to read and/or write - parameter ASCII and binary files. Parameters are given by example: + parameter ASCII and binary files. + + Parameters are given for example as follows: + phl S # phaselabel ff1 0.1 # freqmin ff2 0.5 # freqmax @@ -31,7 +34,7 @@ class AutoPickParameter(object): proPh Sn # nextprominentphase ''' - def __init__(self, fn): + def __init__(self, fn=None): ''' Initialize parameter object: @@ -41,45 +44,47 @@ class AutoPickParameter(object): self.filename = fn parFileCont = {} try: - inputFile = open(self.filename, 'r') - lines = inputFile.readlines() - for line in lines: - parspl = line.split('\t')[:2] - parFileCont[parspl[0]] = parspl[1] - for key, value in parFileCont.iteritems(): - try: - val = int(value) - except: + if self.filename is not None: + inputFile = open(self.filename, 'r') + lines = inputFile.readlines() + for line in lines: + parspl = line.split('\t')[:2] + parFileCont[parspl[0].strip()] = parspl[1] + for key, value in parFileCont.iteritems(): try: - val = float(value) + val = int(value) except: - if value.find(';') > 0: - vallist = value.strip().split(';') - val = [] - for val0 in vallist: - val0 = float(val0) - val.append(val0) - else: - val = str(value.strip()) - parFileCont[key] = val + try: + val = float(value) + except: + if value.find(';') > 0: + vallist = value.strip().split(';') + val = [] + for val0 in vallist: + val0 = float(val0) + val.append(val0) + else: + val = str(value.strip()) + parFileCont[key] = val + else: + parFileCont = {} except Exception, e: - raise 'ParameterError:\n %s' % e - finally: - parFileCont = None + self._printParameterError(e) + parFileCont = {} self.__parameter = parFileCont def __str__(self): string = '' string += 'Automated picking parameter:\n\n' - if self.__parameter is not None: + if self.__parameter: for key, value in self.__parameter.iteritems(): - string += '%s:\t\t%s' % (key, value) + string += '%s:\t\t%s\n' % (key, value) else: string += 'Empty parameter dictionary.' return string def __repr__(self): - return 'AutoPickParameter(%s)' % self.filename + return "AutoPickParameter('%s')" % self.filename def __nonzero__(self): return self.__parameter @@ -90,22 +95,21 @@ class AutoPickParameter(object): try: return self.__parameter[param] except KeyError, e: - raise 'ParameterError:\n %s' % e + self._printParameterError(e) except TypeError: try: - return self.__parameter[param] + return self.__parameter[args] except KeyError, e: - raise 'ParameterError:\n %s' % e + self._printParameterError(e) def setParam(self, **kwargs): - for param, value in kwargs: + for param, value in kwargs.iteritems(): try: self.__parameter[param] = value except KeyError, e: - raise 'ParameterError:\n %s' % e + self._printParameterError(e) finally: - self.__str__() + print self - -class ParameterError(Exception): - pass + def _printParameterError(self, errmsg): + print 'ParameterError:\n non-existent parameter %s' % errmsg From c3ec80d9472326fda1a465432df553b9390ed6f6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 18 Feb 2014 09:35:55 +0100 Subject: [PATCH 0025/1144] planned imports of PyLoT of not yet existing PyLoT classes commented from pylot/__init__.py --- pylot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/__init__.py b/pylot/__init__.py index 5f716ae7..1297ecab 100755 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -29,5 +29,5 @@ The development of PyLoT is part of the joint research project MAGS2. import os.path as osp from obspy.core.utcdatetime import UTCDateTime from obspy.core.util.attribdict import AttribDict -from pylot.core.trace import Stats, Trace -from pylot.core.stream import Stream, read +#from pylot.core.trace import Stats, Trace +#from pylot.core.stream import Stream, read From ede06550cd93df677c0463292c1bea480a836b05 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 18 Feb 2014 09:38:40 +0100 Subject: [PATCH 0026/1144] reorganized initialization to IO-type class SeisCompDataStructure; see also ticket #118 --- pylot/core/readdata/types.py | 73 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/pylot/core/readdata/types.py b/pylot/core/readdata/types.py index bef1ba45..619e4547 100644 --- a/pylot/core/readdata/types.py +++ b/pylot/core/readdata/types.py @@ -2,48 +2,49 @@ # # Provide user the opportunity to read arbitrary organized database # types. This means e.g. seiscomp data structure (SDS) or event based -# EGELADOS structure. +# EGELADOS structure. # import os class GenericDataBase(object): - ''' - GenericDataBase type holds all information about the current data- - base working on. - ''' - def __init__(self, stexp=None, **kwargs): - dbRegExp = { - - structExpression = os.path.split(stexp) - self.dataBaseDict = kwargs - - - + ''' + GenericDataBase type holds all information about the current data- + base working on. + ''' + def __init__(self, stexp=None, **kwargs): + dbRegExp = {} + + structExpression = os.path.split(stexp) + self.dataBaseDict = kwargs + class SeiscompDataStructure(GenericDataBase): - # Data type options - typeOptions = {'waveform':'D', #Waveform data - 'detect':'E', #Detection data - 'log':'L', #Log data - 'timing':'T', #Timing data - 'calib':'C', #Calibration data - 'resp':'R', #Response data - 'opaque':'O' #Opaque data - } - # SDS fields' default values - # definitions from - # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS - sdsFields = {'SDSdir':['data','SDS'], #base directory list - 'YEAR':'1970', #4 digits - 'NET':'XX', #up to 8 characters - 'STA':'XXXX', #up to 8 characters - 'CHAN':'HHZ', #up to 8 characters - 'TYPE':typeOption['waveform'], #1 character - 'LOC':'', #up to 8 characters - 'DAY':'001' #3 digit day of year - } - def __init__(self, **kwargs): - + def __init__(self, **kwargs): + # Data type options + self.__typeOptions = {'waveform': 'D', # Waveform data + 'detect': 'E', # Detection data + 'log': 'L', # Log data + 'timing': 'T', # Timing data + 'calib': 'C', # Calibration data + 'resp': 'R', # Response data + 'opaque': 'O' # Opaque data + } + # SDS fields' default values + # definitions from + # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS + self.__sdsFields = {'SDSdir': ['data', 'SDS'], # base directory list + 'YEAR': '1970', # 4 digits + 'NET': 'XX', # up to 8 characters + 'STA': 'XXXX', # up to 8 characters + 'CHAN': 'HHZ', # up to 8 characters + 'TYPE': self.getType('waveform'), # 1 character + 'LOC': '', # up to 8 characters + 'DAY': '001' # 3 digit day of year + } + pass + + def getType(self, typeName=None): + pass From 98c50e5312a98a74957597329720c1649e12a938 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Tue, 18 Feb 2014 14:16:55 +0100 Subject: [PATCH 0027/1144] made some modifications on special methods --- pylot/core/readinput/types.py | 109 +++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/pylot/core/readinput/types.py b/pylot/core/readinput/types.py index 61894a52..464beab0 100644 --- a/pylot/core/readinput/types.py +++ b/pylot/core/readinput/types.py @@ -1,11 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -""" -Created on Thu Feb 13 09:58:45 2014 - -@author: sebastianw -""" - class AutoPickParameter(object): ''' @@ -41,11 +35,68 @@ class AutoPickParameter(object): read content of an ASCII file an form a type consistent dictionary contain all parameters. ''' - self.filename = fn - parFileCont = {} + self.__filename = fn + self.__parameter = self._readParameterFile() + + # Human-readable string representation of the object + def __str__(self): + string = '' + string += 'Automated picking parameter:\n\n' + if self.__parameter: + for key, value in self.__parameter.iteritems(): + string += '%s:\t\t%s\n' % (key, value) + else: + string += 'Empty parameter dictionary.' + return string + + # String representation of the object + def __repr__(self): + return "AutoPickParameter('%s')" % self.__filename + + # Boolean test + def __nonzero__(self): + return self.__parameter + + def __getitem__(self, key): + return self.getParam(key) + + def getParam(self, *args): + try: + for param in args: + try: + return self.__parameter[param] + except KeyError, e: + self._printParameterError(e) + except TypeError: + try: + return self.__parameter[args] + except KeyError, e: + self._printParameterError(e) + + def __setitem__(self, key, value): + kwargs = {} + kwargs[key] = value + kwargs['directcall'] = True + self.setParam(**kwargs) + + def setParam(self, directcall=None, **kwargs): + for param, value in kwargs.iteritems(): + try: + self.__parameter[param] = value + except KeyError, e: + self._printParameterError(e) + finally: + if not directcall: + print self + + def __len__(self): + return len(self.__parameter.keys()) + + def _readParameterFile(self): + parFileCont = {} try: if self.filename is not None: - inputFile = open(self.filename, 'r') + inputFile = open(self.__filename, 'r') lines = inputFile.readlines() for line in lines: parspl = line.split('\t')[:2] @@ -71,45 +122,7 @@ class AutoPickParameter(object): except Exception, e: self._printParameterError(e) parFileCont = {} - self.__parameter = parFileCont - - def __str__(self): - string = '' - string += 'Automated picking parameter:\n\n' - if self.__parameter: - for key, value in self.__parameter.iteritems(): - string += '%s:\t\t%s\n' % (key, value) - else: - string += 'Empty parameter dictionary.' - return string - - def __repr__(self): - return "AutoPickParameter('%s')" % self.filename - - def __nonzero__(self): - return self.__parameter - - def getParam(self, *args): - try: - for param in args: - try: - return self.__parameter[param] - except KeyError, e: - self._printParameterError(e) - except TypeError: - try: - return self.__parameter[args] - except KeyError, e: - self._printParameterError(e) - - def setParam(self, **kwargs): - for param, value in kwargs.iteritems(): - try: - self.__parameter[param] = value - except KeyError, e: - self._printParameterError(e) - finally: - print self + return parFileCont def _printParameterError(self, errmsg): print 'ParameterError:\n non-existent parameter %s' % errmsg From 45999de6d2b5424a96000b99866bbec1825d8cfc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Tue, 18 Feb 2014 15:07:50 +0100 Subject: [PATCH 0028/1144] made some modifications (debugging special method implementation) item assignment not working yet --- pylot/core/readinput/types.py | 68 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/pylot/core/readinput/types.py b/pylot/core/readinput/types.py index 464beab0..50e527bc 100644 --- a/pylot/core/readinput/types.py +++ b/pylot/core/readinput/types.py @@ -36,7 +36,36 @@ class AutoPickParameter(object): contain all parameters. ''' self.__filename = fn - self.__parameter = self._readParameterFile() + parFileCont = {} + try: + if self.__filename is not None: + inputFile = open(self.__filename, 'r') + lines = inputFile.readlines() + for line in lines: + parspl = line.split('\t')[:2] + parFileCont[parspl[0].strip()] = parspl[1] + for key, value in parFileCont.iteritems(): + try: + val = int(value) + except: + try: + val = float(value) + except: + if value.find(';') > 0: + vallist = value.strip().split(';') + val = [] + for val0 in vallist: + val0 = float(val0) + val.append(val0) + else: + val = str(value.strip()) + parFileCont[key] = val + else: + parFileCont = {} + except Exception, e: + self._printParameterError(e) + parFileCont = {} + self.__parameter = parFileCont # Human-readable string representation of the object def __str__(self): @@ -85,44 +114,11 @@ class AutoPickParameter(object): self.__parameter[param] = value except KeyError, e: self._printParameterError(e) - finally: - if not directcall: - print self + if not directcall: + print self def __len__(self): return len(self.__parameter.keys()) - def _readParameterFile(self): - parFileCont = {} - try: - if self.filename is not None: - inputFile = open(self.__filename, 'r') - lines = inputFile.readlines() - for line in lines: - parspl = line.split('\t')[:2] - parFileCont[parspl[0].strip()] = parspl[1] - for key, value in parFileCont.iteritems(): - try: - val = int(value) - except: - try: - val = float(value) - except: - if value.find(';') > 0: - vallist = value.strip().split(';') - val = [] - for val0 in vallist: - val0 = float(val0) - val.append(val0) - else: - val = str(value.strip()) - parFileCont[key] = val - else: - parFileCont = {} - except Exception, e: - self._printParameterError(e) - parFileCont = {} - return parFileCont - def _printParameterError(self, errmsg): print 'ParameterError:\n non-existent parameter %s' % errmsg From 270e3b6d009246411825a725d4b2e0414c5c8ac5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 19 Feb 2014 13:07:39 +0100 Subject: [PATCH 0029/1144] modifications concerning the usage of __specialmethods__ made --- pylot/core/readinput/types.py | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/pylot/core/readinput/types.py b/pylot/core/readinput/types.py index 50e527bc..2658be46 100644 --- a/pylot/core/readinput/types.py +++ b/pylot/core/readinput/types.py @@ -87,38 +87,40 @@ class AutoPickParameter(object): return self.__parameter def __getitem__(self, key): - return self.getParam(key) + return self.__parameter[key] - def getParam(self, *args): + def __setitem__(self, key, value): + self.__parameter[key] = value + + def __delitem__(self, key): + del self.__parameter[key] + + def __iter__(self): + return iter(self.__parameter) + + def __len__(self): + return len(self.__parameter.keys()) + + def getParam(self, *args): try: for param in args: try: - return self.__parameter[param] + return self.__getitem__[param] except KeyError, e: self._printParameterError(e) except TypeError: try: - return self.__parameter[args] + return self.__getitem__[args] except KeyError, e: self._printParameterError(e) - def __setitem__(self, key, value): - kwargs = {} - kwargs[key] = value - kwargs['directcall'] = True - self.setParam(**kwargs) - - def setParam(self, directcall=None, **kwargs): + def setParam(self, **kwargs): for param, value in kwargs.iteritems(): try: - self.__parameter[param] = value + self.__setitem__[param] = value except KeyError, e: self._printParameterError(e) - if not directcall: - print self - - def __len__(self): - return len(self.__parameter.keys()) - + print self + def _printParameterError(self, errmsg): print 'ParameterError:\n non-existent parameter %s' % errmsg From bddc2ab83ea4c2eac44e1397ba31f65447d2410f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 19 Feb 2014 14:43:56 +0100 Subject: [PATCH 0030/1144] bugfix: corrected call to special method __getitem__ --- pylot/core/readinput/types.py | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pylot/core/readinput/types.py b/pylot/core/readinput/types.py index 2658be46..54aadcbc 100644 --- a/pylot/core/readinput/types.py +++ b/pylot/core/readinput/types.py @@ -67,7 +67,7 @@ class AutoPickParameter(object): parFileCont = {} self.__parameter = parFileCont - # Human-readable string representation of the object + # Human-readable string representation of the object def __str__(self): string = '' string += 'Automated picking parameter:\n\n' @@ -78,49 +78,49 @@ class AutoPickParameter(object): string += 'Empty parameter dictionary.' return string - # String representation of the object + # String representation of the object def __repr__(self): return "AutoPickParameter('%s')" % self.__filename - # Boolean test + # Boolean test def __nonzero__(self): return self.__parameter - def __getitem__(self, key): - return self.__parameter[key] + def __getitem__(self, key): + return self.__parameter[key] - def __setitem__(self, key, value): - self.__parameter[key] = value + def __setitem__(self, key, value): + self.__parameter[key] = value - def __delitem__(self, key): - del self.__parameter[key] + def __delitem__(self, key): + del self.__parameter[key] - def __iter__(self): - return iter(self.__parameter) - - def __len__(self): - return len(self.__parameter.keys()) + def __iter__(self): + return iter(self.__parameter) - def getParam(self, *args): + def __len__(self): + return len(self.__parameter.keys()) + + def getParam(self, *args): try: for param in args: try: - return self.__getitem__[param] + return self.__getitem__(param) except KeyError, e: self._printParameterError(e) except TypeError: try: - return self.__getitem__[args] + return self.__getitem__(args) except KeyError, e: self._printParameterError(e) - def setParam(self, **kwargs): + def setParam(self, **kwargs): for param, value in kwargs.iteritems(): try: - self.__setitem__[param] = value + self.__setitem__(param, value) except KeyError, e: self._printParameterError(e) - print self + print self def _printParameterError(self, errmsg): print 'ParameterError:\n non-existent parameter %s' % errmsg From e38b917623347b9b2bc18210736fb95271ed4559 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 19 Feb 2014 14:58:45 +0100 Subject: [PATCH 0031/1144] removing QtDesigner project files due to decision to code the PyQt stuff by ourselves --- pylot/ui/PyLoT_main/PyLoT_main.pro | 20 ---------- pylot/ui/PyLoT_main/main.cpp | 11 ------ pylot/ui/PyLoT_main/qt_pylot.cpp | 14 ------- pylot/ui/PyLoT_main/qt_pylot.h | 22 ----------- pylot/ui/PyLoT_main/qt_pylot.ui | 62 ------------------------------ 5 files changed, 129 deletions(-) delete mode 100644 pylot/ui/PyLoT_main/PyLoT_main.pro delete mode 100644 pylot/ui/PyLoT_main/main.cpp delete mode 100644 pylot/ui/PyLoT_main/qt_pylot.cpp delete mode 100644 pylot/ui/PyLoT_main/qt_pylot.h delete mode 100644 pylot/ui/PyLoT_main/qt_pylot.ui diff --git a/pylot/ui/PyLoT_main/PyLoT_main.pro b/pylot/ui/PyLoT_main/PyLoT_main.pro deleted file mode 100644 index e119b23a..00000000 --- a/pylot/ui/PyLoT_main/PyLoT_main.pro +++ /dev/null @@ -1,20 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2013-12-09T13:16:21 -# -#------------------------------------------------- - -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = PyLoT_main -TEMPLATE = app - - -SOURCES += main.cpp\ - qt_pylot.cpp - -HEADERS += qt_pylot.h - -FORMS += qt_pylot.ui diff --git a/pylot/ui/PyLoT_main/main.cpp b/pylot/ui/PyLoT_main/main.cpp deleted file mode 100644 index 49f1e337..00000000 --- a/pylot/ui/PyLoT_main/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "qt_pylot.h" -#include - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - Qt_PyLoT w; - w.show(); - - return a.exec(); -} diff --git a/pylot/ui/PyLoT_main/qt_pylot.cpp b/pylot/ui/PyLoT_main/qt_pylot.cpp deleted file mode 100644 index 967482a0..00000000 --- a/pylot/ui/PyLoT_main/qt_pylot.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "qt_pylot.h" -#include "ui_qt_pylot.h" - -Qt_PyLoT::Qt_PyLoT(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::Qt_PyLoT) -{ - ui->setupUi(this); -} - -Qt_PyLoT::~Qt_PyLoT() -{ - delete ui; -} diff --git a/pylot/ui/PyLoT_main/qt_pylot.h b/pylot/ui/PyLoT_main/qt_pylot.h deleted file mode 100644 index 5384d4ac..00000000 --- a/pylot/ui/PyLoT_main/qt_pylot.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef QT_PYLOT_H -#define QT_PYLOT_H - -#include - -namespace Ui { -class Qt_PyLoT; -} - -class Qt_PyLoT : public QMainWindow -{ - Q_OBJECT - -public: - explicit Qt_PyLoT(QWidget *parent = 0); - ~Qt_PyLoT(); - -private: - Ui::Qt_PyLoT *ui; -}; - -#endif // QT_PYLOT_H diff --git a/pylot/ui/PyLoT_main/qt_pylot.ui b/pylot/ui/PyLoT_main/qt_pylot.ui deleted file mode 100644 index 0b0b0da2..00000000 --- a/pylot/ui/PyLoT_main/qt_pylot.ui +++ /dev/null @@ -1,62 +0,0 @@ - - - Qt_PyLoT - - - - 0 - 0 - 1024 - 768 - - - - Qt_PyLoT - - - - - - 170 - 30 - 841 - 641 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 0 - 0 - 1024 - 22 - - - - - - TopToolBarArea - - - false - - - - - - File - - - - - - - From c0f70603683a06ceb9dcfb0e72ed052520181e0c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 20 Feb 2014 14:20:57 +0100 Subject: [PATCH 0032/1144] SeiscompDataStructure objects contain all relevant information to expand a desired data path (including unix shell wildcards) [not tested for Windows yet] --- pylot/core/readdata/types.py | 66 +++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/pylot/core/readdata/types.py b/pylot/core/readdata/types.py index 619e4547..b4b9dbec 100644 --- a/pylot/core/readdata/types.py +++ b/pylot/core/readdata/types.py @@ -20,9 +20,19 @@ class GenericDataBase(object): self.dataBaseDict = kwargs -class SeiscompDataStructure(GenericDataBase): +class SeiscompDataStructure(object): - def __init__(self, **kwargs): + def __init__(self, dataType='waveform', **kwargs): + ''' + Object initialization method: + + :type dataType: str + :param dataType: Desired data type. Default: ``'waveform'`` + ''' + # imports + from obspy.core import UTCDateTime + + now = UTCDateTime() # Data type options self.__typeOptions = {'waveform': 'D', # Waveform data 'detect': 'E', # Detection data @@ -32,19 +42,51 @@ class SeiscompDataStructure(GenericDataBase): 'resp': 'R', # Response data 'opaque': 'O' # Opaque data } + + if dataType in self.__typeOptions.keys(): + self.dataType = dataType + else: + self.dataType = 'waveform' # default value for dataType + # SDS fields' default values # definitions from # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS - self.__sdsFields = {'SDSdir': ['data', 'SDS'], # base directory list - 'YEAR': '1970', # 4 digits - 'NET': 'XX', # up to 8 characters - 'STA': 'XXXX', # up to 8 characters - 'CHAN': 'HHZ', # up to 8 characters - 'TYPE': self.getType('waveform'), # 1 character + self.__sdsFields = {'SDSdir': '/data/SDS', # base directory + 'YEAR': '{0:04d}'.format(now.year), # 4 digits + 'NET': '??', # up to 8 characters + 'STA': '????', # up to 8 characters + 'CHAN': 'HH?', # up to 8 characters + 'TYPE': self._getType(), # 1 character 'LOC': '', # up to 8 characters - 'DAY': '001' # 3 digit day of year + 'DAY': '{0:03d}'.format(now.julday) # 3 digit doy } - pass + self.modifiyFields(**kwargs) - def getType(self, typeName=None): - pass + def modifiyFields(self, **kwargs): + if kwargs and isinstance(kwargs, dict): + for key, value in kwargs.iteritems(): + try: + if key in self.__sdsFields.keys(): + self.__sdsFields[key] = str(value) + else: + raise KeyError('unknown SDS wildcard: %s.' % key) + except KeyError, e: + errmsg = '' + errmsg += 'WARNING:\n' + errmsg += 'unable to set values for SDS fields\n' + errmsg += '%s; desired value was: %s\n' % (e, value) + print errmsg + + def _getType(self): + return self.__typeOptions[self.dataType] + + def expandDataPath(self): + fullChan = '{0}.{1}'.format(self.__sdsFields['CHAN'], self._getType()) + dataPath = os.path.join(self.__sdsFields['SDSdir'], + self.__sdsFields['YEAR'], + self.__sdsFields['NET'], + self.__sdsFields['STA'], + fullChan, + '*{0}'.format(self.__sdsFields['DAY']) + ) + return dataPath From d0b3f0ee5d91aeee0204307a149d0f6f170a3c58 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 21 Feb 2014 23:39:42 +0100 Subject: [PATCH 0033/1144] cleanup: module naming re-structured --- pylot/core/{readdata => read}/__init__.py | 0 pylot/core/{readdata/types.py => read/data.py} | 2 ++ pylot/core/{readinput/types.py => read/inputs.py} | 1 + pylot/core/readinput/__init__.py | 0 4 files changed, 3 insertions(+) rename pylot/core/{readdata => read}/__init__.py (100%) rename pylot/core/{readdata/types.py => read/data.py} (98%) rename pylot/core/{readinput/types.py => read/inputs.py} (99%) delete mode 100644 pylot/core/readinput/__init__.py diff --git a/pylot/core/readdata/__init__.py b/pylot/core/read/__init__.py similarity index 100% rename from pylot/core/readdata/__init__.py rename to pylot/core/read/__init__.py diff --git a/pylot/core/readdata/types.py b/pylot/core/read/data.py similarity index 98% rename from pylot/core/readdata/types.py rename to pylot/core/read/data.py index b4b9dbec..ae4a9932 100644 --- a/pylot/core/readdata/types.py +++ b/pylot/core/read/data.py @@ -65,6 +65,8 @@ class SeiscompDataStructure(object): def modifiyFields(self, **kwargs): if kwargs and isinstance(kwargs, dict): for key, value in kwargs.iteritems(): + key = str(key) + value = str(value) try: if key in self.__sdsFields.keys(): self.__sdsFields[key] = str(value) diff --git a/pylot/core/readinput/types.py b/pylot/core/read/inputs.py similarity index 99% rename from pylot/core/readinput/types.py rename to pylot/core/read/inputs.py index 54aadcbc..473f91d7 100644 --- a/pylot/core/readinput/types.py +++ b/pylot/core/read/inputs.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- + class AutoPickParameter(object): ''' AutoPickParameters is a parameter type object capable to read and/or write diff --git a/pylot/core/readinput/__init__.py b/pylot/core/readinput/__init__.py deleted file mode 100644 index e69de29b..00000000 From 84f3a29a8638420fbf21106e174789c67710ca46 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 13 Mar 2014 09:24:17 +0100 Subject: [PATCH 0034/1144] new file defaults.py written to provide default value if no user configuration file is available --- pylot/core/util/defaults.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pylot/core/util/defaults.py diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py new file mode 100644 index 00000000..b9642d4a --- /dev/null +++ b/pylot/core/util/defaults.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on Wed Feb 26 12:31:25 2014 + +@author: sebastianw +""" + +FILTERDEFAULTS = {'P': {'filtertype': None, + 'order': None, + 'freq': None}, + 'S': {'filtertype': 'bandpass', + 'order': '4', + 'freq': [.5, 5]}} From c83a11a2c3dcaeb26fca5b9476bad527a03dbcb6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 13 Mar 2014 13:25:37 +0100 Subject: [PATCH 0035/1144] moved class FilterOptions to the read module --- pylot/core/read/inputs.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 473f91d7..cb48319a 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -125,3 +125,34 @@ class AutoPickParameter(object): def _printParameterError(self, errmsg): print 'ParameterError:\n non-existent parameter %s' % errmsg + + +class FilterOptions(object): + + def __init__(self, filtertype=None, freq=None, order=None): + self.__filterInformation = {} + self._setfilterType(filtertype) + self._setFreq(freq) + self._setOrder(order) + + def _getFreq(self): + return self.__filterInformation['freq'] + + def _setFreq(self, freq): + self.__filterInformation['freq'] = freq + + def _getOrder(self): + return self.__filterInformation['order'] + + def _setOrder(self, order): + self.__filterInformation['order'] = order + + def _getFilterType(self): + return self.__filterInformation['filtertype'] + + def _setFilterType(self, filtertype): + self.__filterInformation['filtertype'] = filtertype + + filterType = property(fget=_getFilterType, fset=_setFilterType) + order = property(fget=_getOrder, fset=_setOrder) + freq = property(fget=_getFreq, fset=_setFreq) From ac27a8ef2e417f0b56f2b72d15750a16bfad388f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 13 Mar 2014 13:26:13 +0100 Subject: [PATCH 0036/1144] clean-up --- pylot/__init__.py | 1 - pylot/core/stream.py | 13 ------------- pylot/core/trace.py | 18 ------------------ 3 files changed, 32 deletions(-) delete mode 100755 pylot/core/stream.py delete mode 100755 pylot/core/trace.py diff --git a/pylot/__init__.py b/pylot/__init__.py index 1297ecab..788a394f 100755 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -26,7 +26,6 @@ The development of PyLoT is part of the joint research project MAGS2. (http://www.gnu.org/copyleft/lesser.html) ''' -import os.path as osp from obspy.core.utcdatetime import UTCDateTime from obspy.core.util.attribdict import AttribDict #from pylot.core.trace import Stats, Trace diff --git a/pylot/core/stream.py b/pylot/core/stream.py deleted file mode 100755 index fcfc41b0..00000000 --- a/pylot/core/stream.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 8 15:13:13 2013 - -@author: sebastianw -""" - -from obspy.core import Stream as Obspystream - - -class Stream(Obspystream): - pass - diff --git a/pylot/core/trace.py b/pylot/core/trace.py deleted file mode 100755 index 8f961d30..00000000 --- a/pylot/core/trace.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 8 15:12:43 2013 - -@author: sebastianw -""" - -from obspy.core import Trace as Obspytrace -from obspy.core.util import AttribDict - - -class Stats(AttribDict): - pass - - -class Trace(Obspytrace): - pass - \ No newline at end of file From 8edea03fe77260be6b02d74e4e894a782ebb20e0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 13 Mar 2014 13:27:34 +0100 Subject: [PATCH 0037/1144] cleanup, switched to PySide (more flexible licensing), started to implement the filter options depending on the phase to be picked --- README | 2 +- pylot/QtPyLoT.py | 108 +++++++++++++++--------------------- pylot/core/util/__init__.py | 1 + 3 files changed, 47 insertions(+), 64 deletions(-) diff --git a/README b/README index 01953a71..c5452495 100644 --- a/README +++ b/README @@ -6,7 +6,7 @@ The Python picking and Localisation Tool This python library contains a graphical user interfaces for picking seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki) -and the Qt4 libraries to be installed first. +and the PySide Qt4 bindings for python to be installed first. PILOT has originally been developed in Mathworks' MatLab. In order to distribute PILOT without facing portability problems, it has been decided diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 34b5375a..7948e27e 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -1,101 +1,83 @@ +#!/usr/bin/env python +# +# # Main program: QtPyLoT.py -# -# -# -# -# -# -# -# -# -# -# -# import os import platform import sys -from PyQt4.QtCore import * -from PyQt4.QtGui import * +from PySide.QtCore import * +from PySide.QtGui import * import helpform from pylot.core.util import _getVersionString +from pylot.core.read.inputs import FilterOptions +from pylot.core.util import FILTERDEFAULTS # Version information __version__ = _getVersionString() + class MainWindow(QMainWindow): - + def __init__(self, parent=None): super(MainWindow, self).__init__(parent) - - filterDefaults - self.filterOptions = FilterOptions(filterDefaults) - - filterDockWidget = FilterOptionsDock("Filter Options", self) - - - class PickWindow(QDialog): + + filterOptionsP = FILTERDEFAULTS['P'] + filterOptionsS = FILTERDEFAULTS['S'] + self.filterOptionsP = FilterOptions(**filterOptionsP) + self.filterOptionsS = FilterOptions(**filterOptionsS) + + filteroptions = [self.filterOptionsP if not self.seismicPhase == 'S' + else self.filterOptionsS] + filterDockWidget = FilterOptionsDock(titleString="Filter Options", + parent=self, + filterOptions=filteroptions) + self. + + +class PickWindow(QDialog): def __init__(self, station=None, parent=None): - super(PickWindow, self).__init__(parent) + super(PickWindow, self).__init__(parent) + + filterDockWidget = FilterOptionsDock(titleString="Filter Options", + parent=self, + filterOptions=filteroptions) - filterDockWidget = FilterOptionsDock() class PropertiesWindow(QDialog): def __init__(self, parent=None): - super(PropertiesWindow, self).__init__(parent) + super(PropertiesWindow, self).__init__(parent) + class FilterOptionsDock(QDockWidget): - def __init__(self, titleString="Filter options", filterOptions=None): - super(FilterOptionsDock, self).__init__() + def __init__(self, parent=None, titleString="Filter options", + filterOptions=None): + super(FilterOptionsDock, self).__init__() - if filterOptions and not isinstance(filterOptions, FilterOptions): - try: - fOptions = FilterOptions(filterOptions) - except e: - raise OptionsError, '%s' % e + if filterOptions and not isinstance(filterOptions, FilterOptions): + try: + fOptions = FilterOptions(**filterOptions) + filterOptions = fOptions + except e: + raise OptionsError('%s' % e) -class FilterOptions(object): + - def __init__(self, filtertype=None, freq=None, order=None): - self.__filterInformation = {} - self._setfilterType(filtertype) - self._setFreq(freq) - self._setOrder(order) - - def _getFreq(self): - return self.__filterInformation['freq'] - def _setFreq(self, freq): - self.__filterInformation['freq'] = freq - - def _getOrder(self): - return self.__filterInformation['order'] +class OptionsError(Exception): + pass - def _setOrder(self, order): - self.__filterInformation['order'] = order - - def _getFilterType(self): - return self.__filterInformation['filtertype'] - - def _setFilterType(self, filtertype): - self.__filterInformation['filtertype'] = filtertype - - filterType = property(fget=_getFilterType, fset=_setFilterType) - order = property(fget=_getOrder, fset=_setOrder) - freq = property(fget=_getFreq, fset=_setFreq) - -class OptionsError(Exception): pass if __name__ == '__main__': - ##Creating a Qt application + # Creating a Qt application pylot_app = QApplication(sys.argv) pylot_main = MainWindow() pylot_main.setWindowTitle('PyLoT-The Picking and Localization Tool') - # Show window and run the app + # Show main window and run the app pylot_main.show() pylot_app.exec_() diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 2d4a37e4..e0301057 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1 +1,2 @@ from pylot.core.util.version import get_git_version as _getVersionString +from pylot.core.util.defaults import FILTERDEFAULTS From 9a2d127e306e15a2589583f40e3ac7574424ee17 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 19 Mar 2014 12:14:54 +0100 Subject: [PATCH 0038/1144] added class MPLWidget in order to create updatable Matplotlib Figures within a Qt GUI --- pylot/core/util/widgets.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 pylot/core/util/widgets.py diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py new file mode 100644 index 00000000..d99fe947 --- /dev/null +++ b/pylot/core/util/widgets.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Mar 19 11:27:35 2014 + +@author: sebastianw +""" + +import matplotlib + +matplotlib.use('Qt4Agg') +matplotlib.rcParams['backend.qt4'] = 'PySide' + +from matplotlib.figure import Figure +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg + + +class MPLWidget(FigureCanvasQTAgg): + + def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): + super(MPLWidget, self).__init__(Figure()) + + self.setParent(parent) + self.figure = Figure() + self.canvas = FigureCanvasQTAgg(self.figure) + self.axes = self.figure.add_subplot(111) + + self.axes.set_xlabel(xlabel) + self.axes.set_ylabel(ylabel) + self.axes.set_title(title) From e347e8eef900881ba0896441b4bb0df989284cd6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 19 Mar 2014 12:15:30 +0100 Subject: [PATCH 0039/1144] implement new module widgets --- pylot/core/util/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index e0301057..3ae8eee9 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,2 +1,3 @@ from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.defaults import FILTERDEFAULTS +from pylot.core.util.widgets import * From fbbfcbcaea3d2dc011358b348cfda8cd1cb1cd54 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 19 Mar 2014 12:16:41 +0100 Subject: [PATCH 0040/1144] make use of new module widgets, set a matplotlib figure the central GUI element --- pylot/QtPyLoT.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 7948e27e..fad213cc 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -12,6 +12,7 @@ import helpform from pylot.core.util import _getVersionString from pylot.core.read.inputs import FilterOptions from pylot.core.util import FILTERDEFAULTS +from pylot.core.util import MPLWidget # Version information __version__ = _getVersionString() @@ -22,6 +23,10 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) + # create central matplotlib figure widget + dataPlot = setupPlot() + self.setCentralWidget(dataPlot) + filterOptionsP = FILTERDEFAULTS['P'] filterOptionsS = FILTERDEFAULTS['S'] self.filterOptionsP = FilterOptions(**filterOptionsP) @@ -32,7 +37,13 @@ class MainWindow(QMainWindow): filterDockWidget = FilterOptionsDock(titleString="Filter Options", parent=self, filterOptions=filteroptions) - self. + + def setupPlot(self): + # create a matplotlib widget + self.DataPlot = MPLWidget() + # create a layout inside the blank widget and add the matplotlib widget + layout = QtGui.QVBoxLayout(self.ui.widget_PlotArea) + layout.addWidget(self.DataPlot, 1) class PickWindow(QDialog): @@ -64,8 +75,6 @@ class FilterOptionsDock(QDockWidget): except e: raise OptionsError('%s' % e) - - class OptionsError(Exception): pass From 253a49c06b840dd07b10f3fb99103bb80a0328e1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 19 Mar 2014 12:24:41 +0100 Subject: [PATCH 0041/1144] implementation of the PyLoT icon --- pylot/PyLoT.ico | Bin 0 -> 2238 bytes pylot/QtPyLoT.py | 10 ++++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 pylot/PyLoT.ico diff --git a/pylot/PyLoT.ico b/pylot/PyLoT.ico new file mode 100644 index 0000000000000000000000000000000000000000..81bd4cf535ba07ec53377db17c01dcf1e5c261b9 GIT binary patch literal 2238 zcmcgsXH-;48ojg8jqZ#B3#bG^VGxyNVL)6kjRFd*$moiKf?-J`X_Y)GF3?CEnqUAC z8-Z5IND>;!h~ylk146ezFLXnJs3`Lq^bDRe^LM{@ZoRMW{i^PJuc{vK8FeWtf@)VF zeFY#5K#c-LVFv{X|8uc&tDmDfW>MFpy=s@S-3Bh=N^p`oFHO`A4BQ&SV0H*dz4EnBd4>sDxK zX+c|C8#+2V(ACw2o}M1`_4ToB+cp>&7-0MM?JzVngprXEcJADXUAuN+_wL=;vu6+X z?%j)h`}V=u*cc`zCNMQM#s2;Kap1rKn3<-~dNQM>sh-!P(gv=g*&qi;D|fU0vbk<_33ncX)Vs;KGFqxOnj*JUu<( z<>du$Z*O?}c*DoX2fn_(@bmM7zrQ~&UAlxTSFRu+AOL}Zfd~o;LU3>}Xfzr^LPBuu z+BIa_ixCzUhVbxkL_|a&GBOfTQBjDFjs~4hM@&o%Vq;^G>&r)6TpZ%#<8l4^b=YiHW#<`! zBeW#GM?paW3JVKSR8)lG;$oDPl;H8>$7sk9ptQ7eLBDiSHDRsU(h_)#fW!J=N;T}}9*2ETJ!UeKkjkw3OXRB;;#VKJF920s?G|Lq^W zi#?%y~s! zOE}Fu`(GRN@ze5uZ@V}{_1aKE*m}yfga`+lbJ&@+g!zSDLsEt3iekx#lUoQG`&zr7 zh%3E}^)K_&@&x5{Ro&?FT*Lj8KB&ed#6_c)jQgo*y3!e3W>QlDec%i8$|5(J9?Ce7 zkUA}YLhRN?QVzxJlxUA{Lk4|*nOqdC<}G<9LfrLxNU*HLjaSG`jIx$?tdEuZx`e*N zkSmdSX4ZuKH&&9qd^~cK7NcfR8g={^CGm$97!tC2cQ7F$N%o@VPUmeJ-B#v)MBtI^ z#ItMs`C`F1b6dg$m&@l1hRZIT&$>qoqy6T)u<&^1L;rOy4X4XHLbW6N9ll{TH9fn2 z=HlIST4?OA+`~e{6EgzjT}GvfNseJMxop@an)u(-?{7UyrG;PlgJ)Q1WcdBdvNYkB zL8NZaRnqj=2FibGa{74^E!slSKkTY$)O`=>?1_IJAOiDagy`5TtuQ+|BtphAB-~7Y`Iv{q~k$I{GYtKh9(G` z5qi*zPwY^g-w)p2roq;BHlfW^D0!&CLQ`c5&KRf6P7P9BeZDpNH=PNBWw k`qeuLqe9fX_$5D?K++LS2**XdH0pr=7(e+__&<_A0L7AxJOBUy literal 0 HcmV?d00001 diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index fad213cc..b25dacca 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -23,9 +23,11 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) + self.setWindowIcon(QIcon("PyLoT.ico")) + # create central matplotlib figure widget - dataPlot = setupPlot() - self.setCentralWidget(dataPlot) + setupPlot() + self.setCentralWidget(self.DataPlot) filterOptionsP = FILTERDEFAULTS['P'] filterOptionsS = FILTERDEFAULTS['S'] @@ -45,6 +47,10 @@ class MainWindow(QMainWindow): layout = QtGui.QVBoxLayout(self.ui.widget_PlotArea) layout.addWidget(self.DataPlot, 1) + def plotData(self, data): + if data is not None and isinstance(data, Stream): + self.DataPlot.height + class PickWindow(QDialog): From 5b044a3f1424c3b169a1b8577ae0d800e4f7ba29 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Mar 2014 15:34:42 +0100 Subject: [PATCH 0042/1144] container module for error handling added --- pylot/core/util/errors.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pylot/core/util/errors.py diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py new file mode 100644 index 00000000..68125c2d --- /dev/null +++ b/pylot/core/util/errors.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Mar 20 09:47:04 2014 + +@author: sebastianw +""" + + +class OptionsError(Exception): + pass From dac3be5110480c01e1f05c4e01bcc851d6c029aa Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Mar 2014 21:29:41 +0100 Subject: [PATCH 0043/1144] package wide imports for convenience --- pylot/core/util/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 3ae8eee9..ae9d355b 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,3 +1,4 @@ from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.widgets import * +from pylot.core.util.errors import * From 90936bd47a414b37566f9cc723a9294201785365 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Mar 2014 05:25:46 +0100 Subject: [PATCH 0044/1144] emphasize modular structure by outsourcing widgets and errors --- pylot/QtPyLoT.py | 66 ++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index b25dacca..b5b01c12 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -23,23 +23,34 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) - self.setWindowIcon(QIcon("PyLoT.ico")) - - # create central matplotlib figure widget - setupPlot() - self.setCentralWidget(self.DataPlot) - filterOptionsP = FILTERDEFAULTS['P'] filterOptionsS = FILTERDEFAULTS['S'] self.filterOptionsP = FilterOptions(**filterOptionsP) self.filterOptionsS = FilterOptions(**filterOptionsS) - filteroptions = [self.filterOptionsP if not self.seismicPhase == 'S' - else self.filterOptionsS] + self.loadData() + + self.updateFilterOptions() + + self.setupUi() + + def setupUi(self): + self.setWindowIcon(QIcon("PyLoT.ico")) + + # create central matplotlib figure widget + dataLayout = setupPlot() + filterDockWidget = FilterOptionsDock(titleString="Filter Options", parent=self, filterOptions=filteroptions) + statLayout = self.layoutStationButtons() + + maingrid = QGridLayout() + maingrid.setSpacing(10) + maingrid.addLayout(statLayout, 0, 0) + maingrid.addWidget() + def setupPlot(self): # create a matplotlib widget self.DataPlot = MPLWidget() @@ -47,44 +58,15 @@ class MainWindow(QMainWindow): layout = QtGui.QVBoxLayout(self.ui.widget_PlotArea) layout.addWidget(self.DataPlot, 1) + return layout + def plotData(self, data): if data is not None and isinstance(data, Stream): self.DataPlot.height - -class PickWindow(QDialog): - - def __init__(self, station=None, parent=None): - super(PickWindow, self).__init__(parent) - - filterDockWidget = FilterOptionsDock(titleString="Filter Options", - parent=self, - filterOptions=filteroptions) - - -class PropertiesWindow(QDialog): - - def __init__(self, parent=None): - super(PropertiesWindow, self).__init__(parent) - - -class FilterOptionsDock(QDockWidget): - - def __init__(self, parent=None, titleString="Filter options", - filterOptions=None): - super(FilterOptionsDock, self).__init__() - - if filterOptions and not isinstance(filterOptions, FilterOptions): - try: - fOptions = FilterOptions(**filterOptions) - filterOptions = fOptions - except e: - raise OptionsError('%s' % e) - - -class OptionsError(Exception): - pass - + def updateFilterOptions(self): + self.filteroptions = [self.filterOptionsP if not self.seismicPhase == 'S' + else self.filterOptionsS] if __name__ == '__main__': # Creating a Qt application From 0af8ab2b0885d37ece74b1b9e1af652103312b66 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Mar 2014 05:28:16 +0100 Subject: [PATCH 0045/1144] holds now all widgets' classes used in the main application --- pylot/core/util/widgets.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d99fe947..35a6c426 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -27,3 +27,31 @@ class MPLWidget(FigureCanvasQTAgg): self.axes.set_xlabel(xlabel) self.axes.set_ylabel(ylabel) self.axes.set_title(title) + +class PickWindow(QDialog): + + def __init__(self, station=None, parent=None): + super(PickWindow, self).__init__(parent) + + filterDockWidget = FilterOptionsDock(titleString="Filter Options", + parent=self, + filterOptions=filteroptions) + +class PropertiesWindow(QDialog): + + def __init__(self, parent=None): + super(PropertiesWindow, self).__init__(parent) + + +class FilterOptionsDock(QDockWidget): + + def __init__(self, parent=None, titleString="Filter options", + filterOptions=None): + super(FilterOptionsDock, self).__init__() + + if filterOptions and not isinstance(filterOptions, FilterOptions): + try: + fOptions = FilterOptions(**filterOptions) + filterOptions = fOptions + except e: + raise OptionsError('%s' % e) \ No newline at end of file From d58b671d369576ee63b0a09e184e8336a3358d1c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Mar 2014 05:30:55 +0100 Subject: [PATCH 0046/1144] started to write initialization method for the GenericDataBase class in order to read data from an arbitrary data folder structure --- pylot/core/read/data.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index ae4a9932..cd84f9fc 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -13,26 +13,43 @@ class GenericDataBase(object): GenericDataBase type holds all information about the current data- base working on. ''' - def __init__(self, stexp=None, **kwargs): + def __init__(self, stexp=None, folderdepth=4, **kwargs): dbRegExp = {} - - structExpression = os.path.split(stexp) + structExpression = [] + depth = 0 + while stexp is not os.path.sep: + [stexp, tlexp] = os.path.split(stexp) + structExpression.append(tlexp) + depth += 1 + if depth is folderdepth: + rootExpression = stexp + break + structExpression.reverse() + + self.folderDepth = folderdepth self.dataBaseDict = kwargs class SeiscompDataStructure(object): - def __init__(self, dataType='waveform', **kwargs): + def __init__(self, dataType='waveform', date=None, **kwargs): ''' Object initialization method: - :type dataType: str - :param dataType: Desired data type. Default: ``'waveform'`` + :param str dataType: Desired data type. Default: ``'waveform'`` + :param date: Either date string or an instance of + obspy.core.utcdatetime.UTCDateTime. Default: ``None`` + :type date: str or UTCDateTime or None ''' # imports from obspy.core import UTCDateTime - now = UTCDateTime() + if date is not None and not isinstance(date, UTCDateTime): + try: + dod = UTCDateTime(date) + except: + dod = UTCDateTime() + # Data type options self.__typeOptions = {'waveform': 'D', # Waveform data 'detect': 'E', # Detection data @@ -47,10 +64,13 @@ class SeiscompDataStructure(object): self.dataType = dataType else: self.dataType = 'waveform' # default value for dataType + print '''Warning: Selected datatype ('%s') not available.\n + Using 'waveform' instead!'''.format(dataType) # SDS fields' default values # definitions from # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS + self.__sdsFields = {'SDSdir': '/data/SDS', # base directory 'YEAR': '{0:04d}'.format(now.year), # 4 digits 'NET': '??', # up to 8 characters From 9b8413beafc21877f10a9d86839ad89291df543b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Mar 2014 22:26:15 +0100 Subject: [PATCH 0047/1144] icons and help documents added; main application has been modified slightly; new method definitions added (ToDo) --- pylot/QtPyLoT.py | 41 +++++++++++++++++++++++++------- pylot/core/util/help/index.html | 17 +++++++++++++ pylot/core/util/icons/pylot.ico | Bin 0 -> 2238 bytes pylot/core/util/resources.qrc | 8 +++++++ 4 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 pylot/core/util/help/index.html create mode 100644 pylot/core/util/icons/pylot.ico create mode 100644 pylot/core/util/resources.qrc diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index b5b01c12..9b3235ce 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -34,6 +34,9 @@ class MainWindow(QMainWindow): self.setupUi() + def loadData(self): + pass + def setupUi(self): self.setWindowIcon(QIcon("PyLoT.ico")) @@ -44,7 +47,7 @@ class MainWindow(QMainWindow): parent=self, filterOptions=filteroptions) - statLayout = self.layoutStationButtons() + statLayout = self.layoutStationButtons(self.numStations) maingrid = QGridLayout() maingrid.setSpacing(10) @@ -62,19 +65,39 @@ class MainWindow(QMainWindow): def plotData(self, data): if data is not None and isinstance(data, Stream): - self.DataPlot.height + self.stats.numStations = data. def updateFilterOptions(self): - self.filteroptions = [self.filterOptionsP if not self.seismicPhase == 'S' - else self.filterOptionsS] + self.filteroptions = [self.filterOptionsP + if not self.seismicPhase == 'S' + else self.filterOptionsS] -if __name__ == '__main__': - # Creating a Qt application + def layoutStationButtons(self, numStations): + layout = QVBoxLayout() + for n in range(numStations): + stationButtons[n] = QPushButton('%s'.format(self.)) + + def helpHelp(self): + if internet_on(): + pass + + +def main(): + # create th Qt application pylot_app = QApplication(sys.argv) - pylot_main = MainWindow() - pylot_main.setWindowTitle('PyLoT-The Picking and Localization Tool') + # set Application Information + pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") + pylot_app.setOrganizationDomain("rub.de") + pylot_app.setApplicationName("PyLoT") + pylot_app.setWindowIcon(QIcon(":/pylot.ico")) + + # create the main window + pylot_form = MainWindow() # Show main window and run the app - pylot_main.show() + pylot_form.show() pylot_app.exec_() + + +main() diff --git a/pylot/core/util/help/index.html b/pylot/core/util/help/index.html new file mode 100644 index 00000000..dabf3001 --- /dev/null +++ b/pylot/core/util/help/index.html @@ -0,0 +1,17 @@ +PyLoT - the Python picking and Localisation Tool + +

PyLoT is a program which is capable of picking seismic phases, +exporting these as numerous standard phase format and localize the corresponding +seismic event with external software as, e.g.:

+
    +
  • NonLinLoc
  • +
  • HypoInvers
  • +
  • HypoSat
  • +
  • whatever you want ...
  • +
+

Read more on the +PyLoT WikiPage.

+

Bug reports are very much appreciated and can also be delivered on our +PyLoT TracPage after +successful registration.

+ diff --git a/pylot/core/util/icons/pylot.ico b/pylot/core/util/icons/pylot.ico new file mode 100644 index 0000000000000000000000000000000000000000..81bd4cf535ba07ec53377db17c01dcf1e5c261b9 GIT binary patch literal 2238 zcmcgsXH-;48ojg8jqZ#B3#bG^VGxyNVL)6kjRFd*$moiKf?-J`X_Y)GF3?CEnqUAC z8-Z5IND>;!h~ylk146ezFLXnJs3`Lq^bDRe^LM{@ZoRMW{i^PJuc{vK8FeWtf@)VF zeFY#5K#c-LVFv{X|8uc&tDmDfW>MFpy=s@S-3Bh=N^p`oFHO`A4BQ&SV0H*dz4EnBd4>sDxK zX+c|C8#+2V(ACw2o}M1`_4ToB+cp>&7-0MM?JzVngprXEcJADXUAuN+_wL=;vu6+X z?%j)h`}V=u*cc`zCNMQM#s2;Kap1rKn3<-~dNQM>sh-!P(gv=g*&qi;D|fU0vbk<_33ncX)Vs;KGFqxOnj*JUu<( z<>du$Z*O?}c*DoX2fn_(@bmM7zrQ~&UAlxTSFRu+AOL}Zfd~o;LU3>}Xfzr^LPBuu z+BIa_ixCzUhVbxkL_|a&GBOfTQBjDFjs~4hM@&o%Vq;^G>&r)6TpZ%#<8l4^b=YiHW#<`! zBeW#GM?paW3JVKSR8)lG;$oDPl;H8>$7sk9ptQ7eLBDiSHDRsU(h_)#fW!J=N;T}}9*2ETJ!UeKkjkw3OXRB;;#VKJF920s?G|Lq^W zi#?%y~s! zOE}Fu`(GRN@ze5uZ@V}{_1aKE*m}yfga`+lbJ&@+g!zSDLsEt3iekx#lUoQG`&zr7 zh%3E}^)K_&@&x5{Ro&?FT*Lj8KB&ed#6_c)jQgo*y3!e3W>QlDec%i8$|5(J9?Ce7 zkUA}YLhRN?QVzxJlxUA{Lk4|*nOqdC<}G<9LfrLxNU*HLjaSG`jIx$?tdEuZx`e*N zkSmdSX4ZuKH&&9qd^~cK7NcfR8g={^CGm$97!tC2cQ7F$N%o@VPUmeJ-B#v)MBtI^ z#ItMs`C`F1b6dg$m&@l1hRZIT&$>qoqy6T)u<&^1L;rOy4X4XHLbW6N9ll{TH9fn2 z=HlIST4?OA+`~e{6EgzjT}GvfNseJMxop@an)u(-?{7UyrG;PlgJ)Q1WcdBdvNYkB zL8NZaRnqj=2FibGa{74^E!slSKkTY$)O`=>?1_IJAOiDagy`5TtuQ+|BtphAB-~7Y`Iv{q~k$I{GYtKh9(G` z5qi*zPwY^g-w)p2roq;BHlfW^D0!&CLQ`c5&KRf6P7P9BeZDpNH=PNBWw k`qeuLqe9fX_$5D?K++LS2**XdH0pr=7(e+__&<_A0L7AxJOBUy literal 0 HcmV?d00001 diff --git a/pylot/core/util/resources.qrc b/pylot/core/util/resources.qrc new file mode 100644 index 00000000..f66d2b85 --- /dev/null +++ b/pylot/core/util/resources.qrc @@ -0,0 +1,8 @@ + + + icons/pylot.ico + + + help/index.html + + From b049dda90f12c694b3cdbea1862afe286da36d1b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 31 Mar 2014 12:57:08 +0200 Subject: [PATCH 0048/1144] imports fixed; doc string as RST added --- pylot/QtPyLoT.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 9b3235ce..f1d2a225 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -1,7 +1,24 @@ #!/usr/bin/env python -# -# -# Main program: QtPyLoT.py +# -*- coding: utf-8 -*- +""" +PyLoT: Main program +=================== +PyLoT is a seismic data processing software capable of picking seismic +phases (symmetric and asymmetric error assignment), exporting these to +several common phase data formats and post process the data, e.g. locating +events, via external localization software. +Additionally PyLoT is meant as an interface to autoPyLoT which can +automatically pick seismic phases, if the parameters have properly been +chosen for the particular data set. + +:author: + Sebastian Wehling-Benatelli +:copyright: + The PyLoT Development Team (https://ariadne.geophysik.rub.de/trac/PyLoT) +:license: + GNU Lesser General Public License, Version 3 + (http://www.gnu.org/copyleft/lesser.html) +""" import os import platform @@ -12,7 +29,8 @@ import helpform from pylot.core.util import _getVersionString from pylot.core.read.inputs import FilterOptions from pylot.core.util import FILTERDEFAULTS -from pylot.core.util import MPLWidget +from pylot.core.util import checkurl +from pylot.core.util import (PickDlg, FilterOptionsDock, PropertiesDlg) # Version information __version__ = _getVersionString() From e6b49cfdb3d072a169cc9d20a183bc555f1603c0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 31 Mar 2014 12:58:58 +0200 Subject: [PATCH 0049/1144] module pylot.core.util.connection added: contains routines for web and network utilization --- pylot/core/util/connection.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 pylot/core/util/connection.py diff --git a/pylot/core/util/connection.py b/pylot/core/util/connection.py new file mode 100644 index 00000000..4f99ab81 --- /dev/null +++ b/pylot/core/util/connection.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +import urllib2 + + +def checkurl(url='https://ariadne.geophysik.rub.de/trac/PyLoT'): + try: + urllib2.urlopen(url, timeout=1) + return True + except urllib2.URLError: + pass + return False From c26782bf7ca90f5f3166fdd90de51896e3e8bbb0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 31 Mar 2014 12:59:52 +0200 Subject: [PATCH 0050/1144] imports fixed and classes renamed for better readability --- pylot/core/util/__init__.py | 6 ++++-- pylot/core/util/widgets.py | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index ae9d355b..bfda9905 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,4 +1,6 @@ from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.defaults import FILTERDEFAULTS -from pylot.core.util.widgets import * -from pylot.core.util.errors import * +from pylot.core.util.widgets import (PickDlg, MPLWidget, PropertiesDlg, + FilterOptionsDock) +from pylot.core.util.errors import OptionsError +from pylot.core.util.connection import checkurl diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 35a6c426..1f531d5a 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -12,6 +12,7 @@ matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg +from PySide.QtGui import (QDialog, QDockWidget) class MPLWidget(FigureCanvasQTAgg): @@ -28,16 +29,18 @@ class MPLWidget(FigureCanvasQTAgg): self.axes.set_ylabel(ylabel) self.axes.set_title(title) -class PickWindow(QDialog): + +class PickDlg(QDialog): def __init__(self, station=None, parent=None): - super(PickWindow, self).__init__(parent) + super(PickDlg, self).__init__(parent) filterDockWidget = FilterOptionsDock(titleString="Filter Options", parent=self, filterOptions=filteroptions) -class PropertiesWindow(QDialog): + +class PropertiesDlg(QDialog): def __init__(self, parent=None): super(PropertiesWindow, self).__init__(parent) @@ -54,4 +57,4 @@ class FilterOptionsDock(QDockWidget): fOptions = FilterOptions(**filterOptions) filterOptions = fOptions except e: - raise OptionsError('%s' % e) \ No newline at end of file + raise OptionsError('%s' % e) From faacfc423cf2cb1c7ba4446b6ea21a2332439443 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 31 Mar 2014 13:00:57 +0200 Subject: [PATCH 0051/1144] resources file added in order to provide binary media file for the User Interfaces --- pylot/core/util/resources.qrc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/resources.qrc b/pylot/core/util/resources.qrc index f66d2b85..5c8b7c06 100644 --- a/pylot/core/util/resources.qrc +++ b/pylot/core/util/resources.qrc @@ -1,8 +1,7 @@ - - icons/pylot.ico - - - help/index.html + + icons/pylot.ico + + help/index.html From ef8adc6c0aca9de82cd04325447016f846184825 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 31 Mar 2014 13:01:51 +0200 Subject: [PATCH 0052/1144] doc strings corrected --- pylot/__init__.py | 6 +++--- pylot/core/__init__.py | 1 - pylot/core/read/data.py | 16 ++++++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pylot/__init__.py b/pylot/__init__.py index 788a394f..c724596b 100755 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -3,9 +3,9 @@ # Purpose: Convience imports for PyLoT # ''' +================================================ PyLoT - the Python picking and Localization Tool - -The Python picking and Localisation Tool +================================================ This python library contains a graphical user interfaces for picking seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki) @@ -20,7 +20,7 @@ benefit a lot compared to the former MatLab version. The development of PyLoT is part of the joint research project MAGS2. :copyright: - The PyLoT-Development Team + The PyLoT Development Team :license: GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) diff --git a/pylot/core/__init__.py b/pylot/core/__init__.py index 4287ca86..e69de29b 100755 --- a/pylot/core/__init__.py +++ b/pylot/core/__init__.py @@ -1 +0,0 @@ -# \ No newline at end of file diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index cd84f9fc..711971d2 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -31,16 +31,16 @@ class GenericDataBase(object): class SeiscompDataStructure(object): + ''' + Dictionary containing the data acces information for an SDS data archive: + + :param str dataType: Desired data type. Default: ``'waveform'`` + :param date: Either date string or an instance of + :class:`obspy.core.utcdatetime.UTCDateTime. Default: ``None`` + :type date: str or UTCDateTime or None + ''' def __init__(self, dataType='waveform', date=None, **kwargs): - ''' - Object initialization method: - - :param str dataType: Desired data type. Default: ``'waveform'`` - :param date: Either date string or an instance of - obspy.core.utcdatetime.UTCDateTime. Default: ``None`` - :type date: str or UTCDateTime or None - ''' # imports from obspy.core import UTCDateTime From 88b2a319fd7d51560add4acb8b391911126d5d60 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 1 Apr 2014 16:46:10 +0200 Subject: [PATCH 0053/1144] added status label; not sure how to implement loading of data (different types of databases) --- pylot/QtPyLoT.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index f1d2a225..ccd33728 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -26,6 +26,7 @@ import sys from PySide.QtCore import * from PySide.QtGui import * import helpform +from obspy.core import (read, UTCDateTime) from pylot.core.util import _getVersionString from pylot.core.read.inputs import FilterOptions from pylot.core.util import FILTERDEFAULTS @@ -47,13 +48,15 @@ class MainWindow(QMainWindow): self.filterOptionsS = FilterOptions(**filterOptionsS) self.loadData() - + self.updateArchiveType() self.updateFilterOptions() self.setupUi() def loadData(self): - pass + loadDataDlg = LoadDataDlg(self) + + dataStream = read() def setupUi(self): self.setWindowIcon(QIcon("PyLoT.ico")) @@ -65,16 +68,24 @@ class MainWindow(QMainWindow): parent=self, filterOptions=filteroptions) + self.eventLabel = QLabel() + self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) + status = self.statusBar() + status.setSizeGripEnabled(False) + status.addPermanentWidget(self.eventLabel) + status.showMessage("Ready", 5000) + statLayout = self.layoutStationButtons(self.numStations) maingrid = QGridLayout() maingrid.setSpacing(10) maingrid.addLayout(statLayout, 0, 0) + maingrid.addLayout(dataLayout, 1, 0) maingrid.addWidget() def setupPlot(self): # create a matplotlib widget - self.DataPlot = MPLWidget() + self.DataPlot = MPLWidget(parent=self) # create a layout inside the blank widget and add the matplotlib widget layout = QtGui.QVBoxLayout(self.ui.widget_PlotArea) layout.addWidget(self.DataPlot, 1) @@ -83,13 +94,16 @@ class MainWindow(QMainWindow): def plotData(self, data): if data is not None and isinstance(data, Stream): - self.stats.numStations = data. + pass def updateFilterOptions(self): self.filteroptions = [self.filterOptionsP if not self.seismicPhase == 'S' else self.filterOptionsS] + def updateStatus(self, message): + self.statusBar().showMessage(message, 5000) + def layoutStationButtons(self, numStations): layout = QVBoxLayout() for n in range(numStations): From 36531c9923f6c5ae523c2445471841d834806cf5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 1 Apr 2014 16:47:24 +0200 Subject: [PATCH 0054/1144] convenience imports added --- pylot/core/read/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index e69de29b..de68ca8a 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -0,0 +1,2 @@ +from pylot.core.read.data import (GenericDataStructure, SeiscompDataStructure) +from pylot.core.read.inputs import (AutoPickParameter, FilterOptions) \ No newline at end of file From 0dec3eb7f03053859cea012dfdc2f974f3f16bd9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 1 Apr 2014 16:48:16 +0200 Subject: [PATCH 0055/1144] unified class naming --- pylot/core/read/data.py | 2 +- pylot/core/util/widgets.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 711971d2..236b852c 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -8,7 +8,7 @@ import os -class GenericDataBase(object): +class GenericDataStructure(object): ''' GenericDataBase type holds all information about the current data- base working on. diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1f531d5a..d0cc9403 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -14,6 +14,9 @@ from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg from PySide.QtGui import (QDialog, QDockWidget) +from pylot.core.util import OptionsError +from pylot.core.util import FilterOptions + class MPLWidget(FigureCanvasQTAgg): @@ -43,7 +46,7 @@ class PickDlg(QDialog): class PropertiesDlg(QDialog): def __init__(self, parent=None): - super(PropertiesWindow, self).__init__(parent) + super(PropertiesDlg, self).__init__(parent) class FilterOptionsDock(QDockWidget): @@ -56,5 +59,11 @@ class FilterOptionsDock(QDockWidget): try: fOptions = FilterOptions(**filterOptions) filterOptions = fOptions - except e: + except Exception, e: raise OptionsError('%s' % e) + + +class LoadDataDlg(QDialog): + + def __init__(self, parent=None): + super(LoadDataDlg, self).__init__(parent) From bead7a24c38506717d9ca97a27a3f686bb006fcc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 1 Apr 2014 16:49:28 +0200 Subject: [PATCH 0056/1144] documentation updated in order to automatically provide online documentation via sphinx.ext.autodoc --- pylot/core/read/inputs.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index cb48319a..fb57f2e3 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -6,9 +6,13 @@ class AutoPickParameter(object): ''' AutoPickParameters is a parameter type object capable to read and/or write parameter ASCII and binary files. + + :param fn str: Filename of the input file Parameters are given for example as follows: - + ========== ========== ======================================= + Name Value Comment + ========== ========== ======================================= phl S # phaselabel ff1 0.1 # freqmin ff2 0.5 # freqmax @@ -27,6 +31,7 @@ class AutoPickParameter(object): aerr 30;60 # adjusted_error_slope_fitting_loc_glob tsn 20;5;20;10 # length_signal_window_S/N proPh Sn # nextprominentphase + ========== ========== ======================================= ''' def __init__(self, fn=None): From 8df11078b4aa1619289cd4799878983282a78dee Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 10 Apr 2014 09:31:16 +0200 Subject: [PATCH 0057/1144] bugfix: private methods name corrected; set default values instead of 'None' --- pylot/core/read/inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index fb57f2e3..4c8569f9 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -134,9 +134,9 @@ class AutoPickParameter(object): class FilterOptions(object): - def __init__(self, filtertype=None, freq=None, order=None): + def __init__(self, filtertype='bandpass', freq=[2., 5.], order=3): self.__filterInformation = {} - self._setfilterType(filtertype) + self._setFilterType(filtertype) self._setFreq(freq) self._setOrder(order) From 6af43fc9fd3a23e264a6465c5dbca746b9181a25 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 10 Apr 2014 09:34:13 +0200 Subject: [PATCH 0058/1144] bugfix: imports corrected; imports updated, layout of 'FilterOptionsDock' started --- pylot/core/util/widgets.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d0cc9403..a2805958 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -12,10 +12,10 @@ matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg -from PySide.QtGui import (QDialog, QDockWidget) - +from PySide.QtGui import (QDialog, QDockWidget, QDoubleSpinBox, QLabel, + QGroupBox, QGridLayout) from pylot.core.util import OptionsError -from pylot.core.util import FilterOptions +from pylot.core.read import FilterOptions class MPLWidget(FigureCanvasQTAgg): @@ -61,6 +61,38 @@ class FilterOptionsDock(QDockWidget): filterOptions = fOptions except Exception, e: raise OptionsError('%s' % e) + else: + filterOptions = FilterOptions() + + freqminLabel = QLabel() + freqminLabel.setText("minimum:") + freqminSpinBox = QDoubleSpinBox() + freqminSpinBox.setRange(5e-7, 1e6) + freqminSpinBox.setDecimals(2) + freqminSpinBox.setValue(filterOptions.freq[0]) + freqmaxLabel = QLabel() + freqmaxLabel.setText("maximum:") + freqmaxSpinBox = QDoubleSpinBox() + freqmaxSpinBox.setRange(5e-7, 1e6) + freqmaxSpinBox.setDecimals(2) + + if filterOptions.filterType not in ['bandpass', 'bandstop']: + freqminLabel.setText("cutoff:") + freqmaxLabel.setEnabled(False) + freqmaxSpinBox.setEnabled(False) + + freqGroupBox = QGroupBox("Frequency range") + gbLayout = QGridLayout() + gbLayout.addWidget(freqminLabel, 0, 0) + gbLayout.addWidget(freqminSpinBox, 0, 1) + gbLayout.addWidget(freqmaxLabel, 1, 0) + gbLayout.addWidget(freqmaxSpinBox, 1, 1) + freqGroupBox.setLayout(gbLayout) + + grid = QGridLayout() + grid.addWidget(freqGroupBox, 0, 0, 2, 2) + + self.setLayout(grid) class LoadDataDlg(QDialog): From fb2553e98083306ff0ce02475f90b8dc5acbd024 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 11 Apr 2014 19:39:29 +0200 Subject: [PATCH 0059/1144] check main case; corrected usage of import --- pylot/QtPyLoT.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index ccd33728..834ee723 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -87,7 +87,7 @@ class MainWindow(QMainWindow): # create a matplotlib widget self.DataPlot = MPLWidget(parent=self) # create a layout inside the blank widget and add the matplotlib widget - layout = QtGui.QVBoxLayout(self.ui.widget_PlotArea) + layout = QVBoxLayout(self.ui.widget_PlotArea) layout.addWidget(self.DataPlot, 1) return layout @@ -131,5 +131,5 @@ def main(): pylot_form.show() pylot_app.exec_() - -main() +if __name__ == "__main__": + main() From d9ca0141de91d13ffc87bd48c9bdaa5b3ca02d64 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 11 Apr 2014 19:40:11 +0200 Subject: [PATCH 0060/1144] alphabetic order of convenience imports --- pylot/core/util/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index bfda9905..13232278 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,6 +1,6 @@ -from pylot.core.util.version import get_git_version as _getVersionString +from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS +from pylot.core.util.errors import OptionsError +from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.widgets import (PickDlg, MPLWidget, PropertiesDlg, FilterOptionsDock) -from pylot.core.util.errors import OptionsError -from pylot.core.util.connection import checkurl From 307e960d34cdf6654d9c020dda1021be02c5ac88 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 9 May 2014 15:00:08 +0200 Subject: [PATCH 0061/1144] pushbutton layout implemented (variable number of buttons according to the number of stations used) --- pylot/QtPyLoT.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 834ee723..e5b61f1b 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -107,7 +107,18 @@ class MainWindow(QMainWindow): def layoutStationButtons(self, numStations): layout = QVBoxLayout() for n in range(numStations): - stationButtons[n] = QPushButton('%s'.format(self.)) + tr = data.select(component=self.dispOptions.comp) + try: + stationButtons[n] = QPushButton('%s'.format( + tr[n].stats.station)) + except IndexError: + error = QErrorMessage(self) + errorString = QString() + errorString.setText('''Number of stations does not match number + of traces!''') + error.showMessage(errorString) + self.__del__() + layout.addWidget(stationButtons) def helpHelp(self): if internet_on(): From 3278470deb8afbb55511385df0c467d4f88926da Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 14 May 2014 11:45:45 +0200 Subject: [PATCH 0062/1144] Started to work on ticket #122 --- pylot/core/read/data.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 236b852c..d4f92d0d 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -25,7 +25,7 @@ class GenericDataStructure(object): rootExpression = stexp break structExpression.reverse() - + self.folderDepth = folderdepth self.dataBaseDict = kwargs @@ -40,15 +40,33 @@ class SeiscompDataStructure(object): :type date: str or UTCDateTime or None ''' - def __init__(self, dataType='waveform', date=None, **kwargs): + def __init__(self, dataType='waveform', sdate=None, edate=None, **kwargs): # imports from obspy.core import UTCDateTime + import numpy as np - if date is not None and not isinstance(date, UTCDateTime): - try: - dod = UTCDateTime(date) - except: - dod = UTCDateTime() + def checkDate(date): + if not isinstance(date, UTCDateTime): + return True + return False + + try: + if checkDate(sdate): + sdate = UTCDateTime(sdate) + if checkDate(edate): + edate = UTCDateTime(edate) + except TypeError: + edate = UTCDateTime() + sdate = edate - np.pi*1e7/2 + + year = '' + if not edate.year == sdate.year: + nyears = edate.year - sdate.year + for yr in range(nyears): + year += '{0:04d},'.format(sdate.year+yr) + year = '{'+year[:-1]+'}' + else: + year = '{0:04d},'.format(sdate.year) # Data type options self.__typeOptions = {'waveform': 'D', # Waveform data @@ -72,13 +90,13 @@ class SeiscompDataStructure(object): # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS self.__sdsFields = {'SDSdir': '/data/SDS', # base directory - 'YEAR': '{0:04d}'.format(now.year), # 4 digits + 'YEAR': year, # 4 digits 'NET': '??', # up to 8 characters 'STA': '????', # up to 8 characters 'CHAN': 'HH?', # up to 8 characters 'TYPE': self._getType(), # 1 character 'LOC': '', # up to 8 characters - 'DAY': '{0:03d}'.format(now.julday) # 3 digit doy + 'DAY': '{0:03d}'.format(sdate.julday) # 3 digits } self.modifiyFields(**kwargs) From cb7eb481d9a8bca79b36e8c78a5f27ec6d9b0945 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 10 Jun 2014 16:37:07 +0200 Subject: [PATCH 0063/1144] improved imports for better debugging --- pylot/QtPyLoT.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index e5b61f1b..1d71a337 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -25,13 +25,16 @@ import platform import sys from PySide.QtCore import * from PySide.QtGui import * -import helpform from obspy.core import (read, UTCDateTime) from pylot.core.util import _getVersionString -from pylot.core.read.inputs import FilterOptions +from pylot.core.read import FilterOptions from pylot.core.util import FILTERDEFAULTS from pylot.core.util import checkurl -from pylot.core.util import (PickDlg, FilterOptionsDock, PropertiesDlg) +from pylot.core.util import (PickDlg, + FilterOptionsDock, + PropertiesDlg, + MPLWidget, + HelpForm) # Version information __version__ = _getVersionString() From e659e13a0eb17c5a4c22d934144f77c486bce5b7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 05:38:41 +0200 Subject: [PATCH 0064/1144] FilterOptionsWidget debugged; next it will be tested as a QDockWidget (QMainWindow necessary) --- pylot/core/util/widgets.py | 157 ++++++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 29 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a2805958..9e426f38 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -38,9 +38,7 @@ class PickDlg(QDialog): def __init__(self, station=None, parent=None): super(PickDlg, self).__init__(parent) - filterDockWidget = FilterOptionsDock(titleString="Filter Options", - parent=self, - filterOptions=filteroptions) + pass class PropertiesDlg(QDialog): @@ -49,7 +47,7 @@ class PropertiesDlg(QDialog): super(PropertiesDlg, self).__init__(parent) -class FilterOptionsDock(QDockWidget): +class FilterOptionsDock(QDialog): def __init__(self, parent=None, titleString="Filter options", filterOptions=None): @@ -64,38 +62,139 @@ class FilterOptionsDock(QDockWidget): else: filterOptions = FilterOptions() - freqminLabel = QLabel() - freqminLabel.setText("minimum:") - freqminSpinBox = QDoubleSpinBox() - freqminSpinBox.setRange(5e-7, 1e6) - freqminSpinBox.setDecimals(2) - freqminSpinBox.setValue(filterOptions.freq[0]) - freqmaxLabel = QLabel() - freqmaxLabel.setText("maximum:") - freqmaxSpinBox = QDoubleSpinBox() - freqmaxSpinBox.setRange(5e-7, 1e6) - freqmaxSpinBox.setDecimals(2) + self.filterOptions = filterOptions - if filterOptions.filterType not in ['bandpass', 'bandstop']: - freqminLabel.setText("cutoff:") - freqmaxLabel.setEnabled(False) - freqmaxSpinBox.setEnabled(False) + self.freqminLabel = QLabel() + self.freqminLabel.setText("minimum:") + self.freqminSpinBox = QDoubleSpinBox() + self.freqminSpinBox.setRange(5e-7, 1e6) + self.freqminSpinBox.setDecimals(2) + self.freqminSpinBox.setSuffix(' Hz') + self.freqminSpinBox.setValue(filterOptions.freq[0]) + self.freqmaxLabel = QLabel() + self.freqmaxLabel.setText("maximum:") + self.freqmaxSpinBox = QDoubleSpinBox() + self.freqmaxSpinBox.setRange(5e-7, 1e6) + self.freqmaxSpinBox.setDecimals(2) + self.freqmaxSpinBox.setSuffix(' Hz') + if self.filterOptions.filterType in ['bandpass', 'bandstop']: + self.freqmaxSpinBox.setValue(self.filterOptions.freq[1]) - freqGroupBox = QGroupBox("Frequency range") - gbLayout = QGridLayout() - gbLayout.addWidget(freqminLabel, 0, 0) - gbLayout.addWidget(freqminSpinBox, 0, 1) - gbLayout.addWidget(freqmaxLabel, 1, 0) - gbLayout.addWidget(freqmaxSpinBox, 1, 1) - freqGroupBox.setLayout(gbLayout) + typeOptions = ["bandpass", "bandstop", "lowpass", "highpass"] - grid = QGridLayout() - grid.addWidget(freqGroupBox, 0, 0, 2, 2) + self.orderLabel = QLabel() + self.orderLabel.setText("Order:") + self.orderSpinBox = QSpinBox() + self.orderSpinBox.setRange(2, 10) + self.selectTypeLabel = QLabel() + self.selectTypeLabel.setText("Select filter type:") + self.selectTypeCombo = QComboBox() + self.selectTypeCombo.addItems(typeOptions) + self.selectTypeLayout = QVBoxLayout() + self.selectTypeLayout.addWidget(self.orderLabel) + self.selectTypeLayout.addWidget(self.orderSpinBox) + self.selectTypeLayout.addWidget(self.selectTypeLabel) + self.selectTypeLayout.addWidget(self.selectTypeCombo) - self.setLayout(grid) + self.freqGroupBox = QGroupBox("Frequency range") + self.freqGroupLayout = QGridLayout() + self.freqGroupLayout.addWidget(self.freqminLabel, 0, 0) + self.freqGroupLayout.addWidget(self.freqminSpinBox, 0, 1) + self.freqGroupLayout.addWidget(self.freqmaxLabel, 1, 0) + self.freqGroupLayout.addWidget(self.freqmaxSpinBox, 1, 1) + self.freqGroupBox.setLayout(self.freqGroupLayout) + + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Apply | + QDialogButtonBox.Close) + + self.layoutEditables = QHBoxLayout() + self.layoutEditables.addWidget(self.freqGroupBox) + self.layoutEditables.addLayout(self.selectTypeLayout) + + self.setLayout(self.layoutEditables) + + self.connect(self.freqminSpinBox, SIGNAL("valueChanged(double)"), + self.updateUi) + self.connect(self.freqmaxSpinBox, SIGNAL("valueChanged(double)"), + self.updateUi) + self.connect(self.orderSpinBox, SIGNAL("valueChanged(int)"), + self.updateUi) + self.connect(self.selectTypeCombo, SIGNAL("currentIndexChanged(int)"), + self.updateUi) + self.updateUi() + + def updateUi(self): + if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: + self.freqminLabel.setText("cutoff:") + self.freqmaxLabel.setEnabled(False) + self.freqmaxSpinBox.setEnabled(False) + self.freqmaxSpinBox.setValue(self.freqminSpinBox.value()) + else: + self.freqminLabel.setText("minimum:") + self.freqmaxLabel.setEnabled(True) + self.freqmaxSpinBox.setEnabled(True) + + self.filterOptions.filterType = self.selectTypeCombo.currentText() + freq = [] + freq.append(self.freqminSpinBox.value()) + if self.filterOptions.filterType in ['bandpass', 'bandstop']: + if self.freqminSpinBox.value() > self.freqmaxSpinBox.value(): + QMessageBox.warning(self, "Value error", + "Maximum frequency must be at least the " + "same value as minimum frequency (notch)!") + self.freqmaxSpinBox.setValue(self.freqminSpinBox.value()) + self.freqmaxSpinBox.selectAll() + self.freqmaxSpinBox.setFocus() + return + freq.append(self.freqmaxSpinBox.value()) + self.filterOptions.freq = freq + self.filterOptions.order = self.orderSpinBox.value() + return self.filterOptions class LoadDataDlg(QDialog): def __init__(self, parent=None): super(LoadDataDlg, self).__init__(parent) + + pass + + +class HelpForm(QDialog): + + def __init__(self, page, parent=None): + super(HelpForm, self).__init__(parent) + self.setAttribute(Qt.WA_DeleteOnClose) + self.setAttribute(Qt.WA_GroupLeader) + + backAction = QAction(QIcon(":/back.png"), "&Back", self) + backAction.setShortcut(QKeySequence.Back) + homeAction = QAction(QIcon(":/home.png"), "&Home", self) + homeAction.setShortcut("Home") + self.pageLabel = QLabel() + + toolBar = QToolBar() + toolBar.addAction(backAction) + toolBar.addAction(homeAction) + toolBar.addWidget(self.pageLabel) + self.textBrowser = QTextBrowser() + + layout = QVBoxLayout() + layout.addWidget(toolBar) + layout.addWidget(self.textBrowser, 1) + self.setLayout(layout) + + self.connect(backAction, SIGNAL("triggered()"), + self.textBrowser, SLOT("backward()")) + self.connect(homeAction, SIGNAL("triggered()"), + self.textBrowser, SLOT("home()")) + self.connect(self.textBrowser, SIGNAL("sourceChanged(QUrl)"), + self.updatePageTitle) + + self.textBrowser.setSearchPaths([":/help"]) + self.textBrowser.setSource(QUrl(page)) + self.resize(400, 600) + self.setWindowTitle("{0} Help".format(QApplication.applicationName())) + + def updatePageTitle(self): + self.pageLabel.setText(self.textBrowser.documentTitle()) From e7507a325f2bd7451fd91c280ccbb835bc88a764 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 05:43:27 +0200 Subject: [PATCH 0065/1144] fixed usage of keyword arguments; --- pylot/core/read/__init__.py | 2 +- pylot/core/read/inputs.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index de68ca8a..6530848e 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -1,2 +1,2 @@ from pylot.core.read.data import (GenericDataStructure, SeiscompDataStructure) -from pylot.core.read.inputs import (AutoPickParameter, FilterOptions) \ No newline at end of file +from pylot.core.read.inputs import (AutoPickParameter, FilterOptions) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 4c8569f9..cf827b19 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -6,7 +6,7 @@ class AutoPickParameter(object): ''' AutoPickParameters is a parameter type object capable to read and/or write parameter ASCII and binary files. - + :param fn str: Filename of the input file Parameters are given for example as follows: @@ -134,7 +134,8 @@ class AutoPickParameter(object): class FilterOptions(object): - def __init__(self, filtertype='bandpass', freq=[2., 5.], order=3): + def __init__(self, filtertype='bandpass', freq=[2., 5.], order=3, + **kwargs): self.__filterInformation = {} self._setFilterType(filtertype) self._setFreq(freq) From cb80170ccf447912a1e31d6e3a1241c531af25b2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 05:48:21 +0200 Subject: [PATCH 0066/1144] some non-functional snippets deleted to promote GUI debugging --- pylot/QtPyLoT.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 1d71a337..52601afc 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -57,22 +57,20 @@ class MainWindow(QMainWindow): self.setupUi() def loadData(self): - loadDataDlg = LoadDataDlg(self) - - dataStream = read() + pass def setupUi(self): self.setWindowIcon(QIcon("PyLoT.ico")) # create central matplotlib figure widget - dataLayout = setupPlot() + self.DataPlot = MPLWidget(parent=self) filterDockWidget = FilterOptionsDock(titleString="Filter Options", parent=self, filterOptions=filteroptions) self.eventLabel = QLabel() - self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) + self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) status = self.statusBar() status.setSizeGripEnabled(False) status.addPermanentWidget(self.eventLabel) @@ -84,16 +82,7 @@ class MainWindow(QMainWindow): maingrid.setSpacing(10) maingrid.addLayout(statLayout, 0, 0) maingrid.addLayout(dataLayout, 1, 0) - maingrid.addWidget() - - def setupPlot(self): - # create a matplotlib widget - self.DataPlot = MPLWidget(parent=self) - # create a layout inside the blank widget and add the matplotlib widget - layout = QVBoxLayout(self.ui.widget_PlotArea) - layout.addWidget(self.DataPlot, 1) - - return layout + maingrid.setCentralWidget(self.DataPlot) def plotData(self, data): if data is not None and isinstance(data, Stream): @@ -123,6 +112,8 @@ class MainWindow(QMainWindow): self.__del__() layout.addWidget(stationButtons) + return layout + def helpHelp(self): if internet_on(): pass From 46f5e55c8e0acfcb7dac712ed330a636da2638b0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 05:49:53 +0200 Subject: [PATCH 0067/1144] improved imports for better debugging --- pylot/core/util/widgets.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9e426f38..92b49319 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -12,8 +12,29 @@ matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg -from PySide.QtGui import (QDialog, QDockWidget, QDoubleSpinBox, QLabel, - QGroupBox, QGridLayout) +from PySide.QtGui import (QAction, + QApplication, + QCheckBox, + QComboBox, + QDialog, + QDialogButtonBox, + QDockWidget, + QDoubleSpinBox, + QGroupBox, + QGridLayout, + QHBoxLayout, + QIcon, + QKeySequence, + QLabel, + QMessageBox, + QSpinBox, + QTextBrowser, + QToolBar, + QVBoxLayout) +from PySide.QtCore import (Qt, + QUrl, + SIGNAL, + SLOT) from pylot.core.util import OptionsError from pylot.core.read import FilterOptions From 0481fa748be321a4c0e0e092eef2f6141fbef95b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 15:19:37 +0200 Subject: [PATCH 0068/1144] the help form should look like this or similar but it is not working at the moment; maybe QTextBrowser element of HelpForm should be replaced by QWebView (pending) --- pylot/QtPyLoT.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 52601afc..801219c0 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -115,8 +115,11 @@ class MainWindow(QMainWindow): return layout def helpHelp(self): - if internet_on(): - pass + if checkurl(): + form = HelpForm('https://ariadne.geophysik.rub.de/trac/PyLoT/wiki') + else: + form = HelpForm(':/help.html') + form.show() def main(): From 4c8174f5aecc2ec6233561936c986f5d0827534c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 15:20:55 +0200 Subject: [PATCH 0069/1144] convenience imports completed --- pylot/__init__.py | 4 ++-- pylot/core/util/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/__init__.py b/pylot/__init__.py index c724596b..d2934384 100755 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -28,5 +28,5 @@ The development of PyLoT is part of the joint research project MAGS2. from obspy.core.utcdatetime import UTCDateTime from obspy.core.util.attribdict import AttribDict -#from pylot.core.trace import Stats, Trace -#from pylot.core.stream import Stream, read +from pylot.core.read import * +from pylot.core.util import * diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 13232278..9d5fd893 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -2,5 +2,5 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError from pylot.core.util.version import get_git_version as _getVersionString -from pylot.core.util.widgets import (PickDlg, MPLWidget, PropertiesDlg, +from pylot.core.util.widgets import (HelpForm, PickDlg, MPLWidget, PropertiesDlg, FilterOptionsDock) From 00f52c444a5c32894d921e8e276bfd9b064eeb82 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 15:21:30 +0200 Subject: [PATCH 0070/1144] file head fixed --- pylot/core/util/connection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/connection.py b/pylot/core/util/connection.py index 4f99ab81..9fafa46f 100644 --- a/pylot/core/util/connection.py +++ b/pylot/core/util/connection.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- import urllib2 From a385553a291867a161c6f300ef390adeeb9410f5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Jun 2014 15:25:26 +0200 Subject: [PATCH 0071/1144] enhancements and fixes: import QTabBar started to implement PropertiesDlg deleted unnecessary buttonBox assignment entered default page for HelpForm (NOT WORKING YET) --- pylot/core/util/widgets.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 92b49319..cf8cd63c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -28,6 +28,7 @@ from PySide.QtGui import (QAction, QLabel, QMessageBox, QSpinBox, + QTabBar, QTextBrowser, QToolBar, QVBoxLayout) @@ -67,6 +68,19 @@ class PropertiesDlg(QDialog): def __init__(self, parent=None): super(PropertiesDlg, self).__init__(parent) + self.tabBar = QTabBar() + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Apply | + QDialogButtonBox.Close) + + self.connect(self.buttonBox.button(QDialogButtonBox.Apply), + SIGNAL("clicked()"), self.apply) + self.connect(self.buttonBox, SIGNAL("rejected()"), + self, SLOT("reject()")) + pass + + def apply(self): + pass + class FilterOptionsDock(QDialog): @@ -125,9 +139,6 @@ class FilterOptionsDock(QDialog): self.freqGroupLayout.addWidget(self.freqmaxSpinBox, 1, 1) self.freqGroupBox.setLayout(self.freqGroupLayout) - self.buttonBox = QDialogButtonBox(QDialogButtonBox.Apply | - QDialogButtonBox.Close) - self.layoutEditables = QHBoxLayout() self.layoutEditables.addWidget(self.freqGroupBox) self.layoutEditables.addLayout(self.selectTypeLayout) @@ -183,7 +194,8 @@ class LoadDataDlg(QDialog): class HelpForm(QDialog): - def __init__(self, page, parent=None): + def __init__(self, page='https://ariadne.geophysik.rub.de/trac/PyLoT/wiki', + parent=None): super(HelpForm, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_GroupLeader) From 0cee4cd644e962730029d4234fccee83b2a3cbba Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 3 Jul 2014 10:15:25 +0200 Subject: [PATCH 0072/1144] modified: imports adjusted to only those used in the code really --- pylot/core/util/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index cf8cd63c..70283c41 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -26,12 +26,14 @@ from PySide.QtGui import (QAction, QIcon, QKeySequence, QLabel, + QLineEdit, QMessageBox, QSpinBox, - QTabBar, + QTabWidget, QTextBrowser, QToolBar, - QVBoxLayout) + QVBoxLayout, + QWidget) from PySide.QtCore import (Qt, QUrl, SIGNAL, From a079bd331b95d82859bd90d3f4fe879153dfe592 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 3 Jul 2014 10:17:25 +0200 Subject: [PATCH 0073/1144] deletion: line deleted contain call to a not implemented method --- pylot/QtPyLoT.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 801219c0..8750a858 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -51,7 +51,6 @@ class MainWindow(QMainWindow): self.filterOptionsS = FilterOptions(**filterOptionsS) self.loadData() - self.updateArchiveType() self.updateFilterOptions() self.setupUi() From ff39d97b82eb0cb6ee8ef93a8cdcf857bcd569c9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 3 Jul 2014 10:18:42 +0200 Subject: [PATCH 0074/1144] modification: url of the wiki-page changed to full domain (certificate validation issue) --- pylot/QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 8750a858..2e4e9a30 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -115,7 +115,7 @@ class MainWindow(QMainWindow): def helpHelp(self): if checkurl(): - form = HelpForm('https://ariadne.geophysik.rub.de/trac/PyLoT/wiki') + form = HelpForm('https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') else: form = HelpForm(':/help.html') form.show() From 29700e793e755fec7d92611f93dbd54110eed093 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 3 Jul 2014 10:19:51 +0200 Subject: [PATCH 0075/1144] modification: fixed qt-resources alias for the offline help alternative --- pylot/core/util/resources.qrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/resources.qrc b/pylot/core/util/resources.qrc index 5c8b7c06..a4fcaef9 100644 --- a/pylot/core/util/resources.qrc +++ b/pylot/core/util/resources.qrc @@ -2,6 +2,6 @@ icons/pylot.ico - help/index.html + help/index.html From 1266ced05843d7a9211653f573a3f99c251843a3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 3 Jul 2014 10:21:36 +0200 Subject: [PATCH 0076/1144] modification: PropertiesDlg started (not working) --- pylot/core/util/widgets.py | 68 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 70283c41..c3f15704 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -70,10 +70,26 @@ class PropertiesDlg(QDialog): def __init__(self, parent=None): super(PropertiesDlg, self).__init__(parent) - self.tabBar = QTabBar() - self.buttonBox = QDialogButtonBox(QDialogButtonBox.Apply | + appName = QApplication.applicationName() + + self.setWindowTitle("{0} Properties".format(appName)) + + tabWidget = QTabWidget() + tabWidget.addTab(InputsTab(self), "Inputs") + tabWidget.addTab(OutputsTab(self), "Outputs") + tabWidget.addTab(PhasesTab(self), "Phases") + tabWidget.addTab(GraphicsTab(self), "Graphics") + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Apply | QDialogButtonBox.Close) + layout = QVBoxLayout() + layout.addWidget(tabWidget) + layout.addWidget(self.buttonBox) + self.setLayout(layout) + + self.connect(self.buttonBox, SIGNAL("accepted()"), self, + SLOT("accept()")) self.connect(self.buttonBox.button(QDialogButtonBox.Apply), SIGNAL("clicked()"), self.apply) self.connect(self.buttonBox, SIGNAL("rejected()"), @@ -84,6 +100,54 @@ class PropertiesDlg(QDialog): pass +class InputsTab(QWidget): + + def __init__(self, parent=None): + super(InputsTab, self).__init__(parent) + + dataDirLabel = QLabel("data directory:") + dataDirEdit = QLineEdit() + + layout = QGridLayout() + layout.addWidget(dataDirLabel, 0, 0) + layout.addWidget(dataDirEdit, 0, 1) + + self.setLayout(layout) + + +class OutputsTab(QWidget): + + def __init__(self, parent=None): + super(OutputsTab, self).__init__(parent) + + eventOutputLabel = QLabel("event ouput format") + eventOutputComboBox = QComboBox() + eventoutputformats = ["QuakeML", "VelEst"] + eventOutputComboBox.addItems(eventoutputformats) + + layout = QGridLayout() + layout.addWidget(eventOutputLabel, 0, 0) + layout.addWidget(eventOutputComboBox, 0, 1) + + self.setLayout(layout) + + +class PhasesTab(QWidget): + + def __init__(self, parent=None): + super(PhasesTab, self).__init__(parent) + + pass + + +class GraphicsTab(QWidget): + + def __init__(self, parent=None): + super(GraphicsTab, self).__init__(parent) + + pass + + class FilterOptionsDock(QDialog): def __init__(self, parent=None, titleString="Filter options", From 34e27f3e674435516d53991b5c98a04ad62b99a2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 16 Jul 2014 12:02:57 +0200 Subject: [PATCH 0077/1144] modified: enhanced readability of convience imports --- pylot/core/read/__init__.py | 7 +++++-- pylot/core/util/__init__.py | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index 6530848e..70fee522 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -1,2 +1,5 @@ -from pylot.core.read.data import (GenericDataStructure, SeiscompDataStructure) -from pylot.core.read.inputs import (AutoPickParameter, FilterOptions) +from pylot.core.read.data import (Data, + GenericDataStructure, + SeiscompDataStructure) +from pylot.core.read.inputs import (AutoPickParameter, + FilterOptions) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 9d5fd893..cf976bc3 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -2,5 +2,8 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError from pylot.core.util.version import get_git_version as _getVersionString -from pylot.core.util.widgets import (HelpForm, PickDlg, MPLWidget, PropertiesDlg, +from pylot.core.util.widgets import (HelpForm, + PickDlg, + MPLWidget, + PropertiesDlg, FilterOptionsDock) From 5268d35d39416fe0fc96f2f7beb7409154ea9896 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 16 Jul 2014 12:06:05 +0200 Subject: [PATCH 0078/1144] modified: introduce data container class --- pylot/QtPyLoT.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 2e4e9a30..6b3ad572 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -27,7 +27,8 @@ from PySide.QtCore import * from PySide.QtGui import * from obspy.core import (read, UTCDateTime) from pylot.core.util import _getVersionString -from pylot.core.read import FilterOptions +from pylot.core.read import (Data, + FilterOptions) from pylot.core.util import FILTERDEFAULTS from pylot.core.util import checkurl from pylot.core.util import (PickDlg, @@ -56,13 +57,18 @@ class MainWindow(QMainWindow): self.setupUi() def loadData(self): - pass + self.data = Data() def setupUi(self): self.setWindowIcon(QIcon("PyLoT.ico")) + xlab = 'time since {0}'.format() + # create central matplotlib figure widget - self.DataPlot = MPLWidget(parent=self) + self.DataPlot = MPLWidget(parent=self, + xlabel=xlab, + ylabel=ylab, + title=eventtitle) filterDockWidget = FilterOptionsDock(titleString="Filter Options", parent=self, @@ -83,9 +89,8 @@ class MainWindow(QMainWindow): maingrid.addLayout(dataLayout, 1, 0) maingrid.setCentralWidget(self.DataPlot) - def plotData(self, data): - if data is not None and isinstance(data, Stream): - pass + def plotData(self): + pass def updateFilterOptions(self): self.filteroptions = [self.filterOptionsP From 6e2c1851ec761ce489d4dfb3c4aa69e1cf2a9e86 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 16 Jul 2014 12:07:42 +0200 Subject: [PATCH 0079/1144] modified: added imports added: new class Data added (container class for waveform- and event data) --- pylot/core/read/data.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index d4f92d0d..622a55c8 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -6,6 +6,42 @@ # import os +from obspy.core import (read, Stream) +from obspy.core.event import Event + + +class Data(object): + ''' + Data container class providing ObSpy-Stream and -Event instances as + variables. + ''' + + def __init__(self, parent=None, wfdata=None, evtdata=None): + if wfdata is not None and isinstance(wfdata, Stream): + self.wfdata = wfdata + elif wfdata is not None: + try: + self.wfdata = read(wfdata) + except IOError, e: + msg = 'An I/O error occured while loading data!' + inform = 'Variable wfdata will be empty.' + details = '{0}'.format(e) + if parent is not None: + from PySide.QtGui import QMessageBox + warnio = QMessageBox(parent=parent) + warnio.setText(msg) + warnio.setDetailedText(details) + warnio.setStandarButtons(QMessageBox.Ok) + warnio.setIcon(QMessageBox.Warning) + else: + print msg, '\n', details + self.wfdata = Stream() + else: + self.wfdata = Stream() + if evtdata is not None and isinstance(evtdata, Event): + self.evtdata = evtdata + else: + self.evtdata = Event() class GenericDataStructure(object): From 8de25cc1496340168c4d3ca880648dca54773ea4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Thu, 17 Jul 2014 10:28:29 +0200 Subject: [PATCH 0080/1144] modified: docstring for class FilterOptions established using Sphinx-markups --- pylot/core/read/inputs.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index cf827b19..8ae0c8fb 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -133,7 +133,36 @@ class AutoPickParameter(object): class FilterOptions(object): + ''' + FilterOptions is a parameter object type providing Butterworth filter option + parameter for PyLoT. Its easy to access properties helps to manage file + based as well as parameter manipulation within the GUI. + + :type filtertype: str, optional + :param filtertype: String containing the desired filtertype For information + about the supported filter types see _`Supported Filter` section . + + :type freq: list, optional + :param freq: list of float(s) describing the cutoff limits of the filter + + :type order: int, optional + :param order: Integer value describing the order of the desired Butterworth + filter. + + .. rubric:: _`Supported Filter` + ``'bandpass'`` + Butterworth-Bandpass + + ``'bandstop'`` + Butterworth-Bandstop + + ``'lowpass'`` + Butterworth-Lowpass + + ``'highpass'`` + Butterworth-Highpass + ''' def __init__(self, filtertype='bandpass', freq=[2., 5.], order=3, **kwargs): self.__filterInformation = {} From dde360d4cca271ecc940185cca7a6f5d8b5f4d57 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Thu, 17 Jul 2014 11:04:16 +0200 Subject: [PATCH 0081/1144] modified: docstrings modified --- pylot/core/read/data.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 622a55c8..a61ae69b 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -# -# Provide user the opportunity to read arbitrary organized database -# types. This means e.g. seiscomp data structure (SDS) or event based -# EGELADOS structure. -# +# -*- coding: utf-8 -*- import os from obspy.core import (read, Stream) @@ -13,7 +9,12 @@ from obspy.core.event import Event class Data(object): ''' Data container class providing ObSpy-Stream and -Event instances as - variables. + variables. + + :type parent: PySide.QtGui.QWidget object, optional + :param parent: A PySide.QtGui.QWidget class utilized when + called by a GUI to display a PySide.QtGui.QMessageBox instead of printing + to standard out. ''' def __init__(self, parent=None, wfdata=None, evtdata=None): @@ -71,9 +72,9 @@ class SeiscompDataStructure(object): Dictionary containing the data acces information for an SDS data archive: :param str dataType: Desired data type. Default: ``'waveform'`` - :param date: Either date string or an instance of + :param sdate, edate: Either date string or an instance of :class:`obspy.core.utcdatetime.UTCDateTime. Default: ``None`` - :type date: str or UTCDateTime or None + :type sdate, edate: str or UTCDateTime or None ''' def __init__(self, dataType='waveform', sdate=None, edate=None, **kwargs): From e4ddb8b55ed2a881d9d692e9c6d2b7416dcdec96 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Fri, 25 Jul 2014 14:14:19 +0200 Subject: [PATCH 0082/1144] new module utils within package util containing helpers for this and that --- pylot/core/util/utils.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 pylot/core/util/utils.py diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py new file mode 100644 index 00000000..fd1fa215 --- /dev/null +++ b/pylot/core/util/utils.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# +# -*- coding: utf-8 -*- + +import re + +def fnConstructor(s): + + badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') + badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)') + + fn = badchars.sub('_', s) + + if badsuffix.match(fn): + fn = '_' + fn + return fn From 898169647bd473cb9744da76cbd0b2e75f24c717 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Fri, 25 Jul 2014 14:15:06 +0200 Subject: [PATCH 0083/1144] added new convenience import from new module within package --- pylot/core/util/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index cf976bc3..98a35d35 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,6 +1,7 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError +from pylot.core.util.utils import fnConstructor from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.widgets import (HelpForm, PickDlg, From db7686112450021de9778185a805dc707ed93ce3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Fri, 25 Jul 2014 14:16:11 +0200 Subject: [PATCH 0084/1144] added write support for Events in QuakeML and JSON format utilizing ObsPy --- pylot/core/read/data.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index a61ae69b..ab9b57ec 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -3,7 +3,8 @@ import os from obspy.core import (read, Stream) -from obspy.core.event import Event +from obspy.core.event import (Event, Catalog) +from pylot.core.util import fnConstructor class Data(object): @@ -44,6 +45,25 @@ class Data(object): else: self.evtdata = Event() + def exportEvent(self, fnout=None, format='QUAKEML'): + + if fnout is None: + fnout = self.event.resource_id.__str__().split('/')[-1] + # handle forbidden filenames especially on windows systems + fnout = fnConstructor(fnout) + + format = format.upper().strip() + + # export event to QuakeML or JSON via ObsPy + if format in 'QUAKEML' or 'JSON': + cat = Catalog() + cat.append(self.event) + cat.write(fnout + format.lower(), format=format) + + # export event to VelEst format + if format in 'VELEST': + pass + class GenericDataStructure(object): ''' From 12c6fecf742c25e963a791ac98ca26dd3bfefc5b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 11:55:06 +0100 Subject: [PATCH 0085/1144] modified establishment of data plot --- pylot/QtPyLoT.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 6b3ad572..a450197b 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -63,12 +63,13 @@ class MainWindow(QMainWindow): self.setWindowIcon(QIcon("PyLoT.ico")) xlab = 'time since {0}'.format() + plottitle = self._getCurrentPlotType() # create central matplotlib figure widget self.DataPlot = MPLWidget(parent=self, xlabel=xlab, - ylabel=ylab, - title=eventtitle) + ylabel=None, + title=plottitle) filterDockWidget = FilterOptionsDock(titleString="Filter Options", parent=self, @@ -90,7 +91,7 @@ class MainWindow(QMainWindow): maingrid.setCentralWidget(self.DataPlot) def plotData(self): - pass + self.data.plotData(self.DataPlot) def updateFilterOptions(self): self.filteroptions = [self.filterOptionsP From 2d776f84da994ad71b944d850b80cee2397fa292 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 11:55:39 +0100 Subject: [PATCH 0086/1144] deleted unnecessary import --- pylot/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/__init__.py b/pylot/__init__.py index d2934384..21263514 100755 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -27,6 +27,5 @@ The development of PyLoT is part of the joint research project MAGS2. ''' from obspy.core.utcdatetime import UTCDateTime -from obspy.core.util.attribdict import AttribDict from pylot.core.read import * from pylot.core.util import * From c41eb36a34ce6c1b97356b5bc7905d7c5abeedfa Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 11:56:44 +0100 Subject: [PATCH 0087/1144] widget name changed to match the actual QtGUI type name --- pylot/QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index a450197b..7eb80a88 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -32,7 +32,7 @@ from pylot.core.read import (Data, from pylot.core.util import FILTERDEFAULTS from pylot.core.util import checkurl from pylot.core.util import (PickDlg, - FilterOptionsDock, + FilterOptionsDialog, PropertiesDlg, MPLWidget, HelpForm) @@ -71,7 +71,7 @@ class MainWindow(QMainWindow): ylabel=None, title=plottitle) - filterDockWidget = FilterOptionsDock(titleString="Filter Options", + filterDockWidget = FilterOptionsDialog(titleString="Filter Options", parent=self, filterOptions=filteroptions) From 77e7f666f0fe13e70bd330498a928f2eaf6a4cb3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 11:57:34 +0100 Subject: [PATCH 0088/1144] added 'TestType' to plotting options (only for testing) --- pylot/QtPyLoT.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index 7eb80a88..c51fe212 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -56,6 +56,9 @@ class MainWindow(QMainWindow): self.setupUi() + def _getCurrentPlotType(self): + return 'TestType' + def loadData(self): self.data = Data() From c03b8a220f31ad726eb8d24e4faef1322ce554e3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:04:01 +0100 Subject: [PATCH 0089/1144] changed loading of filter options, now immediately either a error status message or a success message is displayed in the main window if the filter parameter could not be loaded or were loaded respectively --- pylot/QtPyLoT.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pylot/QtPyLoT.py b/pylot/QtPyLoT.py index c51fe212..f2225468 100755 --- a/pylot/QtPyLoT.py +++ b/pylot/QtPyLoT.py @@ -97,9 +97,14 @@ class MainWindow(QMainWindow): self.data.plotData(self.DataPlot) def updateFilterOptions(self): - self.filteroptions = [self.filterOptionsP - if not self.seismicPhase == 'S' - else self.filterOptionsS] + try: + self.filteroptions = [self.filterOptionsP + if not self.seismicPhase == 'S' + else self.filterOptionsS] + except e: + self.updateStatus('Error: %s' % e + ' ... no filteroptions loaded') + else: + self.updateStatus('Filteroptions succesfully loaded ...') def updateStatus(self, message): self.statusBar().showMessage(message, 5000) From 32cf20b81d96f5fe9526eb59e153032caf235eeb Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:06:30 +0100 Subject: [PATCH 0090/1144] avoid imports within class definitions --- pylot/core/read/data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index ab9b57ec..c62bb4fe 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import os +from PySide.QtGui import QMessageBox from obspy.core import (read, Stream) from obspy.core.event import (Event, Catalog) from pylot.core.util import fnConstructor @@ -29,10 +30,10 @@ class Data(object): inform = 'Variable wfdata will be empty.' details = '{0}'.format(e) if parent is not None: - from PySide.QtGui import QMessageBox warnio = QMessageBox(parent=parent) warnio.setText(msg) warnio.setDetailedText(details) + warnio.setInformativeText(inform) warnio.setStandarButtons(QMessageBox.Ok) warnio.setIcon(QMessageBox.Warning) else: From d4bf29e4ff2109333938653da5899b63a4ef40e8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:10:35 +0100 Subject: [PATCH 0091/1144] avoid using python keywords as format as variable name; empty method definition for data plotting --- pylot/core/read/data.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index c62bb4fe..4c3a0137 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -12,7 +12,7 @@ class Data(object): ''' Data container class providing ObSpy-Stream and -Event instances as variables. - + :type parent: PySide.QtGui.QWidget object, optional :param parent: A PySide.QtGui.QWidget class utilized when called by a GUI to display a PySide.QtGui.QMessageBox instead of printing @@ -46,25 +46,35 @@ class Data(object): else: self.evtdata = Event() - def exportEvent(self, fnout=None, format='QUAKEML'): + def exportEvent(self, fnout=None, evtformat='QUAKEML'): if fnout is None: fnout = self.event.resource_id.__str__().split('/')[-1] # handle forbidden filenames especially on windows systems fnout = fnConstructor(fnout) - format = format.upper().strip() + evtformat = evtformat.upper().strip() # export event to QuakeML or JSON via ObsPy - if format in 'QUAKEML' or 'JSON': + if evtformat in 'QUAKEML' or 'JSON': cat = Catalog() cat.append(self.event) - cat.write(fnout + format.lower(), format=format) - + cat.write(fnout + evtformat.lower(), format=evtformat) + # export event to VelEst format - if format in 'VELEST': + if evtformat in 'VELEST': + ''' + Test whether the station code is longer than 4 digits. + If that is the case a warning should be displayed explaining that + VelEst can only handle 4 digit stationcodes. Thus, the leading too + much digits will be skipped, e.g. "STS12" becomes "TS12" + ''' pass + def plotData(self, widget): + + pass #axes = widget.axes + class GenericDataStructure(object): ''' From c3e072e952984fedaca793fdb9a99b9bd3158fec Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:11:35 +0100 Subject: [PATCH 0092/1144] trying to implement generic data structure import --- pylot/core/read/data.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 4c3a0137..c2837408 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -82,11 +82,15 @@ class GenericDataStructure(object): base working on. ''' def __init__(self, stexp=None, folderdepth=4, **kwargs): - dbRegExp = {} structExpression = [] depth = 0 while stexp is not os.path.sep: - [stexp, tlexp] = os.path.split(stexp) + try: + [stexp, tlexp] = os.path.split(stexp) + except AttributeError: + rootExpression = None + structExpression = None + break structExpression.append(tlexp) depth += 1 if depth is folderdepth: @@ -95,7 +99,7 @@ class GenericDataStructure(object): structExpression.reverse() self.folderDepth = folderdepth - self.dataBaseDict = kwargs + self.dataBaseDict = {} class SeiscompDataStructure(object): From 4f440d282d8392e362d9cbf3b45d3b70977def62 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:14:59 +0100 Subject: [PATCH 0093/1144] added a string representation for data type FilterOptions --- pylot/core/read/inputs.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 8ae0c8fb..a33068cd 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -134,21 +134,21 @@ class AutoPickParameter(object): class FilterOptions(object): ''' - FilterOptions is a parameter object type providing Butterworth filter option - parameter for PyLoT. Its easy to access properties helps to manage file - based as well as parameter manipulation within the GUI. - + FilterOptions is a parameter object type providing Butterworth filter + option parameter for PyLoT. Its easy to access properties helps to manage + file based as well as parameter manipulation within the GUI. + :type filtertype: str, optional :param filtertype: String containing the desired filtertype For information about the supported filter types see _`Supported Filter` section . - + :type freq: list, optional :param freq: list of float(s) describing the cutoff limits of the filter - + :type order: int, optional :param order: Integer value describing the order of the desired Butterworth filter. - + .. rubric:: _`Supported Filter` ``'bandpass'`` @@ -170,6 +170,16 @@ class FilterOptions(object): self._setFreq(freq) self._setOrder(order) + def __str__(self): + hrs = '''\n\tFilter parameter:\n + Type:\t\t{ftype}\n + Frequencies:\t{freq}\n + Order:\t\t{order}\n + '''.format(ftype=self.filterType, + freq=self.freq, + order=self.order) + return hrs + def _getFreq(self): return self.__filterInformation['freq'] From 185b308166d7128ef9ec9436f3846c75f50541b6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:15:53 +0100 Subject: [PATCH 0094/1144] adding new package containing picking relevant data types --- pylot/core/pick/__init__.py | 1 + pylot/core/pick/automatic.py | 7 +++++++ pylot/core/pick/conventional.py | 7 +++++++ pylot/core/pick/reference.py | 14 ++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 pylot/core/pick/__init__.py create mode 100644 pylot/core/pick/automatic.py create mode 100644 pylot/core/pick/conventional.py create mode 100644 pylot/core/pick/reference.py diff --git a/pylot/core/pick/__init__.py b/pylot/core/pick/__init__.py new file mode 100644 index 00000000..4287ca86 --- /dev/null +++ b/pylot/core/pick/__init__.py @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/pylot/core/pick/automatic.py b/pylot/core/pick/automatic.py new file mode 100644 index 00000000..60a5693a --- /dev/null +++ b/pylot/core/pick/automatic.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 2 18:41:18 2014 + +@author: sebastianw +""" + diff --git a/pylot/core/pick/conventional.py b/pylot/core/pick/conventional.py new file mode 100644 index 00000000..388fab94 --- /dev/null +++ b/pylot/core/pick/conventional.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 2 18:41:08 2014 + +@author: sebastianw +""" + diff --git a/pylot/core/pick/reference.py b/pylot/core/pick/reference.py new file mode 100644 index 00000000..8e49f229 --- /dev/null +++ b/pylot/core/pick/reference.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 2 18:40:55 2014 + +@author: sebastianw +""" + +from obspy.core.event import Pick + + +class ReferencePick(Pick): + + def __init__(self): + Pick.__init__() From 224c6b625c5ecc002d48e00d67ea92152f6ecdc1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:19:04 +0100 Subject: [PATCH 0095/1144] FilterOptionsDialog processes parameter filter option of type FilterOption --- pylot/core/util/widgets.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index c3f15704..20ebc95c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -148,22 +148,17 @@ class GraphicsTab(QWidget): pass -class FilterOptionsDock(QDialog): +class FilterOptionsDialog(QDialog): def __init__(self, parent=None, titleString="Filter options", filterOptions=None): - super(FilterOptionsDock, self).__init__() - - if filterOptions and not isinstance(filterOptions, FilterOptions): - try: - fOptions = FilterOptions(**filterOptions) - filterOptions = fOptions - except Exception, e: - raise OptionsError('%s' % e) - else: - filterOptions = FilterOptions() - - self.filterOptions = filterOptions + """ + PyLoT widget FilterOptionsDialog is a QDialog object. It is an UI to + adjust parameters for filtering seismic data. + """ + super(FilterOptionsDialog, self).__init__() + + self.filterOptions = [filterOptions if filterOptions is not None else FilterOptions()][0] self.freqminLabel = QLabel() self.freqminLabel.setText("minimum:") From da18039c878ae8abbc3c380e4ad180708dfed6d6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 27 Oct 2014 12:19:25 +0100 Subject: [PATCH 0096/1144] line added --- pylot/core/util/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index fd1fa215..afbf5750 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -4,6 +4,7 @@ import re + def fnConstructor(s): badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') From 3b613d833078124b6c17185221c3957b6ba7c837 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 30 Oct 2014 08:13:05 +0100 Subject: [PATCH 0097/1144] see ticket #129 (future changes very likely) --- pylot/core/util/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 98a35d35..2d62d12c 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -3,8 +3,4 @@ from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError from pylot.core.util.utils import fnConstructor from pylot.core.util.version import get_git_version as _getVersionString -from pylot.core.util.widgets import (HelpForm, - PickDlg, - MPLWidget, - PropertiesDlg, - FilterOptionsDock) + From bff84ede8132fdf0255c20cca940151c1a17fc77 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 30 Oct 2014 13:36:19 +0100 Subject: [PATCH 0098/1144] moved QtPyLoT.py (main program) to the base directory --- pylot/QtPyLoT.py => QtPyLoT.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pylot/QtPyLoT.py => QtPyLoT.py (100%) diff --git a/pylot/QtPyLoT.py b/QtPyLoT.py similarity index 100% rename from pylot/QtPyLoT.py rename to QtPyLoT.py From 97344c9f21f7111fe813ec32a51c8bd806fcdf53 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 30 Oct 2014 13:38:03 +0100 Subject: [PATCH 0099/1144] cleaned up object type class definition for FilterOptions, programs now only use methods to access attributes --- pylot/core/read/inputs.py | 41 +++++++++++++++++--------------------- pylot/core/util/widgets.py | 14 +++++++++---- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index a33068cd..ee840b8f 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -165,39 +165,34 @@ class FilterOptions(object): ''' def __init__(self, filtertype='bandpass', freq=[2., 5.], order=3, **kwargs): - self.__filterInformation = {} - self._setFilterType(filtertype) - self._setFreq(freq) - self._setOrder(order) + self.setFilterType(filtertype) + self.setFreq(freq) + self.setOrder(order) def __str__(self): hrs = '''\n\tFilter parameter:\n Type:\t\t{ftype}\n Frequencies:\t{freq}\n Order:\t\t{order}\n - '''.format(ftype=self.filterType, - freq=self.freq, - order=self.order) + '''.format(ftype=self.getFilterType(), + freq=self.getFreq(), + order=self.getOrder()) return hrs - def _getFreq(self): - return self.__filterInformation['freq'] + def getFreq(self): + return self.freq - def _setFreq(self, freq): - self.__filterInformation['freq'] = freq + def setFreq(self, freq): + self.freq = freq - def _getOrder(self): - return self.__filterInformation['order'] + def getOrder(self): + return self.order - def _setOrder(self, order): - self.__filterInformation['order'] = order + def setOrder(self, order): + self.order = order - def _getFilterType(self): - return self.__filterInformation['filtertype'] + def getFilterType(self): + return self.filterType - def _setFilterType(self, filtertype): - self.__filterInformation['filtertype'] = filtertype - - filterType = property(fget=_getFilterType, fset=_setFilterType) - order = property(fget=_getOrder, fset=_setOrder) - freq = property(fget=_getFreq, fset=_setFreq) + def setFilterType(self, filtertype): + self.filterType = filtertype diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 20ebc95c..a0476cd0 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -157,8 +157,11 @@ class FilterOptionsDialog(QDialog): adjust parameters for filtering seismic data. """ super(FilterOptionsDialog, self).__init__() - - self.filterOptions = [filterOptions if filterOptions is not None else FilterOptions()][0] + + if filterOptions is not None: + self.filterOptions = filterOptions + else: + self.filterOptions = FilterOptions() self.freqminLabel = QLabel() self.freqminLabel.setText("minimum:") @@ -166,7 +169,7 @@ class FilterOptionsDialog(QDialog): self.freqminSpinBox.setRange(5e-7, 1e6) self.freqminSpinBox.setDecimals(2) self.freqminSpinBox.setSuffix(' Hz') - self.freqminSpinBox.setValue(filterOptions.freq[0]) + self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0]) self.freqmaxLabel = QLabel() self.freqmaxLabel.setText("maximum:") self.freqmaxSpinBox = QDoubleSpinBox() @@ -174,7 +177,7 @@ class FilterOptionsDialog(QDialog): self.freqmaxSpinBox.setDecimals(2) self.freqmaxSpinBox.setSuffix(' Hz') if self.filterOptions.filterType in ['bandpass', 'bandstop']: - self.freqmaxSpinBox.setValue(self.filterOptions.freq[1]) + self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) typeOptions = ["bandpass", "bandstop", "lowpass", "highpass"] @@ -243,6 +246,9 @@ class FilterOptionsDialog(QDialog): self.filterOptions.freq = freq self.filterOptions.order = self.orderSpinBox.value() return self.filterOptions + + def getFilterOptions(self): + return self.filterOptions class LoadDataDlg(QDialog): From 7fd191dcc6092ba5c8d6dbe8219151ac87617dd6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 30 Oct 2014 13:38:41 +0100 Subject: [PATCH 0100/1144] component testing scripts added --- testPropDlg.py | 11 +++++++++++ testUIcomponents.py | 15 +++++++++++++++ 2 files changed, 26 insertions(+) create mode 100755 testPropDlg.py create mode 100755 testUIcomponents.py diff --git a/testPropDlg.py b/testPropDlg.py new file mode 100755 index 00000000..d4c68871 --- /dev/null +++ b/testPropDlg.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import sys, time +from PySide.QtGui import QApplication +from pylot.core.util.widgets import PropertiesDlg + +app = QApplication(sys.argv) + +win = PropertiesDlg() +win.show() +app.exec_() \ No newline at end of file diff --git a/testUIcomponents.py b/testUIcomponents.py new file mode 100755 index 00000000..ed2ae33c --- /dev/null +++ b/testUIcomponents.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +import sys, time +from PySide.QtGui import QApplication +from pylot.core.util.widgets import FilterOptionsDialog, PropertiesDlg, HelpForm + +dialogs = [FilterOptionsDialog, PropertiesDlg, HelpForm] + +app = QApplication(sys.argv) + +for dlg in dialogs: + win = dlg() + win.show() + time.sleep(10) + win.destroy() \ No newline at end of file From 013c948b3362523186fcf8885b2a2f9d9253a6d3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 6 Nov 2014 15:05:56 +0100 Subject: [PATCH 0101/1144] fix convenience import problems --- pylot/__init__.py | 6 +----- pylot/core/util/__init__.py | 5 +++++ pylot/core/util/widgets.py | 28 ++++++++++++---------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/pylot/__init__.py b/pylot/__init__.py index 21263514..7e113674 100755 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -24,8 +24,4 @@ The development of PyLoT is part of the joint research project MAGS2. :license: GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) -''' - -from obspy.core.utcdatetime import UTCDateTime -from pylot.core.read import * -from pylot.core.util import * +''' \ No newline at end of file diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 2d62d12c..26b84168 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -2,5 +2,10 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError from pylot.core.util.utils import fnConstructor +from pylot.core.util.widgets import PickDlg +from pylot.core.util.widgets import HelpForm +from pylot.core.util.widgets import FilterOptionsDialog +from pylot.core.util.widgets import PropertiesDlg +from pylot.core.util.widgets import MPLWidget from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a0476cd0..4eb109dd 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -14,11 +14,9 @@ from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg from PySide.QtGui import (QAction, QApplication, - QCheckBox, QComboBox, QDialog, QDialogButtonBox, - QDockWidget, QDoubleSpinBox, QGroupBox, QGridLayout, @@ -30,7 +28,6 @@ from PySide.QtGui import (QAction, QMessageBox, QSpinBox, QTabWidget, - QTextBrowser, QToolBar, QVBoxLayout, QWidget) @@ -38,7 +35,7 @@ from PySide.QtCore import (Qt, QUrl, SIGNAL, SLOT) -from pylot.core.util import OptionsError +from PySide.QtWebKit import QWebView from pylot.core.read import FilterOptions @@ -217,7 +214,6 @@ class FilterOptionsDialog(QDialog): self.updateUi) self.connect(self.selectTypeCombo, SIGNAL("currentIndexChanged(int)"), self.updateUi) - self.updateUi() def updateUi(self): if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: @@ -248,7 +244,7 @@ class FilterOptionsDialog(QDialog): return self.filterOptions def getFilterOptions(self): - return self.filterOptions + return self.filterOptions class LoadDataDlg(QDialog): @@ -261,8 +257,7 @@ class LoadDataDlg(QDialog): class HelpForm(QDialog): - def __init__(self, page='https://ariadne.geophysik.rub.de/trac/PyLoT/wiki', - parent=None): + def __init__(self, page=QUrl(':/help.html'), parent=None): super(HelpForm, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_GroupLeader) @@ -277,24 +272,25 @@ class HelpForm(QDialog): toolBar.addAction(backAction) toolBar.addAction(homeAction) toolBar.addWidget(self.pageLabel) - self.textBrowser = QTextBrowser() + self.webBrowser = QWebView() + self.webBrowser.setHtml(page) layout = QVBoxLayout() layout.addWidget(toolBar) - layout.addWidget(self.textBrowser, 1) + layout.addWidget(self.webBrowser, 1) self.setLayout(layout) self.connect(backAction, SIGNAL("triggered()"), - self.textBrowser, SLOT("backward()")) + self.webBrowser, SLOT("backward()")) self.connect(homeAction, SIGNAL("triggered()"), - self.textBrowser, SLOT("home()")) - self.connect(self.textBrowser, SIGNAL("sourceChanged(QUrl)"), + self.webBrowser, SLOT("home()")) + self.connect(self.webBrowser, SIGNAL("sourceChanged(QUrl)"), self.updatePageTitle) - self.textBrowser.setSearchPaths([":/help"]) - self.textBrowser.setSource(QUrl(page)) + self.webBrowser.setSearchPaths([":/help"]) + self.webBrowser.setSource(QUrl(page)) self.resize(400, 600) self.setWindowTitle("{0} Help".format(QApplication.applicationName())) def updatePageTitle(self): - self.pageLabel.setText(self.textBrowser.documentTitle()) + self.pageLabel.setText(self.webBrowser.documentTitle()) From 86803cdff004a298914cf18463be863cde93503d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 6 Nov 2014 15:07:05 +0100 Subject: [PATCH 0102/1144] debugging UI problems to get the main application running --- QtPyLoT.py | 32 ++++++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/help/index.html | 4 ++-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f2225468..0abcac96 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -26,6 +26,7 @@ import sys from PySide.QtCore import * from PySide.QtGui import * from obspy.core import (read, UTCDateTime) +from pylot import * from pylot.core.util import _getVersionString from pylot.core.read import (Data, FilterOptions) @@ -46,13 +47,17 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) + # initialize filter parameter filterOptionsP = FILTERDEFAULTS['P'] filterOptionsS = FILTERDEFAULTS['S'] self.filterOptionsP = FilterOptions(**filterOptionsP) self.filterOptionsS = FilterOptions(**filterOptionsS) + # initialize data + self.data = None self.loadData() self.updateFilterOptions() + self.startTime = min([tr.stats.starttime for tr in self.data]) self.setupUi() @@ -63,9 +68,9 @@ class MainWindow(QMainWindow): self.data = Data() def setupUi(self): - self.setWindowIcon(QIcon("PyLoT.ico")) + self.setWindowIcon(QIcon(":/pylot.ico")) - xlab = 'time since {0}'.format() + xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') plottitle = self._getCurrentPlotType() # create central matplotlib figure widget @@ -74,9 +79,9 @@ class MainWindow(QMainWindow): ylabel=None, title=plottitle) - filterDockWidget = FilterOptionsDialog(titleString="Filter Options", + filterDlg = FilterOptionsDialog(titleString="Filter Options", parent=self, - filterOptions=filteroptions) + filterOptions=self.filteroptions) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) @@ -86,6 +91,7 @@ class MainWindow(QMainWindow): status.showMessage("Ready", 5000) statLayout = self.layoutStationButtons(self.numStations) + dataLayout = MPLWidget() maingrid = QGridLayout() maingrid.setSpacing(10) @@ -96,12 +102,18 @@ class MainWindow(QMainWindow): def plotData(self): self.data.plotData(self.DataPlot) + def adjustFilterOptions(self): + filterDlg = FilterOptionsDialog(titleString="Filter Options", + parent=self, + filterOptions=self.filteroptions) + filterDlg.connect() + def updateFilterOptions(self): try: self.filteroptions = [self.filterOptionsP if not self.seismicPhase == 'S' else self.filterOptionsS] - except e: + except Exception, e: self.updateStatus('Error: %s' % e + ' ... no filteroptions loaded') else: self.updateStatus('Filteroptions succesfully loaded ...') @@ -111,16 +123,16 @@ class MainWindow(QMainWindow): def layoutStationButtons(self, numStations): layout = QVBoxLayout() + stationButtons = [] for n in range(numStations): - tr = data.select(component=self.dispOptions.comp) + tr = self.data.select(component=self.dispOptions.comp) try: stationButtons[n] = QPushButton('%s'.format( tr[n].stats.station)) except IndexError: error = QErrorMessage(self) - errorString = QString() - errorString.setText('''Number of stations does not match number - of traces!''') + errorString = '''Number of stations does not match number of + traces!''' error.showMessage(errorString) self.__del__() layout.addWidget(stationButtons) @@ -136,7 +148,7 @@ class MainWindow(QMainWindow): def main(): - # create th Qt application + # create the Qt application pylot_app = QApplication(sys.argv) # set Application Information diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index b7161198..7c85ce04 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0.1a1 \ No newline at end of file +7fd1-dirty diff --git a/pylot/core/util/help/index.html b/pylot/core/util/help/index.html index dabf3001..2ce2ef02 100644 --- a/pylot/core/util/help/index.html +++ b/pylot/core/util/help/index.html @@ -4,13 +4,13 @@ exporting these as numerous standard phase format and localize the corresponding seismic event with external software as, e.g.:

Read more on the -PyLoT WikiPage.

+PyLoT WikiPage.

Bug reports are very much appreciated and can also be delivered on our PyLoT TracPage after successful registration.

From 8c66f1823a1e8cd2d7f88a5fba2ab177c37b1bbb Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 13 Nov 2014 11:27:52 +0100 Subject: [PATCH 0103/1144] cleaning up main window for first test runs --- QtPyLoT.py | 57 +++++++++++++++++++------------------ pylot/core/util/__init__.py | 2 ++ pylot/core/util/layouts.py | 23 +++++++++++++++ pylot/core/util/widgets.py | 20 +++++-------- 4 files changed, 61 insertions(+), 41 deletions(-) create mode 100644 pylot/core/util/layouts.py diff --git a/QtPyLoT.py b/QtPyLoT.py index 0abcac96..8e1b8c22 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -37,6 +37,7 @@ from pylot.core.util import (PickDlg, PropertiesDlg, MPLWidget, HelpForm) +from pylot.core.util import layoutStationButtons # Version information __version__ = _getVersionString() @@ -67,6 +68,12 @@ class MainWindow(QMainWindow): def loadData(self): self.data = Data() + def getData(self): + return self.data + + def getDataWidget(self): + return self.DataPlot + def setupUi(self): self.setWindowIcon(QIcon(":/pylot.ico")) @@ -79,10 +86,6 @@ class MainWindow(QMainWindow): ylabel=None, title=plottitle) - filterDlg = FilterOptionsDialog(titleString="Filter Options", - parent=self, - filterOptions=self.filteroptions) - self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) status = self.statusBar() @@ -90,23 +93,33 @@ class MainWindow(QMainWindow): status.addPermanentWidget(self.eventLabel) status.showMessage("Ready", 5000) - statLayout = self.layoutStationButtons(self.numStations) - dataLayout = MPLWidget() + statLayout = layoutStationButtons(self.getData(), self.getComponent()) + dataLayout = self.getDataWidget() maingrid = QGridLayout() maingrid.setSpacing(10) maingrid.addLayout(statLayout, 0, 0) maingrid.addLayout(dataLayout, 1, 0) - maingrid.setCentralWidget(self.DataPlot) + maingrid.setCentralWidget(dataLayout) def plotData(self): self.data.plotData(self.DataPlot) def adjustFilterOptions(self): - filterDlg = FilterOptionsDialog(titleString="Filter Options", + fstring = "Filter Options ({0})".format(self.getSeismicPhase()) + filterDlg = FilterOptionsDialog(titleString=fstring, parent=self, - filterOptions=self.filteroptions) - filterDlg.connect() + filterOptions=self.getFilterOptions()) + filterDlg.accepted.connect(filterDlg.getFilterOptions) + + def getFilterOptions(self): + return self.filteroptions + + def setFilterOptions(self, filterOptions): + cases = {'P':self.filterOptionsP, + 'S':self.filterOptionsS} + cases[self.getSeismicPhase()] = filterOptions + self.updateFilterOptions() def updateFilterOptions(self): try: @@ -118,27 +131,15 @@ class MainWindow(QMainWindow): else: self.updateStatus('Filteroptions succesfully loaded ...') + def getSeismicPhase(self): + return self.seismicPhase + + def setSeismicPhase(self, phase): + self.seismicPhase = self.seismicPhaseButton.getValue() + def updateStatus(self, message): self.statusBar().showMessage(message, 5000) - def layoutStationButtons(self, numStations): - layout = QVBoxLayout() - stationButtons = [] - for n in range(numStations): - tr = self.data.select(component=self.dispOptions.comp) - try: - stationButtons[n] = QPushButton('%s'.format( - tr[n].stats.station)) - except IndexError: - error = QErrorMessage(self) - errorString = '''Number of stations does not match number of - traces!''' - error.showMessage(errorString) - self.__del__() - layout.addWidget(stationButtons) - - return layout - def helpHelp(self): if checkurl(): form = HelpForm('https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 26b84168..ef703f2e 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,6 +1,7 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError +from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor from pylot.core.util.widgets import PickDlg from pylot.core.util.widgets import HelpForm @@ -9,3 +10,4 @@ from pylot.core.util.widgets import PropertiesDlg from pylot.core.util.widgets import MPLWidget from pylot.core.util.version import get_git_version as _getVersionString + diff --git a/pylot/core/util/layouts.py b/pylot/core/util/layouts.py new file mode 100644 index 00000000..7f7cf7a5 --- /dev/null +++ b/pylot/core/util/layouts.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# +# -*- coding: utf-8 -*- +''' +Created on 10.11.2014 + +@author: sebastianw +''' + +from PySide.QtGui import (QVBoxLayout, + QPushButton) + +def layoutStationButtons(data, comp): + layout = QVBoxLayout() + stationButtons = [] + st = data.select(component=comp) + numStations = len(st) + for n in range(numStations): + stationButtons[n] = QPushButton('%s'.format( + st[n].stats.station)) + layout.addWidget(stationButtons) + + return layout \ No newline at end of file diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 4eb109dd..1bb32bb6 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -206,14 +206,10 @@ class FilterOptionsDialog(QDialog): self.setLayout(self.layoutEditables) - self.connect(self.freqminSpinBox, SIGNAL("valueChanged(double)"), - self.updateUi) - self.connect(self.freqmaxSpinBox, SIGNAL("valueChanged(double)"), - self.updateUi) - self.connect(self.orderSpinBox, SIGNAL("valueChanged(int)"), - self.updateUi) - self.connect(self.selectTypeCombo, SIGNAL("currentIndexChanged(int)"), - self.updateUi) + self.freqminSpinBox.connect(self.updateUi) + self.freqmaxSpinBox.connect(self.updateUi) + self.orderSpinBox.connect(self.updateUi) + self.selectTypeCombo.connect(self.updateUi) def updateUi(self): if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: @@ -241,7 +237,7 @@ class FilterOptionsDialog(QDialog): freq.append(self.freqmaxSpinBox.value()) self.filterOptions.freq = freq self.filterOptions.order = self.orderSpinBox.value() - return self.filterOptions + return self.getFilterOptions() def getFilterOptions(self): return self.filterOptions @@ -257,7 +253,7 @@ class LoadDataDlg(QDialog): class HelpForm(QDialog): - def __init__(self, page=QUrl(':/help.html'), parent=None): + def __init__(self, page=QUrl('https://ariadne.geophysik.rub.de/trac/PyLoT'), parent=None): super(HelpForm, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_GroupLeader) @@ -273,7 +269,7 @@ class HelpForm(QDialog): toolBar.addAction(homeAction) toolBar.addWidget(self.pageLabel) self.webBrowser = QWebView() - self.webBrowser.setHtml(page) + self.webBrowser.load(page) layout = QVBoxLayout() layout.addWidget(toolBar) @@ -287,8 +283,6 @@ class HelpForm(QDialog): self.connect(self.webBrowser, SIGNAL("sourceChanged(QUrl)"), self.updatePageTitle) - self.webBrowser.setSearchPaths([":/help"]) - self.webBrowser.setSource(QUrl(page)) self.resize(400, 600) self.setWindowTitle("{0} Help".format(QApplication.applicationName())) From c40aec192c2f882fc02361820adcf7c2c71ebace Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 13 Nov 2014 11:30:19 +0100 Subject: [PATCH 0104/1144] test modules added and modified (not working yet) --- pylot/core/util/testUtils.py | 26 ++++++++++++++++++++++++++ pylot/core/util/testWidgets.py | 18 ++++++++++++++++++ testHelpForm.py | 11 +++++++++++ testUIcomponents.py | 2 +- 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 pylot/core/util/testUtils.py create mode 100644 pylot/core/util/testWidgets.py create mode 100755 testHelpForm.py diff --git a/pylot/core/util/testUtils.py b/pylot/core/util/testUtils.py new file mode 100644 index 00000000..9913c940 --- /dev/null +++ b/pylot/core/util/testUtils.py @@ -0,0 +1,26 @@ +''' +Created on 10.11.2014 + +@author: sebastianw +''' +import unittest + + +class Test(unittest.TestCase): + + + def setUp(self): + pass + + + def tearDown(self): + pass + + + def testName(self): + pass + + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.testName'] + unittest.main() \ No newline at end of file diff --git a/pylot/core/util/testWidgets.py b/pylot/core/util/testWidgets.py new file mode 100644 index 00000000..f5582362 --- /dev/null +++ b/pylot/core/util/testWidgets.py @@ -0,0 +1,18 @@ +''' +Created on 10.11.2014 + +@author: sebastianw +''' +import unittest + + +class Test(unittest.TestCase): + + + def testName(self): + pass + + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.testName'] + unittest.main() \ No newline at end of file diff --git a/testHelpForm.py b/testHelpForm.py new file mode 100755 index 00000000..fe1d5a3e --- /dev/null +++ b/testHelpForm.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import sys, time +from PySide.QtGui import QApplication +from pylot.core.util.widgets import HelpForm + +app = QApplication(sys.argv) + +win = HelpForm() +win.show() +app.exec_() diff --git a/testUIcomponents.py b/testUIcomponents.py index ed2ae33c..f422df81 100755 --- a/testUIcomponents.py +++ b/testUIcomponents.py @@ -11,5 +11,5 @@ app = QApplication(sys.argv) for dlg in dialogs: win = dlg() win.show() - time.sleep(10) + time.sleep(1) win.destroy() \ No newline at end of file From fbce83293d5881cef3bbd0ce7ca3716dd8f0aad0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 14 Nov 2014 07:40:00 +0100 Subject: [PATCH 0105/1144] =?UTF-8?q?initial=20import=20of=20classes=20for?= =?UTF-8?q?=20automatic=20picking=20purposes=20[just=20imported=20by=20me;?= =?UTF-8?q?=20module=20has=20originally=20been=20written=20by=20Ludger=20K?= =?UTF-8?q?=C3=BCperkoch]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pylot/core/pick/CharFuns.py | 342 ++++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 pylot/core/pick/CharFuns.py diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py new file mode 100644 index 00000000..0fb5def1 --- /dev/null +++ b/pylot/core/pick/CharFuns.py @@ -0,0 +1,342 @@ +# -*- coding: utf-8 -*- +""" +Created Oct/Nov 2014 + +Implementation of the Characteristic Functions (CF) published and described in: + +Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & EGELADOS Working Group, 2010: +Automated determination of P-phase arrival times at regional and local distances +using higher order statistics, Geophys. J. Int., 181, 1159-1170 + +Kueperkoch, L., Meier, T., Bruestle, A., Lee, J., Friederich, W., & EGELADOS +Working Group, 2012: Automated determination of S-phase arrival times using +autoregressive prediction: application ot local and regional distances, Geophys. J. Int., +188, 687-702. + +:author: MAGS2 EP3 working group +""" +import numpy as np +from obspy.core import Stream + +class CharacteristicFunction(object): + ''' + SuperClass for different types of characteristic functions. + ''' + def __init__(self, data, cut, t2, order, t1=None, fnoise=0.001): + ''' + Initialize data type object with information from the original + Seismogram. + + :param: data + :type: `~obspy.core.stream.Stream` + + :param: cut + :type: tuple + + :param: t2 + :type: float + + :param: order + :type: int + + :param: t1 + :type: float (optional, only for AR) + + :param: fnoise + :type: float (optional, only for AR) + ''' + + assert isinstance(data, Stream), "%s is not a Stream object" % str(data) + + self.orig_data = data[0] + self.dt = self.orig_data.stats.delta + self.setCut(cut) + self.setTime1(t1) + self.setTime2(t2) + self.setOrder(order) + self.setFnoise(fnoise) + self.calcCF(self.getDataArray()) + self.arpara = np.array([]) + self.xpred = np.array([]) + + def __str__(self): + return '''\n\t{name} object:\n + Cut:\t\t{cut}\n + t1:\t{t1}\n + t2:\t{t2}\n + Order:\t\t{order}\n + Fnoise:\t{fnoise}\n + '''.format(name=type(self).__name__, + cut=self.getCut(), + t1=self.getTime1(), + t2=self.getTime2(), + order=self.getOrder(), + fnoise=self.getFnoise()) + + def getCut(self): + return self.cut + + def setCut(self, cut): + self.cut = cut + + def getTime1(self): + return self.t1 + + def setTime1(self, t1): + self.t1 = t1 + + def getTime2(self): + return self.t2 + + def setTime2(self, t2): + self.t2 = t2 + + def getOrder(self): + return self.order + + def setOrder(self, order): + self.order = order + + def getIncrement(self): + return self.dt + + def getFnoise(self): + return self.fnoise + + def setFnoise(self, fnoise): + self.fnoise = fnoise + + def getCF(self): + return self.cf + + def getDataArray(self, cut=None): + ''' + If cut times are given, time series is cut from cut[0] (start time) + till cut[1] (stop time) in order to calculate CF for certain part + only where you expect the signal! + input: cut (tuple) () + cutting window + ''' + if cut is not None: + if self.cut[0] == 0: + start = 0 + else: + start = self.cut[0] / self.dt + stop = self.cut[1] / self.dt + data = self.orig_data.data[start:stop] + return data + + return self.orig_data.data + + def calcCF(self, data=None): + self.cf = data + + def arDet(self, data, order, rind, ldet): + pass + + def arPred(self, data, arpara, rind, lpred): + pass + + + +class AICcf(CharacteristicFunction): + ''' + Function to calculate the Akaike Information Criterion (AIC) after + Maeda (1985). + :param: data, time series (whether seismogram or CF) + :type: tuple + + Output: AIC function + ''' + + def calcCF(self, data): + print 'Calculating AIC ...' + xnp = self.getDataArray() + datlen = len(xnp) + k = np.arange(1, datlen) + cf = np.zeros(datlen) + cumsumcf = np.cumsum(np.power(xnp, 2)) + i = np.where(cumsumcf == 0) + cumsumcf[i] = np.finfo(np.float64).eps + cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * + np.log((cumsumcf[datlen - 1] - + cumsumcf[k - 1]) / (datlen - k + 1))) + cf[0] = cf[1] + inf = np.isinf(cf) + ff = np.where(inf == 'True') + if len(ff) >= 1: + cf[ff] = 0 + + self.cf = cf - np.mean(cf) + + +class HOScf(CharacteristicFunction): + ''' + Function to calculate skewness (statistics of order 3) or kurtosis + (statistics of order 4), using one long moving window, as published + in Kueperkoch et al. (2010). + ''' + + def calcCF(self, data): + + xnp = self.getDataArray(self.getCut()) + + if self.getOrder() == 3: # this is skewness + print 'Calculating skewness ...' + y = np.power(xnp, 3) + y1 = np.power(xnp, 2) + elif self.getOrder() == 4: # this is kurtosis + print 'Calculating kurtosis ...' + y = np.power(xnp, 4) + y1 = np.power(xnp, 2) + + # Initialisation + # t2: long term moving window + ilta = round(self.getTime2() / self.getIncrement()) + lta = y[0] + lta1 = y1[0] + + # moving windows + LTA = np.zeros(len(xnp)) + for j in range(3, len(xnp)): + if j <= ilta: + lta = (y[j] + lta * (j - 1)) / j + lta1 = (y1[j] + lta1 * (j - 1)) / j + else: + lta = (y[j] - y[j - ilta]) / ilta + lta + lta1 = (y1[j] - y1[j - ilta]) / ilta + lta1 + # define LTA + if self.getOrder() == 3: + LTA[j] = lta / np.power(lta1, 1.5) + elif self.getOrder() == 4: + LTA[j] = lta / np.power(lta1, 2) + + LTA[0:3] = 0 + + self.cf = LTA + + +class ARZcf(CharacteristicFunction): + + def calcCF(self, data): + + print 'Calculating AR-prediction error from single trace ...' + xnp = self.getDataArray(self.getCut()) + + # some parameters needed + # add noise to time series + xnoise = xnp + np.random.normal(0.0, 1.0, len(xnp)) * self.getFnoise() * max(abs(xnp)) + tend = len(xnp) + # Time1: length of AR-determination window [sec] + # Time2: length of AR-prediction window [sec] + ldet = int(round(self.getTime1() / self.getIncrement())) # length of AR-determination window [samples] + lpred = int(np.ceil(self.getTime2() / self.getIncrement())) # length of AR-prediction window [samples] + + cf = [] + step = ldet + self.getOrder() - 1 + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): + if i == step: + ''' + In order to speed up the algorithm AR parameters are kept for time + intervals of length lpred + ''' + # determination of AR coefficients + self.arDet(xnoise, self.getOrder(), i - ldet, i) + step = step + lpred + + # AR prediction of waveform using calculated AR coefficients + self.arPred(xnp, self.arpara, i + 1, lpred) + # prediction error = CF + err = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred] - xnp[i:i + lpred], 2)) / lpred) + cf.append(err) + + # convert list to numpy array + cf = np.asarray(cf) + self.cf = cf + + def arDet(self, data, order, rind, ldet): + ''' + Function to calculate AR parameters arpara after Thomas Meier (CAU), published + in Kueperkoch et al. (2012). This function solves SLE using the Moore- + Penrose inverse, i.e. the least-squares approach. + :param: data, time series to calculate AR parameters from + :type: array + + :param: order, order of AR process + :type: int + + :param: rind, first running summation index + :type: int + + :param: ldet, length of AR-determination window (=end of summation index) + :type: int + + Output: AR parameters arpara + ''' + + # recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) + rhs = np.zeros(self.getOrder()) + for k in range(0, self.getOrder()): + for i in range(rind, ldet): + rhs[k] = rhs[k] + data[i] * data[i - k] + + # recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) + A = np.array([[0, 0], [0, 0]]) + for k in range(1, self.getOrder() + 1): + for j in range(1, k + 1): + for i in range(rind, ldet): + ki = k - 1 + ji = j - 1 + A[ki, ji] = A[ki, ji] + data[i - ji] * data[i - ki] + + A[ji, ki] = A[ki, ji] + + # apply Moore-Penrose inverse for SVD yielding the AR-parameters + self.arpara = np.dot(np.linalg.pinv(A), rhs) + + def arPred(self, data, arpara, rind, lpred): + ''' + Function to predict waveform, assuming an autoregressive process of order + p (=size(arpara)), with AR parameters arpara calculated in arDet. After + Thomas Meier (CAU), published in Kueperkoch et al. (2012). + :param: data, time series to be predicted + :type: array + + :param: arpara, AR parameters + :type: float + + :param: rind, first running summation index + :type: int + + :param: lpred, length of prediction window (=end of summation index) + :type: int + + Output: predicted waveform z + ''' + # be sure of the summation indeces + if rind < len(arpara) + 1: + rind = len(arpara) + 1 + if rind > len(data) - lpred + 1: + rind = len(data) - lpred + 1 + if lpred < 1: + lpred = 1 + if lpred > len(data) - 1: + lpred = len(data) - 1 + + z = np.append(data[0:rind], np.zeros(lpred)) + for i in range(rind, rind + lpred): + for j in range(1, len(arpara) + 1): + ji = j - 1 + z[i] = z[i] + arpara[ji] * z[i - ji] + + self.xpred = z + + +class ARHcf(CharacteristicFunction): + + pass + + +class AR3Ccf(CharacteristicFunction): + + pass From 03033f57a139b9f51310eb581663d43cc5d1c1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 20 Nov 2014 09:05:30 +0100 Subject: [PATCH 0106/1144] Included autoregressive prediction on horizontal components --- pylot/core/pick/CharFuns.py | 247 +++++++++++++++++++++++++++--------- 1 file changed, 190 insertions(+), 57 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 0fb5def1..1893455f 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -17,6 +17,7 @@ autoregressive prediction: application ot local and regional distances, Geophys. """ import numpy as np from obspy.core import Stream +import scipy class CharacteristicFunction(object): ''' @@ -46,10 +47,10 @@ class CharacteristicFunction(object): :type: float (optional, only for AR) ''' - assert isinstance(data, Stream), "%s is not a Stream object" % str(data) + assert isinstance(data, Stream), "%s is not a stream object" % str(data) - self.orig_data = data[0] - self.dt = self.orig_data.stats.delta + self.orig_data = data + self.dt = self.orig_data[0].stats.delta self.setCut(cut) self.setTime1(t1) self.setTime2(t2) @@ -118,26 +119,33 @@ class CharacteristicFunction(object): cutting window ''' if cut is not None: - if self.cut[0] == 0: - start = 0 - else: - start = self.cut[0] / self.dt - stop = self.cut[1] / self.dt - data = self.orig_data.data[start:stop] - return data - - return self.orig_data.data - + if self.cut[0] == 0: + start = 0 + else: + start = self.cut[0] / self.dt + stop = self.cut[1] / self.dt + if len(self.orig_data) == 1: + data = self.orig_data[0].data[start:stop] + return data + elif len(self.orig_data) == 2: + hh = self.orig_data.copy() + h1 = hh[0].copy() + h2 = hh[1].copy() + hh[0].data = h1.data[start:stop] + hh[1].data = h2.data[start:stop] + data = hh + return data + else: + if len(self.orig_data) == 1: + data = self.orig_data[0] + return data + elif len(self.orig_data) == 2: + data = self.orig_data + return data + def calcCF(self, data=None): self.cf = data - def arDet(self, data, order, rind, ldet): - pass - - def arPred(self, data, arpara, rind, lpred): - pass - - class AICcf(CharacteristicFunction): ''' @@ -150,6 +158,7 @@ class AICcf(CharacteristicFunction): ''' def calcCF(self, data): + print 'Calculating AIC ...' xnp = self.getDataArray() datlen = len(xnp) @@ -158,8 +167,8 @@ class AICcf(CharacteristicFunction): cumsumcf = np.cumsum(np.power(xnp, 2)) i = np.where(cumsumcf == 0) cumsumcf[i] = np.finfo(np.float64).eps - cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * - np.log((cumsumcf[datlen - 1] - + cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * + np.log((cumsumcf[datlen - 1] - cumsumcf[k - 1]) / (datlen - k + 1))) cf[0] = cf[1] inf = np.isinf(cf) @@ -180,7 +189,6 @@ class HOScf(CharacteristicFunction): def calcCF(self, data): xnp = self.getDataArray(self.getCut()) - if self.getOrder() == 3: # this is skewness print 'Calculating skewness ...' y = np.power(xnp, 3) @@ -190,22 +198,22 @@ class HOScf(CharacteristicFunction): y = np.power(xnp, 4) y1 = np.power(xnp, 2) - # Initialisation - # t2: long term moving window + #Initialisation + #t2: long term moving window ilta = round(self.getTime2() / self.getIncrement()) lta = y[0] lta1 = y1[0] - # moving windows + #moving windows LTA = np.zeros(len(xnp)) for j in range(3, len(xnp)): if j <= ilta: - lta = (y[j] + lta * (j - 1)) / j - lta1 = (y1[j] + lta1 * (j - 1)) / j + lta = (y[j] + lta * (j-1)) / j + lta1 = (y1[j] + lta1 * (j-1)) / j else: lta = (y[j] - y[j - ilta]) / ilta + lta lta1 = (y1[j] - y1[j - ilta]) / ilta + lta1 - # define LTA + #define LTA if self.getOrder() == 3: LTA[j] = lta / np.power(lta1, 1.5) elif self.getOrder() == 4: @@ -222,39 +230,38 @@ class ARZcf(CharacteristicFunction): print 'Calculating AR-prediction error from single trace ...' xnp = self.getDataArray(self.getCut()) - - # some parameters needed - # add noise to time series + #some parameters needed + #add noise to time series xnoise = xnp + np.random.normal(0.0, 1.0, len(xnp)) * self.getFnoise() * max(abs(xnp)) tend = len(xnp) - # Time1: length of AR-determination window [sec] - # Time2: length of AR-prediction window [sec] - ldet = int(round(self.getTime1() / self.getIncrement())) # length of AR-determination window [samples] - lpred = int(np.ceil(self.getTime2() / self.getIncrement())) # length of AR-prediction window [samples] + #Time1: length of AR-determination window [sec] + #Time2: length of AR-prediction window [sec] + ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] + lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] cf = [] step = ldet + self.getOrder() - 1 for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): if i == step: - ''' - In order to speed up the algorithm AR parameters are kept for time - intervals of length lpred - ''' - # determination of AR coefficients - self.arDet(xnoise, self.getOrder(), i - ldet, i) - step = step + lpred + ''' + In order to speed up the algorithm AR parameters are kept for time + intervals of length ldet + ''' + #determination of AR coefficients + self.arDetZ(xnoise, self.getOrder(), i-ldet, i) + step = step + ldet - # AR prediction of waveform using calculated AR coefficients - self.arPred(xnp, self.arpara, i + 1, lpred) - # prediction error = CF + #AR prediction of waveform using calculated AR coefficients + self.arPredZ(xnp, self.arpara, i + 1, lpred) + #prediction error = CF err = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred] - xnp[i:i + lpred], 2)) / lpred) cf.append(err) - # convert list to numpy array + #convert list to numpy array cf = np.asarray(cf) self.cf = cf - def arDet(self, data, order, rind, ldet): + def arDetZ(self, data, order, rind, ldet): ''' Function to calculate AR parameters arpara after Thomas Meier (CAU), published in Kueperkoch et al. (2012). This function solves SLE using the Moore- @@ -274,27 +281,27 @@ class ARZcf(CharacteristicFunction): Output: AR parameters arpara ''' - # recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) + #recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) rhs = np.zeros(self.getOrder()) for k in range(0, self.getOrder()): for i in range(rind, ldet): rhs[k] = rhs[k] + data[i] * data[i - k] - # recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) - A = np.array([[0, 0], [0, 0]]) + #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) + A = np.zeros((2,2)) for k in range(1, self.getOrder() + 1): for j in range(1, k + 1): for i in range(rind, ldet): ki = k - 1 ji = j - 1 - A[ki, ji] = A[ki, ji] + data[i - ji] * data[i - ki] + A[ki,ji] = A[ki,ji] + data[i - ji] * data[i - ki] - A[ji, ki] = A[ki, ji] + A[ji,ki] = A[ki,ji] - # apply Moore-Penrose inverse for SVD yielding the AR-parameters + #apply Moore-Penrose pseudo inverse for SVD yielding the AR-parameters self.arpara = np.dot(np.linalg.pinv(A), rhs) - def arPred(self, data, arpara, rind, lpred): + def arPredZ(self, data, arpara, rind, lpred): ''' Function to predict waveform, assuming an autoregressive process of order p (=size(arpara)), with AR parameters arpara calculated in arDet. After @@ -313,7 +320,7 @@ class ARZcf(CharacteristicFunction): Output: predicted waveform z ''' - # be sure of the summation indeces + #be sure of the summation indeces if rind < len(arpara) + 1: rind = len(arpara) + 1 if rind > len(data) - lpred + 1: @@ -334,8 +341,134 @@ class ARZcf(CharacteristicFunction): class ARHcf(CharacteristicFunction): - pass + def calcCF(self, data): + print 'Calculating AR-prediction error from both horizontal traces ...' + + xnp = self.getDataArray(self.getCut()) + + #some parameters needed + #add noise to time series + xenoise = xnp[0].data + np.random.normal(0.0, 1.0, len(xnp[0].data)) * self.getFnoise() * max(abs(xnp[0].data)) + xnnoise = xnp[1].data + np.random.normal(0.0, 1.0, len(xnp[1].data)) * self.getFnoise() * max(abs(xnp[1].data)) + Xnoise = np.array( [xenoise.tolist(), xnnoise.tolist()] ) + tend = len(xnp[0].data) + #Time1: length of AR-determination window [sec] + #Time2: length of AR-prediction window [sec] + ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] + lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] + + cf = [] + arstep = ldet + self.getOrder() - 3 + for i in range(ldet + self.getOrder() - 3, tend - lpred + 1): + if i == arstep: + ''' + In order to speed up the algorithm AR parameters are kept for time + intervals of length ldet + ''' + #determination of AR coefficients + self.arDetH(Xnoise, self.getOrder(), i-ldet, i) + arstep = arstep + ldet + + #AR prediction of waveform using calculated AR coefficients + self.arPredH(xnp, self.arpara, i + 1, lpred) + #prediction error = CF + err = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2)) / (2 * lpred)) + cf.append(err) + + #convert list to numpy array + cf = np.asarray(cf) + self.cf = cf + + def arDetH(self, data, order, rind, ldet): + ''' + Function to calculate AR parameters arpara after Thomas Meier (CAU), published + in Kueperkoch et al. (2012). This function solves SLE using the Moore- + Penrose inverse, i.e. the least-squares approach. "data" is a structured array. + AR parameters are calculated based on both horizontal components in order + to account for polarization. + :param: data, horizontal component seismograms to calculate AR parameters from + :type: structured array + + :param: order, order of AR process + :type: int + + :param: rind, first running summation index + :type: int + + :param: ldet, length of AR-determination window (=end of summation index) + :type: int + + Output: AR parameters arpara + ''' + + #recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) + rhs = np.zeros(self.getOrder()) + for k in range(0, self.getOrder()): + for i in range(rind, ldet): + rhs[k] = rhs[k] + data[0,i] * data[0,i - k] + data[1,i] * data[1,i - k] + + #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) + A = np.zeros((4,4)) + for k in range(1, self.getOrder() + 1): + for j in range(1, k + 1): + for i in range(rind, ldet): + ki = k - 1 + ji = j - 1 + A[ki,ji] = A[ki,ji] + data[0,i - ji] * data[0,i - ki] + data[1,i - ji] *data[1,i - ki] + + A[ji,ki] = A[ki,ji] + + #apply Moore-Penrose pseudo inverse for SVD yielding the AR-parameters + #self.arpara = np.dot(np.linalg.pinv(A), rhs) + #self.arpara = np.linalg.solve(A, rhs) + #arpara = scipy.linalg.lstsq(A, rhs) + #arpara = np.linalg.lstsq(A, rhs) + #self.arpara = arpara[0] + self.arpara = np.dot(scipy.linalg.pinv(A), rhs) + + + def arPredH(self, data, arpara, rind, lpred): + ''' + Function to predict waveform, assuming an autoregressive process of order + p (=size(arpara)), with AR parameters arpara calculated in arDet. After + Thomas Meier (CAU), published in Kueperkoch et al. (2012). + :param: data, horizontal component seismograms to be predicted + :type: structured array + + :param: arpara, AR parameters + :type: float + + :param: rind, first running summation index + :type: int + + :param: lpred, length of prediction window (=end of summation index) + :type: int + + Output: predicted waveform z + :type: structured array + ''' + #be sure of the summation indeces + if rind < len(arpara) + 1: + rind = len(arpara) + 1 + if rind > len(data[0]) - lpred + 1: + rind = len(data[0]) - lpred + 1 + if lpred < 1: + lpred = 1 + if lpred > len(data[0]) - 1: + lpred = len(data[0]) - 1 + + z1 = np.append(data[0][0:rind], np.zeros(lpred)) + z2 = np.append(data[1][0:rind], np.zeros(lpred)) + for i in range(rind, rind + lpred): + for j in range(1, len(arpara) + 1): + ji = j - 1 + z1[i] = z1[i] + arpara[ji] * z1[i - ji] + z2[i] = z2[i] + arpara[ji] * z2[i - ji] + + z = np.array( [z1.tolist(), z2.tolist()] ) + self.xpred = z class AR3Ccf(CharacteristicFunction): From 7da6b57ed10a91db1c247327d8c31e3b80b8dc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 20 Nov 2014 09:06:13 +0100 Subject: [PATCH 0107/1144] Modified to handle two-component data --- pylot/core/pick/run_makeCF.py | 151 ++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100755 pylot/core/pick/run_makeCF.py diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py new file mode 100755 index 00000000..3f18b9d1 --- /dev/null +++ b/pylot/core/pick/run_makeCF.py @@ -0,0 +1,151 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" + Script to run autoPyLoT-script "makeCF.py". + Only for test purposes! +""" + +from obspy.core import read +import matplotlib.pyplot as plt +import numpy as np +from CharFuns import * +import glob +import argparse + +def run_makeCF(project, database, event, iplot, station=None): + #parameters for CF calculation + t2 = 7 #length of moving window for HOS calculation [sec] + p = 4 #order of statistics + cuttimes = [10, 40] #start and end time vor CF calculation + bpz = [2, 30] #corner frequencies of bandpass filter, vertical component + bph = [2, 15] #corner frequencies of bandpass filter, horizontal components + tdetz= 1.2 #length of AR-determination window [sec], vertical component + tdeth= 0.8 #length of AR-determination window [sec], horizontal components + tpredz = 0.4 #length of AR-prediction window [sec], vertical component + tpredh = 0.4 #length of AR-prediction window [sec], horizontal components + addnoise = 0.001 #add noise to seismogram for stable AR prediction + arzorder = 2 #chosen order of AR process, vertical component + arhorder = 4 #chosen order of AR process, horizontal components + #get waveform data + if station: + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHZ.msd' % (project, database, event, station) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHE.msd' % (project, database, event, station) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHN.msd' % (project, database, event, station) + else: + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHZ.msd' % (project, database, event) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHE.msd' % (project, database, event) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHN.msd' % (project, database, event) + wfzfiles = glob.glob(dpz) + wfefiles = glob.glob(dpe) + wfnfiles = glob.glob(dpn) + if wfzfiles: + for i in range(len(wfzfiles)): + print 'Vertical component data found ...' + print wfzfiles[i] + st = read('%s' % wfzfiles[i]) + st_copy = st.copy() + #filter and taper data + tr_filt = st[0].copy() + tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + st_copy[0].data = tr_filt.data + ############################################################## + #calculate HOS-CF using subclass HOScf of class CharacteristicFunction + hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf + ############################################################## + #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_aic = tr_filt.copy() + tr_aic.data = hoscf.getCF() + st_copy[0].data = tr_aic.data + aiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf + ############################################################## + #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction + #get stream object of filtered data + st_copy[0].data = tr_filt.data + arzcf = ARZcf(st_copy, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf + ############################################################## + #calculate AIC-ARZ-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_arzaic = tr_filt.copy() + tr_arzaic.data = arzcf.getCF() + st_copy[0].data = tr_arzaic.data + araiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf + elif not wfzfiles: + print 'No vertical component data found!' + + if wfefiles and wfnfiles: + for i in range(len(wfefiles)): + print 'Horizontal component data found ...' + print wfefiles[i] + print wfnfiles[i] + #merge streams + H = read('%s' % wfefiles[i]) + H += read('%s' % wfnfiles[i]) + H_copy = H.copy() + #filter and taper data + trH1_filt = H[0].copy() + trH2_filt = H[1].copy() + trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + H_copy[0].data = trH1_filt.data + H_copy[1].data = trH2_filt.data + ############################################################## + #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction + arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf + ############################################################## + if iplot: + #plot vertical trace + plt.figure() + tr = st[0] + #time vectors + tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) + tCF = np.arange(cuttimes[0], cuttimes[1], tr.stats.delta) + tARZCF = np.arange(cuttimes[0] + tdetz + tpredz, cuttimes[1], tr.stats.delta) + p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p2 = plt.plot(tCF, hoscf.getCF()/max(hoscf.getCF()), 'r') + p3 = plt.plot(tCF, aiccf.getCF()/max(aiccf.getCF()), 'b') + p4 = plt.plot(tARZCF, arzcf.getCF()/max(arzcf.getCF()), 'g') + p5 = plt.plot(tARZCF, araiccf.getCF()/max(araiccf.getCF()), 'y') + plt.yticks([]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + plt.title([tr.stats.station, tr.stats.channel]) + plt.suptitle(tr.stats.starttime) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) + #plot horizontal traces + plt.figure(2) + plt.subplot(211) + th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) + th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) + tARHCF = np.arange(cuttimes[0] + tdeth + tpredh, cuttimes[1], trH1_filt.stats.delta) + p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + p22 = plt.plot(tARHCF, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.yticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.suptitle(trH1_filt.stats.starttime) + plt.legend([p21, p22], ['Data', 'ARH-CF']) + plt.subplot(212) + p23 = plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + p24 = plt.plot(tARHCF, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.yticks([]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + plt.show() + raw_input() + plt.close() + +parser = argparse.ArgumentParser() +parser.add_argument('--project', type=str, help='project name (e.g. Insheim)') +parser.add_argument('--database', type=str, help='event data base (e.g. 2014.09_Insheim)') +parser.add_argument('--event', type=str, help='event ID (e.g. e0010.015.14)') +parser.add_argument('--iplot', help='anything, if set, figure occurs') +parser.add_argument('--station', type=str, help='Station ID (e.g. INS3) (optional)') +args = parser.parse_args() + +run_makeCF(args.project, args.database, args.event, args.iplot, args.station) From 0e8c5a7e48a04d40f1c548a54367f30f3a09b4ba Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 21 Nov 2014 10:04:32 +0100 Subject: [PATCH 0108/1144] exporting is much easier using obspy routines --- pylot/core/read/data.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index c2837408..5092a92f 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -55,21 +55,15 @@ class Data(object): evtformat = evtformat.upper().strip() - # export event to QuakeML or JSON via ObsPy - if evtformat in 'QUAKEML' or 'JSON': - cat = Catalog() - cat.append(self.event) + # establish catalog object (event object has no write method) + cat = Catalog() + cat.append(self.event) + # try exporting event via ObsPy + try: cat.write(fnout + evtformat.lower(), format=evtformat) - - # export event to VelEst format - if evtformat in 'VELEST': - ''' - Test whether the station code is longer than 4 digits. - If that is the case a warning should be displayed explaining that - VelEst can only handle 4 digit stationcodes. Thus, the leading too - much digits will be skipped, e.g. "STS12" becomes "TS12" - ''' - pass + except KeyError, e: + raise KeyError('''{0} export format + not implemented: {1}'''.format(evtformat, e)) def plotData(self, widget): From 25921e371859d16b0b729a2076a9c2a005c9490d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 21 Nov 2014 10:05:27 +0100 Subject: [PATCH 0109/1144] started to improve documentation in read/data --- pylot/core/read/data.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 5092a92f..56eb6f52 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -14,9 +14,15 @@ class Data(object): variables. :type parent: PySide.QtGui.QWidget object, optional - :param parent: A PySide.QtGui.QWidget class utilized when + :param parent: A PySide.QtGui.QWidget object utilized when called by a GUI to display a PySide.QtGui.QMessageBox instead of printing to standard out. + :type wfdata: ~obspy.core.stream.Stream object, optional + :param wfdata: ~obspy.core.stream.Stream object containing all available + waveform data for the actual event + :type evtdata: ~obspy.core.event.Event object, optional + :param evtdata ~obspy.core.event.Event object containing all derived or + loaded event. Container object holding, e.g. phase arrivals, etc. ''' def __init__(self, parent=None, wfdata=None, evtdata=None): From 9958c862873d632250576b48fd0026a730c7560e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 21 Nov 2014 10:06:04 +0100 Subject: [PATCH 0110/1144] fixed wrong usage of attribute wfdata --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 8e1b8c22..67244982 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -58,7 +58,7 @@ class MainWindow(QMainWindow): self.data = None self.loadData() self.updateFilterOptions() - self.startTime = min([tr.stats.starttime for tr in self.data]) + self.startTime = min([tr.stats.starttime for tr in self.data.wfdata]) self.setupUi() From 2a385512ee084ddafe863f24c0b646c0993e36fd Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 21 Nov 2014 10:08:18 +0100 Subject: [PATCH 0111/1144] version number changes each time the Main program is started (should not be the case, to be fixed before release) --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 7c85ce04..cdcfb290 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -7fd1-dirty +fbce-dirty From 8fa9ec74c0c51ff74fd02055142d52975a0923f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 21 Nov 2014 14:50:51 +0100 Subject: [PATCH 0112/1144] Included AR prediction on all 3 components --- pylot/core/pick/CharFuns.py | 202 +++++++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 48 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 1893455f..e7e681d0 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -17,7 +17,6 @@ autoregressive prediction: application ot local and regional distances, Geophys. """ import numpy as np from obspy.core import Stream -import scipy class CharacteristicFunction(object): ''' @@ -125,7 +124,10 @@ class CharacteristicFunction(object): start = self.cut[0] / self.dt stop = self.cut[1] / self.dt if len(self.orig_data) == 1: - data = self.orig_data[0].data[start:stop] + zz = self.orig_data.copy() + z1 = zz[0].copy() + zz[0].data = z1.data[start:stop] + data = zz return data elif len(self.orig_data) == 2: hh = self.orig_data.copy() @@ -135,13 +137,19 @@ class CharacteristicFunction(object): hh[1].data = h2.data[start:stop] data = hh return data + elif len(self.orig_data) == 3: + hh = self.orig_data.copy() + h1 = hh[0].copy() + h2 = hh[1].copy() + h3 = hh[2].copy() + hh[0].data = h1.data[start:stop] + hh[1].data = h2.data[start:stop] + hh[2].data = h3.data[start:stop] + data = hh + return data else: - if len(self.orig_data) == 1: - data = self.orig_data[0] - return data - elif len(self.orig_data) == 2: - data = self.orig_data - return data + data = self.orig_data + return data def calcCF(self, data=None): self.cf = data @@ -160,7 +168,8 @@ class AICcf(CharacteristicFunction): def calcCF(self, data): print 'Calculating AIC ...' - xnp = self.getDataArray() + x = self.getDataArray() + xnp = x[0].data datlen = len(xnp) k = np.arange(1, datlen) cf = np.zeros(datlen) @@ -188,7 +197,8 @@ class HOScf(CharacteristicFunction): def calcCF(self, data): - xnp = self.getDataArray(self.getCut()) + x = self.getDataArray(self.getCut()) + xnp =x[0].data if self.getOrder() == 3: # this is skewness print 'Calculating skewness ...' y = np.power(xnp, 3) @@ -206,8 +216,10 @@ class HOScf(CharacteristicFunction): #moving windows LTA = np.zeros(len(xnp)) - for j in range(3, len(xnp)): - if j <= ilta: + for j in range(0, len(xnp)): + if j < 4: + LTA[j] = 0 + elif j <= ilta: lta = (y[j] + lta * (j-1)) / j lta1 = (y1[j] + lta1 * (j-1)) / j else: @@ -218,9 +230,7 @@ class HOScf(CharacteristicFunction): LTA[j] = lta / np.power(lta1, 1.5) elif self.getOrder() == 4: LTA[j] = lta / np.power(lta1, 2) - - LTA[0:3] = 0 - + self.cf = LTA @@ -229,7 +239,8 @@ class ARZcf(CharacteristicFunction): def calcCF(self, data): print 'Calculating AR-prediction error from single trace ...' - xnp = self.getDataArray(self.getCut()) + x = self.getDataArray(self.getCut()) + xnp = x[0].data #some parameters needed #add noise to time series xnoise = xnp + np.random.normal(0.0, 1.0, len(xnp)) * self.getFnoise() * max(abs(xnp)) @@ -240,17 +251,9 @@ class ARZcf(CharacteristicFunction): lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] cf = [] - step = ldet + self.getOrder() - 1 - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): - if i == step: - ''' - In order to speed up the algorithm AR parameters are kept for time - intervals of length ldet - ''' - #determination of AR coefficients - self.arDetZ(xnoise, self.getOrder(), i-ldet, i) - step = step + ldet - + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, lpred / 16): + #determination of AR coefficients + self.arDetZ(xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients self.arPredZ(xnp, self.arpara, i + 1, lpred) #prediction error = CF @@ -298,7 +301,7 @@ class ARZcf(CharacteristicFunction): A[ji,ki] = A[ki,ji] - #apply Moore-Penrose pseudo inverse for SVD yielding the AR-parameters + #apply Moore-Penrose inverse for SVD yielding the AR-parameters self.arpara = np.dot(np.linalg.pinv(A), rhs) def arPredZ(self, data, arpara, rind, lpred): @@ -359,17 +362,8 @@ class ARHcf(CharacteristicFunction): lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] cf = [] - arstep = ldet + self.getOrder() - 3 - for i in range(ldet + self.getOrder() - 3, tend - lpred + 1): - if i == arstep: - ''' - In order to speed up the algorithm AR parameters are kept for time - intervals of length ldet - ''' - #determination of AR coefficients - self.arDetH(Xnoise, self.getOrder(), i-ldet, i) - arstep = arstep + ldet - + for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, lpred / 4): + self.arDetH(Xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients self.arPredH(xnp, self.arpara, i + 1, lpred) #prediction error = CF @@ -420,14 +414,8 @@ class ARHcf(CharacteristicFunction): A[ji,ki] = A[ki,ji] - #apply Moore-Penrose pseudo inverse for SVD yielding the AR-parameters - #self.arpara = np.dot(np.linalg.pinv(A), rhs) - #self.arpara = np.linalg.solve(A, rhs) - #arpara = scipy.linalg.lstsq(A, rhs) - #arpara = np.linalg.lstsq(A, rhs) - #self.arpara = arpara[0] - self.arpara = np.dot(scipy.linalg.pinv(A), rhs) - + #apply Moore-Penrose inverse for SVD yielding the AR-parameters + self.arpara = np.dot(np.linalg.pinv(A), rhs) def arPredH(self, data, arpara, rind, lpred): ''' @@ -472,4 +460,122 @@ class ARHcf(CharacteristicFunction): class AR3Ccf(CharacteristicFunction): - pass + def calcCF(self, data): + + print 'Calculating AR-prediction error from all 3 components ...' + + xnp = self.getDataArray(self.getCut()) + + #some parameters needed + #add noise to time series + xenoise = xnp[0].data + np.random.normal(0.0, 1.0, len(xnp[0].data)) * self.getFnoise() * max(abs(xnp[0].data)) + xnnoise = xnp[1].data + np.random.normal(0.0, 1.0, len(xnp[1].data)) * self.getFnoise() * max(abs(xnp[1].data)) + xznoise = xnp[2].data + np.random.normal(0.0, 1.0, len(xnp[2].data)) * self.getFnoise() * max(abs(xnp[2].data)) + Xnoise = np.array( [xenoise.tolist(), xnnoise.tolist(), xznoise.tolist()] ) + tend = len(xnp[0].data) + #Time1: length of AR-determination window [sec] + #Time2: length of AR-prediction window [sec] + ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] + lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] + + cf = [] + for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, lpred / 4): + self.arDet3C(Xnoise, self.getOrder(), i-ldet, i) + #AR prediction of waveform using calculated AR coefficients + self.arPred3C(xnp, self.arpara, i + 1, lpred) + #prediction error = CF + err = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2) \ + + np.power(self.xpred[2][i:i + lpred] - xnp[2][i:i + lpred], 2)) / (3 * lpred)) + cf.append(err) + + #convert list to numpy array + cf = np.asarray(cf) + self.cf = cf + + def arDet3C(self, data, order, rind, ldet): + ''' + Function to calculate AR parameters arpara after Thomas Meier (CAU), published + in Kueperkoch et al. (2012). This function solves SLE using the Moore- + Penrose inverse, i.e. the least-squares approach. "data" is a structured array. + AR parameters are calculated based on both horizontal components and vertical + componant. + :param: data, horizontal component seismograms to calculate AR parameters from + :type: structured array + + :param: order, order of AR process + :type: int + + :param: rind, first running summation index + :type: int + + :param: ldet, length of AR-determination window (=end of summation index) + :type: int + + Output: AR parameters arpara + ''' + + #recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) + rhs = np.zeros(self.getOrder()) + for k in range(0, self.getOrder()): + for i in range(rind, ldet): + rhs[k] = rhs[k] + data[0,i] * data[0,i - k] + data[1,i] * data[1,i - k] \ + + data[2,i] * data[2,i - k] + + #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) + A = np.zeros((4,4)) + for k in range(1, self.getOrder() + 1): + for j in range(1, k + 1): + for i in range(rind, ldet): + ki = k - 1 + ji = j - 1 + A[ki,ji] = A[ki,ji] + data[0,i - ji] * data[0,i - ki] + data[1,i - ji] *data[1,i - ki] \ + + data[2,i - ji] *data[2,i - ki] + + A[ji,ki] = A[ki,ji] + + #apply Moore-Penrose inverse for SVD yielding the AR-parameters + self.arpara = np.dot(np.linalg.pinv(A), rhs) + + def arPred3C(self, data, arpara, rind, lpred): + ''' + Function to predict waveform, assuming an autoregressive process of order + p (=size(arpara)), with AR parameters arpara calculated in arDet3C. After + Thomas Meier (CAU), published in Kueperkoch et al. (2012). + :param: data, horizontal and vertical component seismograms to be predicted + :type: structured array + + :param: arpara, AR parameters + :type: float + + :param: rind, first running summation index + :type: int + + :param: lpred, length of prediction window (=end of summation index) + :type: int + + Output: predicted waveform z + :type: structured array + ''' + #be sure of the summation indeces + if rind < len(arpara) + 1: + rind = len(arpara) + 1 + if rind > len(data[0]) - lpred + 1: + rind = len(data[0]) - lpred + 1 + if lpred < 1: + lpred = 1 + if lpred > len(data[0]) - 1: + lpred = len(data[0]) - 1 + + z1 = np.append(data[0][0:rind], np.zeros(lpred)) + z2 = np.append(data[1][0:rind], np.zeros(lpred)) + z3 = np.append(data[2][0:rind], np.zeros(lpred)) + for i in range(rind, rind + lpred): + for j in range(1, len(arpara) + 1): + ji = j - 1 + z1[i] = z1[i] + arpara[ji] * z1[i - ji] + z2[i] = z2[i] + arpara[ji] * z2[i - ji] + z3[i] = z3[i] + arpara[ji] * z3[i - ji] + + z = np.array( [z1.tolist(), z2.tolist(), z3.tolist()] ) + self.xpred = z From 8fb9ca9dc2e5d240341b6eb1c38c1a70fffc0274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 21 Nov 2014 14:52:19 +0100 Subject: [PATCH 0113/1144] Modified for running updated CharFuns.py showing all kinds of CFs on all 3 components --- pylot/core/pick/run_makeCF.py | 69 ++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 3f18b9d1..fed7c2e9 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -97,19 +97,42 @@ def run_makeCF(project, database, event, iplot, station=None): #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf ############################################################## + #create stream with 3 traces + #merge streams + AllC = read('%s' % wfefiles[i]) + AllC += read('%s' % wfnfiles[i]) + AllC += read('%s' % wfzfiles[i]) + #filter and taper data + All1_filt = AllC[0].copy() + All2_filt = AllC[1].copy() + All3_filt = AllC[2].copy() + All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + All1_filt.taper(max_percentage=0.05, type='hann') + All2_filt.taper(max_percentage=0.05, type='hann') + All3_filt.taper(max_percentage=0.05, type='hann') + AllC[0].data = All1_filt.data + AllC[1].data = All2_filt.data + AllC[2].data = All3_filt.data + #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction + ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf + ############################################################## if iplot: #plot vertical trace plt.figure() tr = st[0] - #time vectors + tstepz = tpredz / 16 tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) - tCF = np.arange(cuttimes[0], cuttimes[1], tr.stats.delta) - tARZCF = np.arange(cuttimes[0] + tdetz + tpredz, cuttimes[1], tr.stats.delta) + thoscf = np.arange(0, len(hoscf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] + taiccf = np.arange(0, len(aiccf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] + tarzcf = np.arange(0, len(arzcf.getCF()) * tstepz, tstepz) + cuttimes[0] + tdetz +tpredz + taraiccf = np.arange(0, len(araiccf.getCF()) * tstepz, tstepz) + cuttimes[0] +tdetz + tpredz p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p2 = plt.plot(tCF, hoscf.getCF()/max(hoscf.getCF()), 'r') - p3 = plt.plot(tCF, aiccf.getCF()/max(aiccf.getCF()), 'b') - p4 = plt.plot(tARZCF, arzcf.getCF()/max(arzcf.getCF()), 'g') - p5 = plt.plot(tARZCF, araiccf.getCF()/max(araiccf.getCF()), 'y') + p2 = plt.plot(thoscf, hoscf.getCF()/max(hoscf.getCF()), 'r') + p3 = plt.plot(taiccf, aiccf.getCF()/max(aiccf.getCF()), 'b') + p4 = plt.plot(tarzcf, arzcf.getCF()/max(arzcf.getCF()), 'g') + p5 = plt.plot(taraiccf, araiccf.getCF()/max(araiccf.getCF()), 'y') plt.yticks([]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') @@ -119,11 +142,12 @@ def run_makeCF(project, database, event, iplot, station=None): #plot horizontal traces plt.figure(2) plt.subplot(211) + tsteph = tpredh / 4 th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) - tARHCF = np.arange(cuttimes[0] + tdeth + tpredh, cuttimes[1], trH1_filt.stats.delta) + tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - p22 = plt.plot(tARHCF, arhcf.getCF()/max(arhcf.getCF()), 'r') + p22 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') plt.yticks([]) plt.ylabel('Normalized Counts') plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) @@ -131,11 +155,36 @@ def run_makeCF(project, database, event, iplot, station=None): plt.legend([p21, p22], ['Data', 'ARH-CF']) plt.subplot(212) p23 = plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - p24 = plt.plot(tARHCF, arhcf.getCF()/max(arhcf.getCF()), 'r') + p24 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) plt.yticks([]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') + #plot 3-component window + plt.figure(3) + tar3ccf = np.arange(0, len(ar3ccf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdetz +tpredz + plt.subplot(311) + p31 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p32 = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([tr.stats.station, tr.stats.channel]) + plt.legend([p31, p32], ['Data', 'AR3C-CF']) + plt.subplot(312) + plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.subplot(313) + plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.xlabel('Time [s]') plt.show() raw_input() plt.close() From 758de94fff6c6413aa8d6828c18b1801adcc8d3f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 24 Nov 2014 05:39:57 +0100 Subject: [PATCH 0114/1144] indentation fixed --- pylot/core/pick/run_makeCF.py | 311 +++++++++++++++++----------------- 1 file changed, 155 insertions(+), 156 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index fed7c2e9..6cc80df2 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -15,13 +15,13 @@ import argparse def run_makeCF(project, database, event, iplot, station=None): #parameters for CF calculation - t2 = 7 #length of moving window for HOS calculation [sec] - p = 4 #order of statistics + t2 = 7 #length of moving window for HOS calculation [sec] + p = 4 #order of statistics cuttimes = [10, 40] #start and end time vor CF calculation bpz = [2, 30] #corner frequencies of bandpass filter, vertical component bph = [2, 15] #corner frequencies of bandpass filter, horizontal components - tdetz= 1.2 #length of AR-determination window [sec], vertical component - tdeth= 0.8 #length of AR-determination window [sec], horizontal components + tdetz= 1.2 #length of AR-determination window [sec], vertical component + tdeth= 0.8 #length of AR-determination window [sec], horizontal components tpredz = 0.4 #length of AR-prediction window [sec], vertical component tpredh = 0.4 #length of AR-prediction window [sec], horizontal components addnoise = 0.001 #add noise to seismogram for stable AR prediction @@ -29,166 +29,165 @@ def run_makeCF(project, database, event, iplot, station=None): arhorder = 4 #chosen order of AR process, horizontal components #get waveform data if station: - dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHZ.msd' % (project, database, event, station) - dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHE.msd' % (project, database, event, station) - dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHN.msd' % (project, database, event, station) + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHZ.msd' % (project, database, event, station) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHE.msd' % (project, database, event, station) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHN.msd' % (project, database, event, station) else: - dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHZ.msd' % (project, database, event) - dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHE.msd' % (project, database, event) - dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHN.msd' % (project, database, event) + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHZ.msd' % (project, database, event) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHE.msd' % (project, database, event) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHN.msd' % (project, database, event) wfzfiles = glob.glob(dpz) wfefiles = glob.glob(dpe) wfnfiles = glob.glob(dpn) if wfzfiles: - for i in range(len(wfzfiles)): - print 'Vertical component data found ...' - print wfzfiles[i] - st = read('%s' % wfzfiles[i]) - st_copy = st.copy() - #filter and taper data - tr_filt = st[0].copy() - tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) - tr_filt.taper(max_percentage=0.05, type='hann') - st_copy[0].data = tr_filt.data - ############################################################## - #calculate HOS-CF using subclass HOScf of class CharacteristicFunction - hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf - ############################################################## - #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it - tr_aic = tr_filt.copy() - tr_aic.data = hoscf.getCF() - st_copy[0].data = tr_aic.data - aiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf - ############################################################## - #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction - #get stream object of filtered data - st_copy[0].data = tr_filt.data - arzcf = ARZcf(st_copy, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf - ############################################################## - #calculate AIC-ARZ-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it - tr_arzaic = tr_filt.copy() - tr_arzaic.data = arzcf.getCF() - st_copy[0].data = tr_arzaic.data - araiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf + for i in range(len(wfzfiles)): + print 'Vertical component data found ...' + print wfzfiles[i] + st = read('%s' % wfzfiles[i]) + st_copy = st.copy() + #filter and taper data + tr_filt = st[0].copy() + tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + st_copy[0].data = tr_filt.data + ############################################################## + #calculate HOS-CF using subclass HOScf of class CharacteristicFunction + hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf + ############################################################## + #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_aic = tr_filt.copy() + tr_aic.data = hoscf.getCF() + st_copy[0].data = tr_aic.data + aiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf + ############################################################## + #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction + #get stream object of filtered data + st_copy[0].data = tr_filt.data + arzcf = ARZcf(st_copy, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf + ############################################################## + #calculate AIC-ARZ-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_arzaic = tr_filt.copy() + tr_arzaic.data = arzcf.getCF() + st_copy[0].data = tr_arzaic.data + araiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf elif not wfzfiles: - print 'No vertical component data found!' + print 'No vertical component data found!' if wfefiles and wfnfiles: - for i in range(len(wfefiles)): - print 'Horizontal component data found ...' - print wfefiles[i] - print wfnfiles[i] - #merge streams - H = read('%s' % wfefiles[i]) - H += read('%s' % wfnfiles[i]) - H_copy = H.copy() - #filter and taper data - trH1_filt = H[0].copy() - trH2_filt = H[1].copy() - trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - H_copy[0].data = trH1_filt.data - H_copy[1].data = trH2_filt.data - ############################################################## - #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction - arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf - ############################################################## - #create stream with 3 traces - #merge streams - AllC = read('%s' % wfefiles[i]) - AllC += read('%s' % wfnfiles[i]) - AllC += read('%s' % wfzfiles[i]) - #filter and taper data - All1_filt = AllC[0].copy() - All2_filt = AllC[1].copy() - All3_filt = AllC[2].copy() - All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) - All1_filt.taper(max_percentage=0.05, type='hann') - All2_filt.taper(max_percentage=0.05, type='hann') - All3_filt.taper(max_percentage=0.05, type='hann') - AllC[0].data = All1_filt.data - AllC[1].data = All2_filt.data - AllC[2].data = All3_filt.data - #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction - ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf - ############################################################## - if iplot: - #plot vertical trace - plt.figure() - tr = st[0] - tstepz = tpredz / 16 - tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) - thoscf = np.arange(0, len(hoscf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] - taiccf = np.arange(0, len(aiccf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] - tarzcf = np.arange(0, len(arzcf.getCF()) * tstepz, tstepz) + cuttimes[0] + tdetz +tpredz - taraiccf = np.arange(0, len(araiccf.getCF()) * tstepz, tstepz) + cuttimes[0] +tdetz + tpredz - p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p2 = plt.plot(thoscf, hoscf.getCF()/max(hoscf.getCF()), 'r') - p3 = plt.plot(taiccf, aiccf.getCF()/max(aiccf.getCF()), 'b') - p4 = plt.plot(tarzcf, arzcf.getCF()/max(arzcf.getCF()), 'g') - p5 = plt.plot(taraiccf, araiccf.getCF()/max(araiccf.getCF()), 'y') - plt.yticks([]) - plt.xlabel('Time [s]') - plt.ylabel('Normalized Counts') - plt.title([tr.stats.station, tr.stats.channel]) - plt.suptitle(tr.stats.starttime) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) - #plot horizontal traces - plt.figure(2) - plt.subplot(211) - tsteph = tpredh / 4 - th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) - th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) - tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh - p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - p22 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') - plt.yticks([]) - plt.ylabel('Normalized Counts') - plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) - plt.suptitle(trH1_filt.stats.starttime) - plt.legend([p21, p22], ['Data', 'ARH-CF']) - plt.subplot(212) - p23 = plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - p24 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') - plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) - plt.yticks([]) - plt.xlabel('Time [s]') - plt.ylabel('Normalized Counts') - #plot 3-component window - plt.figure(3) - tar3ccf = np.arange(0, len(ar3ccf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdetz +tpredz - plt.subplot(311) - p31 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p32 = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') - plt.yticks([]) - plt.xticks([]) - plt.ylabel('Normalized Counts') - plt.title([tr.stats.station, tr.stats.channel]) - plt.legend([p31, p32], ['Data', 'AR3C-CF']) - plt.subplot(312) - plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') - plt.yticks([]) - plt.xticks([]) - plt.ylabel('Normalized Counts') - plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) - plt.subplot(313) - plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') - plt.yticks([]) - plt.ylabel('Normalized Counts') - plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) - plt.xlabel('Time [s]') - plt.show() - raw_input() - plt.close() - + for i in range(len(wfefiles)): + print 'Horizontal component data found ...' + print wfefiles[i] + print wfnfiles[i] + #merge streams + H = read('%s' % wfefiles[i]) + H += read('%s' % wfnfiles[i]) + H_copy = H.copy() + #filter and taper data + trH1_filt = H[0].copy() + trH2_filt = H[1].copy() + trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + H_copy[0].data = trH1_filt.data + H_copy[1].data = trH2_filt.data + ############################################################## + #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction + arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf + ############################################################## + #create stream with 3 traces + #merge streams + AllC = read('%s' % wfefiles[i]) + AllC += read('%s' % wfnfiles[i]) + AllC += read('%s' % wfzfiles[i]) + #filter and taper data + All1_filt = AllC[0].copy() + All2_filt = AllC[1].copy() + All3_filt = AllC[2].copy() + All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + All1_filt.taper(max_percentage=0.05, type='hann') + All2_filt.taper(max_percentage=0.05, type='hann') + All3_filt.taper(max_percentage=0.05, type='hann') + AllC[0].data = All1_filt.data + AllC[1].data = All2_filt.data + AllC[2].data = All3_filt.data + #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction + ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf + ############################################################## + if iplot: + #plot vertical trace + plt.figure() + tr = st[0] + tstepz = tpredz / 16 + tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) + thoscf = np.arange(0, len(hoscf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] + taiccf = np.arange(0, len(aiccf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] + tarzcf = np.arange(0, len(arzcf.getCF()) * tstepz, tstepz) + cuttimes[0] + tdetz +tpredz + taraiccf = np.arange(0, len(araiccf.getCF()) * tstepz, tstepz) + cuttimes[0] +tdetz + tpredz + p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p2 = plt.plot(thoscf, hoscf.getCF()/max(hoscf.getCF()), 'r') + p3 = plt.plot(taiccf, aiccf.getCF()/max(aiccf.getCF()), 'b') + p4 = plt.plot(tarzcf, arzcf.getCF()/max(arzcf.getCF()), 'g') + p5 = plt.plot(taraiccf, araiccf.getCF()/max(araiccf.getCF()), 'y') + plt.yticks([]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + plt.title([tr.stats.station, tr.stats.channel]) + plt.suptitle(tr.stats.starttime) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) + #plot horizontal traces + plt.figure(2) + plt.subplot(211) + tsteph = tpredh / 4 + th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) + th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) + tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh + p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + p22 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.yticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.suptitle(trH1_filt.stats.starttime) + plt.legend([p21, p22], ['Data', 'ARH-CF']) + plt.subplot(212) + p23 = plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + p24 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.yticks([]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + #plot 3-component window + plt.figure(3) + tar3ccf = np.arange(0, len(ar3ccf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdetz +tpredz + plt.subplot(311) + p31 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p32 = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([tr.stats.station, tr.stats.channel]) + plt.legend([p31, p32], ['Data', 'AR3C-CF']) + plt.subplot(312) + plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.subplot(313) + plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.xlabel('Time [s]') + plt.show() + raw_input() + plt.close() parser = argparse.ArgumentParser() parser.add_argument('--project', type=str, help='project name (e.g. Insheim)') parser.add_argument('--database', type=str, help='event data base (e.g. 2014.09_Insheim)') From 8cb638e5bc3c11e445a5a95b641539523bbf623d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 25 Nov 2014 05:04:47 +0100 Subject: [PATCH 0115/1144] indentation fixed --- pylot/core/pick/CharFuns.py | 62 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index e7e681d0..9fc05685 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -118,38 +118,38 @@ class CharacteristicFunction(object): cutting window ''' if cut is not None: - if self.cut[0] == 0: - start = 0 - else: - start = self.cut[0] / self.dt - stop = self.cut[1] / self.dt - if len(self.orig_data) == 1: - zz = self.orig_data.copy() - z1 = zz[0].copy() - zz[0].data = z1.data[start:stop] - data = zz - return data - elif len(self.orig_data) == 2: - hh = self.orig_data.copy() - h1 = hh[0].copy() - h2 = hh[1].copy() - hh[0].data = h1.data[start:stop] - hh[1].data = h2.data[start:stop] - data = hh - return data - elif len(self.orig_data) == 3: - hh = self.orig_data.copy() - h1 = hh[0].copy() - h2 = hh[1].copy() - h3 = hh[2].copy() - hh[0].data = h1.data[start:stop] - hh[1].data = h2.data[start:stop] - hh[2].data = h3.data[start:stop] - data = hh - return data + if self.cut[0] == 0: + start = 0 + else: + start = self.cut[0] / self.dt + stop = self.cut[1] / self.dt + if len(self.orig_data) == 1: + zz = self.orig_data.copy() + z1 = zz[0].copy() + zz[0].data = z1.data[start:stop] + data = zz + return data + elif len(self.orig_data) == 2: + hh = self.orig_data.copy() + h1 = hh[0].copy() + h2 = hh[1].copy() + hh[0].data = h1.data[start:stop] + hh[1].data = h2.data[start:stop] + data = hh + return data + elif len(self.orig_data) == 3: + hh = self.orig_data.copy() + h1 = hh[0].copy() + h2 = hh[1].copy() + h3 = hh[2].copy() + hh[0].data = h1.data[start:stop] + hh[1].data = h2.data[start:stop] + hh[2].data = h3.data[start:stop] + data = hh + return data else: - data = self.orig_data - return data + data = self.orig_data + return data def calcCF(self, data=None): self.cf = data From d0d17ee656cd00c3e3ec3b44483133821c325afb Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 26 Nov 2014 08:45:04 +0100 Subject: [PATCH 0116/1144] reorganization of graphical and help resources; they are not part of the code and therefore they do not really match in util package --- {pylot/core/util/help => help}/index.html | 0 {pylot/core/util/icons => icons}/pylot.ico | Bin qrc_resources.py | 21 ++++++++++++++++++ .../core/util/resources.qrc => resources.qrc | 0 4 files changed, 21 insertions(+) rename {pylot/core/util/help => help}/index.html (100%) rename {pylot/core/util/icons => icons}/pylot.ico (100%) create mode 100644 qrc_resources.py rename pylot/core/util/resources.qrc => resources.qrc (100%) diff --git a/pylot/core/util/help/index.html b/help/index.html similarity index 100% rename from pylot/core/util/help/index.html rename to help/index.html diff --git a/pylot/core/util/icons/pylot.ico b/icons/pylot.ico similarity index 100% rename from pylot/core/util/icons/pylot.ico rename to icons/pylot.ico diff --git a/qrc_resources.py b/qrc_resources.py new file mode 100644 index 00000000..cb089909 --- /dev/null +++ b/qrc_resources.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created: Di. Nov. 25 10:10:42 2014 +# by: The Resource Compiler for PySide (Qt v4.8.6) +# +# WARNING! All changes made in this file will be lost! + +from PySide import QtCore + +qt_resource_data = "\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +qt_resource_name = "\x00\x09\x034&\xbc\x00h\x00e\x00l\x00p\x00.\x00h\x00t\x00m\x00l\x00\x08\x0aaB\x7f\x00i\x00c\x00o\x00n\x00.\x00i\x00c\x00o" +qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x02\xfd" +def qInitResources(): + QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/pylot/core/util/resources.qrc b/resources.qrc similarity index 100% rename from pylot/core/util/resources.qrc rename to resources.qrc From 9ff18c742550d278e1b51c312dfd9e817865052c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 26 Nov 2014 08:46:16 +0100 Subject: [PATCH 0117/1144] changes made in order to get the GUI working for the first time --- QtPyLoT.py | 18 +++++++++++++----- pylot/RELEASE-VERSION | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 67244982..ec620c25 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -38,6 +38,7 @@ from pylot.core.util import (PickDlg, MPLWidget, HelpForm) from pylot.core.util import layoutStationButtons +import qrc_resources # Version information __version__ = _getVersionString() @@ -58,7 +59,10 @@ class MainWindow(QMainWindow): self.data = None self.loadData() self.updateFilterOptions() - self.startTime = min([tr.stats.starttime for tr in self.data.wfdata]) + try: + self.startTime = min([tr.stats.starttime for tr in self.data.wfdata]) + except: + self.startTime = UTCDateTime() self.setupUi() @@ -66,7 +70,10 @@ class MainWindow(QMainWindow): return 'TestType' def loadData(self): - self.data = Data() + self.data = None + + def getComponent(self): + return self def getData(self): return self.data @@ -99,11 +106,12 @@ class MainWindow(QMainWindow): maingrid = QGridLayout() maingrid.setSpacing(10) maingrid.addLayout(statLayout, 0, 0) - maingrid.addLayout(dataLayout, 1, 0) - maingrid.setCentralWidget(dataLayout) + maingrid.addWidget(dataLayout, 1, 0) + self.setLayout(maingrid) + #self.setCentralWidget(dataLayout) def plotData(self): - self.data.plotData(self.DataPlot) + pass #self.data.plotData(self.DataPlot) def adjustFilterOptions(self): fstring = "Filter Options ({0})".format(self.getSeismicPhase()) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index cdcfb290..749b26f6 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -fbce-dirty +8cb6-dirty From 4bb03d6418119e25d43713e9ad69216d69a32ee2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 26 Nov 2014 08:47:21 +0100 Subject: [PATCH 0118/1144] all picks classes are now collected in the same module --- pylot/core/pick/automatic.py | 7 ------- pylot/core/pick/conventional.py | 7 ------- pylot/core/pick/{reference.py => picks.py} | 12 ++++++++++++ 3 files changed, 12 insertions(+), 14 deletions(-) delete mode 100644 pylot/core/pick/automatic.py delete mode 100644 pylot/core/pick/conventional.py rename pylot/core/pick/{reference.py => picks.py} (56%) diff --git a/pylot/core/pick/automatic.py b/pylot/core/pick/automatic.py deleted file mode 100644 index 60a5693a..00000000 --- a/pylot/core/pick/automatic.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Oct 2 18:41:18 2014 - -@author: sebastianw -""" - diff --git a/pylot/core/pick/conventional.py b/pylot/core/pick/conventional.py deleted file mode 100644 index 388fab94..00000000 --- a/pylot/core/pick/conventional.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Oct 2 18:41:08 2014 - -@author: sebastianw -""" - diff --git a/pylot/core/pick/reference.py b/pylot/core/pick/picks.py similarity index 56% rename from pylot/core/pick/reference.py rename to pylot/core/pick/picks.py index 8e49f229..8a303432 100644 --- a/pylot/core/pick/reference.py +++ b/pylot/core/pick/picks.py @@ -12,3 +12,15 @@ class ReferencePick(Pick): def __init__(self): Pick.__init__() + + +class ConventionalPick(Pick): + + def __init__(self): + Pick.__init__() + + +class AutomaticPick(Pick): + + def __init__(self): + Pick.__init__() \ No newline at end of file From 4b7bfc6aa99f7b69d02dfb5667feb937162add98 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 26 Nov 2014 08:48:42 +0100 Subject: [PATCH 0119/1144] make GUI working even without actual data --- pylot/core/read/data.py | 11 ++++++++--- pylot/core/util/layouts.py | 18 +++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 56eb6f52..b11aea34 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -10,8 +10,7 @@ from pylot.core.util import fnConstructor class Data(object): ''' - Data container class providing ObSpy-Stream and -Event instances as - variables. + Data container with attributes wfdata holding ~obspy.core.stream. :type parent: PySide.QtGui.QWidget object, optional :param parent: A PySide.QtGui.QWidget object utilized when @@ -75,6 +74,12 @@ class Data(object): pass #axes = widget.axes + def getEventID(self): + try: + return self.evtdata.get('resource_id').id + except: + return 'smi:bug/pylot/1234' + class GenericDataStructure(object): ''' @@ -104,7 +109,7 @@ class GenericDataStructure(object): class SeiscompDataStructure(object): ''' - Dictionary containing the data acces information for an SDS data archive: + Dictionary containing the data access information for an SDS data archive: :param str dataType: Desired data type. Default: ``'waveform'`` :param sdate, edate: Either date string or an instance of diff --git a/pylot/core/util/layouts.py b/pylot/core/util/layouts.py index 7f7cf7a5..632c2473 100644 --- a/pylot/core/util/layouts.py +++ b/pylot/core/util/layouts.py @@ -13,11 +13,15 @@ from PySide.QtGui import (QVBoxLayout, def layoutStationButtons(data, comp): layout = QVBoxLayout() stationButtons = [] - st = data.select(component=comp) - numStations = len(st) - for n in range(numStations): - stationButtons[n] = QPushButton('%s'.format( - st[n].stats.station)) - layout.addWidget(stationButtons) - + try: + st = data.select(component=comp) + numStations = len(st) + for n in range(numStations): + stat = st[n].stats.station + stationButtons.append(QPushButton('%s'.format(stat))) + except: + for n in range(5): + stationButtons.append(QPushButton('ST%02d'.format(n))) + for button in stationButtons: + layout.addWidget(button) return layout \ No newline at end of file From 411b36598716306111f520b80226292866f836ae Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 26 Nov 2014 09:09:19 +0100 Subject: [PATCH 0120/1144] template for argparse program imported; will be changed to make PyLoT (build, install, re- or uninstall); --- makePyLoT.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 makePyLoT.py diff --git a/makePyLoT.py b/makePyLoT.py new file mode 100644 index 00000000..aceb981b --- /dev/null +++ b/makePyLoT.py @@ -0,0 +1,140 @@ +#!/usr/local/bin/python2.7 +# encoding: utf-8 +''' +makePyLoT -- build and install PyLoT + +makePyLoT is a python make file in order to establish the folder structure and +meet requisites + +It defines +:class CLIError: +:method main: + +:author: Sebastian Wehling-Benatelli + +:copyright: 2014 MAGS2 EP3 Working Group. All rights reserved. + +:license: GNU Lesser General Public License, Version 3 + (http://www.gnu.org/copyleft/lesser.html) + +:contact: sebastian.wehling@rub.de + +updated: Updated +''' + +import sys +import os + +from argparse import ArgumentParser +from argparse import RawDescriptionHelpFormatter + +__all__ = [] +__version__ = 0.1 +__date__ = '2014-11-26' +__updated__ = '2014-11-26' + +DEBUG = 1 +TESTRUN = 0 +PROFILE = 0 + +class CLIError(Exception): + '''Generic exception to raise and log different fatal errors.''' + def __init__(self, msg): + super(CLIError).__init__(type(self)) + self.msg = "E: %s" % msg + def __str__(self): + return self.msg + def __unicode__(self): + return self.msg + +def main(argv=None): # IGNORE:C0111 + '''Command line options.''' + + if argv is None: + argv = sys.argv + else: + sys.argv.extend(argv) + + program_name = os.path.basename(sys.argv[0]) + program_version = "v%s" % __version__ + program_build_date = str(__updated__) + program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) + program_shortdesc = __import__('__main__').__doc__.split("\n")[1] + program_license = '''%s + + Created by Sebastian Wehling-Benatelli on %s. + Copyright 2014 MAGS2 EP3 Working Group. All rights reserved. + + GNU Lesser General Public License, Version 3 + (http://www.gnu.org/copyleft/lesser.html) + + Distributed on an "AS IS" basis without warranties + or conditions of any kind, either express or implied. + +USAGE +''' % (program_shortdesc, str(__date__)) + + try: + # Setup argument parser + parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) + parser.add_argument("-r", "--recursive", dest="recurse", action="store_true", help="recurse into subfolders [default: %(default)s]") + parser.add_argument("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %(default)s]") + parser.add_argument("-i", "--include", dest="include", help="only include paths matching this regex pattern. Note: exclude is given preference over include. [default: %(default)s]", metavar="RE" ) + parser.add_argument("-e", "--exclude", dest="exclude", help="exclude paths matching this regex pattern. [default: %(default)s]", metavar="RE" ) + parser.add_argument('-V', '--version', action='version', version=program_version_message) + parser.add_argument(dest="paths", help="paths to folder(s) with source file(s) [default: %(default)s]", metavar="path", nargs='+') + + # Process arguments + args = parser.parse_args() + + paths = args.paths + verbose = args.verbose + recurse = args.recurse + inpat = args.include + expat = args.exclude + + if verbose > 0: + print("Verbose mode on") + if recurse: + print("Recursive mode on") + else: + print("Recursive mode off") + + if inpat and expat and inpat == expat: + raise CLIError("include and exclude pattern are equal! Nothing will be processed.") + + for inpath in paths: + ### do something with inpath ### + print(inpath) + return 0 + except KeyboardInterrupt: + ### handle keyboard interrupt ### + return 0 + except Exception, e: + if DEBUG or TESTRUN: + raise(e) + indent = len(program_name) * " " + sys.stderr.write(program_name + ": " + repr(e) + "\n") + sys.stderr.write(indent + " for help use --help") + return 2 + +if __name__ == "__main__": + if DEBUG: + sys.argv.append("-h") + sys.argv.append("-v") + sys.argv.append("-r") + if TESTRUN: + import doctest + doctest.testmod() + if PROFILE: + import cProfile + import pstats + profile_filename = 'makePyLoT_profile.txt' + cProfile.run('main()', profile_filename) + statsfile = open("profile_stats.txt", "wb") + p = pstats.Stats(profile_filename, stream=statsfile) + stats = p.strip_dirs().sort_stats('cumulative') + stats.print_stats() + statsfile.close() + sys.exit(0) + sys.exit(main()) \ No newline at end of file From 094213bd21372a89d547233205321705c2b0b0d9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Nov 2014 10:13:17 +0100 Subject: [PATCH 0121/1144] method createAction implemented in order to create menu entries --- QtPyLoT.py | 42 ++++++++++++++++++++++++++++++++++++++++-- pylot/RELEASE-VERSION | 2 +- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index ec620c25..452b4469 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -69,6 +69,21 @@ class MainWindow(QMainWindow): def _getCurrentPlotType(self): return 'TestType' + def createAction(self, text, slot=None, shortcut=None, icon=None, + tip=None, checkable=False): + action = QAction(text, self) + if icon is not None: + action.setIcon(icon) + if shortcut is not None: + action.setShortcut(shortcut) + if tip is not None: + action.setToolTip(tip) + if slot is not None: + action.triggered.connect(slot) + if checkable: + action.setCheckable(True) + return action + def loadData(self): self.data = None @@ -92,9 +107,33 @@ class MainWindow(QMainWindow): xlabel=xlab, ylabel=None, title=plottitle) + + self.setCentralWidget(self.getDataWidget()) + + self.openEventAction = self.createAction("&Open event ...", + self.loadData, + QKeySequence.Open, + QStyle.SP_DirOpenIcon, + "Open an event.") + self.quitAction = self.createAction("&Quit", self.cleanUp, + QKeySequence.Close, + QStyle.SP_MediaStop, + "Close event and quit PyLoT") + self.filterAction = self.createAction("&Filter ...", self.filterData, + "Ctrl+F", ":/filter.png", + """Toggle un-/filtered waveforms + to be displayed, accroding to the + desired seismic phase.""", True) + + self.selectPAction = self.createAction("&P", self.alterPhase, "Ctrl+P", + ":/picon.png", "Toggle P phase.", + True) + self.selectSAction = self.createAction("&S", self.alterPhase, "Ctrl+S", + ":/sicon.png", "Toggle S phase", + True) self.eventLabel = QLabel() - self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) + self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) status = self.statusBar() status.setSizeGripEnabled(False) status.addPermanentWidget(self.eventLabel) @@ -108,7 +147,6 @@ class MainWindow(QMainWindow): maingrid.addLayout(statLayout, 0, 0) maingrid.addWidget(dataLayout, 1, 0) self.setLayout(maingrid) - #self.setCentralWidget(dataLayout) def plotData(self): pass #self.data.plotData(self.DataPlot) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 749b26f6..c7992fb6 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -8cb6-dirty +0.0.0-g4b7b From 8ed281ae2d9e45c842d93da99f641842cdfb8bb4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Nov 2014 10:57:33 +0100 Subject: [PATCH 0122/1144] printer icon added licensed under Creative Commons (Attribution 3.0); source: https://www.iconfinder.com/icons/59191/print_printer_icon#size=32 (27.11.2014) --- icons/printer.png | Bin 0 -> 937 bytes resources.qrc | 1 + 2 files changed, 1 insertion(+) create mode 100644 icons/printer.png diff --git a/icons/printer.png b/icons/printer.png new file mode 100644 index 0000000000000000000000000000000000000000..339ccab167bb0d0a7a1a9988b917754ba1526571 GIT binary patch literal 937 zcmV;a16KTrP)z_n+Tj;P@a|Mbk9UbzM;Jg;c9m*xA`( z)T_enCLlOOqfrP3gPsuB-rk0MJ`c_WMB_`J&j-Ok5W?ZGCj>}8=~QtXEkJ)1^a$Y* zzOV(a6A;Q*6WlWbLV)?Sv;~r|#{?vX_+KnQ-uhtSK6oiZiD738poey7T$|N=T8c5& zLGeGHngASolLw3{=v)`RkdLgfj^k{#QoU>##yacl?2Kc$<@^DE)bIDpaAElDhtL)v zD3r@(D3{9lLZOi4?XmVbWEBmCLNWuQ1zqsK#>=tZIQM)j(}-%SmI0#^s1YvX2QUU>A-Q>=4m&+i^cRvg6U zc8-V60i(kyhD3xq$*s_0PzMXQg#|gg8WFYxC=*ih;GBax$*oY#S1J{$Q^j#4Kx1+c zDoiLs{xGYU&t+xENwnmYXWK`Lnm#EN;{!5#mug% zuBJp3i^X7bbF-O%rO^tOUOsnafr->)lOB}-Ve^>_(?4!pzj?@d3N}r|CwXGTwGZq? zfPND;pUO;K-T1n3`|kWbXgC)XAIQC0a%F+B(Q!u~pniHL1BA`!W`r`k_GRtT^70#4 z{k$sodj8Hl!&L|@ROGl+D!KLn>ap;kxhyl2kvFaGqur253_{o8u4}#VzAKWzv7TP| z@acUV=ccjslal_vfs3JVNTyE$&MrN5Wr5Vlsa*n?35$gyTY2~PR_)UYwnfDJNO~oK zo+EK-ezVE=#C?oJB6R|^5;qng%b5hWwze4g50!eVZ*gnlu~w~IbHKv2<#WEF zv=iV+-u_~u+zliF&j0K_P+RGqS2Nqmn5SR7&UsP`kdHaZF98MsZ%>DZeSWjR00000 LNkvXXu0mjf=a;{C literal 0 HcmV?d00001 diff --git a/resources.qrc b/resources.qrc index a4fcaef9..ba83c9c3 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,6 +1,7 @@ icons/pylot.ico + icons/printer.png help/index.html From d47623ed6516aa1420cdecf0821a60a88f213c8d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Nov 2014 10:59:34 +0100 Subject: [PATCH 0123/1144] implementation of standard icons corrected --- QtPyLoT.py | 5 +++-- pylot/RELEASE-VERSION | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 452b4469..9d9e86c8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -113,11 +113,11 @@ class MainWindow(QMainWindow): self.openEventAction = self.createAction("&Open event ...", self.loadData, QKeySequence.Open, - QStyle.SP_DirOpenIcon, + self.style().standardIcon(QStyle.SP_DirOpenIcon), "Open an event.") self.quitAction = self.createAction("&Quit", self.cleanUp, QKeySequence.Close, - QStyle.SP_MediaStop, + self.style().standardIcon(QStyle.SP_MediaStop), "Close event and quit PyLoT") self.filterAction = self.createAction("&Filter ...", self.filterData, "Ctrl+F", ":/filter.png", @@ -131,6 +131,7 @@ class MainWindow(QMainWindow): self.selectSAction = self.createAction("&S", self.alterPhase, "Ctrl+S", ":/sicon.png", "Toggle S phase", True) + self.printAction = self.createAction("&Print event ...", self.printEvent, QKeySequence.Print, QIcon(":/printer.png"), tip, checkable) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index c7992fb6..7c0e69f3 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0.0.0-g4b7b +0.0.0-g0942 From c7f09988e57b458894f87b0a5c93be7575bfe8c9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Nov 2014 11:51:40 +0100 Subject: [PATCH 0124/1144] started implementation of a makePyLoT routine capable of building and installing PyLoT to a desired directory --- makePyLoT.py | 57 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/makePyLoT.py b/makePyLoT.py index aceb981b..b1b390dc 100644 --- a/makePyLoT.py +++ b/makePyLoT.py @@ -33,7 +33,7 @@ __version__ = 0.1 __date__ = '2014-11-26' __updated__ = '2014-11-26' -DEBUG = 1 +DEBUG = 0 TESTRUN = 0 PROFILE = 0 @@ -58,7 +58,7 @@ def main(argv=None): # IGNORE:C0111 program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) - program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) + program_version_message = '%makePyLoT %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s @@ -77,38 +77,35 @@ USAGE try: # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) - parser.add_argument("-r", "--recursive", dest="recurse", action="store_true", help="recurse into subfolders [default: %(default)s]") - parser.add_argument("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %(default)s]") - parser.add_argument("-i", "--include", dest="include", help="only include paths matching this regex pattern. Note: exclude is given preference over include. [default: %(default)s]", metavar="RE" ) - parser.add_argument("-e", "--exclude", dest="exclude", help="exclude paths matching this regex pattern. [default: %(default)s]", metavar="RE" ) + parser.add_argument("-b", "--build", dest="build", action="store_true", help="build PyLoT") + parser.add_argument("-v", "--verbose", dest="verbose", action="count", help="set verbosity level") + parser.add_argument("-i", "--install", dest="install", action="store_true", help="install PyLoT on the system") + parser.add_argument("-d", "--directory", dest="directory", help="installation directory", metavar="RE" ) parser.add_argument('-V', '--version', action='version', version=program_version_message) - parser.add_argument(dest="paths", help="paths to folder(s) with source file(s) [default: %(default)s]", metavar="path", nargs='+') # Process arguments args = parser.parse_args() - paths = args.paths verbose = args.verbose - recurse = args.recurse - inpat = args.include - expat = args.exclude + build = args.build + install = args.install + directory = args.directory if verbose > 0: print("Verbose mode on") - if recurse: - print("Recursive mode on") - else: - print("Recursive mode off") - - if inpat and expat and inpat == expat: - raise CLIError("include and exclude pattern are equal! Nothing will be processed.") - - for inpath in paths: - ### do something with inpath ### - print(inpath) + if install and not directory: + raise CLIError("trying to install without destination; please specify an installation directory") + if build and install: + print("Building and installing PyLoT ...") + buildPyLoT() + installPyLoT() + elif build and not install: + print("Building PyLoT without installing! Please wait ...") + buildPyLoT() + cleanUp() return 0 except KeyboardInterrupt: - ### handle keyboard interrupt ### + cleanUp() return 0 except Exception, e: if DEBUG or TESTRUN: @@ -118,11 +115,23 @@ USAGE sys.stderr.write(indent + " for help use --help") return 2 +def buildPyLoT(): + if sys.platform.startswith('win' or 'microsoft'): + raise CLIError("building on Windows system not tested yet; implementation pending") + elif sys.platform == 'darwin': + pass + + +def installPyLoT(): + pass + +def cleanUp(): + pass + if __name__ == "__main__": if DEBUG: sys.argv.append("-h") sys.argv.append("-v") - sys.argv.append("-r") if TESTRUN: import doctest doctest.testmod() From 9dc57e3977ad084bc80d25d1630e25c411b462cb Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Nov 2014 09:19:16 +0100 Subject: [PATCH 0125/1144] icon for P and S phase selection added --- QtPyLoT.py | 26 ++++++++++++++++++-------- icons/picon.png | Bin 0 -> 674 bytes icons/sicon.png | Bin 0 -> 726 bytes resources.qrc | 3 ++- 4 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 icons/picon.png create mode 100644 icons/sicon.png diff --git a/QtPyLoT.py b/QtPyLoT.py index 9d9e86c8..d7cee37f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -11,6 +11,9 @@ Additionally PyLoT is meant as an interface to autoPyLoT which can automatically pick seismic phases, if the parameters have properly been chosen for the particular data set. +Some icons are out of a free of charge icon set, which can be found here: +https://www.iconfinder.com/iconsets/flavour + :author: Sebastian Wehling-Benatelli :copyright: @@ -97,7 +100,7 @@ class MainWindow(QMainWindow): return self.DataPlot def setupUi(self): - self.setWindowIcon(QIcon(":/pylot.ico")) + self.setWindowIcon(QIcon(":/icon.ico")) xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') plottitle = self._getCurrentPlotType() @@ -110,14 +113,16 @@ class MainWindow(QMainWindow): self.setCentralWidget(self.getDataWidget()) + openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) + quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) self.openEventAction = self.createAction("&Open event ...", self.loadData, QKeySequence.Open, - self.style().standardIcon(QStyle.SP_DirOpenIcon), + openIcon, "Open an event.") self.quitAction = self.createAction("&Quit", self.cleanUp, QKeySequence.Close, - self.style().standardIcon(QStyle.SP_MediaStop), + quitIcon, "Close event and quit PyLoT") self.filterAction = self.createAction("&Filter ...", self.filterData, "Ctrl+F", ":/filter.png", @@ -131,7 +136,11 @@ class MainWindow(QMainWindow): self.selectSAction = self.createAction("&S", self.alterPhase, "Ctrl+S", ":/sicon.png", "Toggle S phase", True) - self.printAction = self.createAction("&Print event ...", self.printEvent, QKeySequence.Print, QIcon(":/printer.png"), tip, checkable) + self.printAction = self.createAction("&Print event ...", + self.printEvent, + QKeySequence.Print, + QIcon(":/printer.png"), + "Print waveform overview.") self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) @@ -174,15 +183,16 @@ class MainWindow(QMainWindow): if not self.seismicPhase == 'S' else self.filterOptionsS] except Exception, e: - self.updateStatus('Error: %s' % e + ' ... no filteroptions loaded') + self.updateStatus('Error ...') + QErrorMessage(e) else: - self.updateStatus('Filteroptions succesfully loaded ...') + self.updateStatus('Filter loaded ...') def getSeismicPhase(self): return self.seismicPhase def setSeismicPhase(self, phase): - self.seismicPhase = self.seismicPhaseButton.getValue() + self.seismicPhase = self.seismicPhaseButtonGroup.getValue() def updateStatus(self, message): self.statusBar().showMessage(message, 5000) @@ -203,7 +213,7 @@ def main(): pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") pylot_app.setOrganizationDomain("rub.de") pylot_app.setApplicationName("PyLoT") - pylot_app.setWindowIcon(QIcon(":/pylot.ico")) + pylot_app.setWindowIcon(QIcon(":/icon.ico")) # create the main window pylot_form = MainWindow() diff --git a/icons/picon.png b/icons/picon.png new file mode 100644 index 0000000000000000000000000000000000000000..d41c0db8a77a35b65af1488c5c5b85101893fee9 GIT binary patch literal 674 zcmV;T0$u%yP)+NPcN z=FPNeniWMs|9Q(l=sOWe1QLNnAQ4Ce5`nLfLEG)N;C_~6+=_i}-K{7Jx0U*~9SjB; z!p3tU2)i|?C8$y$#z|-j#VAlt z3Pq%u02Yk%O{oBu7K6k4z>h)!Y_hZG^GT=Ei4KPYZ8jS^pU>u)peO{bR={-#Be&o0 zX}Mg|ZnrbwEs6PjPK(8Y-tRY7((jj6#KGqqjRuX!V>X&jr}TQg3^5RKy#mr|;!z;)UW z8a)wHuqNL*^7hGexm;*6nJ9tl6<`2j6dCk-y*6gSce`Dh&1Pn5j_##U0E0F;3(xao zbTk^Fud!0nGY6oJfrvS+eBDpuf2~Na%y&ePE%0lsd<8)3ffXa}?_(wPHNYV87_gKg z^s#T90$2?{(ydmD`u#p1XOkix5`A?Fu>}=hv)QCxugBm-A~=Qg3ZQ%ixh~!-MuE~( zD7J{%z*qnt7IFN`jQdUj1E-1t#|r43vLVoHYVcSAtc0t8Vw#|s3xNezp2xe+M9}!1 zfXN(>NA_LX@_P8{oj&r;@;W8}NklI}>eEL5g9BUoN0tbD1NQ@;K_W|=f&c&j07*qo IM6N<$f+C+Z!Tuwx#|XQ52~_Xt&$ZdcBUz<#NIMO2gg(O%O+Jxm?n4 zIHc3*L<}ZBnM~xKj+`_~-_R`Lxc_`U({{V1e!ow-T#f|y`<>G1v^=nQd|V@72aJHD z#8IzQDneesPjgMiJmU5O44)ey4egO~O}tnv(&2DmCkzCCS%BoS2&mO+G@H$6u~_g1 z2EY4)07R(OYEie_jW_OSG@|Kr%56m4y#O3JY3qJ=f<+7l17eKFV-|ttaw`DajgO0b z>+Ezol+WjBwOXlh7VAO*LIOri*ZOybLV+Df8V?+?Z~!=h1WGYDjk$FI)QT+MbrkXE z^GW;tp33F2atO=|5!&XUjFJ2Ld_E^ewOS>{>-A#t@sqh;ubYq*p)NNu{G#7|IMraUyc?nbXdtVraEC9}hQ^?!_Y$J9+%D?6^alm;BflaKg zrGT-RJWe5upHk+11DoWq@%C|KQb@i50Pw@b&&aNqW}N^07*qo IM6N<$f@cg-MF0Q* literal 0 HcmV?d00001 diff --git a/resources.qrc b/resources.qrc index ba83c9c3..94fbd52f 100644 --- a/resources.qrc +++ b/resources.qrc @@ -2,7 +2,8 @@ icons/pylot.ico icons/printer.png - + icons/picon.png + icons/sicon.png help/index.html From d405e9e6f9dc4c10a09bac10e3c6469af3d81814 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Nov 2014 11:15:49 +0100 Subject: [PATCH 0126/1144] debug GUI --- QtPyLoT.py | 89 +++++++++++++++++++++++++++++--------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 10 ++--- 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index d7cee37f..75a3ef26 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -52,9 +52,15 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) + settings = QSettings() + self.setWindowTitle("PyLoT - do seismic processing the pythonic way") + self.setWindowIcon(QIcon(":/icon.ico")) + self.seismicPhase = str(settings.value("phase", "P")) + # initialize filter parameter filterOptionsP = FILTERDEFAULTS['P'] filterOptionsS = FILTERDEFAULTS['S'] + print filterOptionsP, "\n", filterOptionsS self.filterOptionsP = FilterOptions(**filterOptionsP) self.filterOptionsS = FilterOptions(**filterOptionsS) @@ -62,6 +68,7 @@ class MainWindow(QMainWindow): self.data = None self.loadData() self.updateFilterOptions() + print self.filteroptions try: self.startTime = min([tr.stats.starttime for tr in self.data.wfdata]) except: @@ -87,9 +94,28 @@ class MainWindow(QMainWindow): action.setCheckable(True) return action + def createMenus(self): + + fileMenu = self.menuBar().addMenu("&File") + fileMenu.addAction(self.openEventAction) + fileMenu.addAction(self.saveEventAction) + fileMenu.addAction(self.printAction) + fileMenu.addSeparator() + fileMenu.addAction(self.quitAction) + + editMenu = self.menuBar().addMenu("&Edit") + editMenu.addAction(self.filterAction) + editMenu.addAction(self.filterEditAction) + editMenu.addSeparator() + editMenu.addAction(self.selectPAction) + editMenu.addAction(self.selectSAction) + def loadData(self): self.data = None + def saveData(self): + pass + def getComponent(self): return self @@ -115,52 +141,65 @@ class MainWindow(QMainWindow): openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) + saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) self.openEventAction = self.createAction("&Open event ...", self.loadData, QKeySequence.Open, openIcon, "Open an event.") + self.saveEventAction = self.createAction("&Save event ...", + self.saveData, + QKeySequence.Save, saveIcon, + "Save actual event data.") self.quitAction = self.createAction("&Quit", self.cleanUp, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") self.filterAction = self.createAction("&Filter ...", self.filterData, - "Ctrl+F", ":/filter.png", + "Ctrl+F", QIcon(":/filter.png"), """Toggle un-/filtered waveforms - to be displayed, accroding to the + to be displayed, according to the desired seismic phase.""", True) - - self.selectPAction = self.createAction("&P", self.alterPhase, "Ctrl+P", - ":/picon.png", "Toggle P phase.", - True) - self.selectSAction = self.createAction("&S", self.alterPhase, "Ctrl+S", - ":/sicon.png", "Toggle S phase", - True) + self.filterEditAction = self.createAction("&Filter ...", + self.adjustFilterOptions, + "Alt+F", QIcon(None), + """Adjust filter + parameters.""") + self.selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", + QIcon(":/picon.png"), + "Toggle P phase.", True) + self.selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", + QIcon(":/sicon.png"), + "Toggle S phase", True) self.printAction = self.createAction("&Print event ...", self.printEvent, QKeySequence.Print, QIcon(":/printer.png"), "Print waveform overview.") + self.createMenus() self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) status = self.statusBar() status.setSizeGripEnabled(False) status.addPermanentWidget(self.eventLabel) - status.showMessage("Ready", 5000) + status.showMessage("Ready", 10000) - statLayout = layoutStationButtons(self.getData(), self.getComponent()) - dataLayout = self.getDataWidget() + #statLayout = layoutStationButtons(self.getData(), self.getComponent()) + #dataLayout = self.getDataWidget() - maingrid = QGridLayout() - maingrid.setSpacing(10) - maingrid.addLayout(statLayout, 0, 0) - maingrid.addWidget(dataLayout, 1, 0) - self.setLayout(maingrid) +# maingrid = QGridLayout() +# maingrid.setSpacing(10) +# maingrid.addLayout(statLayout, 0, 0) +# maingrid.addWidget(dataLayout, 1, 0) + #self.setLayout(maingrid) def plotData(self): pass #self.data.plotData(self.DataPlot) + def filterData(self): + pass + def adjustFilterOptions(self): fstring = "Filter Options ({0})".format(self.getSeismicPhase()) filterDlg = FilterOptionsDialog(titleString=fstring, @@ -181,22 +220,32 @@ class MainWindow(QMainWindow): try: self.filteroptions = [self.filterOptionsP if not self.seismicPhase == 'S' - else self.filterOptionsS] + else self.filterOptionsS][0] except Exception, e: self.updateStatus('Error ...') - QErrorMessage(e) + emsg = QErrorMessage(self) + emsg.showMessage('Error: {0}'.format(e)) else: self.updateStatus('Filter loaded ...') def getSeismicPhase(self): return self.seismicPhase + def alterPhase(self): + pass + def setSeismicPhase(self, phase): self.seismicPhase = self.seismicPhaseButtonGroup.getValue() def updateStatus(self, message): self.statusBar().showMessage(message, 5000) + def printEvent(self): + pass + + def cleanUp(self): + pass + def helpHelp(self): if checkurl(): form = HelpForm('https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') @@ -207,7 +256,7 @@ class MainWindow(QMainWindow): def main(): # create the Qt application - pylot_app = QApplication(sys.argv) + pylot_app = QApplication(['PyLoT']) # set Application Information pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 7c0e69f3..736e6bdd 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0.0.0-g0942 +9dc5-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1bb32bb6..0cac4620 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -155,7 +155,7 @@ class FilterOptionsDialog(QDialog): """ super(FilterOptionsDialog, self).__init__() - if filterOptions is not None: + if filterOptions is not None and parent.getSeismicPhase() != "P": self.filterOptions = filterOptions else: self.filterOptions = FilterOptions() @@ -206,10 +206,10 @@ class FilterOptionsDialog(QDialog): self.setLayout(self.layoutEditables) - self.freqminSpinBox.connect(self.updateUi) - self.freqmaxSpinBox.connect(self.updateUi) - self.orderSpinBox.connect(self.updateUi) - self.selectTypeCombo.connect(self.updateUi) + self.freqminSpinBox.valueChanged.connect(self.updateUi) + self.freqmaxSpinBox.valueChanged.connect(self.updateUi) + self.orderSpinBox.valueChanged.connect(self.updateUi) + self.selectTypeCombo.currentIndexChanged.connect(self.updateUi) def updateUi(self): if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: From 04e6a51e99b34a586e9d4c0788eeeca4e55de739 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 29 Nov 2014 11:39:25 +0100 Subject: [PATCH 0127/1144] edit on makePyLoT.py: a symlink is created an darwin systems in order to get the right application name on Mac OS X's menubar --- makePyLoT.py | 13 ++++++++++--- pylot/RELEASE-VERSION | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/makePyLoT.py b/makePyLoT.py index b1b390dc..1328b6f2 100644 --- a/makePyLoT.py +++ b/makePyLoT.py @@ -1,4 +1,4 @@ -#!/usr/local/bin/python2.7 +#!/usr/bin/env python # encoding: utf-8 ''' makePyLoT -- build and install PyLoT @@ -22,8 +22,9 @@ It defines updated: Updated ''' -import sys +import glob import os +import sys from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter @@ -119,7 +120,13 @@ def buildPyLoT(): if sys.platform.startswith('win' or 'microsoft'): raise CLIError("building on Windows system not tested yet; implementation pending") elif sys.platform == 'darwin': - pass + # create a symbolic link to the desired python interpreter in order to + # display the right application name + for path in os.getenv('PATH').split(':'): + found = glob.glob(os.path.join(path, 'python')) + if found: + os.symlink(found, 'PyLoT') + break def installPyLoT(): diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index c7992fb6..8c5cb386 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0.0.0-g4b7b +c7f0-dirty From fc3e1a613e2de4e1df853733f2cf9f0f40ac43e2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 08:40:35 +0100 Subject: [PATCH 0128/1144] added quit functionality --- QtPyLoT.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 75a3ef26..e2078ec3 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -151,7 +151,8 @@ class MainWindow(QMainWindow): self.saveData, QKeySequence.Save, saveIcon, "Save actual event data.") - self.quitAction = self.createAction("&Quit", self.cleanUp, + self.quitAction = self.createAction("&Quit", + QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") @@ -160,7 +161,7 @@ class MainWindow(QMainWindow): """Toggle un-/filtered waveforms to be displayed, according to the desired seismic phase.""", True) - self.filterEditAction = self.createAction("&Filter ...", + self.filterEditAction = self.createAction("&Filter parameter ...", self.adjustFilterOptions, "Alt+F", QIcon(None), """Adjust filter @@ -183,7 +184,7 @@ class MainWindow(QMainWindow): status = self.statusBar() status.setSizeGripEnabled(False) status.addPermanentWidget(self.eventLabel) - status.showMessage("Ready", 10000) + status.showMessage("Ready", 500) #statLayout = layoutStationButtons(self.getData(), self.getComponent()) #dataLayout = self.getDataWidget() @@ -205,7 +206,8 @@ class MainWindow(QMainWindow): filterDlg = FilterOptionsDialog(titleString=fstring, parent=self, filterOptions=self.getFilterOptions()) - filterDlg.accepted.connect(filterDlg.getFilterOptions) + filterDlg.show() + filterDlg.closeEvent.connect(lambda: self.setFilterOptions(filterDlg.getFilterOptions())) def getFilterOptions(self): return self.filteroptions @@ -244,7 +246,8 @@ class MainWindow(QMainWindow): pass def cleanUp(self): - pass + self + return 0 def helpHelp(self): if checkurl(): @@ -256,7 +259,7 @@ class MainWindow(QMainWindow): def main(): # create the Qt application - pylot_app = QApplication(['PyLoT']) + pylot_app = QApplication(sys.argv[0]) # set Application Information pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") @@ -269,7 +272,7 @@ def main(): # Show main window and run the app pylot_form.show() - pylot_app.exec_() + sys.exit(pylot_app.exec_()) if __name__ == "__main__": main() From 746b0735726fd297e7ead0beaadbbaaf10e03a58 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:34:44 +0100 Subject: [PATCH 0129/1144] changed imports as some were not necessary and others are now mandatory --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e2078ec3..1f61a4ac 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -24,7 +24,6 @@ https://www.iconfinder.com/iconsets/flavour """ import os -import platform import sys from PySide.QtCore import * from PySide.QtGui import * @@ -34,6 +33,7 @@ from pylot.core.util import _getVersionString from pylot.core.read import (Data, FilterOptions) from pylot.core.util import FILTERDEFAULTS +from pylot.core.util import fnConstructor from pylot.core.util import checkurl from pylot.core.util import (PickDlg, FilterOptionsDialog, From 9d38ed977188983a2832d8f5f9b2abf112045888 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:36:23 +0100 Subject: [PATCH 0130/1144] new QSettings added --- QtPyLoT.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1f61a4ac..ba91923f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -53,9 +53,14 @@ class MainWindow(QMainWindow): super(MainWindow, self).__init__(parent) settings = QSettings() + self.recentEvents = settings.value("recentEvents", []) self.setWindowTitle("PyLoT - do seismic processing the pythonic way") self.setWindowIcon(QIcon(":/icon.ico")) self.seismicPhase = str(settings.value("phase", "P")) + if settings.value("dataRoot", None) is None: + dirname = QFileDialog().getExistingDirectory() + settings.setValue("dataRoot", dirname) + settings.sync() # initialize filter parameter filterOptionsP = FILTERDEFAULTS['P'] From 4acf634f8d0c514c1dbaa74c9d4fac3774f33e42 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:37:52 +0100 Subject: [PATCH 0131/1144] loadData changed; now uses information from the sender to evaluate the fname to read --- QtPyLoT.py | 52 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index ba91923f..c9f02edd 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -99,24 +99,46 @@ class MainWindow(QMainWindow): action.setCheckable(True) return action - def createMenus(self): + def updateFileMenu(self): - fileMenu = self.menuBar().addMenu("&File") - fileMenu.addAction(self.openEventAction) - fileMenu.addAction(self.saveEventAction) - fileMenu.addAction(self.printAction) - fileMenu.addSeparator() - fileMenu.addAction(self.quitAction) + self.fileMenu.clear() + self.addActions(self.fileMenu, self.fileMenuActions[:-1]) + current = self.data.evtdata.getEventID() + recentEvents = [] + for eventID in self.recentEvents: + fname = fnConstructor(eventID) + if eventID != current and QFile.exists(fname): + recentEvents.append(eventID) + if recentEvents: + self.fileMenu.addSeparator() + for i, eventID in enumerate(recentEvents): + fname = fnConstructor(eventID) + action = QAction(QIcon(":/icon.png"), + "&{0} {1}".format(i + 1, + QFileInfo(fname).fileName()), + self) + action.setData(fname) + self.connect(action, SIGNAL("triggered()"), + self.loadData) + self.fileMenu.addAction(action) + self.fileMenu.addSeparator() + self.fileMenu.addAction(self.fileMenuActions[-1]) - editMenu = self.menuBar().addMenu("&Edit") - editMenu.addAction(self.filterAction) - editMenu.addAction(self.filterEditAction) - editMenu.addSeparator() - editMenu.addAction(self.selectPAction) - editMenu.addAction(self.selectSAction) - def loadData(self): - self.data = None + def loadData(self, fname=None): + if fname is None: + action = self.sender() + if isinstance(action, QAction): + if action.data() is None: + fname = QFileDialog() + else: + fname = unicode(action.data().toString()) + if not self.okToContinue(): + return + else: + return + if fname: + self.data = Data(evtdata=fname) def saveData(self): pass From dc43a3520d8b4e7e867754d80e8c2cd80ec24655 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:39:56 +0100 Subject: [PATCH 0132/1144] do not "save" actions as Attributes of the MainWindow --- QtPyLoT.py | 73 +++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index c9f02edd..14169965 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -169,42 +169,43 @@ class MainWindow(QMainWindow): openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) - self.openEventAction = self.createAction("&Open event ...", - self.loadData, - QKeySequence.Open, - openIcon, - "Open an event.") - self.saveEventAction = self.createAction("&Save event ...", - self.saveData, - QKeySequence.Save, saveIcon, - "Save actual event data.") - self.quitAction = self.createAction("&Quit", - QCoreApplication.instance().quit, - QKeySequence.Close, - quitIcon, - "Close event and quit PyLoT") - self.filterAction = self.createAction("&Filter ...", self.filterData, - "Ctrl+F", QIcon(":/filter.png"), - """Toggle un-/filtered waveforms - to be displayed, according to the - desired seismic phase.""", True) - self.filterEditAction = self.createAction("&Filter parameter ...", - self.adjustFilterOptions, - "Alt+F", QIcon(None), - """Adjust filter - parameters.""") - self.selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", - QIcon(":/picon.png"), - "Toggle P phase.", True) - self.selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", - QIcon(":/sicon.png"), - "Toggle S phase", True) - self.printAction = self.createAction("&Print event ...", - self.printEvent, - QKeySequence.Print, - QIcon(":/printer.png"), - "Print waveform overview.") - self.createMenus() + openEventAction = self.createAction("&Open event ...", self.loadData, + QKeySequence.Open, openIcon, + "Open an event.") + openEventAction.setData(None) + saveEventAction = self.createAction("&Save event ...", self.saveData, + QKeySequence.Save, saveIcon, + "Save actual event data.") + quitAction = self.createAction("&Quit", + QCoreApplication.instance().quit, + QKeySequence.Close, quitIcon, + "Close event and quit PyLoT") + filterAction = self.createAction("&Filter ...", self.filterData, + "Ctrl+F", QIcon(":/filter.png"), + """Toggle un-/filtered waveforms + to be displayed, according to the + desired seismic phase.""", True) + filterEditAction = self.createAction("&Filter parameter ...", + self.adjustFilterOptions, + "Alt+F", QIcon(None), + """Adjust filter parameters.""") + selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", + QIcon(":/picon.png"), + "Toggle P phase.", True) + selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", + QIcon(":/sicon.png"), + "Toggle S phase", True) + printAction = self.createAction("&Print event ...", + self.printEvent, QKeySequence.Print, + QIcon(":/printer.png"), + "Print waveform overview.") + self.fileMenu = self.menuBar().addMenu('&File') + self.fileMenuActions = (openEventAction, saveEventAction, None, + quitAction) + self.fileMenu.aboutToShow.connect(self.updateFileMenu) + self.createMenu('&Edit', (filterAction, filterEditAction, None, + selectPAction, selectSAction, None, + printAction)) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) From ec1cc26b8ccbf7f36b7d09372526f7466c6956ea Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:41:13 +0100 Subject: [PATCH 0133/1144] new method okToContinue written in order to prevent accidental dataloss --- QtPyLoT.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 14169965..26060c28 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -214,14 +214,19 @@ class MainWindow(QMainWindow): status.addPermanentWidget(self.eventLabel) status.showMessage("Ready", 500) - #statLayout = layoutStationButtons(self.getData(), self.getComponent()) - #dataLayout = self.getDataWidget() +# statLayout = layoutStationButtons(self.getData(), self.getComponent()) +# dataLayout = self.getDataWidget() -# maingrid = QGridLayout() -# maingrid.setSpacing(10) -# maingrid.addLayout(statLayout, 0, 0) -# maingrid.addWidget(dataLayout, 1, 0) - #self.setLayout(maingrid) +# maingrid = QGridLayout() +# maingrid.setSpacing(10) +# maingrid.addLayout(statLayout, 0, 0) +# maingrid.addWidget(self.getDataWidget(), 0, 1) +# self.setLayout(maingrid) + + def okToContinue(self): + if self.dirty: + return self.saveData() + return True def plotData(self): pass #self.data.plotData(self.DataPlot) @@ -273,9 +278,8 @@ class MainWindow(QMainWindow): def printEvent(self): pass - def cleanUp(self): - self - return 0 + def closeEvent(self): + return self.saveData() def helpHelp(self): if checkurl(): From d7a4692afc305447b5f37dbeae2ba8c5c831e4e3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:42:50 +0100 Subject: [PATCH 0134/1144] tried to get filter options popup working (not working yet) --- QtPyLoT.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 26060c28..c268f38d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -239,8 +239,10 @@ class MainWindow(QMainWindow): filterDlg = FilterOptionsDialog(titleString=fstring, parent=self, filterOptions=self.getFilterOptions()) - filterDlg.show() - filterDlg.closeEvent.connect(lambda: self.setFilterOptions(filterDlg.getFilterOptions())) + if filterDlg.exec_(): + filterOptions = filterDlg.getFilterOptions() + + self.setFilterOptions(filterOptions) def getFilterOptions(self): return self.filteroptions From 3e559f61da4d73cdbe0cc0e24657748c79f3850c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:43:49 +0100 Subject: [PATCH 0135/1144] fnConstructor now works on eventIDs also --- pylot/core/util/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index afbf5750..c87911c2 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -7,6 +7,8 @@ import re def fnConstructor(s): + s = s.split('/')[-1] + badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)') From 41684cd2820b2a1fb6c9b19e9da0f7b3f1ef5adc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:44:25 +0100 Subject: [PATCH 0136/1144] filterOptions has now a buttonBox --- pylot/core/util/widgets.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0cac4620..a6376f73 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -200,16 +200,22 @@ class FilterOptionsDialog(QDialog): self.freqGroupLayout.addWidget(self.freqmaxSpinBox, 1, 1) self.freqGroupBox.setLayout(self.freqGroupLayout) - self.layoutEditables = QHBoxLayout() - self.layoutEditables.addWidget(self.freqGroupBox) - self.layoutEditables.addLayout(self.selectTypeLayout) + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok| + QDialogButtonBox.Cancel) + + grid = QGridLayout() + grid.addWidget(self.freqGroupBox, 0, 2, 1, 2) + grid.addLayout(self.selectTypeLayout, 1, 2, 1, 2) + grid.addWidget(self.buttonBox, 2, 2, 1, 2) - self.setLayout(self.layoutEditables) + self.setLayout(grid) self.freqminSpinBox.valueChanged.connect(self.updateUi) self.freqmaxSpinBox.valueChanged.connect(self.updateUi) self.orderSpinBox.valueChanged.connect(self.updateUi) self.selectTypeCombo.currentIndexChanged.connect(self.updateUi) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) def updateUi(self): if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: @@ -237,11 +243,14 @@ class FilterOptionsDialog(QDialog): freq.append(self.freqmaxSpinBox.value()) self.filterOptions.freq = freq self.filterOptions.order = self.orderSpinBox.value() - return self.getFilterOptions() def getFilterOptions(self): return self.filterOptions + def accept(self): + self.updateUi() + QDialog.accept(self) + class LoadDataDlg(QDialog): From 553bb9990b0ed3d887d661072313f9842c6e59c5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:45:20 +0100 Subject: [PATCH 0137/1144] loading data by initialization of a Data object (not working yet) --- pylot/core/read/data.py | 45 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index b11aea34..db6b898c 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -4,6 +4,7 @@ import os from PySide.QtGui import QMessageBox from obspy.core import (read, Stream) +from obspy import readEvents from obspy.core.event import (Event, Catalog) from pylot.core.util import fnConstructor @@ -24,37 +25,37 @@ class Data(object): loaded event. Container object holding, e.g. phase arrivals, etc. ''' - def __init__(self, parent=None, wfdata=None, evtdata=None): - if wfdata is not None and isinstance(wfdata, Stream): - self.wfdata = wfdata - elif wfdata is not None: - try: - self.wfdata = read(wfdata) - except IOError, e: - msg = 'An I/O error occured while loading data!' - inform = 'Variable wfdata will be empty.' - details = '{0}'.format(e) - if parent is not None: - warnio = QMessageBox(parent=parent) - warnio.setText(msg) - warnio.setDetailedText(details) - warnio.setInformativeText(inform) - warnio.setStandarButtons(QMessageBox.Ok) - warnio.setIcon(QMessageBox.Warning) - else: - print msg, '\n', details - self.wfdata = Stream() + def __init__(self, parent=None, evtdata=None): + try: + self.wfdata = read() + except IOError, e: + msg = 'An I/O error occured while loading data!' + inform = 'Variable wfdata will be empty.' + details = '{0}'.format(e) + if parent is not None: + warnio = QMessageBox(parent=parent) + warnio.setText(msg) + warnio.setDetailedText(details) + warnio.setInformativeText(inform) + warnio.setStandarButtons(QMessageBox.Ok) + warnio.setIcon(QMessageBox.Warning) + else: + print msg, '\n', details + self.wfdata = Stream() else: self.wfdata = Stream() if evtdata is not None and isinstance(evtdata, Event): self.evtdata = evtdata - else: + elif evtdata is not None: + cat = readEvents(evtdata) + self.evtdata = cat[0] + else: # create an empty Event object self.evtdata = Event() def exportEvent(self, fnout=None, evtformat='QUAKEML'): if fnout is None: - fnout = self.event.resource_id.__str__().split('/')[-1] + fnout = self.evtdata.getEventID() # handle forbidden filenames especially on windows systems fnout = fnConstructor(fnout) From e6ac3374666f8a88f0a72891eb7100451b8235fe Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Dec 2014 12:46:04 +0100 Subject: [PATCH 0138/1144] testing GUI; execution updates RELEASE-VERSION --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 736e6bdd..882eee74 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -9dc5-dirty +fc3e-dirty From 609005433aa9e5e8d114d71557e32017a6f3ffb0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 2 Dec 2014 12:07:02 +0100 Subject: [PATCH 0139/1144] added verbose output functionality --- makePyLoT.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/makePyLoT.py b/makePyLoT.py index 1328b6f2..0a37acd4 100644 --- a/makePyLoT.py +++ b/makePyLoT.py @@ -95,18 +95,20 @@ USAGE if verbose > 0: print("Verbose mode on") if install and not directory: - raise CLIError("trying to install without destination; please specify an installation directory") + raise CLIError("""Trying to install without appropriate + destination; please specify an installation + directory!""") if build and install: - print("Building and installing PyLoT ...") - buildPyLoT() - installPyLoT() + print("Building and installing PyLoT ...\n") + buildPyLoT(verbose) + installPyLoT(verbose) elif build and not install: - print("Building PyLoT without installing! Please wait ...") - buildPyLoT() + print("Building PyLoT without installing! Please wait ...\n") + buildPyLoT(verbose) cleanUp() return 0 except KeyboardInterrupt: - cleanUp() + cleanUp(verbose) return 0 except Exception, e: if DEBUG or TESTRUN: @@ -116,23 +118,30 @@ USAGE sys.stderr.write(indent + " for help use --help") return 2 -def buildPyLoT(): - if sys.platform.startswith('win' or 'microsoft'): +def buildPyLoT(verbosity=None): + system = sys.platform + if verbosity > 1: + msg = ("... on system: {0}\n" + "\n" + " Current working directory: {1}\n" + ).format(system, os.getcwd()) + print msg + if system.startswith(('win', 'microsoft')): raise CLIError("building on Windows system not tested yet; implementation pending") - elif sys.platform == 'darwin': + elif system == 'darwin': # create a symbolic link to the desired python interpreter in order to # display the right application name for path in os.getenv('PATH').split(':'): found = glob.glob(os.path.join(path, 'python')) if found: - os.symlink(found, 'PyLoT') + os.symlink(found, './PyLoT') break -def installPyLoT(): +def installPyLoT(verbosity=None): pass -def cleanUp(): +def cleanUp(verbosity=None): pass if __name__ == "__main__": From 9603f7127ce14dd91640829d2c782e559e8afdf9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 4 Dec 2014 05:13:32 +0100 Subject: [PATCH 0140/1144] imports corrected; menu definition changed --- QtPyLoT.py | 31 +++++++++++++++++++------------ pylot/RELEASE-VERSION | 2 +- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index c268f38d..bc537888 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -23,25 +23,22 @@ https://www.iconfinder.com/iconsets/flavour (http://www.gnu.org/copyleft/lesser.html) """ -import os import sys + from PySide.QtCore import * from PySide.QtGui import * -from obspy.core import (read, UTCDateTime) -from pylot import * +from obspy.core import (UTCDateTime) + from pylot.core.util import _getVersionString from pylot.core.read import (Data, FilterOptions) from pylot.core.util import FILTERDEFAULTS from pylot.core.util import fnConstructor from pylot.core.util import checkurl -from pylot.core.util import (PickDlg, - FilterOptionsDialog, - PropertiesDlg, +from pylot.core.util import (FilterOptionsDialog, MPLWidget, HelpForm) -from pylot.core.util import layoutStationButtons -import qrc_resources + # Version information __version__ = _getVersionString() @@ -86,6 +83,9 @@ class MainWindow(QMainWindow): def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False): + """ + :rtype : ~PySide.QtGui.QAction + """ action = QAction(text, self) if icon is not None: action.setIcon(icon) @@ -203,9 +203,14 @@ class MainWindow(QMainWindow): self.fileMenuActions = (openEventAction, saveEventAction, None, quitAction) self.fileMenu.aboutToShow.connect(self.updateFileMenu) - self.createMenu('&Edit', (filterAction, filterEditAction, None, - selectPAction, selectSAction, None, - printAction)) + + self.editMenu = self.menuBar().addMenu('&Edit') + for action in (filterAction, filterEditAction, None, selectPAction, + selectSAction, None, printAction): + if action is None: + self.editMenu.addSeparator() + else: + self.editMenu.addAction(action) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) @@ -235,13 +240,15 @@ class MainWindow(QMainWindow): pass def adjustFilterOptions(self): + filterOptions = None fstring = "Filter Options ({0})".format(self.getSeismicPhase()) filterDlg = FilterOptionsDialog(titleString=fstring, parent=self, filterOptions=self.getFilterOptions()) if filterDlg.exec_(): filterOptions = filterDlg.getFilterOptions() - + + assert isinstance(filterOptions, FilterOptions) self.setFilterOptions(filterOptions) def getFilterOptions(self): diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 882eee74..3830bb1d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -fc3e-dirty +e6ac-dirty From 5650f7bfc71d741a052b2688fdeca04aa1467728 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 5 Dec 2014 10:26:37 +0100 Subject: [PATCH 0141/1144] started to implement read capability for matlab binary phases files (AUTO- and PHASES.mat) --- pylot/core/read/data.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index db6b898c..f677bb1d 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -46,12 +46,17 @@ class Data(object): self.wfdata = Stream() if evtdata is not None and isinstance(evtdata, Event): self.evtdata = evtdata - elif evtdata is not None: + elif evtdata is not None and not evtdata.endswith('.mat'): cat = readEvents(evtdata) self.evtdata = cat[0] + elif evtdata is not None: + cat = readMatPhases(evtdata) else: # create an empty Event object self.evtdata = Event() + def readMatPhases(self, fname): + pass + def exportEvent(self, fnout=None, evtformat='QUAKEML'): if fnout is None: From ca809c467376f214b5fc981bdb5c7801558c5cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 5 Dec 2014 16:14:03 +0100 Subject: [PATCH 0142/1144] Initial version of new class of methods for automatic picking, AICPicker is running but without quality attributes --- pylot/core/pick/Picker.py | 183 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 pylot/core/pick/Picker.py diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py new file mode 100644 index 00000000..5e2f6f6d --- /dev/null +++ b/pylot/core/pick/Picker.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +""" +Created Dec 2014 +Implementation of the picking algorithms published and described in: + +Küperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, 2010: +Automated determination of P-phase arrival times at regional and local distances +using higher order statistics, Geophys. J. Int., 181, 1159-1170 + +Küperkoch, L., Meier, T., Brüstle, A., Lee, J., Friederich, W., & Egelados +Working Group, 2012: Automated determination of S-phase arrival times using +autoregressive prediction: application ot local and regional distances, Geophys. J. Int., +188, 687-702. + +:author: MAGS2 EP3 working group / Ludger Küperkoch +""" +import numpy as np +import matplotlib.pyplot as plt +import pdb + +class AutoPicking(object): + ''' + Superclass of different, automated picking algorithms applied on a CF determined + using AIC, HOS, or AR prediction. + ''' + def __init__(self, cf, Tcf, dt, Tslope, aerr, TSNR, PickWindow, peps, Tsmooth): + ''' + :param: cf, characteristic function, on which the picking algorithm is applied + :type: array + + :param: Tcf, corresponding time array + :type: array + + :param: dt, sampling interval [s] + :type: float + + :param: Tslope, length of time window after pick used to determine slope + for quality estimation [s] + :type: float + + :param: aerr (adjusted error), percentage of maximum of CF to determine slope for quality estimation + :type: int + + :param: TSNR, length of time windows around pick used to determine SNR [s] + :type: tuple (T_noise, T_gap, T_signal) + + :param: PickWindow, length of pick window [s] + :type: float + + :param: peps, find local minimum at i if aic(i-1)*(1+peps) >= aic(i) + :type: float + + :param: Tsmooth, length of moving smoothing window to calculate smoothed CF [s] + :type: float + ''' + + self.cf = cf + self.Tcf = Tcf + self.dt = dt + self.setTslope(Tslope) + self.setaerr(aerr) + self.setTSNR(TSNR) + self.setPickWindow(PickWindow) + self.setpeps(peps) + self.setTsmooth(Tsmooth) + self.calcPick() + + def __str__(self): + return '''\n\t{name} object:\n + TSlope:\t{Tslope}\n + aerr:\t{aerr}\n + TSNR:\t\t\t{TSNR}\n + PickWindow:\t{PickWindow}\n + peps:\t{peps}\n + Tsmooth:\t{Tsmooth}\n + '''.format(name=type(self).__name__, + Tslope=self.getTslope(), + aerr=self.getaerr(), + TSNR=self.getTSNR(), + PickWindow=self.getPickWindow(), + peps=self.getpeps(), + Tsmooth=self.getTsmooth()) + + def getTslope(self): + return self.Tslope + + def setTslope(self, Tslope): + self.Tslope = Tslope + + def getaerr(self): + return self.aerr + + def setaerr(self, aerr): + self.aerr = aerr + + def getTSNR(self): + return self.TSNR + + def setTSNR(self, TSNR): + self.TSNR = TSNR + + def getPickWindow(self): + return self.PickWindow + + def setPickWindow(self, PickWindow): + self.PickWindow = PickWindow + + def getpeps(self): + return self.peps + + def setpeps(self, peps): + self.peps = peps + + def setTsmooth(self, Tsmooth): + self.Tsmooth = Tsmooth + + def getTsmooth(self): + return self.Tsmooth + + def getpick(self): + return self.Pick + + def calcPick(self): + self.Pick = Pick + +class AICPicker(AutoPicking): + ''' + Method to derive onset time of arriving phase based on CF + derived from AIC. + ''' + + def calcPick(self): + + print 'Get onset (pick) from AIC-CF ...' + + #taper AIC-CF to get rid off side maxima + tap = np.hanning(len(self.cf)) + aic = tap * self.cf + max(abs(self.cf)) + #get maximum of CF as starting point + icfmax = np.argmax(aic) + + #smooth CF + aicsmooth = np.zeros(len(aic)) + ismooth = round(self.Tsmooth / self.dt) + if len(aic) < ismooth: + print 'AICPicker: Tsmooth larger than AIC function!' + self.Pick = -1 + return self.Pick + else: + self.Pick = -1 + for i in range(1, len(aic)): + if i > ismooth: + ii1 = i - ismooth + aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth + else: + aicsmooth[i] = np.mean(aic[0:i]) + + #find common, local minimum in front of maximum + #of smoothed and unsmoothed AIC-CF + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if aic[i - 1] * (1 + self.peps) >= aic[i]: + if aicsmooth[i - 1] * (1 + self.peps) >= aicsmooth[i]: + self.Pick = self.Tcf[i] + break + + #try again with larger peps if picking failed + if self.Pick < 0: + peps2 = self.peps + 0.01 + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if aic[i - 1] * (1 + peps2) >= aic[i]: + if aicsmooth[i - 1] * (1 + peps2) >= aicsmooth[i]: + self.Pick = self.Tcf[i] + break + +class PragPicker(AutoPicking): + ''' + Method of pragmatic picking exploiting information given by CF. + ''' + + def calcPick(self): + + print 'Get onset (pick) from HOS- or AR-CF using pragmatic picking algorithm ...' From 479058a41edecd1170858bdc81cfcacb3c7ffba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 5 Dec 2014 16:32:55 +0100 Subject: [PATCH 0143/1144] Included AICPicker of class Picker --- pylot/core/pick/run_makeCF.py | 328 ++++++++++++++++++---------------- 1 file changed, 173 insertions(+), 155 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 6cc80df2..db03acf0 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -10,18 +10,20 @@ from obspy.core import read import matplotlib.pyplot as plt import numpy as np from CharFuns import * +from Picker import * import glob import argparse +import pdb def run_makeCF(project, database, event, iplot, station=None): #parameters for CF calculation - t2 = 7 #length of moving window for HOS calculation [sec] - p = 4 #order of statistics + t2 = 7 #length of moving window for HOS calculation [sec] + p = 4 #order of statistics cuttimes = [10, 40] #start and end time vor CF calculation bpz = [2, 30] #corner frequencies of bandpass filter, vertical component bph = [2, 15] #corner frequencies of bandpass filter, horizontal components - tdetz= 1.2 #length of AR-determination window [sec], vertical component - tdeth= 0.8 #length of AR-determination window [sec], horizontal components + tdetz= 1.2 #length of AR-determination window [sec], vertical component + tdeth= 0.8 #length of AR-determination window [sec], horizontal components tpredz = 0.4 #length of AR-prediction window [sec], vertical component tpredh = 0.4 #length of AR-prediction window [sec], horizontal components addnoise = 0.001 #add noise to seismogram for stable AR prediction @@ -29,165 +31,181 @@ def run_makeCF(project, database, event, iplot, station=None): arhorder = 4 #chosen order of AR process, horizontal components #get waveform data if station: - dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHZ.msd' % (project, database, event, station) - dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHE.msd' % (project, database, event, station) - dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHN.msd' % (project, database, event, station) + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHZ.msd' % (project, database, event, station) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHE.msd' % (project, database, event, station) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHN.msd' % (project, database, event, station) + #dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_z.gse' % (project, database, event, station) + #dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_e.gse' % (project, database, event, station) + #dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_n.gse' % (project, database, event, station) else: - dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHZ.msd' % (project, database, event) - dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHE.msd' % (project, database, event) - dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHN.msd' % (project, database, event) + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHZ.msd' % (project, database, event) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHE.msd' % (project, database, event) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHN.msd' % (project, database, event) wfzfiles = glob.glob(dpz) wfefiles = glob.glob(dpe) wfnfiles = glob.glob(dpn) if wfzfiles: - for i in range(len(wfzfiles)): - print 'Vertical component data found ...' - print wfzfiles[i] - st = read('%s' % wfzfiles[i]) - st_copy = st.copy() - #filter and taper data - tr_filt = st[0].copy() - tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) - tr_filt.taper(max_percentage=0.05, type='hann') - st_copy[0].data = tr_filt.data - ############################################################## - #calculate HOS-CF using subclass HOScf of class CharacteristicFunction - hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf - ############################################################## - #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it - tr_aic = tr_filt.copy() - tr_aic.data = hoscf.getCF() - st_copy[0].data = tr_aic.data - aiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf - ############################################################## - #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction - #get stream object of filtered data - st_copy[0].data = tr_filt.data - arzcf = ARZcf(st_copy, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf - ############################################################## - #calculate AIC-ARZ-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it - tr_arzaic = tr_filt.copy() - tr_arzaic.data = arzcf.getCF() - st_copy[0].data = tr_arzaic.data - araiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf + for i in range(len(wfzfiles)): + print 'Vertical component data found ...' + print wfzfiles[i] + st = read('%s' % wfzfiles[i]) + st_copy = st.copy() + #filter and taper data + tr_filt = st[0].copy() + tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + st_copy[0].data = tr_filt.data + ############################################################## + #calculate HOS-CF using subclass HOScf of class CharacteristicFunction + hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf + #get corresponding time array + thoscf = np.arange(0, len(hoscf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] + ############################################################## + #get onset time from HOS-CF using class Picker + #hospick = PragPicker(hoscf.getCF(), thoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2) + #pdb.set_trace() + ############################################################## + #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_aic = tr_filt.copy() + tr_aic.data = hoscf.getCF() + st_copy[0].data = tr_aic.data + aiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf + #get corresponding time array + taiccf = np.arange(0, len(aiccf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] + ############################################################## + #get onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking + aicpick = AICPicker(aiccf.getCF(), taiccf, tr_filt.stats.delta, 2, 70, [1, 0.5, 0.2], 2, 0.001, 2.5) + ############################################################## + #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction + #get stream object of filtered data + st_copy[0].data = tr_filt.data + arzcf = ARZcf(st_copy, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf + ############################################################## + #calculate AIC-ARZ-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_arzaic = tr_filt.copy() + tr_arzaic.data = arzcf.getCF() + st_copy[0].data = tr_arzaic.data + araiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf elif not wfzfiles: - print 'No vertical component data found!' + print 'No vertical component data found!' if wfefiles and wfnfiles: - for i in range(len(wfefiles)): - print 'Horizontal component data found ...' - print wfefiles[i] - print wfnfiles[i] - #merge streams - H = read('%s' % wfefiles[i]) - H += read('%s' % wfnfiles[i]) - H_copy = H.copy() - #filter and taper data - trH1_filt = H[0].copy() - trH2_filt = H[1].copy() - trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - H_copy[0].data = trH1_filt.data - H_copy[1].data = trH2_filt.data - ############################################################## - #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction - arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf - ############################################################## - #create stream with 3 traces - #merge streams - AllC = read('%s' % wfefiles[i]) - AllC += read('%s' % wfnfiles[i]) - AllC += read('%s' % wfzfiles[i]) - #filter and taper data - All1_filt = AllC[0].copy() - All2_filt = AllC[1].copy() - All3_filt = AllC[2].copy() - All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) - All1_filt.taper(max_percentage=0.05, type='hann') - All2_filt.taper(max_percentage=0.05, type='hann') - All3_filt.taper(max_percentage=0.05, type='hann') - AllC[0].data = All1_filt.data - AllC[1].data = All2_filt.data - AllC[2].data = All3_filt.data - #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction - ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf - ############################################################## - if iplot: - #plot vertical trace - plt.figure() - tr = st[0] - tstepz = tpredz / 16 - tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) - thoscf = np.arange(0, len(hoscf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] - taiccf = np.arange(0, len(aiccf.getCF()) / tr.stats.sampling_rate, tr.stats.delta) + cuttimes[0] - tarzcf = np.arange(0, len(arzcf.getCF()) * tstepz, tstepz) + cuttimes[0] + tdetz +tpredz - taraiccf = np.arange(0, len(araiccf.getCF()) * tstepz, tstepz) + cuttimes[0] +tdetz + tpredz - p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p2 = plt.plot(thoscf, hoscf.getCF()/max(hoscf.getCF()), 'r') - p3 = plt.plot(taiccf, aiccf.getCF()/max(aiccf.getCF()), 'b') - p4 = plt.plot(tarzcf, arzcf.getCF()/max(arzcf.getCF()), 'g') - p5 = plt.plot(taraiccf, araiccf.getCF()/max(araiccf.getCF()), 'y') - plt.yticks([]) - plt.xlabel('Time [s]') - plt.ylabel('Normalized Counts') - plt.title([tr.stats.station, tr.stats.channel]) - plt.suptitle(tr.stats.starttime) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) - #plot horizontal traces - plt.figure(2) - plt.subplot(211) - tsteph = tpredh / 4 - th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) - th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) - tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh - p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - p22 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') - plt.yticks([]) - plt.ylabel('Normalized Counts') - plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) - plt.suptitle(trH1_filt.stats.starttime) - plt.legend([p21, p22], ['Data', 'ARH-CF']) - plt.subplot(212) - p23 = plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - p24 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') - plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) - plt.yticks([]) - plt.xlabel('Time [s]') - plt.ylabel('Normalized Counts') - #plot 3-component window - plt.figure(3) - tar3ccf = np.arange(0, len(ar3ccf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdetz +tpredz - plt.subplot(311) - p31 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p32 = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') - plt.yticks([]) - plt.xticks([]) - plt.ylabel('Normalized Counts') - plt.title([tr.stats.station, tr.stats.channel]) - plt.legend([p31, p32], ['Data', 'AR3C-CF']) - plt.subplot(312) - plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') - plt.yticks([]) - plt.xticks([]) - plt.ylabel('Normalized Counts') - plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) - plt.subplot(313) - plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') - plt.yticks([]) - plt.ylabel('Normalized Counts') - plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) - plt.xlabel('Time [s]') - plt.show() - raw_input() - plt.close() + for i in range(len(wfefiles)): + print 'Horizontal component data found ...' + print wfefiles[i] + print wfnfiles[i] + #merge streams + H = read('%s' % wfefiles[i]) + H += read('%s' % wfnfiles[i]) + H_copy = H.copy() + #filter and taper data + trH1_filt = H[0].copy() + trH2_filt = H[1].copy() + trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + H_copy[0].data = trH1_filt.data + H_copy[1].data = trH2_filt.data + ############################################################## + #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction + arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf + ############################################################## + #create stream with 3 traces + #merge streams + AllC = read('%s' % wfefiles[i]) + AllC += read('%s' % wfnfiles[i]) + AllC += read('%s' % wfzfiles[i]) + #filter and taper data + All1_filt = AllC[0].copy() + All2_filt = AllC[1].copy() + All3_filt = AllC[2].copy() + All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + All1_filt.taper(max_percentage=0.05, type='hann') + All2_filt.taper(max_percentage=0.05, type='hann') + All3_filt.taper(max_percentage=0.05, type='hann') + AllC[0].data = All1_filt.data + AllC[1].data = All2_filt.data + AllC[2].data = All3_filt.data + #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction + ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf + ############################################################## + if iplot: + #plot vertical trace + plt.figure() + tr = st[0] + tstepz = tpredz / 16 + tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) + tarzcf = np.arange(0, len(arzcf.getCF()) * tstepz, tstepz) + cuttimes[0] + tdetz +tpredz + taraiccf = np.arange(0, len(araiccf.getCF()) * tstepz, tstepz) + cuttimes[0] +tdetz + tpredz + p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p2 = plt.plot(thoscf, hoscf.getCF()/max(hoscf.getCF()), 'r') + p3 = plt.plot(taiccf, aiccf.getCF()/max(aiccf.getCF()), 'b') + p4 = plt.plot(tarzcf, arzcf.getCF()/max(arzcf.getCF()), 'g') + p5 = plt.plot(taraiccf, araiccf.getCF()/max(araiccf.getCF()), 'y') + plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'b') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'b') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'b') + plt.yticks([]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + plt.title([tr.stats.station, tr.stats.channel]) + plt.suptitle(tr.stats.starttime) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) + #plot horizontal traces + plt.figure(2) + plt.subplot(211) + tsteph = tpredh / 4 + th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) + th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) + tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh + p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + p22 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.yticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.suptitle(trH1_filt.stats.starttime) + plt.legend([p21, p22], ['Data', 'ARH-CF']) + plt.subplot(212) + p23 = plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + p24 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.yticks([]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + #plot 3-component window + plt.figure(3) + tar3ccf = np.arange(0, len(ar3ccf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdetz +tpredz + plt.subplot(311) + p31 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p32 = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([tr.stats.station, tr.stats.channel]) + plt.legend([p31, p32], ['Data', 'AR3C-CF']) + plt.subplot(312) + plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.subplot(313) + plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.yticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.xlabel('Time [s]') + plt.show() + raw_input() + plt.close() + parser = argparse.ArgumentParser() parser.add_argument('--project', type=str, help='project name (e.g. Insheim)') parser.add_argument('--database', type=str, help='event data base (e.g. 2014.09_Insheim)') From ce8b954a8bc59714ef56b70bec71207f7e22ae39 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 8 Dec 2014 05:27:54 +0100 Subject: [PATCH 0144/1144] now cf is an CharacteristicFunction object and not an array (changes should also be made to the caller run_makeCF.py) --- pylot/core/pick/CharFuns.py | 9 ++++++++- pylot/core/pick/Picker.py | 12 ++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 9fc05685..f469e67b 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -91,6 +91,10 @@ class CharacteristicFunction(object): def setTime2(self, t2): self.t2 = t2 + def getTimeArray(self): + cut = self.getCut() + return np.arange(cut[0], cut[1], self.getIncrement()) + def getOrder(self): return self.order @@ -98,6 +102,9 @@ class CharacteristicFunction(object): self.order = order def getIncrement(self): + """ + :rtype : int + """ return self.dt def getFnoise(self): @@ -148,7 +155,7 @@ class CharacteristicFunction(object): data = hh return data else: - data = self.orig_data + data = self.orig_data.copy() return data def calcCF(self, data=None): diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 5e2f6f6d..e8d980bc 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -23,10 +23,10 @@ class AutoPicking(object): Superclass of different, automated picking algorithms applied on a CF determined using AIC, HOS, or AR prediction. ''' - def __init__(self, cf, Tcf, dt, Tslope, aerr, TSNR, PickWindow, peps, Tsmooth): + def __init__(self, cf, Tslope, aerr, TSNR, PickWindow, peps, Tsmooth): ''' :param: cf, characteristic function, on which the picking algorithm is applied - :type: array + :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object :param: Tcf, corresponding time array :type: array @@ -54,9 +54,9 @@ class AutoPicking(object): :type: float ''' - self.cf = cf - self.Tcf = Tcf - self.dt = dt + self.cf = cf.getCF() + self.Tcf = cf.getTimeArray() + self.dt = cf.getIncrement() self.setTslope(Tslope) self.setaerr(aerr) self.setTSNR(TSNR) @@ -121,7 +121,7 @@ class AutoPicking(object): return self.Pick def calcPick(self): - self.Pick = Pick + self.Pick = None class AICPicker(AutoPicking): ''' From 752811c8fd855b777a031b5901af82e08e598f31 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 8 Dec 2014 08:45:43 +0100 Subject: [PATCH 0145/1144] implemented method getTimeArray in the same fashion as used in run_makeCF.py --- pylot/core/pick/CharFuns.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index f469e67b..2ff0ec01 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -92,8 +92,10 @@ class CharacteristicFunction(object): self.t2 = t2 def getTimeArray(self): - cut = self.getCut() - return np.arange(cut[0], cut[1], self.getIncrement()) + incr = self.getIncrement() + timeArray = np.arange(0, len(self.getCF()) / incr**-1, + incr) + self.getCut()[0] + return timeArray def getOrder(self): return self.order From 32eee12f25fff2147c08131b146892f88ff59006 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 8 Dec 2014 08:48:33 +0100 Subject: [PATCH 0146/1144] using the new implementation of the AutoPicking object --- pylot/core/pick/run_makeCF.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index db03acf0..8d6d6aab 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -72,10 +72,10 @@ def run_makeCF(project, database, event, iplot, station=None): st_copy[0].data = tr_aic.data aiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf #get corresponding time array - taiccf = np.arange(0, len(aiccf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] + #taiccf = np.arange(0, len(aiccf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] ############################################################## #get onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicpick = AICPicker(aiccf.getCF(), taiccf, tr_filt.stats.delta, 2, 70, [1, 0.5, 0.2], 2, 0.001, 2.5) + aicpick = AICPicker(aiccf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 2.5) ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction #get stream object of filtered data From 04e28943d5ca3e7e22e9158855dca2f450340853 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 8 Dec 2014 08:53:58 +0100 Subject: [PATCH 0147/1144] remove variables which are attributes of classes --- pylot/core/pick/run_makeCF.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 8d6d6aab..16cfbd93 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -58,8 +58,8 @@ def run_makeCF(project, database, event, iplot, station=None): ############################################################## #calculate HOS-CF using subclass HOScf of class CharacteristicFunction hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf - #get corresponding time array - thoscf = np.arange(0, len(hoscf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] + #get corresponding time array -> unnecessary because implemented in CharacteristicFunction + # thoscf = np.arange(0, len(hoscf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] ############################################################## #get onset time from HOS-CF using class Picker #hospick = PragPicker(hoscf.getCF(), thoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2) From ef8bd6572eef2466fd57363bd149fd7f7230d678 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 8 Dec 2014 10:26:14 +0100 Subject: [PATCH 0148/1144] create a working MainWindow --- QtPyLoT.py | 68 ++++++++++++++++++++++++++--------------- pylot/RELEASE-VERSION | 2 +- pylot/core/read/data.py | 7 ++++- qrc_resources.py | 8 ++--- 4 files changed, 55 insertions(+), 30 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index bc537888..70bd2c9b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -23,6 +23,7 @@ https://www.iconfinder.com/iconsets/flavour (http://www.gnu.org/copyleft/lesser.html) """ +import os import sys from PySide.QtCore import * @@ -35,6 +36,7 @@ from pylot.core.read import (Data, from pylot.core.util import FILTERDEFAULTS from pylot.core.util import fnConstructor from pylot.core.util import checkurl +from pylot.core.util import layoutStationButtons from pylot.core.util import (FilterOptionsDialog, MPLWidget, HelpForm) @@ -46,31 +48,39 @@ __version__ = _getVersionString() class MainWindow(QMainWindow): + closing = Signal() + def __init__(self, parent=None): super(MainWindow, self).__init__(parent) settings = QSettings() - self.recentEvents = settings.value("recentEvents", []) - self.setWindowTitle("PyLoT - do seismic processing the pythonic way") + if settings.value("user/FullName", None) is None: + fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") + settings.setValue("user/FullName", fulluser) + settings.setValue("user/Login", os.getlogin()) + settings.sync() + self.recentEvents = settings.value("data/recentEvents", []) + self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(QIcon(":/icon.ico")) self.seismicPhase = str(settings.value("phase", "P")) - if settings.value("dataRoot", None) is None: - dirname = QFileDialog().getExistingDirectory() - settings.setValue("dataRoot", dirname) + if settings.value("data/dataRoot", None) is None: + dirname = QFileDialog().getExistingDirectory(caption = 'Choose data root ...') + settings.setValue("data/dataRoot", dirname) settings.sync() # initialize filter parameter filterOptionsP = FILTERDEFAULTS['P'] filterOptionsS = FILTERDEFAULTS['S'] - print filterOptionsP, "\n", filterOptionsS + # print filterOptionsP, "\n", filterOptionsS self.filterOptionsP = FilterOptions(**filterOptionsP) self.filterOptionsS = FilterOptions(**filterOptionsS) # initialize data self.data = None + self.dirty = False self.loadData() self.updateFilterOptions() - print self.filteroptions + # print self.filteroptions try: self.startTime = min([tr.stats.starttime for tr in self.data.wfdata]) except: @@ -103,7 +113,7 @@ class MainWindow(QMainWindow): self.fileMenu.clear() self.addActions(self.fileMenu, self.fileMenuActions[:-1]) - current = self.data.evtdata.getEventID() + current = self.data.evtdata.getID() recentEvents = [] for eventID in self.recentEvents: fname = fnConstructor(eventID) @@ -141,7 +151,7 @@ class MainWindow(QMainWindow): self.data = Data(evtdata=fname) def saveData(self): - pass + return True def getComponent(self): return self @@ -158,13 +168,19 @@ class MainWindow(QMainWindow): xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') plottitle = self._getCurrentPlotType() + _widget = QWidget() + _layout = QHBoxLayout() + # create central matplotlib figure widget self.DataPlot = MPLWidget(parent=self, xlabel=xlab, ylabel=None, title=plottitle) - - self.setCentralWidget(self.getDataWidget()) + statsButtons = layoutStationButtons(self.getData(), self.getComponent()) + _layout.addLayout(statsButtons) + _layout.addWidget(self.DataPlot) + self.setLayout(_layout) + self.setCentralWidget(_widget) openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) @@ -219,15 +235,6 @@ class MainWindow(QMainWindow): status.addPermanentWidget(self.eventLabel) status.showMessage("Ready", 500) -# statLayout = layoutStationButtons(self.getData(), self.getComponent()) -# dataLayout = self.getDataWidget() - -# maingrid = QGridLayout() -# maingrid.setSpacing(10) -# maingrid.addLayout(statLayout, 0, 0) -# maingrid.addWidget(self.getDataWidget(), 0, 1) -# self.setLayout(maingrid) - def okToContinue(self): if self.dirty: return self.saveData() @@ -283,12 +290,25 @@ class MainWindow(QMainWindow): def updateStatus(self, message): self.statusBar().showMessage(message, 5000) + if self.getData() is not None: + if not self.getData().isNew(): + self.setWindowTitle("PyLoT - processing event %s[*]" % self.getData().getID()) + elif self.getData().isNew(): + self.setWindowTitle("PyLoT - New event [*]") + else: + self.setWindowTitle("PyLoT - seismic processing the python way[*]") + self.setWindowTitle("PyLoT - seismic processing the python way[*]") + self.setWindowModified(self.dirty) + + self.statusBar().showMessage(message, 5000) def printEvent(self): pass - def closeEvent(self): - return self.saveData() + def closeEvent(self, event): + if self.okToContinue(): + self.closing.emit() + QMainWindow.closeEvent(self, event) def helpHelp(self): if checkurl(): @@ -313,7 +333,7 @@ def main(): # Show main window and run the app pylot_form.show() - sys.exit(pylot_app.exec_()) + pylot_app.exec_() if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 3830bb1d..0b303821 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -e6ac-dirty +9603-dirty diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index db6b898c..5b9250f1 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -44,14 +44,19 @@ class Data(object): self.wfdata = Stream() else: self.wfdata = Stream() + self.newevent = False if evtdata is not None and isinstance(evtdata, Event): self.evtdata = evtdata elif evtdata is not None: cat = readEvents(evtdata) self.evtdata = cat[0] else: # create an empty Event object + self.newevent = True self.evtdata = Event() + def isNew(self): + return self.newevent + def exportEvent(self, fnout=None, evtformat='QUAKEML'): if fnout is None: @@ -75,7 +80,7 @@ class Data(object): pass #axes = widget.axes - def getEventID(self): + def getID(self): try: return self.evtdata.get('resource_id').id except: diff --git a/qrc_resources.py b/qrc_resources.py index cb089909..9ee115e0 100644 --- a/qrc_resources.py +++ b/qrc_resources.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Di. Nov. 25 10:10:42 2014 +# Created: Mo. Dez. 8 09:32:58 2014 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" -qt_resource_name = "\x00\x09\x034&\xbc\x00h\x00e\x00l\x00p\x00.\x00h\x00t\x00m\x00l\x00\x08\x0aaB\x7f\x00i\x00c\x00o\x00n\x00.\x00i\x00c\x00o" -qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x02\xfd" +qt_resource_data = "\x00\x00\x02\xa2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\x00\x00\x02iIDATx^\xedYM\x8b\xc20\x10\x9d\x96\xbd(\xa8\xa8\x08\xa27\xaf\xfe\x01\xf5\xa8\xff\xdb\xbb\x07\xaf\x1e\x14\xc4\x83\x17\x11\x05\x11?@\xa1K\x02sI\x98}\xc9NYYHaH\xda\xa6\x9d\xf7\xe6\xcd\xb4i\x9a\x15EA\xffy\xcb?\xe8;\x11H\x04\x12\x81D \x11H\x04\x12\x81\xaf\x90A\xdb\xed\xb6\xe0~\x96e\xdc\x8a}n\xdd\xad(\x0a\xb7\x95\xfa\xb6\x1d\x0c\x06\x19\xc2\xc6s!\x08\xbeV\xab10\xdc\xca\x1b\x03\x84\xed\xf5z\x0d\x22\xf1\x15\x0a\xbe\xd3\xe9\xd0\x1fo\x86\x84\xc5 \x90\xc0\x04\x18|\xb5Z\xa5\xdb\xed\xc6\xd1\x95\x22\x1e\xa7\x80\xac\x881\x0e\x18+\xa1.b\x00\xde\xf6\xfd}6\xff\x9c2\x18\x98\x00p&\x163\x9b|/\x7f\xbc\x8aD\x1e\x10u\x09\xbcD\x0c\x19R\xd4%\xa8R \xc6Ih\x0a\xc5(PN\x0aE\xa4\x99\x00\x16\x8c\xf3M\xa9\x00\x96\x16\x83\xc3\xfb\xc0\x8fB\x01l\xb2\xb3\xe7\xf3I\xa7\xd3\x89\x0e\x87\x03m6\x1b:\x9f\xcf\xe6\x98\xa0(\x04\xadV\xc0u\x08#\xb7\xdf\xefi\xb9\x5c\xd2n\xb73\xe0-\x89\xf9|N\x8b\xc5\x82\xde\xef7V\xd2\xef\x97V\xc4\xc1\xe7\x1b\x8d\x06\x8d\xc7c6\x9aN\xa7\xf4z\xbd\x0c1\x10q\xbd\x02\xd8\xe2\xa3d\xdf\xe4\xc3\xe1\xd0\xa6\xd6\xe3\xf1\xd0\xdcKO \xf6\xbd\xc1\xd6n\xb7\xed\xb1\xfb\xfd\x0e\x80~\x80\x00\x00\xc4\xc0Y\x0d\x97\xf0\xe7\x09\x84,O\xaeV+\xaa\xd7\xebT\xa9T\xc0u\xda\x0f\x1a=\x11S\xb0&\xdf9\xf2\xf6\xc9t\xb9\x5ch2\x99(\x81\xeb\x15`\x00b\x14\x19\xf4z\xbd6f\xc1w\xbb]\x9a\xcdffj\x8e\xee\xa5P\x00\x8369\x0b\xcf\xf3ct4\x1a\xa1\xaf\xb1R\xd23\x07\xa0\x8d\x81\x889\xad|\xddO\xe3\x7f\xadH\xae\xccwD\x90-\xf0k\xac|\x05@\xeb\x81\x15#\xee\xefc%\xf55\xc0 \xf1\x18\xb0\x94\x22\xf4\xb1o\x9d\x02X\x0d?\xd2\xadV\x8b\xfa\xfd>\x1fg\x93\x22\x1e\x12}u\x0a\xb1\x05\x15_\xb3\xd9\xa4^\xaf\xc7\xe0D\x228\x85\xf4\x0a\xa0|\x05\xb9.\xde+F\x81\xd2S(\xb6\x88\xd9\xc0X\x00\x1e\x16q\xfc\xcb\x8c\xfbN\x01\x03\xa7\x8a\x02\xc7\x0a\xe8\x9d\xb2!\xd0lj\xf0X\x01\xac\x84\xab\x80b\x9a\xa0\x98\x0b\x81\x05V\x9e\xc7\xbb\xceD\xd0\xf8\x9d\x80\xc9\x1c\x8fG\xf6]\xda\xf2z\xf8\xea\x9d>\xf2\xce\xf2:&\x00ID/A\xea\xd3F\xff\x83\x03[\xfaG\x96\x08|\x03\xf7\x03\x9fA\x22K\x9b\x82\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xa9\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x03KIDATx\xda\xd4W\xcdk\x13A\x14\x7f\x93\x9d\xcdWs\xc8\xc1\xe6RZAj\x03\xb6H\xdb`\x9b\xda\xd8\x8f\x80\x0azmZ\xfc\x0b\xbc+\x88\xff\x83\xf4PDA\xf0\x1f\x90\xeaE\xb1\xa0B/F\x1b?\xaa=\xa8\xa0\xf6b\xa1XTj\x0e%i\xd2d\xc7\xf7\xa6\xd9\xb8\x99\xec\x96\xacn+\xbee\x98\xccn\xe6\xfd~\xf3\xe6\xbd7o\xd8\xf7\x9f\xdf`\xe0\xf8 XE\xd34\xd0u]P\xef\x85T\xabU\xd8\xd9\xd9a\xd4\xab\xc2\xed& 8D\xa3Q\x08\x06\x83\x9e\x10\xd8\xde\xde\x86|>\x0f\xce\x04D\xe3K\x9f\xcf\x07\xc1@\x10\xc2\xe1\xb0'\x04H?\xe9Tq\x1c-@\x7f\x14\xf4\x08\xe1\x11\xbe\xb0\x05\xaf\x13\x10\xcaW\x13\xdc3\x02B\x80\xf9\xb4\xb4\x05\x92\xb0\xc7\x04$\x86\xf8_,@\xde\xfa`\xe1>x)C\x89ag\x0b\xa0\x87vh\x5c\x9b\xd5|Z\x8a1\xd6A\xf1?\x9e\x9a\x00\x1c{\x93\x07\x8c*\xe8\x5c\x17\xbe\x90\x8f\xac\xb1\x8e\xe3l\xb5R\xbdl\x18\xc6:\xeb\xec\xec\x8cc\xb8\xe5\xfc\x01\x7f\xd4\xef\xf7\xcbpa\xf8\xec\x87\xd0\x16 (\x94\xcbe(\x97\xca\xf9B\xa1\x90\xe4\xed\xb1\xf69d\x15\x0d\x85B2\x03\xa2\x05`?\x85\xfc!\x10\x08@\x91\x17\xa3m\x91\xb69\x8e\x80is\xe5^:\xde^BXD\x02\xd3s\x9a\xa3\xc3\xe948\x08`\x95\x04as\xd3\xe4\x07M\x80\x84\xb0\xb9\x99t\xfe\x89 ,7\x84!=\xd3*4^x\xf4\xd0S\xacsg\xcf\xef\x1eHV\x1c\xc4\xe6v\x8e\x87\xce\x01\xa3\xc3)\x86D\x84:\xc9\xad\xd0bP\x07\x0b\xb7\x85\x059\xbb\x1a\x11\xb6\x04(\x13R\xf2\xe0\x9c\x83:\xc9\xadP\xccW*\x15\xa9S\xc5q$@cr\x10*L(B\xfe6\xeeMp{\x02\x86h\xf2\x01z''\xd4\xbeQ{\xb2\xf8\xd8\x15\xf0\xe9\xf4\x99\xdd\xdc\xa2\xe8Rq\xb8]\xe1Q?\x0dk=\xad`4\x99\x92\xa6lEh\xeb\xc8rdEU\x97\x8a\xe3\xb8\x05\xd6\x9a\x80\xda\xb3\x5c\xd6\x95\x05&\xc7\xd2\xbf\xcd\xeeP_\xc8-\x90,m>Xk\x02j#C'm\x8bJ;\xa13\xc5\xcc\xae\xaa\xae\xa6D\x14\x8b\xc5`ss\xb3\x99\x80\xa5\xd1\x0a\x96^>we\x81\x89\xd4d\x93\x1e\x95\x00a\xf3\x99\x0b\xd3?n\x5c\xbfy\xc8z\x0a6M\xc4'yb\xc4\xb5\x0f\xecE\x80~\x136\x9f\xcaL]\xdd\xf8\xbaq\xfb\xee\xfc=h8\x17\x14\x1f\xc8\xbdZre\x81\xb1\xd1qG\x1f\xa0~z&\x03\x84\xcd\xe8f\x842\xbb\xf6e\xed\xd2\xf2\xf2\x1bX\xfd\xbc*\xf7z\xfe\xce<\xc3U\x08,T\xe4\xb8T*\xb9\xf6\x01\xea\xb1\xf0\xa0\xb9,3\x93\x917\xad\xee\xa3\xdd\x90H\x0cB\xd7\xe1\xaek\xbd\xf1\xbe+\x92\xc0\xb1\x9e^\xf8\xf0\xe9}\x1c\xe7\xa6\xb1\xf5\x93\x92\xfe\xbe\x81\x8b\xa1pH\xa6O\x02\xce.=ue\x81\xd4\xc8\xa9]\x02\x98\x09\x8b\x85\x22[y\xf7\xf6V\xed\xd3\x0a\xb6E\xc4\xfcHz%\x81\x9e#qi~\xb3\xc9\xf8\xc4}\x8cD\x22u\x02\xb4\x127\x16 \xcb\x99\x04\xb6\xb6\xb6\x18\xf9\x0f\x95z\xaaoqk\xe2\xb1V\xad\x5c\xe3\x8d\xe1\xa2s\xc0\xc2\xb5\xe5s\xbe\xa1\xb4\x13\xe0H\xde\xfeb\xa2\xdc\x0d$\x01\xce\xff\xec>P[\xe9\x9eW3\xdb\xc9\x98\xa7_\xbc\xceyR\x0b\x90\x8f9\xc9/\x01\x06\x00oO\x87\x87}~\xb3\xc0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\x00\x00\x02\x9dIDATx^\xedY\xc1\xebqA\x14\xbdOoC\xa1H\x14\xa5\xec\xec\xac\x84\x85\x85\x7f\x5c$v\xb2S\x14\x16\x16J,\x08%\xf5\xbe\xee\xab[\xfa\xee\xef}\xc74\xd3\xa7_\xcd\xa9i\xe65c\xe6\x9c{\xee\xbc\xf7\xe6\x09\xa2(\xa2\xdf\x8c\x14}\x0f^\x80\x17\xe0\x05x\x01^\x80\x17\xe0\x05\x84\x84A\x9b\xcd&\x92v\x10\x04R'\xb6\xa5\xfe\x1bQ\x14\xa9:\xa9\xcdh4\x1a\x01\x01\x042\x18\x91\xcff\xb3B\x0c\xd6\x00L\x10\xd6\xd7\xeb\xf5#\x11\xe1\xa7\xe4K\xa5\x12\xfdo\xb0\x08\xe6 \x22\x8c\x05\x08\xf9L&C\xb7\xdbM\xa2\x9b\x14q3\x07\x92\x1dap\xc0D\x84\x93M\x8c\xc8s[_K\xd1}N\x82\x81\x05\xe0\xc5\xa4_\x91N\x9eK\x8f\xc7\x22\xcc\x05$M\xa6k\xdd\x87\x0arT\x09t\x98B\xd8\x01\x9cBd\xe2\x80\x9b\x14\x02PD\x14Y4N\xc3\xd2\x01l-&\x87\xaf\xc1:\x16\x0e`\xe0M\xf7x\xd3v\xbb\xa5~\xbfO\xb9\x5c\x8e\x04\xf7\xfb\x9d\xd2\xe9\xb4<\xb0x|\x5c#`\x07\x8c\x80\xa3\xc4Q\xafT*B^\xc0OsM\xc6<\xe2\xf6\x02\x0c\x9f\x1b \x0d\xed\x91rM\xbcX,\xd2\xe1p`'\x0c\x04\x7fY\x80\xe4\xb2\x08\xa8\xd5j4\x9b\xcdh\xb1X\xf0\x06\x06\xbf\xfb\x82\x00D\xa8\xd5jQ\xb7\xdb\x8d7\xeeh4\xa2\xe9t\xcamD\xdc\xbd\x00\x1c9i\xeb~v\x82E\x0c\x06\x03b\x8c\xc7c\x16\x81\xe6r+\x00\xdb\x8d\x8f\x8b|\xeb\xect:\x94\xcf\xe7i\xb5Z\xa9q\x16\xebB\x01B\x02FL\xd7\xfaw\x85B\x81\x1dH\x1a\x0f\x1c\xb1p\x008\x82\x04J18\x8d\xb9u\x00\xd4\x8a,\xdfu\x14\xf1\xe7\xf3I\xfb\xfd\x9e\xca\xe5\xb2r\x08\xcc\x0b\x11\xda\xe6\xa0\x8c\x91\xfb\xfa|>'F\xb5Z%\xc6\xeb\xf5b\xf2\xf1\x93\xb9^\xaf\x9b\x90\x14\xa1.71\xfe\xb6\xd3\xeb\xf5\x98(\xbf\x8d\xf2{Q\xdc\xd7l6\xa9\xddn\xff4\x17\x88>Fh\x10}y\x09S\xf5{_\x18\x86,\x00\xce\x85S\xc8\xdc\x01l#v@\xca\xbf\xe62q\xc0y\x0a\x81M\xac\xae\xa5\x80\xb1\x98 Date: Mon, 8 Dec 2014 08:53:58 +0100 Subject: [PATCH 0149/1144] remove variables which are attributes of classes --- pylot/core/pick/Picker.py | 6 ------ pylot/core/pick/run_makeCF.py | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index e8d980bc..4b76338f 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -28,12 +28,6 @@ class AutoPicking(object): :param: cf, characteristic function, on which the picking algorithm is applied :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object - :param: Tcf, corresponding time array - :type: array - - :param: dt, sampling interval [s] - :type: float - :param: Tslope, length of time window after pick used to determine slope for quality estimation [s] :type: float diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 8d6d6aab..16cfbd93 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -58,8 +58,8 @@ def run_makeCF(project, database, event, iplot, station=None): ############################################################## #calculate HOS-CF using subclass HOScf of class CharacteristicFunction hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf - #get corresponding time array - thoscf = np.arange(0, len(hoscf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] + #get corresponding time array -> unnecessary because implemented in CharacteristicFunction + # thoscf = np.arange(0, len(hoscf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] ############################################################## #get onset time from HOS-CF using class Picker #hospick = PragPicker(hoscf.getCF(), thoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2) From 9e7f20a9fb260e9c88403f5cc5ab85b8e2ab0cf1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 8 Dec 2014 11:38:24 +0100 Subject: [PATCH 0150/1144] create a working MainWindow --- QtPyLoT.py | 11 +++++++---- pylot/RELEASE-VERSION | 2 +- pylot/core/util/layouts.py | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 70bd2c9b..1caf4cd8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -133,7 +133,6 @@ class MainWindow(QMainWindow): self.fileMenu.addAction(action) self.fileMenu.addSeparator() self.fileMenu.addAction(self.fileMenuActions[-1]) - def loadData(self, fname=None): if fname is None: @@ -179,9 +178,7 @@ class MainWindow(QMainWindow): statsButtons = layoutStationButtons(self.getData(), self.getComponent()) _layout.addLayout(statsButtons) _layout.addWidget(self.DataPlot) - self.setLayout(_layout) - self.setCentralWidget(_widget) - + openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) @@ -235,6 +232,12 @@ class MainWindow(QMainWindow): status.addPermanentWidget(self.eventLabel) status.showMessage("Ready", 500) + statsButtons = layoutStationButtons(self.getData(), self.getComponent()) + _layout.addLayout(statsButtons) + _layout.addWidget(self.DataPlot) + _widget.setLayout(_layout) + self.setCentralWidget(_widget) + def okToContinue(self): if self.dirty: return self.saveData() diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 0b303821..c5affb8d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -9603-dirty +ef8b-dirty diff --git a/pylot/core/util/layouts.py b/pylot/core/util/layouts.py index 632c2473..c9011a66 100644 --- a/pylot/core/util/layouts.py +++ b/pylot/core/util/layouts.py @@ -21,7 +21,7 @@ def layoutStationButtons(data, comp): stationButtons.append(QPushButton('%s'.format(stat))) except: for n in range(5): - stationButtons.append(QPushButton('ST%02d'.format(n))) + stationButtons.append(QPushButton('ST{0:02d}'.format(n+1))) for button in stationButtons: layout.addWidget(button) return layout \ No newline at end of file From d665e47d02be89968ac5d6b4a71a918ad30189af Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 9 Dec 2014 05:25:43 +0100 Subject: [PATCH 0151/1144] get values for filter parameters from widget --- QtPyLoT.py | 20 +++++++++++++++----- pylot/core/read/data.py | 7 +++++-- pylot/core/read/inputs.py | 27 +++++++++++++++------------ pylot/core/util/widgets.py | 4 ++-- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1caf4cd8..1855e491 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -247,19 +247,29 @@ class MainWindow(QMainWindow): pass #self.data.plotData(self.DataPlot) def filterData(self): - pass + if self.getData(): + kwargs = {} + freq = self.filteroptions.getFreq() + if len(freq) > 1: + kwargs['freqmin'] = freq[0] + kwargs['freqmax'] = freq[1] + else: + kwargs['freq'] = freq + kwargs['type'] = self.filteroptions.getFilterType() + #kwargs['order'] = self.filteroptions.getOrder() + self.data.filter(**kwargs) def adjustFilterOptions(self): - filterOptions = None + filteroptions = None fstring = "Filter Options ({0})".format(self.getSeismicPhase()) filterDlg = FilterOptionsDialog(titleString=fstring, parent=self, filterOptions=self.getFilterOptions()) if filterDlg.exec_(): - filterOptions = filterDlg.getFilterOptions() + filteroptions = filterDlg.getFilterOptions() - assert isinstance(filterOptions, FilterOptions) - self.setFilterOptions(filterOptions) + assert isinstance(filteroptions, FilterOptions) + self.setFilterOptions(filteroptions) def getFilterOptions(self): return self.filteroptions diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 5b9250f1..8331c6a6 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -27,7 +27,10 @@ class Data(object): def __init__(self, parent=None, evtdata=None): try: - self.wfdata = read() + if parent: + self.wfdata = read(parent.fnames) + else: + self.wfdata = read() except IOError, e: msg = 'An I/O error occured while loading data!' inform = 'Variable wfdata will be empty.' @@ -53,6 +56,7 @@ class Data(object): else: # create an empty Event object self.newevent = True self.evtdata = Event() + self.orig = self.wfdata.copy() def isNew(self): return self.newevent @@ -77,7 +81,6 @@ class Data(object): not implemented: {1}'''.format(evtformat, e)) def plotData(self, widget): - pass #axes = widget.axes def getID(self): diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index ee840b8f..57999fb8 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -165,34 +165,37 @@ class FilterOptions(object): ''' def __init__(self, filtertype='bandpass', freq=[2., 5.], order=3, **kwargs): - self.setFilterType(filtertype) - self.setFreq(freq) - self.setOrder(order) + self._order = order + self._filtertype = filtertype + self._freq = freq def __str__(self): hrs = '''\n\tFilter parameter:\n Type:\t\t{ftype}\n Frequencies:\t{freq}\n Order:\t\t{order}\n - '''.format(ftype=self.getFilterType(), - freq=self.getFreq(), - order=self.getOrder()) + '''.format(ftype=self.getFilterType, + freq=self.getFreq, + order=self.getOrder) return hrs + @property def getFreq(self): - return self.freq + return self.__getattribute__('_freq') def setFreq(self, freq): - self.freq = freq + self.__setattr__('_freq', freq) + @property def getOrder(self): - return self.order + return self.__getattribute__('_order') def setOrder(self, order): - self.order = order + self.__setattr__('_order', order) + @property def getFilterType(self): - return self.filterType + return self.__getattribute__('_filtertype') def setFilterType(self, filtertype): - self.filterType = filtertype + self.__setattr__('_filtertype', filtertype) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a6376f73..e937f2cf 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -166,7 +166,7 @@ class FilterOptionsDialog(QDialog): self.freqminSpinBox.setRange(5e-7, 1e6) self.freqminSpinBox.setDecimals(2) self.freqminSpinBox.setSuffix(' Hz') - self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0]) + self.freqminSpinBox.setValue(self.getFilterOptions().getFreq[0]) self.freqmaxLabel = QLabel() self.freqmaxLabel.setText("maximum:") self.freqmaxSpinBox = QDoubleSpinBox() @@ -174,7 +174,7 @@ class FilterOptionsDialog(QDialog): self.freqmaxSpinBox.setDecimals(2) self.freqmaxSpinBox.setSuffix(' Hz') if self.filterOptions.filterType in ['bandpass', 'bandstop']: - self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) + self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq[1]) typeOptions = ["bandpass", "bandstop", "lowpass", "highpass"] From 3c9865c767f2344aa1a4a5e815ffd3cd07374e7e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 11 Dec 2014 09:43:59 +0100 Subject: [PATCH 0152/1144] add station selection --- pylot/core/read/__init__.py | 11 ++++---- pylot/core/read/data.py | 56 +++++++++++++++++++++---------------- pylot/core/read/inputs.py | 3 -- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index 70fee522..beb833f8 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -1,5 +1,6 @@ -from pylot.core.read.data import (Data, - GenericDataStructure, - SeiscompDataStructure) -from pylot.core.read.inputs import (AutoPickParameter, - FilterOptions) +from pylot.core.read.inputs import AutoPickParameter +from pylot.core.read.inputs import FilterOptions +from pylot.core.read.data import Data +from pylot.core.read.data import GenericDataStructure +from pylot.core.read.data import SeiscompDataStructure + diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 8331c6a6..07b8a10f 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -126,10 +126,19 @@ class SeiscompDataStructure(object): :type sdate, edate: str or UTCDateTime or None ''' + # Data type options + __typeOptions = {'waveform': 'D', # Waveform data + 'detect': 'E', # Detection data + 'log': 'L', # Log data + 'timing': 'T', # Timing data + 'calib': 'C', # Calibration data + 'resp': 'R', # Response data + 'opaque': 'O' # Opaque data + } + def __init__(self, dataType='waveform', sdate=None, edate=None, **kwargs): # imports from obspy.core import UTCDateTime - import numpy as np def checkDate(date): if not isinstance(date, UTCDateTime): @@ -143,7 +152,9 @@ class SeiscompDataStructure(object): edate = UTCDateTime(edate) except TypeError: edate = UTCDateTime() - sdate = edate - np.pi*1e7/2 + halfyear = UTCDateTime('1970-07-01') + sdate = UTCDateTime(edate - halfyear) + del halfyear year = '' if not edate.year == sdate.year: @@ -152,17 +163,7 @@ class SeiscompDataStructure(object): year += '{0:04d},'.format(sdate.year+yr) year = '{'+year[:-1]+'}' else: - year = '{0:04d},'.format(sdate.year) - - # Data type options - self.__typeOptions = {'waveform': 'D', # Waveform data - 'detect': 'E', # Detection data - 'log': 'L', # Log data - 'timing': 'T', # Timing data - 'calib': 'C', # Calibration data - 'resp': 'R', # Response data - 'opaque': 'O' # Opaque data - } + year = '{0:04d}'.format(sdate.year) if dataType in self.__typeOptions.keys(): self.dataType = dataType @@ -180,7 +181,7 @@ class SeiscompDataStructure(object): 'NET': '??', # up to 8 characters 'STA': '????', # up to 8 characters 'CHAN': 'HH?', # up to 8 characters - 'TYPE': self._getType(), # 1 character + 'TYPE': self.getType(), # 1 character 'LOC': '', # up to 8 characters 'DAY': '{0:03d}'.format(sdate.julday) # 3 digits } @@ -190,10 +191,14 @@ class SeiscompDataStructure(object): if kwargs and isinstance(kwargs, dict): for key, value in kwargs.iteritems(): key = str(key) - value = str(value) + if type(value) not in (str, int, float): + for n, val in enumerate(value): + value[n] = str(val) + else: + value = str(value) try: - if key in self.__sdsFields.keys(): - self.__sdsFields[key] = str(value) + if key in self.getSDSFields().keys(): + self.getSDSFields()[key] = value else: raise KeyError('unknown SDS wildcard: %s.' % key) except KeyError, e: @@ -203,16 +208,19 @@ class SeiscompDataStructure(object): errmsg += '%s; desired value was: %s\n' % (e, value) print errmsg - def _getType(self): + def getType(self): return self.__typeOptions[self.dataType] + def getSDSFields(self): + return self.__sdsFields + def expandDataPath(self): - fullChan = '{0}.{1}'.format(self.__sdsFields['CHAN'], self._getType()) - dataPath = os.path.join(self.__sdsFields['SDSdir'], - self.__sdsFields['YEAR'], - self.__sdsFields['NET'], - self.__sdsFields['STA'], + fullChan = '{0}.{1}'.format(self.getSDSFields()['CHAN'], self.getType()) + dataPath = os.path.join(self.getSDSFields()['SDSdir'], + self.getSDSFields()['YEAR'], + self.getSDSFields()['NET'], + self.getSDSFields()['STA'], fullChan, - '*{0}'.format(self.__sdsFields['DAY']) + '*{0}'.format(self.getSDSFields()['DAY']) ) return dataPath diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 57999fb8..87cd16c3 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -179,21 +179,18 @@ class FilterOptions(object): order=self.getOrder) return hrs - @property def getFreq(self): return self.__getattribute__('_freq') def setFreq(self, freq): self.__setattr__('_freq', freq) - @property def getOrder(self): return self.__getattribute__('_order') def setOrder(self, order): self.__setattr__('_order', order) - @property def getFilterType(self): return self.__getattribute__('_filtertype') From 112e0bc7fa53cd0b8c0d9851a5590ca58eaa4dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 11 Dec 2014 15:55:23 +0100 Subject: [PATCH 0153/1144] Modified internal function getTimeArray for calculating correct time stamps even for AR prediction, where CF time array is different to data time array. Implemented getARdetStep and setARdetStep where ARdetStep is the recalculation step of AR coefficients. --- pylot/core/pick/CharFuns.py | 46 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 2ff0ec01..e0b2888e 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -22,7 +22,7 @@ class CharacteristicFunction(object): ''' SuperClass for different types of characteristic functions. ''' - def __init__(self, data, cut, t2, order, t1=None, fnoise=0.001): + def __init__(self, data, cut, t2=None, order=None, t1=None, fnoise=0.001): ''' Initialize data type object with information from the original Seismogram. @@ -55,6 +55,7 @@ class CharacteristicFunction(object): self.setTime2(t2) self.setOrder(order) self.setFnoise(fnoise) + self.setARdetStep(t2) self.calcCF(self.getDataArray()) self.arpara = np.array([]) self.xpred = np.array([]) @@ -66,12 +67,14 @@ class CharacteristicFunction(object): t2:\t{t2}\n Order:\t\t{order}\n Fnoise:\t{fnoise}\n + ARdetStep:\t{ardetstep}\n '''.format(name=type(self).__name__, cut=self.getCut(), t1=self.getTime1(), t2=self.getTime2(), order=self.getOrder(), - fnoise=self.getFnoise()) + fnoise=self.getFnoise(), + ardetstep=self.getARdetStep()) def getCut(self): return self.cut @@ -91,11 +94,13 @@ class CharacteristicFunction(object): def setTime2(self, t2): self.t2 = t2 - def getTimeArray(self): - incr = self.getIncrement() - timeArray = np.arange(0, len(self.getCF()) / incr**-1, - incr) + self.getCut()[0] - return timeArray + def getARdetStep(self): + return self.ARdetStep + + def setARdetStep(self, t1): + if t1: + self.ARdetStep = t1 / 4 + return self.ARdetStep def getOrder(self): return self.order @@ -109,6 +114,29 @@ class CharacteristicFunction(object): """ return self.dt + def getTimeArray(self): + if self.getTime1() == None and self.getTime2() and self.getOrder(): + #for HOS + incr = self.getIncrement() + timeArray = np.arange(0, len(self.getCF()) * incr, + incr) + self.getCut()[0] + elif self.getTime1() == None and self.getTime2() and self.getOrder() == None: + #for AIC-HOS + incr = self.getIncrement() + timeArray = np.arange(0, len(self.getCF()) * incr, + incr) + self.getCut()[0] + elif self.getTime1() and self.getTime2() and self.getOrder() == 0: + #for AIC-AR + incr = self.getARdetStep() + timeArray = np.arange(0, len(self.getCF()) * incr, + incr) + self.getCut()[0] + self.getTime1() + self.getTime2() + elif self.getTime1() and self.getTime2() and self.getOrder(): + #for AR + incr = self.getARdetStep() + timeArray = np.arange(0, len(self.getCF()) * incr, + incr) + self.getCut()[0] + self.getTime1() + self.getTime2() + return timeArray + def getFnoise(self): return self.fnoise @@ -190,7 +218,7 @@ class AICcf(CharacteristicFunction): cumsumcf[k - 1]) / (datlen - k + 1))) cf[0] = cf[1] inf = np.isinf(cf) - ff = np.where(inf == 'True') + ff = np.where(inf == True) if len(ff) >= 1: cf[ff] = 0 @@ -260,7 +288,7 @@ class ARZcf(CharacteristicFunction): lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] cf = [] - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, lpred / 16): + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, lpred / 4): #determination of AR coefficients self.arDetZ(xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients From 201c34a85bff7c2e8111ae0bbb2fa6e0752ea3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 11 Dec 2014 15:57:25 +0100 Subject: [PATCH 0154/1144] Modified for using TimeArray object for plotting and expanded for picking on ARH-CF. --- pylot/core/pick/run_makeCF.py | 57 +++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 16cfbd93..182404d7 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -58,24 +58,19 @@ def run_makeCF(project, database, event, iplot, station=None): ############################################################## #calculate HOS-CF using subclass HOScf of class CharacteristicFunction hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf - #get corresponding time array -> unnecessary because implemented in CharacteristicFunction - # thoscf = np.arange(0, len(hoscf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] ############################################################## #get onset time from HOS-CF using class Picker - #hospick = PragPicker(hoscf.getCF(), thoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2) - #pdb.set_trace() + #hospick = PragPicker(hoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2) ############################################################## #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction #class needs stream object => build it tr_aic = tr_filt.copy() tr_aic.data = hoscf.getCF() st_copy[0].data = tr_aic.data - aiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf - #get corresponding time array - #taiccf = np.arange(0, len(aiccf.getCF()) / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + cuttimes[0] + aiccf = AICcf(st_copy, cuttimes, t2) #instance of AICcf ############################################################## #get onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicpick = AICPicker(aiccf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 2.5) + aicpick = AICPicker(aiccf, 2, 70, [1, 0.5, 0.2], 3) ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction #get stream object of filtered data @@ -87,7 +82,10 @@ def run_makeCF(project, database, event, iplot, station=None): tr_arzaic = tr_filt.copy() tr_arzaic.data = arzcf.getCF() st_copy[0].data = tr_arzaic.data - araiccf = AICcf(st_copy, cuttimes, t2, p) #instance of AICcf + araiccf = AICcf(st_copy, cuttimes, tpredz, 0, tdetz) #instance of AICcf + ############################################################## + #get onset time from AIC-ARZ-CF using subclass AICPicker of class AutoPicking + aicarzpick = AICPicker(araiccf, 2, 70, [1, 0.5, 0.2], 2) elif not wfzfiles: print 'No vertical component data found!' @@ -113,6 +111,17 @@ def run_makeCF(project, database, event, iplot, station=None): #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf ############################################################## + #calculate AIC-ARH-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_arhaic = trH1_filt.copy() + tr_arhaic.data = arhcf.getCF() + H_copy[0].data = tr_arhaic.data + #calculate ARH-AIC-CF + arhaiccf = AICcf(H_copy, cuttimes, tpredh, 0, tdeth) #instance of AICcf + ############################################################## + #get onset time from AIC-ARH-CF using subclass AICPicker of class AutoPicking + aicarhpick = AICPicker(arhaiccf, 2, 70, [1, 0.5, 0.2], 2) + ############################################################### #create stream with 3 traces #merge streams AllC = read('%s' % wfefiles[i]) @@ -138,18 +147,18 @@ def run_makeCF(project, database, event, iplot, station=None): #plot vertical trace plt.figure() tr = st[0] - tstepz = tpredz / 16 tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) - tarzcf = np.arange(0, len(arzcf.getCF()) * tstepz, tstepz) + cuttimes[0] + tdetz +tpredz - taraiccf = np.arange(0, len(araiccf.getCF()) * tstepz, tstepz) + cuttimes[0] +tdetz + tpredz p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p2 = plt.plot(thoscf, hoscf.getCF()/max(hoscf.getCF()), 'r') - p3 = plt.plot(taiccf, aiccf.getCF()/max(aiccf.getCF()), 'b') - p4 = plt.plot(tarzcf, arzcf.getCF()/max(arzcf.getCF()), 'g') - p5 = plt.plot(taraiccf, araiccf.getCF()/max(araiccf.getCF()), 'y') - plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'b') + p2 = plt.plot(hoscf.getTimeArray(), hoscf.getCF() / max(hoscf.getCF()), 'r') + p3 = plt.plot(aiccf.getTimeArray(), aiccf.getCF()/max(aiccf.getCF()), 'b') + p4 = plt.plot(arzcf.getTimeArray(), arzcf.getCF()/max(arzcf.getCF()), 'g') + p5 = plt.plot(araiccf.getTimeArray(), araiccf.getCF()/max(araiccf.getCF()), 'y') + plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'b--') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'b') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([aicarzpick.getpick(), aicarzpick.getpick()], [-1.2, 1.2], 'y--') + plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [1.2, 1.2], 'y') + plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [-1.2, -1.2], 'y') plt.yticks([]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') @@ -165,14 +174,22 @@ def run_makeCF(project, database, event, iplot, station=None): tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') p22 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + p23 = plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) + plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [-1, -1], 'b') plt.yticks([]) plt.ylabel('Normalized Counts') plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) plt.suptitle(trH1_filt.stats.starttime) - plt.legend([p21, p22], ['Data', 'ARH-CF']) + plt.legend([p21, p22, p23], ['Data', 'ARH-CF', 'ARHAIC-CF']) plt.subplot(212) - p23 = plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - p24 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) + plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [-1, -1], 'b') plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) plt.yticks([]) plt.xlabel('Time [s]') From 31273b384e91c8b4fe74119420393ef03caa7473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 11 Dec 2014 16:30:21 +0100 Subject: [PATCH 0155/1144] Simplified AIC-picking algorithm: Onset is definetly the minimum in front of maximum of AIC-CF! Smoothing of AIC-CF no more necessary. --- pylot/core/pick/Picker.py | 47 ++++++++++++--------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 4b76338f..e5857e49 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -23,7 +23,7 @@ class AutoPicking(object): Superclass of different, automated picking algorithms applied on a CF determined using AIC, HOS, or AR prediction. ''' - def __init__(self, cf, Tslope, aerr, TSNR, PickWindow, peps, Tsmooth): + def __init__(self, cf, Tslope, aerr, TSNR, PickWindow, peps=None, Tsmooth=None): ''' :param: cf, characteristic function, on which the picking algorithm is applied :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object @@ -48,6 +48,9 @@ class AutoPicking(object): :type: float ''' + #assert isinstance(cf, CharFuns), "%s is not a CharacteristicFunction object" % str(cf) + #wie kann man hier isinstance benutzen? + self.cf = cf.getCF() self.Tcf = cf.getTimeArray() self.dt = cf.getIncrement() @@ -127,45 +130,23 @@ class AICPicker(AutoPicking): print 'Get onset (pick) from AIC-CF ...' + self.Pick = -1 #taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) #get maximum of CF as starting point icfmax = np.argmax(aic) - - #smooth CF - aicsmooth = np.zeros(len(aic)) - ismooth = round(self.Tsmooth / self.dt) - if len(aic) < ismooth: - print 'AICPicker: Tsmooth larger than AIC function!' - self.Pick = -1 - return self.Pick - else: - self.Pick = -1 - for i in range(1, len(aic)): - if i > ismooth: - ii1 = i - ismooth - aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth - else: - aicsmooth[i] = np.mean(aic[0:i]) - - #find common, local minimum in front of maximum - #of smoothed and unsmoothed AIC-CF + + #find minimum in front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if aic[i - 1] * (1 + self.peps) >= aic[i]: - if aicsmooth[i - 1] * (1 + self.peps) >= aicsmooth[i]: - self.Pick = self.Tcf[i] - break - - #try again with larger peps if picking failed - if self.Pick < 0: - peps2 = self.peps + 0.01 - for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if aic[i - 1] * (1 + peps2) >= aic[i]: - if aicsmooth[i - 1] * (1 + peps2) >= aicsmooth[i]: - self.Pick = self.Tcf[i] - break + if aic[i - 1] >= aic[i]: + self.Pick = self.Tcf[i] + break + if self.Pick == -1: + print 'AICPicker: Could not find minimum, picking window too short?' + + return self.Pick class PragPicker(AutoPicking): ''' From 2eace0d30436b1c817703c33881483a90d63885d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 12 Dec 2014 13:37:18 +0100 Subject: [PATCH 0156/1144] Debugging, cleaning up --- pylot/core/pick/CharFuns.py | 43 ++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index e0b2888e..417a7965 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -74,7 +74,7 @@ class CharacteristicFunction(object): t2=self.getTime2(), order=self.getOrder(), fnoise=self.getFnoise(), - ardetstep=self.getARdetStep()) + ardetstep=self.getARdetStep[0]()) def getCut(self): return self.cut @@ -99,7 +99,9 @@ class CharacteristicFunction(object): def setARdetStep(self, t1): if t1: - self.ARdetStep = t1 / 4 + self.ARdetStep = [] + self.ARdetStep.append(t1 / 4) + self.ARdetStep.append(int(np.ceil(self.getTime2() / self.getIncrement()) / 4)) return self.ARdetStep def getOrder(self): @@ -115,27 +117,14 @@ class CharacteristicFunction(object): return self.dt def getTimeArray(self): - if self.getTime1() == None and self.getTime2() and self.getOrder(): - #for HOS - incr = self.getIncrement() - timeArray = np.arange(0, len(self.getCF()) * incr, - incr) + self.getCut()[0] - elif self.getTime1() == None and self.getTime2() and self.getOrder() == None: - #for AIC-HOS - incr = self.getIncrement() - timeArray = np.arange(0, len(self.getCF()) * incr, - incr) + self.getCut()[0] - elif self.getTime1() and self.getTime2() and self.getOrder() == 0: - #for AIC-AR + if self.getTime1(): incr = self.getARdetStep() - timeArray = np.arange(0, len(self.getCF()) * incr, - incr) + self.getCut()[0] + self.getTime1() + self.getTime2() - elif self.getTime1() and self.getTime2() and self.getOrder(): - #for AR - incr = self.getARdetStep() - timeArray = np.arange(0, len(self.getCF()) * incr, - incr) + self.getCut()[0] + self.getTime1() + self.getTime2() - return timeArray + self.TimeArray = np.arange(0, len(self.getCF()) * incr[0], incr[0]) + self.getCut()[0] \ + + self.getTime1() + self.getTime2() + else: + incr = self.getIncrement() + self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] + return self.TimeArray def getFnoise(self): return self.fnoise @@ -288,7 +277,8 @@ class ARZcf(CharacteristicFunction): lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] cf = [] - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, lpred / 4): + loopstep = self.getARdetStep() + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, loopstep[1]): #determination of AR coefficients self.arDetZ(xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients @@ -301,6 +291,7 @@ class ARZcf(CharacteristicFunction): cf = np.asarray(cf) self.cf = cf + def arDetZ(self, data, order, rind, ldet): ''' Function to calculate AR parameters arpara after Thomas Meier (CAU), published @@ -399,7 +390,8 @@ class ARHcf(CharacteristicFunction): lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] cf = [] - for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, lpred / 4): + loopstep = self.getARdetStep() + for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, loopstep[1]): self.arDetH(Xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients self.arPredH(xnp, self.arpara, i + 1, lpred) @@ -516,7 +508,8 @@ class AR3Ccf(CharacteristicFunction): lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] cf = [] - for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, lpred / 4): + loopstep = self.getARdetStep() + for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, loopstep[1]): self.arDet3C(Xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients self.arPred3C(xnp, self.arpara, i + 1, lpred) From 5d85a4bdc8ddf0033c05d6e170cc72b7a90c959d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 15 Dec 2014 05:15:43 +0100 Subject: [PATCH 0157/1144] returning value in set method is not straight forward --- pylot/core/pick/CharFuns.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 417a7965..fed1bc29 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -102,7 +102,6 @@ class CharacteristicFunction(object): self.ARdetStep = [] self.ARdetStep.append(t1 / 4) self.ARdetStep.append(int(np.ceil(self.getTime2() / self.getIncrement()) / 4)) - return self.ARdetStep def getOrder(self): return self.order From 59930c32381ee254978a048d9e5f8fd5db1627dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 15 Dec 2014 15:03:41 +0100 Subject: [PATCH 0158/1144] Implemented pragmatic picking algorithm developed by TM, JL, and LK --- pylot/core/pick/Picker.py | 116 +++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 15 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index e5857e49..b7cb7465 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -3,16 +3,16 @@ Created Dec 2014 Implementation of the picking algorithms published and described in: -Küperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, 2010: +Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, 2010: Automated determination of P-phase arrival times at regional and local distances using higher order statistics, Geophys. J. Int., 181, 1159-1170 -Küperkoch, L., Meier, T., Brüstle, A., Lee, J., Friederich, W., & Egelados +Kueperkoch, L., Meier, T., Bruestle, A., Lee, J., Friederich, W., & Egelados Working Group, 2012: Automated determination of S-phase arrival times using autoregressive prediction: application ot local and regional distances, Geophys. J. Int., 188, 687-702. -:author: MAGS2 EP3 working group / Ludger Küperkoch +:author: MAGS2 EP3 working group / Ludger Kueperkoch """ import numpy as np import matplotlib.pyplot as plt @@ -23,7 +23,7 @@ class AutoPicking(object): Superclass of different, automated picking algorithms applied on a CF determined using AIC, HOS, or AR prediction. ''' - def __init__(self, cf, Tslope, aerr, TSNR, PickWindow, peps=None, Tsmooth=None): + def __init__(self, cf, Tslope, aerr, TSNR, PickWindow, aus=None, Tsmooth=None, Pick1=None): ''' :param: cf, characteristic function, on which the picking algorithm is applied :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object @@ -41,11 +41,14 @@ class AutoPicking(object): :param: PickWindow, length of pick window [s] :type: float - :param: peps, find local minimum at i if aic(i-1)*(1+peps) >= aic(i) + :param: aus ("artificial uplift of samples"), find local minimum at i if aic(i-1)*(1+aus) >= aic(i) :type: float :param: Tsmooth, length of moving smoothing window to calculate smoothed CF [s] :type: float + + :param: Pick1, initial (prelimenary) onset time, starting point for PragPicker + :type: float ''' #assert isinstance(cf, CharFuns), "%s is not a CharacteristicFunction object" % str(cf) @@ -58,8 +61,9 @@ class AutoPicking(object): self.setaerr(aerr) self.setTSNR(TSNR) self.setPickWindow(PickWindow) - self.setpeps(peps) + self.setaus(aus) self.setTsmooth(Tsmooth) + self.setpick1(Pick1) self.calcPick() def __str__(self): @@ -68,15 +72,17 @@ class AutoPicking(object): aerr:\t{aerr}\n TSNR:\t\t\t{TSNR}\n PickWindow:\t{PickWindow}\n - peps:\t{peps}\n + aus:\t{aus}\n Tsmooth:\t{Tsmooth}\n + Pick1:\t{Pick1}\n '''.format(name=type(self).__name__, Tslope=self.getTslope(), aerr=self.getaerr(), TSNR=self.getTSNR(), PickWindow=self.getPickWindow(), - peps=self.getpeps(), - Tsmooth=self.getTsmooth()) + aus=self.getaus(), + Tsmooth=self.getTsmooth(), + Pick1=self.getpick1()) def getTslope(self): return self.Tslope @@ -102,11 +108,11 @@ class AutoPicking(object): def setPickWindow(self, PickWindow): self.PickWindow = PickWindow - def getpeps(self): - return self.peps + def getaus(self): + return self.aus - def setpeps(self, peps): - self.peps = peps + def setaus(self, aus): + self.aus = aus def setTsmooth(self, Tsmooth): self.Tsmooth = Tsmooth @@ -117,6 +123,12 @@ class AutoPicking(object): def getpick(self): return self.Pick + def getpick1(self): + return self.Pick1 + + def setpick1(self, Pick1): + self.Pick1 = Pick1 + def calcPick(self): self.Pick = None @@ -128,7 +140,7 @@ class AICPicker(AutoPicking): def calcPick(self): - print 'Get onset (pick) from AIC-CF ...' + print 'Get onset time (pick) from AIC-CF ...' self.Pick = -1 #taper AIC-CF to get rid off side maxima @@ -155,4 +167,78 @@ class PragPicker(AutoPicking): def calcPick(self): - print 'Get onset (pick) from HOS- or AR-CF using pragmatic picking algorithm ...' + if self.getpick1() is not None: + print 'Get onset time (pick) from HOS- or AR-CF using pragmatic picking algorithm ...' + + self.Pick = -1 + #smooth CF + ismooth = round(self.Tsmooth / self.dt); + cfsmooth = np.zeros(len(self.cf)) + if len(self.cf) < ismooth: + print 'PragPicker: Tsmooth larger than CF!' + return + else: + for i in range(1, len(self.cf)): + if i > ismooth: + ii1 = i - ismooth; + cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth + else: + cfsmooth[i] = np.mean(self.cf[1 : i]) + + #select picking window + #which is centered around tpick1 + ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ + & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) + cfipick = self.cf[ipick] + Tcfpick = self.Tcf[ipick] + cfsmoothipick = cfsmooth[ipick] + ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) + cfpick1 = 2 * self.cf[ipick1] + + #check trend of CF, i.e. differences of CF and adjust aus regarding this trend + #prominent trend: decrease aus + #flat: use given aus + cfdiff = np.diff(cfipick); + i0diff = np.where(cfdiff > 0) + cfdiff = cfdiff[i0diff] + minaus = min(cfdiff * (1 + self.aus)); + aus1 = max([minaus, self.aus]); + + #at first we look to the right until the end of the pick window is reached + flagpick_r = 0 + flagpick_l = 0 + flagpick = 0 + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_r = self.Tcf[i] + self.Pick = pick_r + flagpick_l = 1 + cfpick_r = self.cf[i] + break + + #now we look to the left + for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_l = self.Tcf[i] + self.Pick = pick_l + flagpick_r = 1 + cfpick_l = self.cf[i] + break + + #now decide which pick: left or right? + if flagpick_l > 0 and flagpick_r > 0: + if cfpick_l <= cfpick_r: + self.Pick = pick_l + else: + self.Pick = pick_r + + else: + self.Pick = -1 + print 'PragPicker: No initial onset time given! Check input!' + return + From fa58ec2aee26bab1fa8370f4133fe3a7cddb6a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 15 Dec 2014 15:04:48 +0100 Subject: [PATCH 0159/1144] Modified for applying pragmatic picking algorithm, new class PragPicker in Picker.py --- pylot/core/pick/run_makeCF.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 182404d7..efbd84d0 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -59,9 +59,6 @@ def run_makeCF(project, database, event, iplot, station=None): #calculate HOS-CF using subclass HOScf of class CharacteristicFunction hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf ############################################################## - #get onset time from HOS-CF using class Picker - #hospick = PragPicker(hoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2) - ############################################################## #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction #class needs stream object => build it tr_aic = tr_filt.copy() @@ -69,9 +66,12 @@ def run_makeCF(project, database, event, iplot, station=None): st_copy[0].data = tr_aic.data aiccf = AICcf(st_copy, cuttimes, t2) #instance of AICcf ############################################################## - #get onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking + #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking aicpick = AICPicker(aiccf, 2, 70, [1, 0.5, 0.2], 3) ############################################################## + #get refined onset time from HOS-CF using class Picker + hospick = PragPicker(hoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2, aicpick.getpick()) + ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction #get stream object of filtered data st_copy[0].data = tr_filt.data @@ -86,6 +86,9 @@ def run_makeCF(project, database, event, iplot, station=None): ############################################################## #get onset time from AIC-ARZ-CF using subclass AICPicker of class AutoPicking aicarzpick = AICPicker(araiccf, 2, 70, [1, 0.5, 0.2], 2) + ############################################################## + #get refined onset time from ARZ-CF using class Picker + arzpick = PragPicker(arzcf, 2, 70, [1, 0.5, 0.2], 2, 0, 0.2, aicarzpick.getpick()) elif not wfzfiles: print 'No vertical component data found!' @@ -156,9 +159,15 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'b--') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'b') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([hospick.getpick(), hospick.getpick()], [-1.3, 1.3], 'r--') + plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [1.3, 1.3], 'r') + plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [-1.3, -1.3], 'r') plt.plot([aicarzpick.getpick(), aicarzpick.getpick()], [-1.2, 1.2], 'y--') plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [1.2, 1.2], 'y') plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [-1.2, -1.2], 'y') + plt.plot([arzpick.getpick(), arzpick.getpick()], [-1.4, 1.4], 'g--') + plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [1.4, 1.4], 'g') + plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [-1.4, -1.4], 'g') plt.yticks([]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') From 13b8a9daec738090e9d8b867b38509e7378f9299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 16 Dec 2014 16:13:52 +0100 Subject: [PATCH 0160/1144] Debugged --- pylot/core/pick/run_makeCF.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index efbd84d0..37bfeee6 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -19,7 +19,7 @@ def run_makeCF(project, database, event, iplot, station=None): #parameters for CF calculation t2 = 7 #length of moving window for HOS calculation [sec] p = 4 #order of statistics - cuttimes = [10, 40] #start and end time vor CF calculation + cuttimes = [5, 40] #start and end time for CF calculation bpz = [2, 30] #corner frequencies of bandpass filter, vertical component bph = [2, 15] #corner frequencies of bandpass filter, horizontal components tdetz= 1.2 #length of AR-determination window [sec], vertical component @@ -182,7 +182,7 @@ def run_makeCF(project, database, event, iplot, station=None): th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - p22 = plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') + p22 = plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') p23 = plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') @@ -194,7 +194,6 @@ def run_makeCF(project, database, event, iplot, station=None): plt.legend([p21, p22, p23], ['Data', 'ARH-CF', 'ARHAIC-CF']) plt.subplot(212) plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - plt.plot(tarhcf, arhcf.getCF()/max(arhcf.getCF()), 'r') plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') From 2fcf325a6e01119545da7df17c88a1ef2b5df1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 16 Dec 2014 16:15:53 +0100 Subject: [PATCH 0161/1144] Debugged getDataArray, same data lengths are now guaranteed --- pylot/core/pick/CharFuns.py | 76 ++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index fed1bc29..64c4f06e 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -117,8 +117,8 @@ class CharacteristicFunction(object): def getTimeArray(self): if self.getTime1(): - incr = self.getARdetStep() - self.TimeArray = np.arange(0, len(self.getCF()) * incr[0], incr[0]) + self.getCut()[0] \ + incr = self.getARdetStep()[0] + self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] \ + self.getTime1() + self.getTime2() else: incr = self.getIncrement() @@ -143,18 +143,31 @@ class CharacteristicFunction(object): cutting window ''' if cut is not None: - if self.cut[0] == 0: - start = 0 - else: - start = self.cut[0] / self.dt - stop = self.cut[1] / self.dt if len(self.orig_data) == 1: + if self.cut[0] == 0 and self.cut[1] == 0: + start = 0 + stop = len(self.orig_data[0]) + elif self.cut[0] == 0 and self.cut[1] is not 0: + start = 0 + stop = self.cut[1] / self.dt + else: + start = self.cut[0] / self.dt + stop = self.cut[1] / self.dt zz = self.orig_data.copy() z1 = zz[0].copy() zz[0].data = z1.data[start:stop] data = zz return data elif len(self.orig_data) == 2: + if self.cut[0] == 0 and self.cut[1] == 0: + start = 0 + stop = min([len(self.orig_data[0]), len(self.orig_data[1])]) + elif self.cut[0] == 0 and self.cut[1] is not 0: + start = 0 + stop = self.cut[1] / self.dt + else: + start = self.cut[0] / self.dt + stop = self.cut[1] / self.dt hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() @@ -163,6 +176,15 @@ class CharacteristicFunction(object): data = hh return data elif len(self.orig_data) == 3: + if self.cut[0] == 0 and self.cut[1] == 0: + start = 0 + stop = min([len(self.orig_data[0]), len(self.orig_data[1]), len(self.orig_data[2])]) + elif self.cut[0] == 0 and self.cut[1] is not 0: + start = 0 + stop = self.cut[1] / self.dt + else: + start = self.cut[0] / self.dt + stop = self.cut[1] / self.dt hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() @@ -195,6 +217,9 @@ class AICcf(CharacteristicFunction): print 'Calculating AIC ...' x = self.getDataArray() xnp = x[0].data + nn = np.isnan(xnp) + if len(nn) > 1: + xnp[nn] = 0 datlen = len(xnp) k = np.arange(1, datlen) cf = np.zeros(datlen) @@ -224,6 +249,9 @@ class HOScf(CharacteristicFunction): x = self.getDataArray(self.getCut()) xnp =x[0].data + nn = np.isnan(xnp) + if len(nn) > 1: + xnp[nn] = 0 if self.getOrder() == 3: # this is skewness print 'Calculating skewness ...' y = np.power(xnp, 3) @@ -256,6 +284,9 @@ class HOScf(CharacteristicFunction): elif self.getOrder() == 4: LTA[j] = lta / np.power(lta1, 2) + nn = np.isnan(LTA) + if len(nn) > 1: + LTA[nn] = 0 self.cf = LTA @@ -266,6 +297,9 @@ class ARZcf(CharacteristicFunction): print 'Calculating AR-prediction error from single trace ...' x = self.getDataArray(self.getCut()) xnp = x[0].data + nn = np.isnan(xnp) + if len(nn) > 1: + xnp[nn] = 0 #some parameters needed #add noise to time series xnoise = xnp + np.random.normal(0.0, 1.0, len(xnp)) * self.getFnoise() * max(abs(xnp)) @@ -288,6 +322,9 @@ class ARZcf(CharacteristicFunction): #convert list to numpy array cf = np.asarray(cf) + nn = np.isnan(cf) + if len(nn) > 1: + cf[nn] = 0 self.cf = cf @@ -376,6 +413,12 @@ class ARHcf(CharacteristicFunction): print 'Calculating AR-prediction error from both horizontal traces ...' xnp = self.getDataArray(self.getCut()) + n0 = np.isnan(xnp[0].data) + if len(n0) > 1: + xnp[0].data[n0] = 0 + n1 = np.isnan(xnp[1].data) + if len(n1) > 1: + xnp[1].data[n1] = 0 #some parameters needed #add noise to time series @@ -390,7 +433,7 @@ class ARHcf(CharacteristicFunction): cf = [] loopstep = self.getARdetStep() - for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, loopstep[1]): + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, loopstep[1]): self.arDetH(Xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients self.arPredH(xnp, self.arpara, i + 1, lpred) @@ -401,6 +444,9 @@ class ARHcf(CharacteristicFunction): #convert list to numpy array cf = np.asarray(cf) + nn = np.isnan(cf) + if len(nn) > 1: + cf[nn] = 0 self.cf = cf def arDetH(self, data, order, rind, ldet): @@ -493,6 +539,15 @@ class AR3Ccf(CharacteristicFunction): print 'Calculating AR-prediction error from all 3 components ...' xnp = self.getDataArray(self.getCut()) + n0 = np.isnan(xnp[0].data) + if len(n0) > 1: + xnp[0].data[n0] = 0 + n1 = np.isnan(xnp[1].data) + if len(n1) > 1: + xnp[1].data[n1] = 0 + n2 = np.isnan(xnp[2].data) + if len(n2) > 1: + xnp[2].data[n2] = 0 #some parameters needed #add noise to time series @@ -508,7 +563,7 @@ class AR3Ccf(CharacteristicFunction): cf = [] loopstep = self.getARdetStep() - for i in range(ldet + self.getOrder() - 3, tend - lpred + 1, loopstep[1]): + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, loopstep[1]): self.arDet3C(Xnoise, self.getOrder(), i-ldet, i) #AR prediction of waveform using calculated AR coefficients self.arPred3C(xnp, self.arpara, i + 1, lpred) @@ -520,6 +575,9 @@ class AR3Ccf(CharacteristicFunction): #convert list to numpy array cf = np.asarray(cf) + nn = np.isnan(cf) + if len(nn) > 1: + cf[nn] = 0 self.cf = cf def arDet3C(self, data, order, rind, ldet): From 0749420f9aaa9e94700e329565f307da1c2c0ebd Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 17 Dec 2014 06:30:03 +0100 Subject: [PATCH 0162/1144] filter possible event format file extension for selection from file dialog --- QtPyLoT.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1855e491..83cd356b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -139,7 +139,11 @@ class MainWindow(QMainWindow): action = self.sender() if isinstance(action, QAction): if action.data() is None: - fname = QFileDialog() + filt = """Supported event formats (*.mat *.qml *.xml *.kor + *.evt)""" + caption = 'Select event to open' + fname = QFileDialog().getOpenFileName(self, caption=caption, + filter=filt) else: fname = unicode(action.data().toString()) if not self.okToContinue(): From 8213cdc575d2c47cd389b1a8d222aec2c870dc01 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 17 Dec 2014 06:33:34 +0100 Subject: [PATCH 0163/1144] PropertiesDlg added to the MainWindow (not tested yet) --- QtPyLoT.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 83cd356b..22579058 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -39,6 +39,7 @@ from pylot.core.util import checkurl from pylot.core.util import layoutStationButtons from pylot.core.util import (FilterOptionsDialog, MPLWidget, + PropertiesDlg, HelpForm) @@ -193,6 +194,9 @@ class MainWindow(QMainWindow): saveEventAction = self.createAction("&Save event ...", self.saveData, QKeySequence.Save, saveIcon, "Save actual event data.") + prefsEventAction = self.createAction("Preferences", self.PyLoTprefs, + QKeySequence.Preferences, None, + "Edit PyLoT app preferences.") quitAction = self.createAction("&Quit", QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, @@ -218,6 +222,7 @@ class MainWindow(QMainWindow): "Print waveform overview.") self.fileMenu = self.menuBar().addMenu('&File') self.fileMenuActions = (openEventAction, saveEventAction, None, + prefsEventAction, None, quitAction) self.fileMenu.aboutToShow.connect(self.updateFileMenu) @@ -327,6 +332,11 @@ class MainWindow(QMainWindow): self.closing.emit() QMainWindow.closeEvent(self, event) + def PyLoTprefs(self): + props = PropertiesDlg(self) + if props.exec_(): + return + def helpHelp(self): if checkurl(): form = HelpForm('https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') From 3fe1e3906e66cfb02f9432cbf4a39d04d7273141 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 17 Dec 2014 06:35:12 +0100 Subject: [PATCH 0164/1144] OUTPUTFORMATS in defaults defined; manage available formats just from here --- pylot/core/read/data.py | 9 +++++++-- pylot/core/util/defaults.py | 2 ++ pylot/core/util/widgets.py | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 07b8a10f..de4f24b6 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -63,10 +63,15 @@ class Data(object): def exportEvent(self, fnout=None, evtformat='QUAKEML'): + from pylot.core.util.defaults import OUTPUTFORMATS + + if evtformat.strip() not in OUTPUTFORMATS.values(): + evtformat = OUTPUTFORMATS.values()[0] + if fnout is None: - fnout = self.evtdata.getEventID() + ID = self.evtdata.getEventID() # handle forbidden filenames especially on windows systems - fnout = fnConstructor(fnout) + fnout = fnConstructor(ID) evtformat = evtformat.upper().strip() diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index b9642d4a..b9815b45 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -12,3 +12,5 @@ FILTERDEFAULTS = {'P': {'filtertype': None, 'S': {'filtertype': 'bandpass', 'order': '4', 'freq': [.5, 5]}} + +OUTPUTFORMATS = {'QuakeML':'QUAKEML', 'VelEst':'VELEST'} \ No newline at end of file diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e937f2cf..b6a5ec5b 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -37,6 +37,7 @@ from PySide.QtCore import (Qt, SLOT) from PySide.QtWebKit import QWebView from pylot.core.read import FilterOptions +from pylot.core.util.defaults import OUTPUTFORMATS class MPLWidget(FigureCanvasQTAgg): @@ -119,7 +120,7 @@ class OutputsTab(QWidget): eventOutputLabel = QLabel("event ouput format") eventOutputComboBox = QComboBox() - eventoutputformats = ["QuakeML", "VelEst"] + eventoutputformats = OUTPUTFORMATS.keys() eventOutputComboBox.addItems(eventoutputformats) layout = QGridLayout() From 9d1a78222ed6c88f426a93bc3e835e4260808d63 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 17 Dec 2014 07:52:55 +0100 Subject: [PATCH 0165/1144] PropertiesDlg changed: retrieve additional information about the user, use QSettings to store the derived parameters --- pylot/core/util/widgets.py | 42 +++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b6a5ec5b..2026de57 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -31,7 +31,8 @@ from PySide.QtGui import (QAction, QToolBar, QVBoxLayout, QWidget) -from PySide.QtCore import (Qt, +from PySide.QtCore import (QSettings, + Qt, QUrl, SIGNAL, SLOT) @@ -72,17 +73,17 @@ class PropertiesDlg(QDialog): self.setWindowTitle("{0} Properties".format(appName)) - tabWidget = QTabWidget() - tabWidget.addTab(InputsTab(self), "Inputs") - tabWidget.addTab(OutputsTab(self), "Outputs") - tabWidget.addTab(PhasesTab(self), "Phases") - tabWidget.addTab(GraphicsTab(self), "Graphics") + self.tabWidget = QTabWidget() + self.tabWidget.addTab(InputsTab(self), "Inputs") + self.tabWidget.addTab(OutputsTab(self), "Outputs") + self.tabWidget.addTab(PhasesTab(self), "Phases") + self.tabWidget.addTab(GraphicsTab(self), "Graphics") self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Close) layout = QVBoxLayout() - layout.addWidget(tabWidget) + layout.addWidget(self.tabWidget) layout.addWidget(self.buttonBox) self.setLayout(layout) @@ -92,23 +93,40 @@ class PropertiesDlg(QDialog): SIGNAL("clicked()"), self.apply) self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) - pass def apply(self): - pass + settings = QSettings() + for widint in range(self.tabWidget.count()): + curwid = self.tabWidget.widget(widint) + values = self.getValues(curwid) + settings.setValue() class InputsTab(QWidget): - def __init__(self, parent=None): + def __init__(self, parent): super(InputsTab, self).__init__(parent) + settings = QSettings() + fulluser = settings.value("user/FullName") + login = settings.value("user/Login") + + fullNameLabel = QLabel("Full name for user '{0}'".format(login)) + + parent.fullNameEdit = QLineEdit() + parent.fullNameEdit.setText(fulluser) + + dataroot = settings.value("data/dataRoot") dataDirLabel = QLabel("data directory:") - dataDirEdit = QLineEdit() + parent.dataDirEdit = QLineEdit() + parent.dataDirEdit.setText(dataroot) + parent.dataDirEdit.selectAll() layout = QGridLayout() layout.addWidget(dataDirLabel, 0, 0) - layout.addWidget(dataDirEdit, 0, 1) + layout.addWidget(parent.dataDirEdit, 0, 1) + layout.addWidget(fullNameLabel, 1, 0) + layout.addWidget(parent.fullNameEdit, 1, 1) self.setLayout(layout) From f0d60de7454a4c2137bed6f88169b2d3aaf39943 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 17 Dec 2014 12:16:32 +0100 Subject: [PATCH 0166/1144] add save data method --- QtPyLoT.py | 13 ++++++++++--- pylot/core/read/data.py | 4 +++- pylot/core/util/errors.py | 3 +++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 22579058..9176326a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -143,18 +143,25 @@ class MainWindow(QMainWindow): filt = """Supported event formats (*.mat *.qml *.xml *.kor *.evt)""" caption = 'Select event to open' - fname = QFileDialog().getOpenFileName(self, caption=caption, + self.fname = QFileDialog().getOpenFileName(self, caption=caption, filter=filt) else: - fname = unicode(action.data().toString()) + self.fname = unicode(action.data().toString()) if not self.okToContinue(): return else: return if fname: - self.data = Data(evtdata=fname) + self.fname = fname + self.data = Data(evtdata=self.fname) def saveData(self): + settings = QSettings() + exform = settings.value('data/exportFormat', 'None') + try: + self.data.exportEvent(self.fname, exform) + except FormatError: + return False return True def getComponent(self): diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 82c75db8..3be72605 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -7,6 +7,7 @@ from obspy.core import (read, Stream) from obspy import readEvents from obspy.core.event import (Event, Catalog) from pylot.core.util import fnConstructor +from pylot.core.util.errors import FormatError class Data(object): @@ -71,7 +72,8 @@ class Data(object): from pylot.core.util.defaults import OUTPUTFORMATS if evtformat.strip() not in OUTPUTFORMATS.values(): - evtformat = OUTPUTFORMATS.values()[0] + errmsg = 'selected format {0} not available'.format(evtformat) + raise FormatError(errmsg) if fnout is None: ID = self.evtdata.getEventID() diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py index 68125c2d..0b2a4780 100644 --- a/pylot/core/util/errors.py +++ b/pylot/core/util/errors.py @@ -8,3 +8,6 @@ Created on Thu Mar 20 09:47:04 2014 class OptionsError(Exception): pass + +class FormatError(Exception): + pass From 1d61b4936c3f528bbd98984bd7f263afb2cf0c63 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 18 Dec 2014 13:49:16 +0100 Subject: [PATCH 0167/1144] add save data method --- pylot/core/read/data.py | 4 +++- pylot/core/util/widgets.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 3be72605..333421a5 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import os +import numpy as np from PySide.QtGui import QMessageBox from obspy.core import (read, Stream) from obspy import readEvents @@ -93,7 +94,8 @@ class Data(object): not implemented: {1}'''.format(evtformat, e)) def plotData(self, widget): - pass #axes = widget.axes + time_ax = np.arange(0, len(self.wfdata[0].data)/self.wfdata[0].stats.sampling_rate, self.wfdata[0].stats.delta) + widget.axes.plot(time_ax, self.wfdata[0].data) def getID(self): try: diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 2026de57..c50b7f0c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -11,7 +11,7 @@ matplotlib.use('Qt4Agg') matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from PySide.QtGui import (QAction, QApplication, QComboBox, @@ -41,14 +41,14 @@ from pylot.core.read import FilterOptions from pylot.core.util.defaults import OUTPUTFORMATS -class MPLWidget(FigureCanvasQTAgg): +class MPLWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): super(MPLWidget, self).__init__(Figure()) self.setParent(parent) self.figure = Figure() - self.canvas = FigureCanvasQTAgg(self.figure) + self.canvas = FigureCanvas(self.figure) self.axes = self.figure.add_subplot(111) self.axes.set_xlabel(xlabel) From 2961867c108ffccfc9877b7799b4e5f6e78bf2cf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 19 Dec 2014 10:50:50 +0100 Subject: [PATCH 0168/1144] insert assert statement in Picker.py --- pylot/core/pick/Picker.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index b7cb7465..6e6f4d8d 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -16,6 +16,7 @@ autoregressive prediction: application ot local and regional distances, Geophys. """ import numpy as np import matplotlib.pyplot as plt +from pylot.core.pick.CharFuns import CharacteristicFunction import pdb class AutoPicking(object): @@ -51,7 +52,7 @@ class AutoPicking(object): :type: float ''' - #assert isinstance(cf, CharFuns), "%s is not a CharacteristicFunction object" % str(cf) + assert isinstance(cf, CharacteristicFunction), "%s is not a CharacteristicFunction object" % str(cf) #wie kann man hier isinstance benutzen? self.cf = cf.getCF() From 9bbbe89b36f42d06f7485b58057ef994322a8d9e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 20 Jan 2015 13:44:35 +0100 Subject: [PATCH 0169/1144] add recent events to file menu --- QtPyLoT.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9176326a..f0f7f934 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -113,15 +113,21 @@ class MainWindow(QMainWindow): def updateFileMenu(self): self.fileMenu.clear() - self.addActions(self.fileMenu, self.fileMenuActions[:-1]) - current = self.data.evtdata.getID() + for action in self.fileMenuActions[:-1]: + if action is None: + self.fileMenu.addSeparator() + else: + self.fileMenu.addAction(action) + try: + current = self.data.evtdata.getID() + except AttributeError: + current = None recentEvents = [] for eventID in self.recentEvents: fname = fnConstructor(eventID) if eventID != current and QFile.exists(fname): recentEvents.append(eventID) if recentEvents: - self.fileMenu.addSeparator() for i, eventID in enumerate(recentEvents): fname = fnConstructor(eventID) action = QAction(QIcon(":/icon.png"), @@ -231,7 +237,10 @@ class MainWindow(QMainWindow): self.fileMenuActions = (openEventAction, saveEventAction, None, prefsEventAction, None, quitAction) + self.fileMenuActions = (openEventAction, saveEventAction, + prefsEventAction, quitAction) self.fileMenu.aboutToShow.connect(self.updateFileMenu) + self.updateFileMenu() self.editMenu = self.menuBar().addMenu('&Edit') for action in (filterAction, filterEditAction, None, selectPAction, From 0bfe2ccc3d04184de97edcb744f25474f49759d2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 20 Jan 2015 13:48:19 +0100 Subject: [PATCH 0170/1144] new method added in order to add actions and separators to the menu in the menubar more easily --- QtPyLoT.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f0f7f934..9c3dd2e4 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -243,12 +243,13 @@ class MainWindow(QMainWindow): self.updateFileMenu() self.editMenu = self.menuBar().addMenu('&Edit') - for action in (filterAction, filterEditAction, None, selectPAction, - selectSAction, None, printAction): - if action is None: - self.editMenu.addSeparator() - else: - self.editMenu.addAction(action) + editActions = (filterAction, filterEditAction, None, selectPAction, + selectSAction, None, printAction) + self.addMenuActions(self.editMenu, editActions) + + self.helpMenu = self.menuBar().addMenu('&Help') + helpActions = (helpAction) + self.addMenuActions(self.helpMenu, helpActions) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) @@ -263,6 +264,13 @@ class MainWindow(QMainWindow): _widget.setLayout(_layout) self.setCentralWidget(_widget) + def addMenuActions(self, menu, actions): + for action in (actions): + if action is None: + menu.addSeparator() + else: + menu.addAction(action) + def okToContinue(self): if self.dirty: return self.saveData() From ff98371a0402d9b952e8dc3258ac9ae661d5be25 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 20 Jan 2015 13:55:15 +0100 Subject: [PATCH 0171/1144] add recent events to file menu --- QtPyLoT.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9c3dd2e4..05fe9d4f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -234,9 +234,6 @@ class MainWindow(QMainWindow): QIcon(":/printer.png"), "Print waveform overview.") self.fileMenu = self.menuBar().addMenu('&File') - self.fileMenuActions = (openEventAction, saveEventAction, None, - prefsEventAction, None, - quitAction) self.fileMenuActions = (openEventAction, saveEventAction, prefsEventAction, quitAction) self.fileMenu.aboutToShow.connect(self.updateFileMenu) From 4769b447a85f04e9bca144640f604abad6deb21d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 20 Jan 2015 13:56:15 +0100 Subject: [PATCH 0172/1144] added help menu to the menubar --- QtPyLoT.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 05fe9d4f..dd52c0ba 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -200,6 +200,7 @@ class MainWindow(QMainWindow): openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) + helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) openEventAction = self.createAction("&Open event ...", self.loadData, QKeySequence.Open, openIcon, "Open an event.") @@ -233,6 +234,11 @@ class MainWindow(QMainWindow): self.printEvent, QKeySequence.Print, QIcon(":/printer.png"), "Print waveform overview.") + helpAction = self.createAction("&Help ...", self.helpHelp, + QKeySequence.HelpContents, helpIcon, + """Show either the documentation + homepage (internet connection available), + or shipped documentation files.""") self.fileMenu = self.menuBar().addMenu('&File') self.fileMenuActions = (openEventAction, saveEventAction, prefsEventAction, quitAction) From 540891f3d7852feeb64046a242163fc2c2434601 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 20 Jan 2015 13:58:23 +0100 Subject: [PATCH 0173/1144] preferences panel works for me; settings saved properly --- QtPyLoT.py | 3 +- pylot/core/util/widgets.py | 78 ++++++++++++++++++++++++++++---------- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index dd52c0ba..a1736525 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -209,7 +209,8 @@ class MainWindow(QMainWindow): QKeySequence.Save, saveIcon, "Save actual event data.") prefsEventAction = self.createAction("Preferences", self.PyLoTprefs, - QKeySequence.Preferences, None, + QKeySequence.Preferences, + QIcon(None), "Edit PyLoT app preferences.") quitAction = self.createAction("&Quit", QCoreApplication.instance().quit, diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 2026de57..df65b102 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -94,15 +94,36 @@ class PropertiesDlg(QDialog): self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + def accept(self, *args, **kwargs): + self.apply() + self.destroy() + + def reject(self, *args, **kwargs): + self.destroy() + def apply(self): - settings = QSettings() for widint in range(self.tabWidget.count()): curwid = self.tabWidget.widget(widint) - values = self.getValues(curwid) - settings.setValue() + values = curwid.getValues() + if values is not None: self.setValues(values) + + def setValues(self, tabValues): + settings = QSettings() + for setting, value in tabValues.iteritems(): + settings.setValue(setting, value) + settings.sync() -class InputsTab(QWidget): +class PropTab(QWidget): + + def __init__(self, parent=None): + super(PropTab, self).__init__(parent) + + def getValues(self): + return None + + +class InputsTab(PropTab): def __init__(self, parent): super(InputsTab, self).__init__(parent) @@ -113,42 +134,61 @@ class InputsTab(QWidget): fullNameLabel = QLabel("Full name for user '{0}'".format(login)) - parent.fullNameEdit = QLineEdit() - parent.fullNameEdit.setText(fulluser) + self.fullNameEdit = QLineEdit() + self.fullNameEdit.setText(fulluser) dataroot = settings.value("data/dataRoot") dataDirLabel = QLabel("data directory:") - parent.dataDirEdit = QLineEdit() - parent.dataDirEdit.setText(dataroot) - parent.dataDirEdit.selectAll() + self.dataDirEdit = QLineEdit() + self.dataDirEdit.setText(dataroot) + self.dataDirEdit.selectAll() layout = QGridLayout() layout.addWidget(dataDirLabel, 0, 0) - layout.addWidget(parent.dataDirEdit, 0, 1) + layout.addWidget(self.dataDirEdit, 0, 1) layout.addWidget(fullNameLabel, 1, 0) - layout.addWidget(parent.fullNameEdit, 1, 1) + layout.addWidget(self.fullNameEdit, 1, 1) self.setLayout(layout) + def getValues(self): + values = {} + values["data/dataRoot"] = self.dataDirEdit.text() + values["user/FullName"] = self.fullNameEdit.text() + return values -class OutputsTab(QWidget): + +class OutputsTab(PropTab): def __init__(self, parent=None): super(OutputsTab, self).__init__(parent) - eventOutputLabel = QLabel("event ouput format") - eventOutputComboBox = QComboBox() - eventoutputformats = OUTPUTFORMATS.keys() - eventOutputComboBox.addItems(eventoutputformats) + settings = QSettings() + curval = settings.value("output/Format", None) + eventOutputLabel = QLabel("event ouput format") + self.eventOutputComboBox = QComboBox() + eventoutputformats = OUTPUTFORMATS.keys() + self.eventOutputComboBox.addItems(eventoutputformats) + + if curval is None: + ind = 0 + else: + ind = self.eventOutputComboBox.findText(curval) + + self.eventOutputComboBox.setCurrentIndex(ind) layout = QGridLayout() layout.addWidget(eventOutputLabel, 0, 0) - layout.addWidget(eventOutputComboBox, 0, 1) + layout.addWidget(self.eventOutputComboBox, 0, 1) self.setLayout(layout) + def getValues(self): + values = {} + values["output/Format"] = self.eventOutputComboBox.currentText() + return values -class PhasesTab(QWidget): +class PhasesTab(PropTab): def __init__(self, parent=None): super(PhasesTab, self).__init__(parent) @@ -156,7 +196,7 @@ class PhasesTab(QWidget): pass -class GraphicsTab(QWidget): +class GraphicsTab(PropTab): def __init__(self, parent=None): super(GraphicsTab, self).__init__(parent) From 45fb1935ef2d75602fa24581b903be4c28dbae01 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 20 Jan 2015 14:02:29 +0100 Subject: [PATCH 0174/1144] added FormatError to convenience imports of package util --- QtPyLoT.py | 1 + pylot/core/util/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index a1736525..dd3c740b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -36,6 +36,7 @@ from pylot.core.read import (Data, from pylot.core.util import FILTERDEFAULTS from pylot.core.util import fnConstructor from pylot.core.util import checkurl +from pylot.core.util import FormatError from pylot.core.util import layoutStationButtons from pylot.core.util import (FilterOptionsDialog, MPLWidget, diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index ef703f2e..3d3ed5db 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,6 +1,7 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError +from pylot.core.util.errors import FormatError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor from pylot.core.util.widgets import PickDlg From b4c19acd993207cb405b106440ace075a7fa5b94 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 20 Jan 2015 14:02:56 +0100 Subject: [PATCH 0175/1144] release version change due to testing --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index c5affb8d..ba219c71 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -ef8b-dirty +2961-dirty From ef50c3d4d8e5cd383eebafa3b43849170c684ed2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 21 Jan 2015 16:07:43 +0100 Subject: [PATCH 0176/1144] bugfix: single element tuple declaration was wrong --- QtPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index dd3c740b..fda3f632 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -254,6 +254,7 @@ class MainWindow(QMainWindow): self.helpMenu = self.menuBar().addMenu('&Help') helpActions = (helpAction) + helpActions = (helpAction, ) self.addMenuActions(self.helpMenu, helpActions) self.eventLabel = QLabel() From 64158174e61e5b88414c3e6a1f3ffc4eab445871 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 22 Jan 2015 16:41:52 +0100 Subject: [PATCH 0177/1144] currently working on the plotting of waveforms; changes made to meet pre-requisites to data plotting --- QtPyLoT.py | 29 ++++++++++++++--- pylot/RELEASE-VERSION | 2 +- pylot/core/read/data.py | 10 +++--- pylot/core/util/__init__.py | 2 ++ pylot/core/util/utils.py | 7 ++++- pylot/core/util/widgets.py | 62 +++++++++++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 12 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index fda3f632..7e5bde9b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -39,6 +39,8 @@ from pylot.core.util import checkurl from pylot.core.util import FormatError from pylot.core.util import layoutStationButtons from pylot.core.util import (FilterOptionsDialog, + NewEventDlg, + createEvent, MPLWidget, PropertiesDlg, HelpForm) @@ -150,8 +152,9 @@ class MainWindow(QMainWindow): filt = """Supported event formats (*.mat *.qml *.xml *.kor *.evt)""" caption = 'Select event to open' - self.fname = QFileDialog().getOpenFileName(self, caption=caption, - filter=filt) + self.fname = QFileDialog().getOpenFileName(self, + caption=caption, + filter=filt) else: self.fname = unicode(action.data().toString()) if not self.okToContinue(): @@ -162,6 +165,9 @@ class MainWindow(QMainWindow): self.fname = fname self.data = Data(evtdata=self.fname) + def getWFFnames(self): + pass + def saveData(self): settings = QSettings() exform = settings.value('data/exportFormat', 'None') @@ -202,6 +208,11 @@ class MainWindow(QMainWindow): quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) + newIcon = self.style().standardIcon(QStyle.SP_FileIcon) + newEventAction = self.createAction("&New event ...", + self.createNewEvent, + QKeySequence.New, newIcon, + "Create a new event.") openEventAction = self.createAction("&Open event ...", self.loadData, QKeySequence.Open, openIcon, "Open an event.") @@ -242,7 +253,8 @@ class MainWindow(QMainWindow): homepage (internet connection available), or shipped documentation files.""") self.fileMenu = self.menuBar().addMenu('&File') - self.fileMenuActions = (openEventAction, saveEventAction, + self.fileMenuActions = (newEventAction, openEventAction, + saveEventAction, None, prefsEventAction, quitAction) self.fileMenu.aboutToShow.connect(self.updateFileMenu) self.updateFileMenu() @@ -253,7 +265,6 @@ class MainWindow(QMainWindow): self.addMenuActions(self.editMenu, editActions) self.helpMenu = self.menuBar().addMenu('&Help') - helpActions = (helpAction) helpActions = (helpAction, ) self.addMenuActions(self.helpMenu, helpActions) @@ -271,7 +282,7 @@ class MainWindow(QMainWindow): self.setCentralWidget(_widget) def addMenuActions(self, menu, actions): - for action in (actions): + for action in actions: if action is None: menu.addSeparator() else: @@ -357,6 +368,14 @@ class MainWindow(QMainWindow): def printEvent(self): pass + def createNewEvent(self): + if self.okToContinue(): + new = NewEventDlg() + if new.exec_(): + evtpar = new.getValues() + self.data = Data(self, evtdata=createEvent(**evtpar)) + self.dirty = True + def closeEvent(self, event): if self.okToContinue(): self.closing.emit() diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index ba219c71..52a4577d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -2961-dirty +ef50-dirty diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 333421a5..3b15a08c 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -30,9 +30,7 @@ class Data(object): def __init__(self, parent=None, evtdata=None): try: if parent: - self.wfdata = read(parent.fnames) - else: - self.wfdata = read() + self.wfdata = read(parent.getWFFnames()) except IOError, e: msg = 'An I/O error occured while loading data!' inform = 'Variable wfdata will be empty.' @@ -45,7 +43,7 @@ class Data(object): warnio.setStandarButtons(QMessageBox.Ok) warnio.setIcon(QMessageBox.Warning) else: - print msg, '\n', details + print msg, "\n", details self.wfdata = Stream() else: self.wfdata = Stream() @@ -56,7 +54,7 @@ class Data(object): cat = readEvents(evtdata) self.evtdata = cat[0] elif evtdata is not None: - cat = readMatPhases(evtdata) + cat = self.readMatPhases(evtdata) else: # create an empty Event object self.newevent = True self.evtdata = Event() @@ -78,6 +76,8 @@ class Data(object): if fnout is None: ID = self.evtdata.getEventID() + else: + ID = self.getID() # handle forbidden filenames especially on windows systems fnout = fnConstructor(ID) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 3d3ed5db..a6b5d183 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -4,10 +4,12 @@ from pylot.core.util.errors import OptionsError from pylot.core.util.errors import FormatError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor +from pylot.core.util.utils import createEvent from pylot.core.util.widgets import PickDlg from pylot.core.util.widgets import HelpForm from pylot.core.util.widgets import FilterOptionsDialog from pylot.core.util.widgets import PropertiesDlg +from pylot.core.util.widgets import NewEventDlg from pylot.core.util.widgets import MPLWidget from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index c87911c2..9748d7eb 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -3,7 +3,7 @@ # -*- coding: utf-8 -*- import re - +from obspy.core.event import * def fnConstructor(s): @@ -17,3 +17,8 @@ def fnConstructor(s): if badsuffix.match(fn): fn = '_' + fn return fn + +def createEvent(origintime, latitude, longitude, depth, **kwargs): + evt = Event() + + diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index f20c0ecf..f2921a68 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -5,6 +5,7 @@ Created on Wed Mar 19 11:27:35 2014 @author: sebastianw """ +import datetime import matplotlib matplotlib.use('Qt4Agg') @@ -15,6 +16,7 @@ from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from PySide.QtGui import (QAction, QApplication, QComboBox, + QDateTimeEdit, QDialog, QDialogButtonBox, QDoubleSpinBox, @@ -204,6 +206,66 @@ class GraphicsTab(PropTab): pass +class NewEventDlg(QDialog): + def __init__(self, parent=None, titleString="Create a new event"): + """ + QDialog object utilized to create a new event manually. + """ + super(NewEventDlg, self).__init__() + + self.setupUI() + + now = datetime.datetime.now() + self.eventTimeEdit.setDateTime(now) + # event dates in the future are forbidden + self.eventTimeEdit.setMaximumDateTime(now) + + self.latEdit.setText("51.0000") + self.lonEdit.setText("7.0000") + self.depEdit.setText("10.0") + + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + def getValues(self): + if self.accepted(): + return {'origintime' : self.eventTimeEdit.dateTime().toPyDateTime(), + 'latitude' : self.latEdit.text(), + 'longitude' : self.lonEdit.text(), + 'depth' : self.depEdit.text()} + + def setupUI(self): + + # create widget objects + timeLabel = QLabel() + timeLabel.setText("Select time: ") + self.eventTimeEdit = QDateTimeEdit() + latLabel = QLabel() + latLabel.setText("Latitude: ") + self.latEdit = QLineEdit() + lonLabel = QLabel() + lonLabel.setText("Longitude: ") + self.lonEdit = QLineEdit() + depLabel = QLabel() + depLabel.setText("Depth: ") + self.depEdit = QLineEdit() + + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) + + grid = QGridLayout() + grid.addWidget(timeLabel, 0, 0) + grid.addWidget(self.eventTimeEdit, 0, 1) + grid.addWidget(latLabel, 1, 0) + grid.addWidget(self.latEdit, 1, 1) + grid.addWidget(lonLabel, 2, 0) + grid.addWidget(self.lonEdit, 2, 1) + grid.addWidget(depLabel, 3, 0) + grid.addWidget(self.depEdit, 3, 1) + grid.addWidget(self.buttonBox, 4, 1) + + self.setLayout(grid) + class FilterOptionsDialog(QDialog): def __init__(self, parent=None, titleString="Filter options", From 23fa136dc5543f6e503a718a433b5deebcd59699 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 23 Jan 2015 10:21:34 +0100 Subject: [PATCH 0178/1144] get data from the NewEvtDlg dialog for event creation --- QtPyLoT.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 7e5bde9b..cf70b57a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -371,10 +371,10 @@ class MainWindow(QMainWindow): def createNewEvent(self): if self.okToContinue(): new = NewEventDlg() - if new.exec_(): + if new.exec_() != QDialog.Rejected: evtpar = new.getValues() - self.data = Data(self, evtdata=createEvent(**evtpar)) - self.dirty = True + self.data = Data(self, evtdata=createEvent(**evtpar)) + self.dirty = True def closeEvent(self, event): if self.okToContinue(): From 2d48ad3bb2137336ba9622c20acb97d2571d959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jan 2015 10:54:58 +0100 Subject: [PATCH 0179/1144] Some changes to be actual to Python2.7 --- pylot/core/pick/run_makeCF.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 37bfeee6..56f7f228 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -13,13 +13,12 @@ from CharFuns import * from Picker import * import glob import argparse -import pdb def run_makeCF(project, database, event, iplot, station=None): #parameters for CF calculation t2 = 7 #length of moving window for HOS calculation [sec] p = 4 #order of statistics - cuttimes = [5, 40] #start and end time for CF calculation + cuttimes = [10, 40] #start and end time for CF calculation bpz = [2, 30] #corner frequencies of bandpass filter, vertical component bph = [2, 15] #corner frequencies of bandpass filter, horizontal components tdetz= 1.2 #length of AR-determination window [sec], vertical component @@ -151,11 +150,11 @@ def run_makeCF(project, database, event, iplot, station=None): plt.figure() tr = st[0] tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) - p1 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p2 = plt.plot(hoscf.getTimeArray(), hoscf.getCF() / max(hoscf.getCF()), 'r') - p3 = plt.plot(aiccf.getTimeArray(), aiccf.getCF()/max(aiccf.getCF()), 'b') - p4 = plt.plot(arzcf.getTimeArray(), arzcf.getCF()/max(arzcf.getCF()), 'g') - p5 = plt.plot(araiccf.getTimeArray(), araiccf.getCF()/max(araiccf.getCF()), 'y') + p1, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p2, = plt.plot(hoscf.getTimeArray(), hoscf.getCF() / max(hoscf.getCF()), 'r') + p3, = plt.plot(aiccf.getTimeArray(), aiccf.getCF()/max(aiccf.getCF()), 'b') + p4, = plt.plot(arzcf.getTimeArray(), arzcf.getCF()/max(arzcf.getCF()), 'g') + p5, = plt.plot(araiccf.getTimeArray(), araiccf.getCF()/max(araiccf.getCF()), 'y') plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'b--') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'b') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'b') @@ -176,14 +175,14 @@ def run_makeCF(project, database, event, iplot, station=None): plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) #plot horizontal traces plt.figure(2) - plt.subplot(211) + plt.subplot(2,1,1) tsteph = tpredh / 4 th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh - p21 = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - p22 = plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') - p23 = plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) + p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + p22, = plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') + p23, = plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [-1, -1], 'b') @@ -192,7 +191,7 @@ def run_makeCF(project, database, event, iplot, station=None): plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) plt.suptitle(trH1_filt.stats.starttime) plt.legend([p21, p22, p23], ['Data', 'ARH-CF', 'ARHAIC-CF']) - plt.subplot(212) + plt.subplot(2,1,2) plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') @@ -205,22 +204,22 @@ def run_makeCF(project, database, event, iplot, station=None): #plot 3-component window plt.figure(3) tar3ccf = np.arange(0, len(ar3ccf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdetz +tpredz - plt.subplot(311) - p31 = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p32 = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.subplot(3,1,1) + p31, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p32, = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') plt.yticks([]) plt.xticks([]) plt.ylabel('Normalized Counts') plt.title([tr.stats.station, tr.stats.channel]) plt.legend([p31, p32], ['Data', 'AR3C-CF']) - plt.subplot(312) + plt.subplot(3,1,2) plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') plt.yticks([]) plt.xticks([]) plt.ylabel('Normalized Counts') plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) - plt.subplot(313) + plt.subplot(3,1,3) plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') plt.yticks([]) From 41a2e840f88fd26fba9bb02b8b7197d2a9e7c2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jan 2015 10:55:18 +0100 Subject: [PATCH 0180/1144] Some changes to be actual to Python2.7 --- pylot/core/pick/Picker.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 6e6f4d8d..a7c3d154 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -17,7 +17,6 @@ autoregressive prediction: application ot local and regional distances, Geophys. import numpy as np import matplotlib.pyplot as plt from pylot.core.pick.CharFuns import CharacteristicFunction -import pdb class AutoPicking(object): ''' @@ -173,7 +172,7 @@ class PragPicker(AutoPicking): self.Pick = -1 #smooth CF - ismooth = round(self.Tsmooth / self.dt); + ismooth = int(round(self.Tsmooth / self.dt)) cfsmooth = np.zeros(len(self.cf)) if len(self.cf) < ismooth: print 'PragPicker: Tsmooth larger than CF!' From 75ffe0c37aca19b8ca28853b2d8d7e696cd2e830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jan 2015 10:55:25 +0100 Subject: [PATCH 0181/1144] Some changes to be actual to Python2.7 --- pylot/core/pick/CharFuns.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 64c4f06e..d52b7898 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -155,7 +155,7 @@ class CharacteristicFunction(object): stop = self.cut[1] / self.dt zz = self.orig_data.copy() z1 = zz[0].copy() - zz[0].data = z1.data[start:stop] + zz[0].data = z1.data[int(start):int(stop)] data = zz return data elif len(self.orig_data) == 2: @@ -171,8 +171,8 @@ class CharacteristicFunction(object): hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() - hh[0].data = h1.data[start:stop] - hh[1].data = h2.data[start:stop] + hh[0].data = h1.data[int(start):int(stop)] + hh[1].data = h2.data[int(start):int(stop)] data = hh return data elif len(self.orig_data) == 3: @@ -189,9 +189,9 @@ class CharacteristicFunction(object): h1 = hh[0].copy() h2 = hh[1].copy() h3 = hh[2].copy() - hh[0].data = h1.data[start:stop] - hh[1].data = h2.data[start:stop] - hh[2].data = h3.data[start:stop] + hh[0].data = h1.data[int(start):int(stop)] + hh[1].data = h2.data[int(start):int(stop)] + hh[2].data = h3.data[int(start):int(stop)] data = hh return data else: @@ -263,10 +263,9 @@ class HOScf(CharacteristicFunction): #Initialisation #t2: long term moving window - ilta = round(self.getTime2() / self.getIncrement()) + ilta = int(round(self.getTime2() / self.getIncrement())) lta = y[0] lta1 = y1[0] - #moving windows LTA = np.zeros(len(xnp)) for j in range(0, len(xnp)): From dbd53024b2979b8182b38094ea78d29aafd55728 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 26 Jan 2015 21:08:07 +0100 Subject: [PATCH 0182/1144] new module: make selection of either data structure easier --- pylot/core/util/structure.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 pylot/core/util/structure.py diff --git a/pylot/core/util/structure.py b/pylot/core/util/structure.py new file mode 100644 index 00000000..36e35b60 --- /dev/null +++ b/pylot/core/util/structure.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on Wed Jan 26 17:47:25 2015 + +@author: sebastianw +""" + +from pylot.core.read import SeiscompDataStructure, PilotDataStructure + +DATASTRUCTURE = {'PILOT':PilotDataStructure, 'SeisComP':SeiscompDataStructure} From d22a2248047892f5f99d17b06317d9057fa15cf6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 26 Jan 2015 21:11:53 +0100 Subject: [PATCH 0183/1144] data plotting; waveform filtering and convenience imports in order to get a overview window displaying data; new data structure available -> PyLoT should be able to read PILOT data --- QtPyLoT.py | 27 ++++++++--- pylot/core/read/__init__.py | 4 +- pylot/core/read/data.py | 91 ++++++++++++++++++++++++++++++------- pylot/core/util/widgets.py | 21 ++++++++- 4 files changed, 117 insertions(+), 26 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index cf70b57a..1d318090 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -166,7 +166,20 @@ class MainWindow(QMainWindow): self.data = Data(evtdata=self.fname) def getWFFnames(self): - pass + try: + evt = self.getData().getEvtData() + if evt.picks: + for pick in evt.picks: + if pick.waveform_id is not None: + fname = pick.waveform_id.getSEEDstring() + if fname not in self.fnames: + self.fnames.append(fname) + else: + if self.dataStructure: + searchPath = self.dataStructure.expandDataPath() + except: + return None + def saveData(self): settings = QSettings() @@ -177,7 +190,7 @@ class MainWindow(QMainWindow): return False return True - def getComponent(self): + def getDispComponent(self): return self def getData(self): @@ -294,20 +307,20 @@ class MainWindow(QMainWindow): return True def plotData(self): - pass #self.data.plotData(self.DataPlot) + self.getData().plotData(self.getDataWidget()) def filterData(self): if self.getData(): kwargs = {} - freq = self.filteroptions.getFreq() + freq = self.getFilterOptions().getFreq() if len(freq) > 1: kwargs['freqmin'] = freq[0] kwargs['freqmax'] = freq[1] else: kwargs['freq'] = freq - kwargs['type'] = self.filteroptions.getFilterType() - #kwargs['order'] = self.filteroptions.getOrder() - self.data.filter(**kwargs) + kwargs['type'] = self.getFilterOptions().getFilterType() + kwargs['corners'] = self.filteroptions.getOrder() + self.getData().filter(kwargs) def adjustFilterOptions(self): filteroptions = None diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index beb833f8..bafce7f4 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -1,6 +1,8 @@ from pylot.core.read.inputs import AutoPickParameter from pylot.core.read.inputs import FilterOptions -from pylot.core.read.data import Data from pylot.core.read.data import GenericDataStructure from pylot.core.read.data import SeiscompDataStructure +from pylot.core.read.data import PilotDataStructure +from pylot.core.read.data import Data + diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 3b15a08c..11786480 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -94,8 +94,10 @@ class Data(object): not implemented: {1}'''.format(evtformat, e)) def plotData(self, widget): - time_ax = np.arange(0, len(self.wfdata[0].data)/self.wfdata[0].stats.sampling_rate, self.wfdata[0].stats.delta) - widget.axes.plot(time_ax, self.wfdata[0].data) + wfst = self.getWFData() + time_ax = np.arange(0, len(wfst[0].data) / wfst[0].stats.sampling_rate, + wfst[0].stats.delta) + widget.axes.plot(time_ax, wfst[0].data) def getID(self): try: @@ -103,12 +105,22 @@ class Data(object): except: return 'smi:bug/pylot/1234' + def filter(self, kwargs): + self.getWFData().filter(**kwargs) + + def getWFData(self): + return self.wfdata + + def getEvtData(self): + return self.evtdata + class GenericDataStructure(object): ''' GenericDataBase type holds all information about the current data- base working on. ''' + def __init__(self, stexp=None, folderdepth=4, **kwargs): structExpression = [] depth = 0 @@ -130,6 +142,53 @@ class GenericDataStructure(object): self.dataBaseDict = {} +class PilotDataStructure(object): + ''' + Object containing the data access information for the old PILOT data + structure. + ''' + + def __init__(self, dataformat='GSE2', fsuffix='gse', + root='/data/Egelados/EVENT_DATA/LOCAL/', database='2006.01', + **kwargs): + self.dataType = dataformat + self.__pdsFields = {'ROOT': root, + 'DATABASE': database, + 'SUFFIX': fsuffix + } + + self.modifiyFields(**kwargs) + + def modifiyFields(self, **kwargs): + if kwargs and isinstance(kwargs, dict): + for key, value in kwargs.iteritems(): + key = str(key) + if type(value) not in (str, int, float): + for n, val in enumerate(value): + value[n] = str(val) + else: + value = str(value) + try: + if key in self.getFields().keys(): + self.getFields()[key] = value + else: + raise KeyError('unknown PDS wildcard: %s.' % key) + except KeyError, e: + errmsg = '' + errmsg += 'WARNING:\n' + errmsg += 'unable to set values for PDS fields\n' + errmsg += '%s; desired value was: %s\n' % (e, value) + print errmsg + + def getType(self): + return self.dataType + + def getFields(self): + return self.__pdsFields + + def expandDataPath(self): + return + class SeiscompDataStructure(object): ''' Dictionary containing the data access information for an SDS data archive: @@ -142,13 +201,13 @@ class SeiscompDataStructure(object): # Data type options __typeOptions = {'waveform': 'D', # Waveform data - 'detect': 'E', # Detection data - 'log': 'L', # Log data - 'timing': 'T', # Timing data - 'calib': 'C', # Calibration data - 'resp': 'R', # Response data - 'opaque': 'O' # Opaque data - } + 'detect': 'E', # Detection data + 'log': 'L', # Log data + 'timing': 'T', # Timing data + 'calib': 'C', # Calibration data + 'resp': 'R', # Response data + 'opaque': 'O' # Opaque data + } def __init__(self, dataType='waveform', sdate=None, edate=None, **kwargs): # imports @@ -174,8 +233,8 @@ class SeiscompDataStructure(object): if not edate.year == sdate.year: nyears = edate.year - sdate.year for yr in range(nyears): - year += '{0:04d},'.format(sdate.year+yr) - year = '{'+year[:-1]+'}' + year += '{0:04d},'.format(sdate.year + yr) + year = '{' + year[:-1] + '}' else: year = '{0:04d}'.format(sdate.year) @@ -198,7 +257,7 @@ class SeiscompDataStructure(object): 'TYPE': self.getType(), # 1 character 'LOC': '', # up to 8 characters 'DAY': '{0:03d}'.format(sdate.julday) # 3 digits - } + } self.modifiyFields(**kwargs) def modifiyFields(self, **kwargs): @@ -211,8 +270,8 @@ class SeiscompDataStructure(object): else: value = str(value) try: - if key in self.getSDSFields().keys(): - self.getSDSFields()[key] = value + if key in self.getFields().keys(): + self.getFields()[key] = value else: raise KeyError('unknown SDS wildcard: %s.' % key) except KeyError, e: @@ -225,7 +284,7 @@ class SeiscompDataStructure(object): def getType(self): return self.__typeOptions[self.dataType] - def getSDSFields(self): + def getFields(self): return self.__sdsFields def expandDataPath(self): @@ -236,5 +295,5 @@ class SeiscompDataStructure(object): self.getSDSFields()['STA'], fullChan, '*{0}'.format(self.getSDSFields()['DAY']) - ) + ) return dataPath diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index f2921a68..b361b8ed 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -134,16 +134,26 @@ class InputsTab(PropTab): fulluser = settings.value("user/FullName") login = settings.value("user/Login") - fullNameLabel = QLabel("Full name for user '{0}'".format(login)) + fullNameLabel = QLabel("Full name for user '{0}': ".format(login)) + # get the full name of the actual user self.fullNameEdit = QLineEdit() self.fullNameEdit.setText(fulluser) + # information about data structure dataroot = settings.value("data/dataRoot") - dataDirLabel = QLabel("data directory:") + dataDirLabel = QLabel("data root directory: ") self.dataDirEdit = QLineEdit() self.dataDirEdit.setText(dataroot) self.dataDirEdit.selectAll() + structureLabel = QLabel("data structure: ") + self.structureSelect = QComboBox() + + from pylot.core.util.structure import DATASTRUCTURE + + datastruct = DATASTRUCTURE.keys() + self.structureSelect.addItems(datastruct) + self.updateWidget(DATASTRUCTURE) layout = QGridLayout() layout.addWidget(dataDirLabel, 0, 0) @@ -159,6 +169,13 @@ class InputsTab(PropTab): values["user/FullName"] = self.fullNameEdit.text() return values + def updateWidget(self, structure): + key = self.structureSelect.currentText() + structure = structure[key] + structure().getFields().keys() + + + class OutputsTab(PropTab): From 89f996ffe376c8fe4a35f2998908e4c29e55274f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 27 Jan 2015 05:19:41 +0100 Subject: [PATCH 0184/1144] problem fixed: renaming of getFields method implemented into expandDataPath method (unified usage of either DataStructure object) --- pylot/core/read/data.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 11786480..54fcb210 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -288,12 +288,12 @@ class SeiscompDataStructure(object): return self.__sdsFields def expandDataPath(self): - fullChan = '{0}.{1}'.format(self.getSDSFields()['CHAN'], self.getType()) - dataPath = os.path.join(self.getSDSFields()['SDSdir'], - self.getSDSFields()['YEAR'], - self.getSDSFields()['NET'], - self.getSDSFields()['STA'], + fullChan = '{0}.{1}'.format(self.getFields()['CHAN'], self.getType()) + dataPath = os.path.join(self.getFields()['SDSdir'], + self.getFields()['YEAR'], + self.getFields()['NET'], + self.getFields()['STA'], fullChan, - '*{0}'.format(self.getSDSFields()['DAY']) + '*{0}'.format(self.getFields()['DAY']) ) return dataPath From a8330b6d91f3fe660d811b97b19f07fca076fe78 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 27 Jan 2015 05:22:06 +0100 Subject: [PATCH 0185/1144] PilotDataStructure's method expandDataPath return the actual datapath as SeiscompDataStructure's does --- pylot/core/read/data.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 54fcb210..ff58fe9b 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -187,7 +187,10 @@ class PilotDataStructure(object): return self.__pdsFields def expandDataPath(self): - return + datapath = os.path.join(self.getFields()['ROOT'], + self.getFields()['DATABASE'], + "*{0}".format(self.getFields()['SUFFIX'])) + return datapath class SeiscompDataStructure(object): ''' From 8eb69822b1287d81e040778b565e6f8e7b1bf27d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 29 Jan 2015 08:41:38 +0100 Subject: [PATCH 0186/1144] re-organization of the MainWindow methods (for convenience only) --- QtPyLoT.py | 196 +++++++++++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 97 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1d318090..66b1229b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -92,8 +92,105 @@ class MainWindow(QMainWindow): self.setupUi() - def _getCurrentPlotType(self): - return 'TestType' + def setupUi(self): + self.setWindowIcon(QIcon(":/icon.ico")) + + xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') + + _widget = QWidget() + _layout = QHBoxLayout() + + # create central matplotlib figure widget + self.DataPlot = MPLWidget(parent=self, + xlabel=xlab, + ylabel=None, + title=plottitle) + statsButtons = layoutStationButtons(self.getData(), self.getComponent()) + _layout.addLayout(statsButtons) + _layout.addWidget(self.DataPlot) + + openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) + quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) + saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) + helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) + newIcon = self.style().standardIcon(QStyle.SP_FileIcon) + newEventAction = self.createAction("&New event ...", + self.createNewEvent, + QKeySequence.New, newIcon, + "Create a new event.") + openEventAction = self.createAction("&Open event ...", self.loadData, + QKeySequence.Open, openIcon, + "Open an event.") + openEventAction.setData(None) + saveEventAction = self.createAction("&Save event ...", self.saveData, + QKeySequence.Save, saveIcon, + "Save actual event data.") + openWFDataAction = self.createAction("Open &waveforms ...", + self.openWaveformData, + "Ctrl+W", QIcon(":/wfIcon.png"), + """Open waveform data (event will + be closed).""") + + prefsEventAction = self.createAction("Preferences", self.PyLoTprefs, + QKeySequence.Preferences, + QIcon(None), + "Edit PyLoT app preferences.") + quitAction = self.createAction("&Quit", + QCoreApplication.instance().quit, + QKeySequence.Close, quitIcon, + "Close event and quit PyLoT") + filterAction = self.createAction("&Filter ...", self.filterData, + "Ctrl+F", QIcon(":/filter.png"), + """Toggle un-/filtered waveforms + to be displayed, according to the + desired seismic phase.""", True) + filterEditAction = self.createAction("&Filter parameter ...", + self.adjustFilterOptions, + "Alt+F", QIcon(None), + """Adjust filter parameters.""") + selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", + QIcon(":/picon.png"), + "Toggle P phase.", True) + selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", + QIcon(":/sicon.png"), + "Toggle S phase", True) + printAction = self.createAction("&Print event ...", + self.printEvent, QKeySequence.Print, + QIcon(":/printer.png"), + "Print waveform overview.") + helpAction = self.createAction("&Help ...", self.helpHelp, + QKeySequence.HelpContents, helpIcon, + """Show either the documentation + homepage (internet connection available), + or shipped documentation files.""") + self.fileMenu = self.menuBar().addMenu('&File') + self.fileMenuActions = (newEventAction, openEventAction, + saveEventAction, openWFDataAction, None, + prefsEventAction, quitAction) + self.fileMenu.aboutToShow.connect(self.updateFileMenu) + self.updateFileMenu() + + self.editMenu = self.menuBar().addMenu('&Edit') + editActions = (filterAction, filterEditAction, None, selectPAction, + selectSAction, None, printAction) + self.addMenuActions(self.editMenu, editActions) + + self.helpMenu = self.menuBar().addMenu('&Help') + helpActions = (helpAction, ) + self.addMenuActions(self.helpMenu, helpActions) + + self.eventLabel = QLabel() + self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) + status = self.statusBar() + status.setSizeGripEnabled(False) + status.addPermanentWidget(self.eventLabel) + status.showMessage("Ready", 500) + + statsButtons = layoutStationButtons(self.getData(), self.getComponent()) + _layout.addLayout(statsButtons) + _layout.addWidget(self.DataPlot) + _widget.setLayout(_layout) + self.setCentralWidget(_widget) def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False): @@ -199,101 +296,6 @@ class MainWindow(QMainWindow): def getDataWidget(self): return self.DataPlot - def setupUi(self): - self.setWindowIcon(QIcon(":/icon.ico")) - - xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') - plottitle = self._getCurrentPlotType() - - _widget = QWidget() - _layout = QHBoxLayout() - - # create central matplotlib figure widget - self.DataPlot = MPLWidget(parent=self, - xlabel=xlab, - ylabel=None, - title=plottitle) - statsButtons = layoutStationButtons(self.getData(), self.getComponent()) - _layout.addLayout(statsButtons) - _layout.addWidget(self.DataPlot) - - openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) - quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) - saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) - helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) - newIcon = self.style().standardIcon(QStyle.SP_FileIcon) - newEventAction = self.createAction("&New event ...", - self.createNewEvent, - QKeySequence.New, newIcon, - "Create a new event.") - openEventAction = self.createAction("&Open event ...", self.loadData, - QKeySequence.Open, openIcon, - "Open an event.") - openEventAction.setData(None) - saveEventAction = self.createAction("&Save event ...", self.saveData, - QKeySequence.Save, saveIcon, - "Save actual event data.") - prefsEventAction = self.createAction("Preferences", self.PyLoTprefs, - QKeySequence.Preferences, - QIcon(None), - "Edit PyLoT app preferences.") - quitAction = self.createAction("&Quit", - QCoreApplication.instance().quit, - QKeySequence.Close, quitIcon, - "Close event and quit PyLoT") - filterAction = self.createAction("&Filter ...", self.filterData, - "Ctrl+F", QIcon(":/filter.png"), - """Toggle un-/filtered waveforms - to be displayed, according to the - desired seismic phase.""", True) - filterEditAction = self.createAction("&Filter parameter ...", - self.adjustFilterOptions, - "Alt+F", QIcon(None), - """Adjust filter parameters.""") - selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", - QIcon(":/picon.png"), - "Toggle P phase.", True) - selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", - QIcon(":/sicon.png"), - "Toggle S phase", True) - printAction = self.createAction("&Print event ...", - self.printEvent, QKeySequence.Print, - QIcon(":/printer.png"), - "Print waveform overview.") - helpAction = self.createAction("&Help ...", self.helpHelp, - QKeySequence.HelpContents, helpIcon, - """Show either the documentation - homepage (internet connection available), - or shipped documentation files.""") - self.fileMenu = self.menuBar().addMenu('&File') - self.fileMenuActions = (newEventAction, openEventAction, - saveEventAction, None, - prefsEventAction, quitAction) - self.fileMenu.aboutToShow.connect(self.updateFileMenu) - self.updateFileMenu() - - self.editMenu = self.menuBar().addMenu('&Edit') - editActions = (filterAction, filterEditAction, None, selectPAction, - selectSAction, None, printAction) - self.addMenuActions(self.editMenu, editActions) - - self.helpMenu = self.menuBar().addMenu('&Help') - helpActions = (helpAction, ) - self.addMenuActions(self.helpMenu, helpActions) - - self.eventLabel = QLabel() - self.eventLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) - status = self.statusBar() - status.setSizeGripEnabled(False) - status.addPermanentWidget(self.eventLabel) - status.showMessage("Ready", 500) - - statsButtons = layoutStationButtons(self.getData(), self.getComponent()) - _layout.addLayout(statsButtons) - _layout.addWidget(self.DataPlot) - _widget.setLayout(_layout) - self.setCentralWidget(_widget) - def addMenuActions(self, menu, actions): for action in actions: if action is None: From f5eda747302feb0f108084e237f8202031e58d76 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 29 Jan 2015 08:48:25 +0100 Subject: [PATCH 0187/1144] method getWFFnames implemented returning the file names of the waveform data files given in a particular data structure case as well as setting the attribute pointing to the same information; method openWaveformData implemented in order to read waveform data into the Data object --- QtPyLoT.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 66b1229b..7bebc4da 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -267,16 +267,28 @@ class MainWindow(QMainWindow): evt = self.getData().getEvtData() if evt.picks: for pick in evt.picks: - if pick.waveform_id is not None: - fname = pick.waveform_id.getSEEDstring() - if fname not in self.fnames: - self.fnames.append(fname) + try: + if pick.waveform_id is not None: + fname = pick.waveform_id.getSEEDstring() + if fname not in self.fnames: + self.fnames.append(fname) + except: + continue else: if self.dataStructure: searchPath = self.dataStructure.expandDataPath() - except: - return None - + self.fnames = QFileDialog.getOpenFileNames(self, + "Select waveform files:", + dir=searchPath) + else: + raise ValueError('dataStructure not specified') + return self.fnames + except ValueError: + props = PropertiesDlg(self) + if props.exec_() == QDialog.Accepted: + return self.getWFFnames() + else: + return def saveData(self): settings = QSettings() @@ -308,6 +320,13 @@ class MainWindow(QMainWindow): return self.saveData() return True + def openWaveformData(self): + if self.fnames and self.okToContinue(): + self.dirty = True + self.data.wfdata = self.data.setWFData(self.fnames) + elif self.fnames is None and self.okToContinue(): + self.data.setWFData(self.getWFFnames()) + def plotData(self): self.getData().plotData(self.getDataWidget()) From eab0ea5a7e00946e95d7945c4c89d2bc3d9e101a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 29 Jan 2015 08:50:39 +0100 Subject: [PATCH 0188/1144] modified the GenericDataStructure class in order to fit into the concept of different data structures (work in progress) --- pylot/core/read/data.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index ff58fe9b..4c5b5478 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -108,6 +108,10 @@ class Data(object): def filter(self, kwargs): self.getWFData().filter(**kwargs) + def setWFData(self, fnames): + for fname in fnames: + self.wfdata += read(fname) + def getWFData(self): return self.wfdata @@ -121,12 +125,13 @@ class GenericDataStructure(object): base working on. ''' - def __init__(self, stexp=None, folderdepth=4, **kwargs): + def __init__(self, structexp='$R/$D/$E', folderdepth=2, **kwargs): + structureOptions = ('$R', '$D', '$E') structExpression = [] depth = 0 - while stexp is not os.path.sep: + while structexp is not os.path.sep: try: - [stexp, tlexp] = os.path.split(stexp) + [structexp, tlexp] = os.path.split(structexp) except AttributeError: rootExpression = None structExpression = None @@ -139,8 +144,16 @@ class GenericDataStructure(object): structExpression.reverse() self.folderDepth = folderdepth - self.dataBaseDict = {} + self.__gdsFields = {'ROOT':rootExpression} + self.modifyFields(**kwargs) + def modifyFields(self, **kwargs): + for key, value in kwargs.iteritems(): + key = str(key).upper() + self.__gdsFields[key] = value + + def getFields(self): + return self.__gdsFields class PilotDataStructure(object): ''' From 9dc5b57ee0ba4946f8f9e26ee537c3bd3ee6f22b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 29 Jan 2015 08:52:01 +0100 Subject: [PATCH 0189/1144] new attribute introduced holding information about the component of the seismic waveforms to display in the overview plot --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 7bebc4da..a024414a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -300,7 +300,7 @@ class MainWindow(QMainWindow): return True def getDispComponent(self): - return self + return self._compDisp def getData(self): return self.data From 36675d4a92bd91a49bc13f9032cc498cb16f15a4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 29 Jan 2015 08:53:01 +0100 Subject: [PATCH 0190/1144] code clean up --- QtPyLoT.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a024414a..17af3dea 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -64,18 +64,20 @@ class MainWindow(QMainWindow): settings.setValue("user/Login", os.getlogin()) settings.sync() self.recentEvents = settings.value("data/recentEvents", []) + self.dataStructure = settings.value("data/structure", None) self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(QIcon(":/icon.ico")) self.seismicPhase = str(settings.value("phase", "P")) if settings.value("data/dataRoot", None) is None: - dirname = QFileDialog().getExistingDirectory(caption = 'Choose data root ...') + dirname = QFileDialog().getExistingDirectory( + caption='Choose data root ...') settings.setValue("data/dataRoot", dirname) settings.sync() # initialize filter parameter filterOptionsP = FILTERDEFAULTS['P'] filterOptionsS = FILTERDEFAULTS['S'] - # print filterOptionsP, "\n", filterOptionsS + self.filterOptionsP = FilterOptions(**filterOptionsP) self.filterOptionsS = FilterOptions(**filterOptionsS) @@ -84,9 +86,9 @@ class MainWindow(QMainWindow): self.dirty = False self.loadData() self.updateFilterOptions() - # print self.filteroptions try: - self.startTime = min([tr.stats.starttime for tr in self.data.wfdata]) + self.startTime = min( + [tr.stats.starttime for tr in self.data.wfdata]) except: self.startTime = UTCDateTime() @@ -359,8 +361,8 @@ class MainWindow(QMainWindow): return self.filteroptions def setFilterOptions(self, filterOptions): - cases = {'P':self.filterOptionsP, - 'S':self.filterOptionsS} + cases = {'P': self.filterOptionsP, + 'S': self.filterOptionsS} cases[self.getSeismicPhase()] = filterOptions self.updateFilterOptions() @@ -389,11 +391,13 @@ class MainWindow(QMainWindow): self.statusBar().showMessage(message, 5000) if self.getData() is not None: if not self.getData().isNew(): - self.setWindowTitle("PyLoT - processing event %s[*]" % self.getData().getID()) + self.setWindowTitle( + "PyLoT - processing event %s[*]" % self.getData().getID()) elif self.getData().isNew(): self.setWindowTitle("PyLoT - New event [*]") else: - self.setWindowTitle("PyLoT - seismic processing the python way[*]") + self.setWindowTitle( + "PyLoT - seismic processing the python way[*]") self.setWindowTitle("PyLoT - seismic processing the python way[*]") self.setWindowModified(self.dirty) @@ -422,7 +426,8 @@ class MainWindow(QMainWindow): def helpHelp(self): if checkurl(): - form = HelpForm('https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') + form = HelpForm( + 'https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') else: form = HelpForm(':/help.html') form.show() @@ -445,5 +450,6 @@ def main(): pylot_form.show() pylot_app.exec_() + if __name__ == "__main__": sys.exit(main()) From 401c09d0b430f4a212c9bc9ecd3f0612b8d1a461 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 4 Feb 2015 14:49:49 +0100 Subject: [PATCH 0191/1144] added new methods to the MPLWidget class to control the axes labels --- pylot/core/util/widgets.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b361b8ed..76e25376 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -53,10 +53,21 @@ class MPLWidget(FigureCanvas): self.canvas = FigureCanvas(self.figure) self.axes = self.figure.add_subplot(111) - self.axes.set_xlabel(xlabel) - self.axes.set_ylabel(ylabel) - self.axes.set_title(title) + self.updateWidget(xlabel, ylabel, title) + def updateXLabel(self, text): + self.axes.set_xlabel(text) + + def updateYLabel(self, text): + self.axes.set_ylabel(text) + + def updateTitle(self, text): + self.axes.set_title(text) + + def updateWidget(self, xlabel, ylabel, title): + self.updateXLabel(xlabel) + self.updateYLabel(ylabel) + self.updateTitle(title) class PickDlg(QDialog): From bc87c12cfaed2154a23cfc4f185ec0f40fecd97f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 4 Feb 2015 14:50:49 +0100 Subject: [PATCH 0192/1144] new attribute dispComponent added to the MainWindow class to control the displayed component --- QtPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 17af3dea..27bdb489 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -68,6 +68,7 @@ class MainWindow(QMainWindow): self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(QIcon(":/icon.ico")) self.seismicPhase = str(settings.value("phase", "P")) + self.dispComponent = str(settings.value("plotting/dispComponent", "Z")) if settings.value("data/dataRoot", None) is None: dirname = QFileDialog().getExistingDirectory( caption='Choose data root ...') From 7092f6e8b5f33f10ec14b18fd6d843196b56abd8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 7 Feb 2015 09:03:03 +0100 Subject: [PATCH 0193/1144] the MainWindow now should give the right component in the title --- QtPyLoT.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 27bdb489..0d724e2e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -103,6 +103,8 @@ class MainWindow(QMainWindow): _widget = QWidget() _layout = QHBoxLayout() + plottitle = "Overview: {0} components ".format(self.getComponent()) + # create central matplotlib figure widget self.DataPlot = MPLWidget(parent=self, xlabel=xlab, @@ -302,8 +304,8 @@ class MainWindow(QMainWindow): return False return True - def getDispComponent(self): - return self._compDisp + def getComponent(self): + return self.dispComponent def getData(self): return self.data From d3199a5798097ea96ea836a4384aacf5f4dac8e2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 7 Feb 2015 09:05:08 +0100 Subject: [PATCH 0194/1144] implementation of reading and plotting seismograms (work in progress) --- QtPyLoT.py | 46 +++++++++++++++++++++++------------------ pylot/core/read/data.py | 46 ++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 0d724e2e..f349c2b5 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -83,7 +83,7 @@ class MainWindow(QMainWindow): self.filterOptionsS = FilterOptions(**filterOptionsS) # initialize data - self.data = None + self.data = Data() self.dirty = False self.loadData() self.updateFilterOptions() @@ -248,22 +248,23 @@ class MainWindow(QMainWindow): def loadData(self, fname=None): if fname is None: - action = self.sender() - if isinstance(action, QAction): - if action.data() is None: - filt = """Supported event formats (*.mat *.qml *.xml *.kor - *.evt)""" - caption = 'Select event to open' - self.fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) - else: - self.fname = unicode(action.data().toString()) + try: + self.data = Data(evtdata=self.fname) + except AttributeError: + action = self.sender() + if isinstance(action, QAction): + if action.data() is None: + filt = """Supported event formats (*.mat *.qml *.xml + *.kor *.evt)""" + caption = 'Select event to open' + self.fname = QFileDialog().getOpenFileName(self, + caption=caption, + filter=filt) + else: + self.fname = unicode(action.data().toString()) if not self.okToContinue(): return - else: - return - if fname: + else: self.fname = fname self.data = Data(evtdata=self.fname) @@ -326,11 +327,16 @@ class MainWindow(QMainWindow): return True def openWaveformData(self): - if self.fnames and self.okToContinue(): - self.dirty = True - self.data.wfdata = self.data.setWFData(self.fnames) - elif self.fnames is None and self.okToContinue(): - self.data.setWFData(self.getWFFnames()) + try: + if self.fnames and self.okToContinue(): + self.dirty = True + self.data.wfdata = self.data.setWFData(self.fnames) + elif self.fnames is None and self.okToContinue(): + self.data.setWFData(self.getWFFnames()) + except AttributeError, e: + print (e) + self.getWFFnames() + self.openWaveformData() def plotData(self): self.getData().plotData(self.getDataWidget()) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 4c5b5478..54cfe5c6 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -4,7 +4,7 @@ import os import numpy as np from PySide.QtGui import QMessageBox -from obspy.core import (read, Stream) +from obspy.core import (read, Stream, UTCDateTime) from obspy import readEvents from obspy.core.event import (Event, Catalog) from pylot.core.util import fnConstructor @@ -30,7 +30,10 @@ class Data(object): def __init__(self, parent=None, evtdata=None): try: if parent: - self.wfdata = read(parent.getWFFnames()) + self.setWFData(parent.getWFFnames()) + self.comp = parent.getComponent() + else: + self.comp = 'Z' except IOError, e: msg = 'An I/O error occured while loading data!' inform = 'Variable wfdata will be empty.' @@ -50,15 +53,23 @@ class Data(object): self.newevent = False if evtdata is not None and isinstance(evtdata, Event): self.evtdata = evtdata - elif evtdata is not None and not evtdata.endswith('.mat'): + elif evtdata is not None and not isinstance(evtdata, dict): cat = readEvents(evtdata) self.evtdata = cat[0] elif evtdata is not None: - cat = self.readMatPhases(evtdata) + cat = self.readPILOTEvent(**evtdata) else: # create an empty Event object self.newevent = True self.evtdata = Event() self.orig = self.wfdata.copy() + min_start = UTCDateTime() + max_end = None + for trace in self.getWFData().select(component = self.getComp()): + if trace.stats.starttime < min_start: + min_start = trace.stats.starttime + if max_end is None or trace.stats.endtime > max_end: + max_end = trace.stats.endtime + self.cuttimes = [min_start, max_end] def isNew(self): return self.newevent @@ -94,10 +105,25 @@ class Data(object): not implemented: {1}'''.format(evtformat, e)) def plotData(self, widget): - wfst = self.getWFData() - time_ax = np.arange(0, len(wfst[0].data) / wfst[0].stats.sampling_rate, - wfst[0].stats.delta) - widget.axes.plot(time_ax, wfst[0].data) + wfst = self.getWFData().select(component = self.getComp()) + for n, trace in enumerate(wfst): + stime = trace.stats.starttime - self.cuttimes[0] + etime = trace.stats.endtime - self.cuttimes[1] + srate = trace.stats.sampling_rate + nsamp = len(trace.data) + tincr = trace.stats.delta + time_ax = np.arange(stime, nsamp / srate, tincr) + trace.normalize() + widget.axes.plot(time_ax, trace.data + n, 'k') + xlabel = 'seconds since {0}'.format(self.cuttimes[0]) + ylabel = '' + zne_text = {'Z':'vertical', 'N':'north-south', 'E':'east-west'} + title = 'overview: {0} components'.format(zne_text[self.getComp()]) + widget.updateWidget(xlabel, ylabel, title) + + + def getComp(self): + return self.comp def getID(self): try: @@ -112,6 +138,10 @@ class Data(object): for fname in fnames: self.wfdata += read(fname) + def appenWFData(self, fnames): + for fname in fnames: + self.wfdata += read(fname) + def getWFData(self): return self.wfdata From f6bf37c92035085adbcf6573bb3b408f20508353 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 7 Feb 2015 09:12:58 +0100 Subject: [PATCH 0195/1144] new package io.py which should contain all import and export routines written by ourselves, such as reading old PILOT phase and location information file; implementation of the readPILOTevent function; new routines in utils: createArrival will be split into two functions: createPick and createArrival; also planned: createOrigin, createAmplitude and createMagnitude as well as giving createEvent functionality --- pylot/RELEASE-VERSION | 2 +- pylot/core/read/io.py | 170 ++++++++++++++++++++++++++++++++++++ pylot/core/util/__init__.py | 2 + pylot/core/util/errors.py | 2 +- pylot/core/util/utils.py | 34 +++++++- 5 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 pylot/core/read/io.py diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 52a4577d..05b25a55 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -ef50-dirty +3667-dirty diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py new file mode 100644 index 00000000..ff6ee866 --- /dev/null +++ b/pylot/core/read/io.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import glob +import scipy.io as sio +import obspy.core.event as ope +from obspy.core import UTCDateTime +from pylot.core.util import getOwner, createPick, createArrival, createEvent + +def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): + """ + readPILOTEvent - function + + Reads Matlab PHASES and LOC files written by Matlab versions of PILOT and + converts the data into an ObsPy Event object which is returned to the + calling program. + + :rtype : ~obspy.core.event.Event + :param eventID: + :param authority: + :param kwargs: + :param phasfn: filename of the old PILOT Matlab PHASES file + :param locfn: filename of the old PILOT Matlab LOC file + :return event: event object containing event and phase information + """ + sdir = os.path.split(phasfn)[0] + if phasfn is not None and os.path.isfile(phasfn): + phases = sio.loadmat(phasfn) + phasctime = UTCDateTime(os.path.getmtime(phasfn)) + phasauthor = getOwner(phasfn) + else: + phases = None + phasctime = None + phasauthor = None + if locfn is not None and os.path.isfile(locfn): + loc = sio.loadmat(locfn) + locctime = UTCDateTime(os.path.getmtime(locfn)) + locauthor = getOwner(locfn) + else: + loc = None + locctime = None + locauthor = None + pickcinfo = ope.CreationInfo(agency_id=authority_id, + author=phasauthor, + creation_time=phasctime) + loccinfo = ope.CreationInfo(agency_id=authority_id, + author=locauthor, + creation_time=locctime) + np = 0 + try: + eventNum = loc['ID'][0] + + # retrieve eventID for the actual database + idsplit = eventNum.split('.') + + # retrieve date information + julday = int(idsplit[1]) + year = int(idsplit[2]) + hour = int(loc['hh']) + minute = int(loc['mm']) + second = int(loc['ss']) + + if UTCDateTime(year=year+2000) < UTCDateTime.utcnow(): + year += 2000 + else: + year += 1900 + + eventDate = UTCDateTime(year=year, julday=julday, hour=hour, + minute=minute, second=second) + + stations = [stat for stat in phases['stat'][0:-1:3]] + + eventid = 'loc/' + eventNum + evresID = ope.ResourceIdentifier(id=eventid) + evresID.convertIDToQuakeMLURI(authority_id=authority_id) + event = ope.Event(resource_id=evresID) + event.creation_info = loccinfo + etype = ope.EventType('earthquake') + event.event_type = etype + for n, pick in enumerate(phases['Ptime']): + kwargs = {'year':int(pick[0]), + 'month':int(pick[1]), + 'day':int(pick[2]), + 'hour':int(pick[3]), + 'minute':int(pick[4]), + 'second':int(str(pick[5]).split('.')[0]), + 'microsecond':int(str(pick[5]).split('.')[1][0:6])} + spick = phases['Stime'][n] + if spick[0] > 0: + skwargs = {'year':int(spick[0]), + 'month':int(spick[1]), + 'day':int(spick[2]), + 'hour':int(spick[3]), + 'minute':int(spick[4]), + 'second':int(str(spick[5]).split('.')[0]), + 'microsecond':int(str(spick[5]).split('.')[1][0:6])} + spicktime = UTCDateTime(**skwargs) + else: + spicktime = None + ppicktime = UTCDateTime(**kwargs) + + for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: + if phase == 'P': + wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], 'z')]) + else: + wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], '[ne]')]) + pick, arrival, np = createArrival(np, picktime, eventNum, + stations[n], pickcinfo, phase, + wffn, authority_id) + event.picks.append(pick) + + origID = 'orig/' + genID + origresID = ResourceIdentifier(id=origID) + origresID.convertIDToQuakeMLURI(authority_id='BUG') + origin = Origin() + origin.resource_id = origresID + otime = self.location[eventid]['Location']['Origin time'] + origin.time = UTCDateTime(otime) + origin.creation_info = self.cinfo + HW = self.location[eventid]['Location']['Hochwert'] + RW = self.location[eventid]['Location']['Rechtswert'] + LAT, LON = coordTrans(HW, RW, tosys=GPS) + origin.latitude = LAT + origin.longitude = LON + origin.depth = 1000. + origin.depth_type = OriginDepthType('operator assigned') + origin.evaluation_mode = 'automatic' + origin.arrivals.append(arrival) + + amplID = 'corrampl/' + genID + amplresID = ResourceIdentifier(id=amplID) + amplresID.convertIDToQuakeMLURI(authority_id='BUG') + amplitude = Amplitude() + amplitude.resource_id = amplresID + amplitude.creation_info = self.cinfo + amp = self.data[eventid][phase]['Amplitude']*1e-9 + amplitude.generic_amplitude = amp + amplitude.unit = AmplitudeUnit('m/s') + amplitude.magnitude_hint = 'Ml' + amplitude.type = AmplitudeCategory('point') + amplitude.pick_id = pickresID + + magnID = 'corrmag/' + genID + magnresID = ResourceIdentifier(id=magnID) + magnresID.convertIDToQuakeMLURI(authority_id='BUG') + magnitude = Magnitude() + magnitude.resource_id = magnresID + magnitude.creation_info = self.cinfo + magnitude.origin_id = origresID + mag = self.location[eventid]['Ml'] + magnitude.mag = mag + magnitude.magnitude_type = 'Ml' + + event = Event(resource_id=evresID) + event.creation_info = self.cinfo + etype = EventType('earthquake') + event.event_type = etype + edesc = EventDescription(text='Prosper Haniel (induced)') + event.event_descriptions.append(edesc) + event.picks.append(pick) + event.origins.append(origin) + event.magnitudes.append(magnitude) + event.amplitudes.append(amplitude) + except AttributeError, e: + raise AttributeError('{0} - Matlab LOC file contain \ + insufficient data!'.format(e)) + + + diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index a6b5d183..4a856b7a 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -5,6 +5,8 @@ from pylot.core.util.errors import FormatError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor from pylot.core.util.utils import createEvent +from pylot.core.util.utils import getOwner +from pylot.core.util.utils import createArrival from pylot.core.util.widgets import PickDlg from pylot.core.util.widgets import HelpForm from pylot.core.util.widgets import FilterOptionsDialog diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py index 0b2a4780..e2157135 100644 --- a/pylot/core/util/errors.py +++ b/pylot/core/util/errors.py @@ -10,4 +10,4 @@ class OptionsError(Exception): pass class FormatError(Exception): - pass + pass \ No newline at end of file diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 9748d7eb..1ec04f60 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -2,8 +2,10 @@ # # -*- coding: utf-8 -*- +import os +import pwd import re -from obspy.core.event import * +import obspy.core.event as ope def fnConstructor(s): @@ -19,6 +21,34 @@ def fnConstructor(s): return fn def createEvent(origintime, latitude, longitude, depth, **kwargs): - evt = Event() + evt = ope.Event() + +def createArrival(picknum, picktime, eventnum, station, cinfo, phase, wfname, + authority_id): + pickID = 'pick/' + eventnum + '/' + station + '/{0:3d}'.format(picknum) + pickresID = ope.ResourceIdentifier(id=pickID) + pickresID.convertIDToQuakeMLURI(authority_id=authority_id) + pick = ope.Pick() + pick.resource_id = pickresID + pick.time = picktime + pick.creation_info = cinfo + pick.phase_hint = phase + pick.waveform_id = ope.ResourceIdentifier(id=wfname, prefix='file:/') + + arriID = 'arrival/' + eventnum + '/' + station + '/{0}'.format(phase) + arriresID = ope.ResourceIdentifier(id=arriID) + arriresID.convertIDToQuakeMLURI(authority_id=authority_id) + arrival = ope.Arrival() + arrival.resource_id = arriresID + arrival.creation_info = cinfo + arrival.pick_id = pickresID + arrival.phase = pick.phase_hint + azi = self.location[eventid]['Backazimuth'] - 180 + arrival.azimuth = azi if azi > -180 else azi + 360 + arrival.distance = self.location[eventid]['Distance']['deg'] + +def getOwner(fn): + return pwd.getpwuid(os.stat(fn).st_uid).pw_name + From c88ba18d2fe6dd2b47ef4583589a14f5602b426a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Feb 2015 13:24:55 +0100 Subject: [PATCH 0196/1144] new functions added for event creation purposes: getHash - returns a hash string from an UTCDateTime object createResourceID - returns a valid PyLoT resourceID for arbitrary types of event data createOrigin - returns an ObsPy Origin object (work in progress) createEvent - returns an ObsPy Event object (work in progress) createPick - returns an ObsPy Pick object (work in progress) createArrival - returns an ObsPy Arrival object (work in progress) --- pylot/core/util/utils.py | 131 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 122 insertions(+), 9 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 1ec04f60..69cea677 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -5,6 +5,8 @@ import os import pwd import re +import hashlib +from obspy.core import UTCDateTime import obspy.core.event as ope def fnConstructor(s): @@ -20,11 +22,94 @@ def fnConstructor(s): fn = '_' + fn return fn -def createEvent(origintime, latitude, longitude, depth, **kwargs): - evt = ope.Event() +def getHash(origintime): + ''' -def createArrival(picknum, picktime, eventnum, station, cinfo, phase, wfname, - authority_id): + :param origintime: + :return: + ''' + hg = hashlib.sha1() + hg.update(origintime.strftime('%Y-%m-%d %H:%M:%S.%f')) + return hg.hexdigest() + +def createResourceID(timetohash, restype, authority_id=None): + ''' + + :param timetohash: + :param restype: + :param authority_id: + :return: + ''' + assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ + "UTCDateTime object" + hid = getHash(timetohash) + resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) + if authority_id is not None: + resID.convertIDToQuakeMLURI(authority_id=authority_id) + + +def createOrigin(origintime, latitude, longitude, depth, resID=None, + authority_id=None): + ''' + createOrigin - function to create an ObsPy Origin + :param origintime: the origins time of occurence + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :param latitude: latitude in decimal degree of the origins location + :type latitude: float + :param longitude: longitude in decimal degree of the origins location + :type longitude: float + :param depth: hypocentral depth of the origin + :type depth: float + :return: An ObsPy :class: `~obspy.core.event.Origin` object + ''' + pass + + +def createEvent(origintime, cinfo, etype, resID=None, + authority_id=None, **kwargs): + ''' + createEvent - funtion to create an ObsPy Event + :param origintime: the events origintime + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :param cinfo: + :param etype: + :param resID: + :param authority_id: + :param kwargs: + :return: An ObsPy :class: `~obspy.core.event.Event` object + ''' + etype = ope.EventType(etype) + if resID is None: + resID = createResourceID(origintime, etype, authority_id) + event = ope.Event(resource_id=resID) + event.creation_info = cinfo + event.event_type = etype + return event + +def createPick(picknum, picktime, eventnum, cinfo, phase, station, wfseedstr, + authority_id): + ''' + createPick - function to create an ObsPy Pick + + :param picknum: number of the created pick + :type picknum: int + :param eventnum: human-readable event identifier + :type eventnum: str + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + :param station: name of the station at which the seismic phase has been + picked + :type station: str + :param wfseedstr: A SEED formatted string of the form + network.station.location.channel in order to set a referenced waveform + :type wfseedstr: str, SEED formatted + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + :return: An ObsPy :class: `~obspy.core.event.Pick` object + ''' pickID = 'pick/' + eventnum + '/' + station + '/{0:3d}'.format(picknum) pickresID = ope.ResourceIdentifier(id=pickID) pickresID.convertIDToQuakeMLURI(authority_id=authority_id) @@ -33,8 +118,33 @@ def createArrival(picknum, picktime, eventnum, station, cinfo, phase, wfname, pick.time = picktime pick.creation_info = cinfo pick.phase_hint = phase - pick.waveform_id = ope.ResourceIdentifier(id=wfname, prefix='file:/') + pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') + return pick +def createArrival(pickresID, eventnum, cinfo, phase, station, authority_id, + azimuth=None, dist=None): + ''' + createArrival - function to create an Obspy Arrival + :param pickresID: Resource identifier of the created pick + :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object + :param eventnum: human-readable event identifier + :type eventnum: str + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + :param station: name of the station at which the seismic phase has been + picked + :type station: str + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + :param azimuth: azimuth between source and receiver + :type azimuth: float or int, optional + :param dist: distance between source and receiver + :type dist: float or int, optional + :return: An ObsPy :class: `~obspy.core.event.Arrival` object + ''' arriID = 'arrival/' + eventnum + '/' + station + '/{0}'.format(phase) arriresID = ope.ResourceIdentifier(id=arriID) arriresID.convertIDToQuakeMLURI(authority_id=authority_id) @@ -42,10 +152,13 @@ def createArrival(picknum, picktime, eventnum, station, cinfo, phase, wfname, arrival.resource_id = arriresID arrival.creation_info = cinfo arrival.pick_id = pickresID - arrival.phase = pick.phase_hint - azi = self.location[eventid]['Backazimuth'] - 180 - arrival.azimuth = azi if azi > -180 else azi + 360 - arrival.distance = self.location[eventid]['Distance']['deg'] + arrival.phase = phase + if azimuth is not None: + arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360 + else: + arrival.azimuth = azimuth + arrival.distance = None + return arrival def getOwner(fn): return pwd.getpwuid(os.stat(fn).st_uid).pw_name From 46a20a10e687306848051f74c5753bbda397fb3d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Feb 2015 13:24:55 +0100 Subject: [PATCH 0197/1144] new functions added for event creation purposes: getHash - returns a hash string from an UTCDateTime object createResourceID - returns a valid PyLoT resourceID for arbitrary types of event data createOrigin - returns an ObsPy Origin object (work in progress) createEvent - returns an ObsPy Event object (work in progress) createPick - returns an ObsPy Pick object (work in progress) createArrival - returns an ObsPy Arrival object (work in progress) createMagnitude - returns an ObsPy Magnitude object (work in progress) createAmplitude - returns an ObsPy Amplitude object (work in progress) testing should be carried out as a next step --- pylot/core/read/io.py | 131 +++++++++-------------- pylot/core/util/__init__.py | 17 +-- pylot/core/util/utils.py | 205 +++++++++++++++++++++++++++++++++--- 3 files changed, 247 insertions(+), 106 deletions(-) diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index ff6ee866..3147ebe2 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -2,11 +2,14 @@ # -*- coding: utf-8 -*- import os -import glob + import scipy.io as sio import obspy.core.event as ope from obspy.core import UTCDateTime -from pylot.core.util import getOwner, createPick, createArrival, createEvent + +from pylot.core.util import getOwner, createPick, createArrival, createEvent, \ + createOrigin, createMagnitude + def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): """ @@ -61,7 +64,7 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): minute = int(loc['mm']) second = int(loc['ss']) - if UTCDateTime(year=year+2000) < UTCDateTime.utcnow(): + if UTCDateTime(year=year + 2000) < UTCDateTime.utcnow(): year += 2000 else: year += 1900 @@ -71,30 +74,31 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): stations = [stat for stat in phases['stat'][0:-1:3]] - eventid = 'loc/' + eventNum - evresID = ope.ResourceIdentifier(id=eventid) - evresID.convertIDToQuakeMLURI(authority_id=authority_id) - event = ope.Event(resource_id=evresID) - event.creation_info = loccinfo - etype = ope.EventType('earthquake') - event.event_type = etype + event = createEvent(eventDate, loccinfo, 'earthquake', eventNum, + authority_id) + + lat = float(loc['LAT']) + lon = float(loc['LON']) + dep = float(loc['DEP']) + + origin = createOrigin(eventDate, loccinfo, lat, lon, dep, eventNum) for n, pick in enumerate(phases['Ptime']): - kwargs = {'year':int(pick[0]), - 'month':int(pick[1]), - 'day':int(pick[2]), - 'hour':int(pick[3]), - 'minute':int(pick[4]), - 'second':int(str(pick[5]).split('.')[0]), - 'microsecond':int(str(pick[5]).split('.')[1][0:6])} + kwargs = {'year': int(pick[0]), + 'month': int(pick[1]), + 'day': int(pick[2]), + 'hour': int(pick[3]), + 'minute': int(pick[4]), + 'second': int(str(pick[5]).split('.')[0]), + 'microsecond': int(str(pick[5]).split('.')[1][0:6])} spick = phases['Stime'][n] if spick[0] > 0: - skwargs = {'year':int(spick[0]), - 'month':int(spick[1]), - 'day':int(spick[2]), - 'hour':int(spick[3]), - 'minute':int(spick[4]), - 'second':int(str(spick[5]).split('.')[0]), - 'microsecond':int(str(spick[5]).split('.')[1][0:6])} + skwargs = {'year': int(spick[0]), + 'month': int(spick[1]), + 'day': int(spick[2]), + 'hour': int(spick[3]), + 'minute': int(spick[4]), + 'second': int(str(spick[5]).split('.')[0]), + 'microsecond': int(str(spick[5]).split('.')[1][0:6])} spicktime = UTCDateTime(**skwargs) else: spicktime = None @@ -102,69 +106,34 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: if phase == 'P': - wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], 'z')]) + wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], + 'z')]) else: - wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], '[ne]')]) - pick, arrival, np = createArrival(np, picktime, eventNum, - stations[n], pickcinfo, phase, - wffn, authority_id) + wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], + '[ne]')]) + pick = createPick(eventDate, np, picktime, eventNum, pickcinfo, + phase, + stat[n], wffn, authority_id) event.picks.append(pick) + pickID = pick.get('resource_id') + arrival = createArrival(pickID, eventNum, pickcinfo, phase, + stat[n], authority_id) + origin.arrivals.append(arrival) + np += 1 - origID = 'orig/' + genID - origresID = ResourceIdentifier(id=origID) - origresID.convertIDToQuakeMLURI(authority_id='BUG') - origin = Origin() - origin.resource_id = origresID - otime = self.location[eventid]['Location']['Origin time'] - origin.time = UTCDateTime(otime) - origin.creation_info = self.cinfo - HW = self.location[eventid]['Location']['Hochwert'] - RW = self.location[eventid]['Location']['Rechtswert'] - LAT, LON = coordTrans(HW, RW, tosys=GPS) - origin.latitude = LAT - origin.longitude = LON - origin.depth = 1000. - origin.depth_type = OriginDepthType('operator assigned') - origin.evaluation_mode = 'automatic' - origin.arrivals.append(arrival) + magnitude = createMagnitude(origin.get('resource_id'), eventDate, + loccinfo, + authority_id) + magnitude.mag = float(loc['Mnet']) + magnitude.magnitude_type = 'Ml' - amplID = 'corrampl/' + genID - amplresID = ResourceIdentifier(id=amplID) - amplresID.convertIDToQuakeMLURI(authority_id='BUG') - amplitude = Amplitude() - amplitude.resource_id = amplresID - amplitude.creation_info = self.cinfo - amp = self.data[eventid][phase]['Amplitude']*1e-9 - amplitude.generic_amplitude = amp - amplitude.unit = AmplitudeUnit('m/s') - amplitude.magnitude_hint = 'Ml' - amplitude.type = AmplitudeCategory('point') - amplitude.pick_id = pickresID + event.picks.append(pick) + event.origins.append(origin) + event.magnitudes.append(magnitude) - magnID = 'corrmag/' + genID - magnresID = ResourceIdentifier(id=magnID) - magnresID.convertIDToQuakeMLURI(authority_id='BUG') - magnitude = Magnitude() - magnitude.resource_id = magnresID - magnitude.creation_info = self.cinfo - magnitude.origin_id = origresID - mag = self.location[eventid]['Ml'] - magnitude.mag = mag - magnitude.magnitude_type = 'Ml' - - event = Event(resource_id=evresID) - event.creation_info = self.cinfo - etype = EventType('earthquake') - event.event_type = etype - edesc = EventDescription(text='Prosper Haniel (induced)') - event.event_descriptions.append(edesc) - event.picks.append(pick) - event.origins.append(origin) - event.magnitudes.append(magnitude) - event.amplitudes.append(amplitude) except AttributeError, e: - raise AttributeError('{0} - Matlab LOC file contain \ - insufficient data!'.format(e)) + raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ + insufficient data!'.format(e, phasfn, locfn)) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 4a856b7a..88c0ec76 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,18 +1,11 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS -from pylot.core.util.errors import OptionsError -from pylot.core.util.errors import FormatError +from pylot.core.util.errors import OptionsError, FormatError from pylot.core.util.layouts import layoutStationButtons -from pylot.core.util.utils import fnConstructor -from pylot.core.util.utils import createEvent -from pylot.core.util.utils import getOwner -from pylot.core.util.utils import createArrival -from pylot.core.util.widgets import PickDlg -from pylot.core.util.widgets import HelpForm -from pylot.core.util.widgets import FilterOptionsDialog -from pylot.core.util.widgets import PropertiesDlg -from pylot.core.util.widgets import NewEventDlg -from pylot.core.util.widgets import MPLWidget +from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ + createPick, createOrigin, createMagnitude, getOwner, getHash +from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ + PropertiesDlg, NewEventDlg, MPLWidget from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 1ec04f60..583a0cbb 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -5,10 +5,12 @@ import os import pwd import re +import hashlib +from obspy.core import UTCDateTime import obspy.core.event as ope -def fnConstructor(s): +def fnConstructor(s): s = s.split('/')[-1] badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') @@ -20,21 +22,161 @@ def fnConstructor(s): fn = '_' + fn return fn -def createEvent(origintime, latitude, longitude, depth, **kwargs): - evt = ope.Event() -def createArrival(picknum, picktime, eventnum, station, cinfo, phase, wfname, - authority_id): - pickID = 'pick/' + eventnum + '/' + station + '/{0:3d}'.format(picknum) - pickresID = ope.ResourceIdentifier(id=pickID) - pickresID.convertIDToQuakeMLURI(authority_id=authority_id) +def getHash(time): + ''' + + :param time: time object for which a hash should be calculated + :type time: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :return: str + ''' + hg = hashlib.sha1() + hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) + return hg.hexdigest() + + +def createResourceID(timetohash, restype, authority_id=None, hrstr=None): + ''' + + :param timetohash: + :param restype: type of the resource, e.g. 'orig', 'earthquake' ... + :type restype: str + :param authority_id: name of the institution carrying out the processing + :type authority_id: str, optional + :return: + ''' + assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ + "UTCDateTime object" + hid = getHash(timetohash) + if hrstr is None: + resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) + else: + resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) + if authority_id is not None: + resID.convertIDToQuakeMLURI(authority_id=authority_id) + return resID + + +def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, + authority_id=None): + ''' + createOrigin - function to create an ObsPy Origin + :param origintime: the origins time of occurence + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :param latitude: latitude in decimal degree of the origins location + :type latitude: float + :param longitude: longitude in decimal degree of the origins location + :type longitude: float + :param depth: hypocentral depth of the origin + :type depth: float + :return: An ObsPy :class: `~obspy.core.event.Origin` object + ''' + if resID is None: + resID = createResourceID(origintime, 'orig', authority_id=authority_id) + elif isinstance(resID, str): + resID = createResourceID(origintime, 'orig', authority_id=authority_id, + hrstr=resID) + + origin = ope.Origin() + origin.resource_id = resID + origin.time = UTCDateTime(origintime) + origin.creation_info = cinfo + origin.latitude = latitude + origin.longitude = longitude + origin.depth = depth + return origin + + +def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): + ''' + createEvent - funtion to create an ObsPy Event + :param origintime: the events origintime + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param etype: Event type str object. converted via ObsPy to a valid event + type string. + :type etype: str + :param resID: Resource identifier of the created event + :type resID: :class: `~obspy.core.event.ResourceIdentifier` object + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + :return: An ObsPy :class: `~obspy.core.event.Event` object + ''' + etype = ope.EventType(etype) + if etype is None: + etype = ope.EventType('earthquake') # defaults to 'earthquake' + if resID is None: + resID = createResourceID(origintime, etype, authority_id) + elif isinstance(resID, str): + resID = createResourceID(origintime, etype, authority_id, resID) + event = ope.Event(resource_id=resID) + event.creation_info = cinfo + event.event_type = etype + return event + + +def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, + wfseedstr, + authority_id): + ''' + createPick - function to create an ObsPy Pick + + :param picknum: number of the created pick + :type picknum: int + :param eventnum: human-readable event identifier + :type eventnum: str + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + :param station: name of the station at which the seismic phase has been + picked + :type station: str + :param wfseedstr: A SEED formatted string of the form + network.station.location.channel in order to set a referenced waveform + :type wfseedstr: str, SEED formatted + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + :return: An ObsPy :class: `~obspy.core.event.Pick` object + ''' + pickID = eventnum + '_' + station + '/{0:3d}'.format(picknum) + pickresID = createResourceID(origintime, 'pick', authority_id, pickID) pick = ope.Pick() pick.resource_id = pickresID pick.time = picktime pick.creation_info = cinfo pick.phase_hint = phase - pick.waveform_id = ope.ResourceIdentifier(id=wfname, prefix='file:/') + pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') + return pick + +def createArrival(pickresID, eventnum, cinfo, phase, station, authority_id, + azimuth=None, dist=None): + ''' + createArrival - function to create an Obspy Arrival + :param pickresID: Resource identifier of the created pick + :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object + :param eventnum: human-readable event identifier + :type eventnum: str + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + :param station: name of the station at which the seismic phase has been + picked + :type station: str + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + :param azimuth: azimuth between source and receiver + :type azimuth: float or int, optional + :param dist: distance between source and receiver + :type dist: float or int, optional + :return: An ObsPy :class: `~obspy.core.event.Arrival` object + ''' arriID = 'arrival/' + eventnum + '/' + station + '/{0}'.format(phase) arriresID = ope.ResourceIdentifier(id=arriID) arriresID.convertIDToQuakeMLURI(authority_id=authority_id) @@ -42,10 +184,47 @@ def createArrival(picknum, picktime, eventnum, station, cinfo, phase, wfname, arrival.resource_id = arriresID arrival.creation_info = cinfo arrival.pick_id = pickresID - arrival.phase = pick.phase_hint - azi = self.location[eventid]['Backazimuth'] - 180 - arrival.azimuth = azi if azi > -180 else azi + 360 - arrival.distance = self.location[eventid]['Distance']['deg'] + arrival.phase = phase + if azimuth is not None: + arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360 + else: + arrival.azimuth = azimuth + arrival.distance = None + return arrival + + +def createMagnitude(originID, origintime, cinfo, authority_id=None): + ''' + createMagnitude - function to create an ObsPy Magnitude object + :param originID: + :param origintime: + :param cinfo: + :param authority_id: + :return: + ''' + magnresID = createResourceID(origintime, 'mag', authority_id) + magnitude = ope.Magnitude() + magnitude.resource_id = magnresID + magnitude.creation_info = cinfo + magnitude.origin_id = originID + return magnitude + + +def createAmplitude(): + pass + amplID = 'corrampl/' + genID + amplresID = ResourceIdentifier(id=amplID) + amplresID.convertIDToQuakeMLURI(authority_id='BUG') + amplitude = Amplitude() + amplitude.resource_id = amplresID + amplitude.creation_info = self.cinfo + amp = self.data[eventid][phase]['Amplitude'] * 1e-9 + amplitude.generic_amplitude = amp + amplitude.unit = AmplitudeUnit('m/s') + amplitude.magnitude_hint = 'Ml' + amplitude.type = AmplitudeCategory('point') + amplitude.pick_id = pickresID + def getOwner(fn): return pwd.getpwuid(os.stat(fn).st_uid).pw_name From bec1ee17164aba45e40e05670a372e9838d37a63 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Feb 2015 08:19:13 +0100 Subject: [PATCH 0198/1144] new function added: createAmplitude - routine for convenient `~obspy.core.event.Amplitude` object creation --- pylot/core/util/__init__.py | 3 ++- pylot/core/util/utils.py | 22 +++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 88c0ec76..e73d5455 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -3,7 +3,8 @@ from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError, FormatError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ - createPick, createOrigin, createMagnitude, getOwner, getHash + createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ + getHash from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ PropertiesDlg, NewEventDlg, MPLWidget from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 88eb6c64..90a43149 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -115,7 +115,6 @@ def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): event.creation_info = cinfo event.event_type = etype return event -def createPick(picknum, picktime, eventnum, cinfo, phase, station, wfseedstr, def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, @@ -211,20 +210,17 @@ def createMagnitude(originID, origintime, cinfo, authority_id=None): return magnitude -def createAmplitude(): - pass - amplID = 'corrampl/' + genID - amplresID = ResourceIdentifier(id=amplID) - amplresID.convertIDToQuakeMLURI(authority_id='BUG') - amplitude = Amplitude() +def createAmplitude(pickID, amp, unit, category, origintime, cinfo, + authority_id=None): + amplresID = createResourceID(origintime, 'ampl', authority_id) + amplitude = ope.Amplitude() amplitude.resource_id = amplresID - amplitude.creation_info = self.cinfo - amp = self.data[eventid][phase]['Amplitude'] * 1e-9 + amplitude.creation_info = cinfo amplitude.generic_amplitude = amp - amplitude.unit = AmplitudeUnit('m/s') - amplitude.magnitude_hint = 'Ml' - amplitude.type = AmplitudeCategory('point') - amplitude.pick_id = pickresID + amplitude.unit = ope.AmplitudeUnit(unit) + amplitude.type = ope.AmplitudeCategory(category) + amplitude.pick_id = pickID + return amplitude def getOwner(fn): From b07f1b5b78633ef433455626d5f9ad1eaa6c4cbd Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Feb 2015 13:11:12 +0100 Subject: [PATCH 0199/1144] picks.py deleted new classes are not needed; `~obspy.core.event.Pick` object used instead --- pylot/core/pick/picks.py | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 pylot/core/pick/picks.py diff --git a/pylot/core/pick/picks.py b/pylot/core/pick/picks.py deleted file mode 100644 index 8a303432..00000000 --- a/pylot/core/pick/picks.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Oct 2 18:40:55 2014 - -@author: sebastianw -""" - -from obspy.core.event import Pick - - -class ReferencePick(Pick): - - def __init__(self): - Pick.__init__() - - -class ConventionalPick(Pick): - - def __init__(self): - Pick.__init__() - - -class AutomaticPick(Pick): - - def __init__(self): - Pick.__init__() \ No newline at end of file From 903e87e1e170c8cc47abf1e8a9229c06c9e8d4ce Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:12:47 +0100 Subject: [PATCH 0200/1144] usage of DATASTRUCTURE modified --- QtPyLoT.py | 4 +++- pylot/core/util/structure.py | 3 ++- pylot/core/util/widgets.py | 18 ++++++------------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f349c2b5..d8a90102 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -44,6 +44,7 @@ from pylot.core.util import (FilterOptionsDialog, MPLWidget, PropertiesDlg, HelpForm) +from pylot.core.util.structure import DATASTRUCTURE # Version information @@ -64,7 +65,8 @@ class MainWindow(QMainWindow): settings.setValue("user/Login", os.getlogin()) settings.sync() self.recentEvents = settings.value("data/recentEvents", []) - self.dataStructure = settings.value("data/structure", None) + self.dataStructure = DATASTRUCTURE[ + settings.value("data/Structure", None)]() self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(QIcon(":/icon.ico")) self.seismicPhase = str(settings.value("phase", "P")) diff --git a/pylot/core/util/structure.py b/pylot/core/util/structure.py index 36e35b60..34c6aaaf 100644 --- a/pylot/core/util/structure.py +++ b/pylot/core/util/structure.py @@ -8,4 +8,5 @@ Created on Wed Jan 26 17:47:25 2015 from pylot.core.read import SeiscompDataStructure, PilotDataStructure -DATASTRUCTURE = {'PILOT':PilotDataStructure, 'SeisComP':SeiscompDataStructure} +DATASTRUCTURE = {'PILOT':PilotDataStructure, 'SeisComP':SeiscompDataStructure, + None:None} diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 76e25376..65c5406d 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -162,15 +162,15 @@ class InputsTab(PropTab): from pylot.core.util.structure import DATASTRUCTURE - datastruct = DATASTRUCTURE.keys() - self.structureSelect.addItems(datastruct) - self.updateWidget(DATASTRUCTURE) + self.structureSelect.addItems(DATASTRUCTURE.keys()) layout = QGridLayout() layout.addWidget(dataDirLabel, 0, 0) layout.addWidget(self.dataDirEdit, 0, 1) layout.addWidget(fullNameLabel, 1, 0) layout.addWidget(self.fullNameEdit, 1, 1) + layout.addWidget(structureLabel, 2, 0) + layout.addWidget(self.structureSelect, 2, 1) self.setLayout(layout) @@ -178,15 +178,9 @@ class InputsTab(PropTab): values = {} values["data/dataRoot"] = self.dataDirEdit.text() values["user/FullName"] = self.fullNameEdit.text() + values["data/Structure"] = self.structureSelect.currentText() return values - def updateWidget(self, structure): - key = self.structureSelect.currentText() - structure = structure[key] - structure().getFields().keys() - - - class OutputsTab(PropTab): @@ -351,7 +345,7 @@ class FilterOptionsDialog(QDialog): self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok| QDialogButtonBox.Cancel) - + grid = QGridLayout() grid.addWidget(self.freqGroupBox, 0, 2, 1, 2) grid.addLayout(self.selectTypeLayout, 1, 2, 1, 2) @@ -392,7 +386,7 @@ class FilterOptionsDialog(QDialog): freq.append(self.freqmaxSpinBox.value()) self.filterOptions.freq = freq self.filterOptions.order = self.orderSpinBox.value() - + def getFilterOptions(self): return self.filterOptions From 35e477c13ffd54a07588330b7eb12249f4292e92 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:14:17 +0100 Subject: [PATCH 0201/1144] by default the last opened event is loaded on restart --- QtPyLoT.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index d8a90102..5912786f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -85,7 +85,12 @@ class MainWindow(QMainWindow): self.filterOptionsS = FilterOptions(**filterOptionsS) # initialize data - self.data = Data() + if self.recentEvents: + lastEvent = self.getLastEvent() + self.data = Data(self, lastEvent) + else: + self.data = Data(self) + self.openWaveformData() self.dirty = False self.loadData() self.updateFilterOptions() From 1c40cb385214f1675b28b12235e76cac3ca30d56 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:15:48 +0100 Subject: [PATCH 0202/1144] bugfix: wrong call to a method --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 5912786f..07c79762 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -231,7 +231,7 @@ class MainWindow(QMainWindow): else: self.fileMenu.addAction(action) try: - current = self.data.evtdata.getID() + current = self.data.getID() except AttributeError: current = None recentEvents = [] From 7e3bcefd191b007be6af12c569397beeac8876f7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:16:20 +0100 Subject: [PATCH 0203/1144] only store the last 5 events --- QtPyLoT.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 07c79762..74bf627b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -239,6 +239,10 @@ class MainWindow(QMainWindow): fname = fnConstructor(eventID) if eventID != current and QFile.exists(fname): recentEvents.append(eventID) + recentEvents.reverse() + self.recentEvents = recentEvents[0:5] + settings = QSettings() + settings.setValue() if recentEvents: for i, eventID in enumerate(recentEvents): fname = fnConstructor(eventID) From 30503185e4490857f03717e1d4e9ab033af679bf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:17:18 +0100 Subject: [PATCH 0204/1144] initialize all attributes that might be used in startup methods (bugfix) --- QtPyLoT.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 74bf627b..03b63c36 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -52,12 +52,12 @@ __version__ = _getVersionString() class MainWindow(QMainWindow): - closing = Signal() def __init__(self, parent=None): super(MainWindow, self).__init__(parent) + self.dirty = False settings = QSettings() if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") @@ -65,6 +65,7 @@ class MainWindow(QMainWindow): settings.setValue("user/Login", os.getlogin()) settings.sync() self.recentEvents = settings.value("data/recentEvents", []) + self.fnames = None self.dataStructure = DATASTRUCTURE[ settings.value("data/Structure", None)]() self.setWindowTitle("PyLoT - do seismic processing the python way") From 81ce16174446479816f38341ba8f23b081400797 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:19:10 +0100 Subject: [PATCH 0205/1144] automatic reformatting of the code --- QtPyLoT.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 03b63c36..bb10c0d9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -266,12 +266,11 @@ class MainWindow(QMainWindow): action = self.sender() if isinstance(action, QAction): if action.data() is None: - filt = """Supported event formats (*.mat *.qml *.xml - *.kor *.evt)""" + filt = """Supported event formats (*.mat *.qml *.xml *.kor *.evt)""" caption = 'Select event to open' self.fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) + caption=caption, + filter=filt) else: self.fname = unicode(action.data().toString()) if not self.okToContinue(): @@ -295,9 +294,11 @@ class MainWindow(QMainWindow): else: if self.dataStructure: searchPath = self.dataStructure.expandDataPath() - self.fnames = QFileDialog.getOpenFileNames(self, - "Select waveform files:", - dir=searchPath) + fnames, = QFileDialog.getOpenFileNames(self, + "Select waveform files:", + dir=searchPath) + self.fnames = fnames + else: raise ValueError('dataStructure not specified') return self.fnames From 922cb6a8076f78612fd93dee21322ebfa789ff6e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:20:29 +0100 Subject: [PATCH 0206/1144] recursive call to openWaveFormData deleted --- QtPyLoT.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index bb10c0d9..7d78bda9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -340,16 +340,12 @@ class MainWindow(QMainWindow): return True def openWaveformData(self): - try: - if self.fnames and self.okToContinue(): - self.dirty = True - self.data.wfdata = self.data.setWFData(self.fnames) - elif self.fnames is None and self.okToContinue(): - self.data.setWFData(self.getWFFnames()) - except AttributeError, e: - print (e) - self.getWFFnames() - self.openWaveformData() + if self.fnames and self.okToContinue(): + self.dirty = True + self.data.setWFData(self.fnames) + elif self.fnames is None and self.okToContinue(): + self.data.setWFData(self.getWFFnames()) + self.plotData() def plotData(self): self.getData().plotData(self.getDataWidget()) From 2b5e8216baa1bc85485a282c92e65ed1ee1d0c58 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:23:01 +0100 Subject: [PATCH 0207/1144] new method the get the latest event that has been opened see also [7e3bcef] --- QtPyLoT.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 7d78bda9..279696ed 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -279,6 +279,9 @@ class MainWindow(QMainWindow): self.fname = fname self.data = Data(evtdata=self.fname) + def getLastEvent(self): + return self.recentEvents[0] + def getWFFnames(self): try: evt = self.getData().getEvtData() From 3508d00c286a2e5b976ae37575235e902f95307b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:24:27 +0100 Subject: [PATCH 0208/1144] catch case where no filename for the actual opened event is defined --- QtPyLoT.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 279696ed..5ef00fb2 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -319,6 +319,9 @@ class MainWindow(QMainWindow): self.data.exportEvent(self.fname, exform) except FormatError: return False + except AttributeError: + fname, = QFileDialog.getSaveFileName(self, 'Save event') + self.data.exportEvent(fname, exform) return True def getComponent(self): From 6a59bee73d1882b1765486fe3b071d4f19101096 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:28:58 +0100 Subject: [PATCH 0209/1144] the call to the parents method makes no sense - deleted; typo corrected --- pylot/core/read/data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 54cfe5c6..0600c7f5 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -30,7 +30,6 @@ class Data(object): def __init__(self, parent=None, evtdata=None): try: if parent: - self.setWFData(parent.getWFFnames()) self.comp = parent.getComponent() else: self.comp = 'Z' @@ -169,7 +168,7 @@ class GenericDataStructure(object): structExpression.append(tlexp) depth += 1 if depth is folderdepth: - rootExpression = stexp + rootExpression = structexp break structExpression.reverse() From 47f0fcb03007c77640cb8aee630b9d18f37d028b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:29:32 +0100 Subject: [PATCH 0210/1144] corrected wrong logic of the routine --- pylot/core/read/data.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 0600c7f5..80ad69ac 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -85,11 +85,11 @@ class Data(object): raise FormatError(errmsg) if fnout is None: - ID = self.evtdata.getEventID() - else: ID = self.getID() - # handle forbidden filenames especially on windows systems - fnout = fnConstructor(ID) + # handle forbidden filenames especially on windows systems + fnout = fnConstructor(ID) + else: + fnout = fnConstructor(fnout) evtformat = evtformat.upper().strip() From e66e0701a406a7d8323eba56749551581aaa449c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:30:45 +0100 Subject: [PATCH 0211/1144] catching a problem with sloppy formatted GSE data --- pylot/core/read/data.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 80ad69ac..875cc7ac 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -134,8 +134,11 @@ class Data(object): self.getWFData().filter(**kwargs) def setWFData(self, fnames): - for fname in fnames: - self.wfdata += read(fname) + for fname in fnames[0]: + try: + self.wfdata += read(fname) + except TypeError: + self.wfdata += read(fname, format='GSE2') def appenWFData(self, fnames): for fname in fnames: From cdd24e23fbd91be20b4953038a75e47296100bd1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:31:20 +0100 Subject: [PATCH 0212/1144] added missing method for GenericDataStructure --- pylot/core/read/data.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 875cc7ac..a7c8ebed 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -187,6 +187,9 @@ class GenericDataStructure(object): def getFields(self): return self.__gdsFields + def expandDataPath(self): + return os.path.join(*self.getFields().values()) + class PilotDataStructure(object): ''' Object containing the data access information for the old PILOT data From 1b86d3bfa450dc497e50c439d3e1d05d0bc1d049 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:32:00 +0100 Subject: [PATCH 0213/1144] nothing changed --- pylot/core/read/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index a7c8ebed..40b7396b 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -100,7 +100,7 @@ class Data(object): try: cat.write(fnout + evtformat.lower(), format=evtformat) except KeyError, e: - raise KeyError('''{0} export format + raise KeyError('''{0} export format not implemented: {1}'''.format(evtformat, e)) def plotData(self, widget): From c35eea45882212f44b7337dbaee4d94af390cf0f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:35:00 +0100 Subject: [PATCH 0214/1144] several bugfixes for the read routine for PILOT event data (testing not yet completed, see also tickets #145 and #146) --- pylot/core/read/io.py | 45 ++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index 3147ebe2..10a390ba 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -64,7 +64,7 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): minute = int(loc['mm']) second = int(loc['ss']) - if UTCDateTime(year=year + 2000) < UTCDateTime.utcnow(): + if year + 2000 < UTCDateTime.utcnow().year: year += 2000 else: year += 1900 @@ -83,13 +83,14 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): origin = createOrigin(eventDate, loccinfo, lat, lon, dep, eventNum) for n, pick in enumerate(phases['Ptime']): - kwargs = {'year': int(pick[0]), - 'month': int(pick[1]), - 'day': int(pick[2]), - 'hour': int(pick[3]), - 'minute': int(pick[4]), - 'second': int(str(pick[5]).split('.')[0]), - 'microsecond': int(str(pick[5]).split('.')[1][0:6])} + if pick[0] > 0: + kwargs = {'year': int(pick[0]), + 'month': int(pick[1]), + 'day': int(pick[2]), + 'hour': int(pick[3]), + 'minute': int(pick[4]), + 'second': int(str(pick[5]).split('.')[0]), + 'microsecond': int(str(pick[5]).split('.')[1][0:6])} spick = phases['Stime'][n] if spick[0] > 0: skwargs = {'year': int(spick[0]), @@ -105,24 +106,24 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): ppicktime = UTCDateTime(**kwargs) for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: - if phase == 'P': - wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], - 'z')]) - else: - wffn = os.path.join([sdir, '{0}*{1}*'.format(stations[n], - '[ne]')]) + if picktime is not None: + if phase == 'P': + wffn = os.path.join(sdir, '{0}*{1}*'.format( + stations[n].strip(), 'z')) + else: + wffn = os.path.join(sdir, '{0}*{1}*'.format( + stations[n].strip(), '[ne]')) + print wffn pick = createPick(eventDate, np, picktime, eventNum, pickcinfo, - phase, - stat[n], wffn, authority_id) + phase, stations[n], wffn, authority_id) event.picks.append(pick) - pickID = pick.get('resource_id') - arrival = createArrival(pickID, eventNum, pickcinfo, phase, - stat[n], authority_id) + pickID = pick.get('id') + arrival = createArrival(eventDate, pickID, eventNum, pickcinfo, + phase, stations[n], authority_id) origin.arrivals.append(arrival) np += 1 - magnitude = createMagnitude(origin.get('resource_id'), eventDate, - loccinfo, + magnitude = createMagnitude(origin.get('id'), eventDate, loccinfo, authority_id) magnitude.mag = float(loc['Mnet']) magnitude.magnitude_type = 'Ml' @@ -130,10 +131,10 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): event.picks.append(pick) event.origins.append(origin) event.magnitudes.append(magnitude) + return event except AttributeError, e: raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ insufficient data!'.format(e, phasfn, locfn)) - From 759697add80f5f3ffcfb4b317206157196150238 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:36:09 +0100 Subject: [PATCH 0215/1144] problem with the creation of a valid resource identifier fixed (still problematic, see #145) --- pylot/core/util/utils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 90a43149..b69347f3 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -153,8 +153,8 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, return pick -def createArrival(pickresID, eventnum, cinfo, phase, station, authority_id, - azimuth=None, dist=None): +def createArrival(origintime, pickresID, eventnum, cinfo, phase, station, + authority_id, azimuth=None, dist=None): ''' createArrival - function to create an Obspy Arrival :param pickresID: Resource identifier of the created pick @@ -177,9 +177,7 @@ def createArrival(pickresID, eventnum, cinfo, phase, station, authority_id, :type dist: float or int, optional :return: An ObsPy :class: `~obspy.core.event.Arrival` object ''' - arriID = 'arrival/' + eventnum + '/' + station + '/{0}'.format(phase) - arriresID = ope.ResourceIdentifier(id=arriID) - arriresID.convertIDToQuakeMLURI(authority_id=authority_id) + arriresID = createResourceID(origintime, 'arrival', authority_id, eventnum) arrival = ope.Arrival() arrival.resource_id = arriresID arrival.creation_info = cinfo From d32b4015082e83dadac7565a7cee837adf970696 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Feb 2015 11:36:44 +0100 Subject: [PATCH 0216/1144] changed due to testing --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 05b25a55..d62b53d4 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -3667-dirty +b07f-dirty From be0bf20382f0c7ccd9917585806bd948d46babd5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 07:01:41 +0100 Subject: [PATCH 0217/1144] cross-correlation analysis --- pylot/core/analysis/__init__.py | 0 pylot/core/analysis/correlation.py | 100 +++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 pylot/core/analysis/__init__.py create mode 100644 pylot/core/analysis/correlation.py diff --git a/pylot/core/analysis/__init__.py b/pylot/core/analysis/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pylot/core/analysis/correlation.py b/pylot/core/analysis/correlation.py new file mode 100644 index 00000000..ef0a6e9e --- /dev/null +++ b/pylot/core/analysis/correlation.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import numpy as np + + +def crosscorrsingle(wf1, wf2, taumax): + ''' + + :param Wx: + :param Wy: + :param taumax: + :return: + ''' + N = len(wf1) + c = np.zeros(2 * taumax - 1) + l = np.zeros(2 * taumax - 1) + for tau in range(taumax): + Cxyplus = 0 + Cxyminus = 0 + for n in range(N - tau): + Cxy1plus = wf1[n] * wf2[n + tau] + Cxy1minus = wf1[n + tau] * wf2[n] + Cxyplus = Cxyplus + Cxy1plus + Cxyminus = Cxyminus + Cxy1minus + + c[(taumax - 1) - tau] = Cxyminus + c[(taumax - 1) + tau] = Cxyplus + l[(taumax - 1) - tau] = -tau + l[(taumax - 1) + tau] = tau + return c, l + + +def crosscorrnormcalc(weights, wfs): + ''' + crosscorrnormcalc - function that calculates the normalization for the + cross correlation carried out by 'wfscrosscorr' + :param weights: weighting factors for the single components + :type weights: tuple + :param wfs: tuple of `~numpy.array` object containing waveform data + :type wfs: tuple + :return: a floating point number yielding the by 'weights' weighted energy + of the waveforms in 'wfs' + :rtype: float + ''' + + # check if the parameters are of the right type + if not isinstance(weights, tuple): + raise TypeError("type of 'weight' should be 'tuple', but is {0}".format( + type(weights))) + if not isinstance(wfs, tuple): + raise TypeError( + "type of parameter 'wfs' should be 'tuple', but is {0}".format( + type(wfs))) + sqrsumwfs = 0. + for n, wf in enumerate(wfs): + sqrsumwf = np.sum(weights[n] ** 2. * wf ** 2.) + sqrsumwfs += sqrsumwf + return np.sqrt(sqrsumwfs) + + +def wfscrosscorr(weights, wfs, taumax): + ''' + wfscrosscorr - function that calculates successive cross-correlations from a set of waveforms stored in a matrix + + base formula is: + C(i)=SUM[p=1:nComponent](eP(p)*(SUM[n=1:N]APp(x,n)*APp(y,n+i)))/(SQRT(SUM[p=1:nComponent]eP(p)^2*(SUM[n=1:N](APp(x,n)^2)))*SQRT(SUM[p=1:nComponent]eP(p)^2*(SUM[n=1:N]APp(y,n)^2))) + whereas + nComponent is the number of components + N is the number of samples + i is the lag-index + + input: + APp rowvectors containing the waveforms of each component p for which the cross-correlation is calculated + tPp rowvectros containing times + eP vector containing the weighting factors for the components (maxsize = [1x3]) + + output: + C cross-correlation function + L lag-vector + + author(s): + + SWB 26.01.2010 as arranged with Thomas Meier and Monika Bischoff + + :param weights: + :param wfs: + :param taumax: + :return: + ''' + + ccnorm = 0. + ccnorm = crosscorrnormcalc(weights, wfs[0]) + ccnorm *= crosscorrnormcalc(weights, wfs[1]) + + c = 0. + for n in range(len(wfs)): + cc, l = crosscorrsingle(wfs[0][n], wfs[1][n], taumax) + c += cc + return c / ccnorm, l From 0e73f21bda0f3358fae845d8aa5b649b07a744d8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 10:24:17 +0100 Subject: [PATCH 0218/1144] imports restructured and optimized --- QtPyLoT.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 5ef00fb2..29cc2490 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -26,27 +26,20 @@ https://www.iconfinder.com/iconsets/flavour import os import sys -from PySide.QtCore import * -from PySide.QtGui import * -from obspy.core import (UTCDateTime) +from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, QFileInfo +from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ + QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ + QDialog, QErrorMessage, QApplication +from obspy.core import UTCDateTime -from pylot.core.util import _getVersionString -from pylot.core.read import (Data, - FilterOptions) -from pylot.core.util import FILTERDEFAULTS -from pylot.core.util import fnConstructor -from pylot.core.util import checkurl -from pylot.core.util import FormatError -from pylot.core.util import layoutStationButtons -from pylot.core.util import (FilterOptionsDialog, - NewEventDlg, - createEvent, - MPLWidget, - PropertiesDlg, - HelpForm) +from pylot.core.read import Data, FilterOptions +from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ + checkurl, FormatError, layoutStationButtons, FilterOptionsDialog, \ + NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm from pylot.core.util.structure import DATASTRUCTURE + # Version information __version__ = _getVersionString() From 61c136fec3feab2f784c4206f59d650aab6ac51c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 10:25:51 +0100 Subject: [PATCH 0219/1144] fixed a startup problem: a figure was opened without parent object (MainWindow) and led to crash before the MainApplication opened properly --- QtPyLoT.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 29cc2490..3ad2917f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -78,13 +78,18 @@ class MainWindow(QMainWindow): self.filterOptionsP = FilterOptions(**filterOptionsP) self.filterOptionsS = FilterOptions(**filterOptionsS) - # initialize data + # UI has to be set up before(!) children widgets are + self.setupUi() + + # initialize event data if self.recentEvents: lastEvent = self.getLastEvent() self.data = Data(self, lastEvent) else: self.data = Data(self) - self.openWaveformData() + + # load and display waveform data + self.loadWaveformData() self.dirty = False self.loadData() self.updateFilterOptions() @@ -94,7 +99,6 @@ class MainWindow(QMainWindow): except: self.startTime = UTCDateTime() - self.setupUi() def setupUi(self): self.setWindowIcon(QIcon(":/icon.ico")) From d477467b02da68972528b1aba52325618560a821 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 10:27:32 +0100 Subject: [PATCH 0220/1144] method renamed to avoid misunderstanding --- QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 3ad2917f..160c5b7e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -136,7 +136,7 @@ class MainWindow(QMainWindow): QKeySequence.Save, saveIcon, "Save actual event data.") openWFDataAction = self.createAction("Open &waveforms ...", - self.openWaveformData, + self.loadWaveformData, "Ctrl+W", QIcon(":/wfIcon.png"), """Open waveform data (event will be closed).""") @@ -342,7 +342,7 @@ class MainWindow(QMainWindow): return self.saveData() return True - def openWaveformData(self): + def loadWaveformData(self): if self.fnames and self.okToContinue(): self.dirty = True self.data.setWFData(self.fnames) From 2b8c60a3d4452cce025d39cb48ab3bae757e973e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 10:28:28 +0100 Subject: [PATCH 0221/1144] now only Signal is imported from PySide avoiding overhead by importing SIGNAL additionally --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 160c5b7e..1b682b6f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -249,7 +249,7 @@ class MainWindow(QMainWindow): QFileInfo(fname).fileName()), self) action.setData(fname) - self.connect(action, SIGNAL("triggered()"), + self.connect(action, Signal("triggered()"), self.loadData) self.fileMenu.addAction(action) self.fileMenu.addSeparator() From fa6db084b8b847d52746a508f0af8cd8d7b02e01 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 10:30:35 +0100 Subject: [PATCH 0222/1144] Data object restructured for convenience; fixed problems in the class logic; restructured imports --- QtPyLoT.py | 14 +++--- pylot/core/read/__init__.py | 10 ++-- pylot/core/read/data.py | 99 ++++++++++++++++++++----------------- 3 files changed, 65 insertions(+), 58 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1b682b6f..718b38b3 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -258,23 +258,25 @@ class MainWindow(QMainWindow): def loadData(self, fname=None): if fname is None: try: - self.data = Data(evtdata=self.fname) + self.data = Data(self, evtdata=self.fname) except AttributeError: action = self.sender() if isinstance(action, QAction): if action.data() is None: - filt = """Supported event formats (*.mat *.qml *.xml *.kor *.evt)""" + filt = "Supported event formats (*.mat *.qml *.xml " \ + "*.kor *.evt)" caption = 'Select event to open' - self.fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) + fname, = QFileDialog().getOpenFileName(self, + caption=caption, + filter=filt) + self.fname = fname else: self.fname = unicode(action.data().toString()) if not self.okToContinue(): return else: self.fname = fname - self.data = Data(evtdata=self.fname) + self.data = Data(self, evtdata=self.fname) def getLastEvent(self): return self.recentEvents[0] diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index bafce7f4..1f7ca5ff 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -1,8 +1,6 @@ -from pylot.core.read.inputs import AutoPickParameter -from pylot.core.read.inputs import FilterOptions -from pylot.core.read.data import GenericDataStructure -from pylot.core.read.data import SeiscompDataStructure -from pylot.core.read.data import PilotDataStructure -from pylot.core.read.data import Data +from pylot.core.read.inputs import AutoPickParameter, FilterOptions +from pylot.core.read.io import readPILOTEvent +from pylot.core.read.data import GenericDataStructure, SeiscompDataStructure, \ + PilotDataStructure, Data diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 40b7396b..164fd39b 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -2,13 +2,15 @@ # -*- coding: utf-8 -*- import os + import numpy as np -from PySide.QtGui import QMessageBox from obspy.core import (read, Stream, UTCDateTime) from obspy import readEvents from obspy.core.event import (Event, Catalog) -from pylot.core.util import fnConstructor -from pylot.core.util.errors import FormatError + +from pylot.core.read import readPILOTEvent + +from pylot.core.util import fnConstructor, createEvent, FormatError class Data(object): @@ -28,26 +30,10 @@ class Data(object): ''' def __init__(self, parent=None, evtdata=None): - try: - if parent: - self.comp = parent.getComponent() - else: - self.comp = 'Z' - except IOError, e: - msg = 'An I/O error occured while loading data!' - inform = 'Variable wfdata will be empty.' - details = '{0}'.format(e) - if parent is not None: - warnio = QMessageBox(parent=parent) - warnio.setText(msg) - warnio.setDetailedText(details) - warnio.setInformativeText(inform) - warnio.setStandarButtons(QMessageBox.Ok) - warnio.setIcon(QMessageBox.Warning) - else: - print msg, "\n", details - self.wfdata = Stream() + if parent: + self.comp = parent.getComponent() else: + self.comp = 'Z' self.wfdata = Stream() self.newevent = False if evtdata is not None and isinstance(evtdata, Event): @@ -56,26 +42,32 @@ class Data(object): cat = readEvents(evtdata) self.evtdata = cat[0] elif evtdata is not None: - cat = self.readPILOTEvent(**evtdata) + cat = readPILOTEvent(**evtdata) else: # create an empty Event object self.newevent = True self.evtdata = Event() - self.orig = self.wfdata.copy() + self.wforiginal = None + self.cuttimes = None + self.dirty = False + + def isNew(self): + return self.newevent + + def getCutTimes(self): + if self.cuttimes is None: + self.updateCutTimes() + return self.cuttimes + + def updateCutTimes(self): min_start = UTCDateTime() max_end = None - for trace in self.getWFData().select(component = self.getComp()): + for trace in self.getWFData().select(component=self.getComp()): if trace.stats.starttime < min_start: min_start = trace.stats.starttime if max_end is None or trace.stats.endtime > max_end: max_end = trace.stats.endtime self.cuttimes = [min_start, max_end] - def isNew(self): - return self.newevent - - def readMatPhases(self, fname): - pass - def exportEvent(self, fnout=None, evtformat='QUAKEML'): from pylot.core.util.defaults import OUTPUTFORMATS @@ -95,7 +87,7 @@ class Data(object): # establish catalog object (event object has no write method) cat = Catalog() - cat.append(self.event) + cat.append(self.getEvtData()) # try exporting event via ObsPy try: cat.write(fnout + evtformat.lower(), format=evtformat) @@ -104,23 +96,22 @@ class Data(object): not implemented: {1}'''.format(evtformat, e)) def plotData(self, widget): - wfst = self.getWFData().select(component = self.getComp()) + wfst = self.getWFData().select(component=self.getComp()) for n, trace in enumerate(wfst): - stime = trace.stats.starttime - self.cuttimes[0] - etime = trace.stats.endtime - self.cuttimes[1] + stime = trace.stats.starttime - self.getCutTimes()[0] + etime = trace.stats.endtime - self.getCutTimes()[1] srate = trace.stats.sampling_rate nsamp = len(trace.data) tincr = trace.stats.delta time_ax = np.arange(stime, nsamp / srate, tincr) trace.normalize() widget.axes.plot(time_ax, trace.data + n, 'k') - xlabel = 'seconds since {0}'.format(self.cuttimes[0]) + xlabel = 'seconds since {0}'.format(self.getCutTimes()[0]) ylabel = '' - zne_text = {'Z':'vertical', 'N':'north-south', 'E':'east-west'} + zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'} title = 'overview: {0} components'.format(zne_text[self.getComp()]) widget.updateWidget(xlabel, ylabel, title) - def getComp(self): return self.comp @@ -132,21 +123,35 @@ class Data(object): def filter(self, kwargs): self.getWFData().filter(**kwargs) + self.dirty = True def setWFData(self, fnames): - for fname in fnames[0]: + self.wfdata = Stream() + self.wforiginal = None + if fnames is not None: + self.appendWFData(fnames) + self.orig = self.getWFData().copy() + self.dirty = False + + def appendWFData(self, fnames): + if self.dirty is not False: + self.resetWFData() + for fname in fnames: try: self.wfdata += read(fname) except TypeError: self.wfdata += read(fname, format='GSE2') - def appenWFData(self, fnames): - for fname in fnames: - self.wfdata += read(fname) - def getWFData(self): return self.wfdata + def getOriginalWFData(self): + return self.wforiginal + + def resetWFData(self): + self.wfdata = self.getOriginalWFData().copy() + self.dirty = False + def getEvtData(self): return self.evtdata @@ -176,7 +181,7 @@ class GenericDataStructure(object): structExpression.reverse() self.folderDepth = folderdepth - self.__gdsFields = {'ROOT':rootExpression} + self.__gdsFields = {'ROOT': rootExpression} self.modifyFields(**kwargs) def modifyFields(self, **kwargs): @@ -190,6 +195,7 @@ class GenericDataStructure(object): def expandDataPath(self): return os.path.join(*self.getFields().values()) + class PilotDataStructure(object): ''' Object containing the data access information for the old PILOT data @@ -201,8 +207,8 @@ class PilotDataStructure(object): **kwargs): self.dataType = dataformat self.__pdsFields = {'ROOT': root, - 'DATABASE': database, - 'SUFFIX': fsuffix + 'DATABASE': database, + 'SUFFIX': fsuffix } self.modifiyFields(**kwargs) @@ -240,6 +246,7 @@ class PilotDataStructure(object): "*{0}".format(self.getFields()['SUFFIX'])) return datapath + class SeiscompDataStructure(object): ''' Dictionary containing the data access information for an SDS data archive: From 8acd84976eaaa598e7219175ca9a1bc29b63c64f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 10:31:25 +0100 Subject: [PATCH 0223/1144] optimization of the codes outer appearance --- QtPyLoT.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 718b38b3..202460ca 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -110,10 +110,8 @@ class MainWindow(QMainWindow): plottitle = "Overview: {0} components ".format(self.getComponent()) - # create central matplotlib figure widget - self.DataPlot = MPLWidget(parent=self, - xlabel=xlab, - ylabel=None, + # create central matplotlib figure canvas widget + self.DataPlot = MPLWidget(parent=self, xlabel=xlab, ylabel=None, title=plottitle) statsButtons = layoutStationButtons(self.getData(), self.getComponent()) _layout.addLayout(statsButtons) @@ -297,7 +295,8 @@ class MainWindow(QMainWindow): if self.dataStructure: searchPath = self.dataStructure.expandDataPath() fnames, = QFileDialog.getOpenFileNames(self, - "Select waveform files:", + "Select waveform " + "files:", dir=searchPath) self.fnames = fnames From 091ab23b90b5a03c9b8f6c8c49902eac5c1ff7ef Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 16 Feb 2015 10:32:19 +0100 Subject: [PATCH 0224/1144] version string changed for testing reasons --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index d62b53d4..d97c6890 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -b07f-dirty +0.0.0-gbe0b From d15441900edf4707533c81beb6449eae42aad5a0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 17 Feb 2015 13:17:01 +0100 Subject: [PATCH 0225/1144] bugfixes in order to make the GUI work --- QtPyLoT.py | 31 ++++++++++++++++--------------- pylot/core/read/data.py | 3 +-- pylot/core/util/__init__.py | 2 +- pylot/core/util/errors.py | 5 ++++- pylot/core/util/utils.py | 3 +-- pylot/core/util/widgets.py | 4 +--- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 202460ca..bc973612 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -35,7 +35,8 @@ from obspy.core import UTCDateTime from pylot.core.read import Data, FilterOptions from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ checkurl, FormatError, layoutStationButtons, FilterOptionsDialog, \ - NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm + NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ + DatastructureError from pylot.core.util.structure import DATASTRUCTURE @@ -93,14 +94,17 @@ class MainWindow(QMainWindow): self.dirty = False self.loadData() self.updateFilterOptions() + + + + def setupUi(self): + try: self.startTime = min( [tr.stats.starttime for tr in self.data.wfdata]) except: self.startTime = UTCDateTime() - - def setupUi(self): self.setWindowIcon(QIcon(":/icon.ico")) xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') @@ -113,8 +117,7 @@ class MainWindow(QMainWindow): # create central matplotlib figure canvas widget self.DataPlot = MPLWidget(parent=self, xlabel=xlab, ylabel=None, title=plottitle) - statsButtons = layoutStationButtons(self.getData(), self.getComponent()) - _layout.addLayout(statsButtons) + _layout.addWidget(self.DataPlot) openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) @@ -147,7 +150,7 @@ class MainWindow(QMainWindow): QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") - filterAction = self.createAction("&Filter ...", self.filterData, + filterAction = self.createAction("&Filter ...", self.filterWaveformData, "Ctrl+F", QIcon(":/filter.png"), """Toggle un-/filtered waveforms to be displayed, according to the @@ -194,9 +197,6 @@ class MainWindow(QMainWindow): status.addPermanentWidget(self.eventLabel) status.showMessage("Ready", 500) - statsButtons = layoutStationButtons(self.getData(), self.getComponent()) - _layout.addLayout(statsButtons) - _layout.addWidget(self.DataPlot) _widget.setLayout(_layout) self.setCentralWidget(_widget) @@ -294,16 +294,17 @@ class MainWindow(QMainWindow): else: if self.dataStructure: searchPath = self.dataStructure.expandDataPath() - fnames, = QFileDialog.getOpenFileNames(self, + fnames = QFileDialog.getOpenFileNames(self, "Select waveform " "files:", dir=searchPath) self.fnames = fnames else: - raise ValueError('dataStructure not specified') + raise DatastructureError('not specified') return self.fnames - except ValueError: + except DatastructureError, e: + print e props = PropertiesDlg(self) if props.exec_() == QDialog.Accepted: return self.getWFFnames() @@ -349,12 +350,12 @@ class MainWindow(QMainWindow): self.data.setWFData(self.fnames) elif self.fnames is None and self.okToContinue(): self.data.setWFData(self.getWFFnames()) - self.plotData() + self.plotWaveformData() - def plotData(self): + def plotWaveformData(self): self.getData().plotData(self.getDataWidget()) - def filterData(self): + def filterWaveformData(self): if self.getData(): kwargs = {} freq = self.getFilterOptions().getFreq() diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 164fd39b..e6048ee8 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -242,8 +242,7 @@ class PilotDataStructure(object): def expandDataPath(self): datapath = os.path.join(self.getFields()['ROOT'], - self.getFields()['DATABASE'], - "*{0}".format(self.getFields()['SUFFIX'])) + self.getFields()['DATABASE']) return datapath diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index e73d5455..0d9575d0 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,6 +1,6 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS -from pylot.core.util.errors import OptionsError, FormatError +from pylot.core.util.errors import OptionsError, FormatError, DatastructureError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py index e2157135..6ae3b8fe 100644 --- a/pylot/core/util/errors.py +++ b/pylot/core/util/errors.py @@ -10,4 +10,7 @@ class OptionsError(Exception): pass class FormatError(Exception): - pass \ No newline at end of file + pass + +class DatastructureError(Exception): + pass diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index b69347f3..805bef25 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -118,8 +118,7 @@ def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, - wfseedstr, - authority_id): + wfseedstr, authority_id): ''' createPick - function to create an ObsPy Pick diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 65c5406d..601c6d6e 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -110,9 +110,7 @@ class PropertiesDlg(QDialog): def accept(self, *args, **kwargs): self.apply() self.destroy() - - def reject(self, *args, **kwargs): - self.destroy() + return self.accepted def apply(self): for widint in range(self.tabWidget.count()): From 5c9f17bce39ce25255fc6f22cb9816ee1968fdfa Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:27:50 +0100 Subject: [PATCH 0226/1144] bugfix: attribute was of wrong kind for which the successive call raised an exception --- QtPyLoT.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index bc973612..1c23fa7a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -357,16 +357,23 @@ class MainWindow(QMainWindow): def filterWaveformData(self): if self.getData(): + def hasfreq(kwargs): + for key in kwargs.keys(): + if not key.startswith('freq'): + return True + return False kwargs = {} freq = self.getFilterOptions().getFreq() - if len(freq) > 1: + if freq is not None and len(freq) > 1: kwargs['freqmin'] = freq[0] kwargs['freqmax'] = freq[1] - else: + elif freq is not None and len(freq) == 1: kwargs['freq'] = freq - kwargs['type'] = self.getFilterOptions().getFilterType() - kwargs['corners'] = self.filteroptions.getOrder() - self.getData().filter(kwargs) + + if hasfreq(kwargs): + kwargs['type'] = self.getFilterOptions().getFilterType() + kwargs['corners'] = self.getFilterOptions().getOrder() + self.getData().filter(kwargs) def adjustFilterOptions(self): filteroptions = None From 82ac85d743a19244968b044c956b1c6fdb3b8678 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:30:24 +0100 Subject: [PATCH 0227/1144] bugfix: return value of QFileDialog method calls must be handled carefully --- QtPyLoT.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1c23fa7a..35e036a2 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -298,7 +298,10 @@ class MainWindow(QMainWindow): "Select waveform " "files:", dir=searchPath) - self.fnames = fnames + if isinstance(fnames[0], list): + self.fnames = fnames[0] + else: + self.fnames = fnames else: raise DatastructureError('not specified') @@ -319,7 +322,7 @@ class MainWindow(QMainWindow): except FormatError: return False except AttributeError: - fname, = QFileDialog.getSaveFileName(self, 'Save event') + fname = QFileDialog.getSaveFileName(self, 'Save event') self.data.exportEvent(fname, exform) return True From 1878b887f67de12d653faa736255ac161edf8b0c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:31:35 +0100 Subject: [PATCH 0228/1144] give information on files which could not be read and thus not be attended to the waveform container --- pylot/core/read/data.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index e6048ee8..7b8a3f94 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -134,13 +134,28 @@ class Data(object): self.dirty = False def appendWFData(self, fnames): + if self.dirty is not False: self.resetWFData() + + assert isinstance(fnames, list), "input parameter 'fnames' is " \ + "supposed to be of type 'list' " \ + "but is actually".format(type( + fnames)) + + warnmsg = '' for fname in fnames: try: self.wfdata += read(fname) except TypeError: - self.wfdata += read(fname, format='GSE2') + try: + self.wfdata += read(fname, format='GSE2') + except Exception: + warnmsg += '{0}\n'.format(fname) + if warnmsg: + warnmsg = 'WARNING: unable to read\n' + warnmsg + print warnmsg + def getWFData(self): return self.wfdata From dd360bd9cc4cffa2fed7707a08aa7ab1b73ca09d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:32:01 +0100 Subject: [PATCH 0229/1144] code clean up --- pylot/core/read/data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 7b8a3f94..3cdc5d67 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -366,6 +366,5 @@ class SeiscompDataStructure(object): self.getFields()['NET'], self.getFields()['STA'], fullChan, - '*{0}'.format(self.getFields()['DAY']) - ) + '*{0}'.format(self.getFields()['DAY'])) return dataPath From 693362a278f6d5f43a86d5db27470a27f8d5b714 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:32:37 +0100 Subject: [PATCH 0230/1144] normalize waveform data to avoid overlapping --- pylot/core/read/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 3cdc5d67..52b9d9fe 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -104,7 +104,7 @@ class Data(object): nsamp = len(trace.data) tincr = trace.stats.delta time_ax = np.arange(stime, nsamp / srate, tincr) - trace.normalize() + trace.normalize(trace.data.max() * 2) widget.axes.plot(time_ax, trace.data + n, 'k') xlabel = 'seconds since {0}'.format(self.getCutTimes()[0]) ylabel = '' From abbe9cb2c43631b9b0e56c1eecd9b72480d2eae8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:34:21 +0100 Subject: [PATCH 0231/1144] bugfix: missed to call get method before trying to fetch an item --- pylot/core/util/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 601c6d6e..03c0abab 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -307,7 +307,7 @@ class FilterOptionsDialog(QDialog): self.freqminSpinBox.setRange(5e-7, 1e6) self.freqminSpinBox.setDecimals(2) self.freqminSpinBox.setSuffix(' Hz') - self.freqminSpinBox.setValue(self.getFilterOptions().getFreq[0]) + self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0]) self.freqmaxLabel = QLabel() self.freqmaxLabel.setText("maximum:") self.freqmaxSpinBox = QDoubleSpinBox() @@ -315,7 +315,7 @@ class FilterOptionsDialog(QDialog): self.freqmaxSpinBox.setDecimals(2) self.freqmaxSpinBox.setSuffix(' Hz') if self.filterOptions.filterType in ['bandpass', 'bandstop']: - self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq[1]) + self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) typeOptions = ["bandpass", "bandstop", "lowpass", "highpass"] From cdb8af56b3871f7feb8dfaca49ccb7b9782a16d6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:35:12 +0100 Subject: [PATCH 0232/1144] bugfix: avoid empty parts of the data display --- pylot/core/util/widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 03c0abab..e6905c11 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -52,6 +52,7 @@ class MPLWidget(FigureCanvas): self.figure = Figure() self.canvas = FigureCanvas(self.figure) self.axes = self.figure.add_subplot(111) + self.axes.autoscale(tight=True) self.updateWidget(xlabel, ylabel, title) From 757a6d784bcf04614e0adf1147705de4ae137cf9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Feb 2015 15:35:54 +0100 Subject: [PATCH 0233/1144] better use call to the classes method then returning a value --- pylot/core/util/widgets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e6905c11..9c417772 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -110,8 +110,7 @@ class PropertiesDlg(QDialog): def accept(self, *args, **kwargs): self.apply() - self.destroy() - return self.accepted + QDialog.accept(self) def apply(self): for widint in range(self.tabWidget.count()): From 5496b2deaa210b565f62c8ddc013b3982f3da274 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 20 Feb 2015 08:28:24 +0100 Subject: [PATCH 0234/1144] bugfix: use get and set methods instead of directly changes values of attributes --- pylot/core/util/widgets.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9c417772..a070b9c8 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -314,7 +314,7 @@ class FilterOptionsDialog(QDialog): self.freqmaxSpinBox.setRange(5e-7, 1e6) self.freqmaxSpinBox.setDecimals(2) self.freqmaxSpinBox.setSuffix(' Hz') - if self.filterOptions.filterType in ['bandpass', 'bandstop']: + if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) typeOptions = ["bandpass", "bandstop", "lowpass", "highpass"] @@ -369,10 +369,10 @@ class FilterOptionsDialog(QDialog): self.freqmaxLabel.setEnabled(True) self.freqmaxSpinBox.setEnabled(True) - self.filterOptions.filterType = self.selectTypeCombo.currentText() + self.getFilterOptions().setFilterType(self.selectTypeCombo.currentText()) freq = [] freq.append(self.freqminSpinBox.value()) - if self.filterOptions.filterType in ['bandpass', 'bandstop']: + if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: if self.freqminSpinBox.value() > self.freqmaxSpinBox.value(): QMessageBox.warning(self, "Value error", "Maximum frequency must be at least the " @@ -382,8 +382,8 @@ class FilterOptionsDialog(QDialog): self.freqmaxSpinBox.setFocus() return freq.append(self.freqmaxSpinBox.value()) - self.filterOptions.freq = freq - self.filterOptions.order = self.orderSpinBox.value() + self.getFilterOptions().setFreq(freq) + self.getFilterOptions().setOrder(self.orderSpinBox.value()) def getFilterOptions(self): return self.filterOptions From 35c184ede7e64a1394f6aa8c466269211a6c719b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 20 Feb 2015 08:30:17 +0100 Subject: [PATCH 0235/1144] bugfix: in order to be able to check for checkable these should be available in the namespace, thus these actions have been set as attributes of the MainWindow object --- QtPyLoT.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 35e036a2..b1721828 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -150,7 +150,7 @@ class MainWindow(QMainWindow): QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") - filterAction = self.createAction("&Filter ...", self.filterWaveformData, + self.filterAction = self.createAction("&Filter ...", self.filterWaveformData, "Ctrl+F", QIcon(":/filter.png"), """Toggle un-/filtered waveforms to be displayed, according to the @@ -159,10 +159,10 @@ class MainWindow(QMainWindow): self.adjustFilterOptions, "Alt+F", QIcon(None), """Adjust filter parameters.""") - selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", + self.selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", QIcon(":/picon.png"), "Toggle P phase.", True) - selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", + self.selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", QIcon(":/sicon.png"), "Toggle S phase", True) printAction = self.createAction("&Print event ...", @@ -182,8 +182,9 @@ class MainWindow(QMainWindow): self.updateFileMenu() self.editMenu = self.menuBar().addMenu('&Edit') - editActions = (filterAction, filterEditAction, None, selectPAction, - selectSAction, None, printAction) + editActions = (self.filterAction, filterEditAction, None, + self.selectPAction, self.selectSAction, None, + printAction) self.addMenuActions(self.editMenu, editActions) self.helpMenu = self.menuBar().addMenu('&Help') From 7dd1519f838bb665c9fe057813c1583a12d8dc19 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 20 Feb 2015 08:35:26 +0100 Subject: [PATCH 0236/1144] use similar method names for similar types of methods (e.g. xWFData for all methods connected to waveform data) --- QtPyLoT.py | 4 +++- pylot/core/read/data.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index b1721828..58fd58b7 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -357,7 +357,7 @@ class MainWindow(QMainWindow): self.plotWaveformData() def plotWaveformData(self): - self.getData().plotData(self.getDataWidget()) + self.getData().plotWFData(self.getDataWidget()) def filterWaveformData(self): if self.getData(): @@ -378,6 +378,8 @@ class MainWindow(QMainWindow): kwargs['type'] = self.getFilterOptions().getFilterType() kwargs['corners'] = self.getFilterOptions().getOrder() self.getData().filter(kwargs) + self.getData().filterWFData(kwargs) + self.getData().resetWFData() def adjustFilterOptions(self): filteroptions = None diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 52b9d9fe..95b33700 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -95,7 +95,7 @@ class Data(object): raise KeyError('''{0} export format not implemented: {1}'''.format(evtformat, e)) - def plotData(self, widget): + def plotWFData(self, widget): wfst = self.getWFData().select(component=self.getComp()) for n, trace in enumerate(wfst): stime = trace.stats.starttime - self.getCutTimes()[0] @@ -121,7 +121,7 @@ class Data(object): except: return 'smi:bug/pylot/1234' - def filter(self, kwargs): + def filterWFData(self, kwargs): self.getWFData().filter(**kwargs) self.dirty = True From b10d8606cc8cf43008fa5ec47bdf2b356cc1b5e5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 20 Feb 2015 08:36:20 +0100 Subject: [PATCH 0237/1144] at least print error messages to screen (helps debugging ^^) --- QtPyLoT.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 58fd58b7..4efdcff4 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -322,8 +322,10 @@ class MainWindow(QMainWindow): self.data.exportEvent(self.fname, exform) except FormatError: return False - except AttributeError: + except AttributeError, e: + print 'warning: {0}'.format(e) fname = QFileDialog.getSaveFileName(self, 'Save event') + fname = fname[0] self.data.exportEvent(fname, exform) return True From 4344a7c8a686e0bde3f384f4abee484619ca910e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 20 Feb 2015 08:37:21 +0100 Subject: [PATCH 0238/1144] unnecessary code overhead; return type is clear anyway (different return type for PyQt!!!) --- QtPyLoT.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 4efdcff4..adda0de4 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -299,10 +299,7 @@ class MainWindow(QMainWindow): "Select waveform " "files:", dir=searchPath) - if isinstance(fnames[0], list): - self.fnames = fnames[0] - else: - self.fnames = fnames + self.fnames = fnames[0] else: raise DatastructureError('not specified') From 4184fcffdc9391542e271cfad8c3927b435fcf23 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 20 Feb 2015 08:38:26 +0100 Subject: [PATCH 0239/1144] make waveform filtering work (in progress) --- QtPyLoT.py | 27 +++++++++++++++------------ pylot/core/read/data.py | 12 ++++-------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index adda0de4..bedca1c7 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -365,20 +365,21 @@ class MainWindow(QMainWindow): if not key.startswith('freq'): return True return False - kwargs = {} - freq = self.getFilterOptions().getFreq() - if freq is not None and len(freq) > 1: - kwargs['freqmin'] = freq[0] - kwargs['freqmax'] = freq[1] - elif freq is not None and len(freq) == 1: - kwargs['freq'] = freq - - if hasfreq(kwargs): - kwargs['type'] = self.getFilterOptions().getFilterType() - kwargs['corners'] = self.getFilterOptions().getOrder() - self.getData().filter(kwargs) + if self.filterAction.isChecked(): + kwargs = {} + freq = self.getFilterOptions().getFreq() + if freq is not None and len(freq) > 1: + kwargs['freqmin'] = freq[0] + kwargs['freqmax'] = freq[1] + elif freq is not None and len(freq) == 1: + kwargs['freq'] = freq + if hasfreq(kwargs): + kwargs['type'] = self.getFilterOptions().getFilterType() + kwargs['corners'] = self.getFilterOptions().getOrder() self.getData().filterWFData(kwargs) + else: self.getData().resetWFData() + self.plotWaveformData() def adjustFilterOptions(self): filteroptions = None @@ -412,6 +413,8 @@ class MainWindow(QMainWindow): emsg.showMessage('Error: {0}'.format(e)) else: self.updateStatus('Filter loaded ...') + if self.filterAction.isChecked(): + self.filterWaveformData() def getSeismicPhase(self): return self.seismicPhase diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 95b33700..1af5a0ea 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -130,18 +130,15 @@ class Data(object): self.wforiginal = None if fnames is not None: self.appendWFData(fnames) - self.orig = self.getWFData().copy() + self.wforiginal = self.getWFData().copy() self.dirty = False def appendWFData(self, fnames): - - if self.dirty is not False: - self.resetWFData() - assert isinstance(fnames, list), "input parameter 'fnames' is " \ "supposed to be of type 'list' " \ - "but is actually".format(type( - fnames)) + "but is actually".format(type(fnames)) + if self.dirty: + self.resetWFData() warnmsg = '' for fname in fnames: @@ -156,7 +153,6 @@ class Data(object): warnmsg = 'WARNING: unable to read\n' + warnmsg print warnmsg - def getWFData(self): return self.wfdata From cc002c946017cf6943eb6524dc5f3fafd4b32f83 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Feb 2015 14:35:00 +0100 Subject: [PATCH 0240/1144] bugfix: wrong return type expectation corrected --- QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index bedca1c7..707f772b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -265,10 +265,10 @@ class MainWindow(QMainWindow): filt = "Supported event formats (*.mat *.qml *.xml " \ "*.kor *.evt)" caption = 'Select event to open' - fname, = QFileDialog().getOpenFileName(self, + fname = QFileDialog().getOpenFileName(self, caption=caption, filter=filt) - self.fname = fname + self.fname = fname[0] else: self.fname = unicode(action.data().toString()) if not self.okToContinue(): From d4d464d400c865db869c4097bdb56c5c109149b6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Feb 2015 14:38:26 +0100 Subject: [PATCH 0241/1144] generalized method addActions in order to be more flexible in adding actions to different QtObject types --- QtPyLoT.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 707f772b..da3bbaa9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -335,12 +335,12 @@ class MainWindow(QMainWindow): def getDataWidget(self): return self.DataPlot - def addMenuActions(self, menu, actions): + def addActions(self, target, actions): for action in actions: if action is None: - menu.addSeparator() + target.addSeparator() else: - menu.addAction(action) + target.addAction(action) def okToContinue(self): if self.dirty: From cb114dc696bfdba9ad818d7866e686777a1fd966 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Feb 2015 14:45:47 +0100 Subject: [PATCH 0242/1144] bugfix: displayed status message has not been updated properly --- QtPyLoT.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index da3bbaa9..a7388d67 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -436,10 +436,8 @@ class MainWindow(QMainWindow): else: self.setWindowTitle( "PyLoT - seismic processing the python way[*]") - self.setWindowTitle("PyLoT - seismic processing the python way[*]") self.setWindowModified(self.dirty) - self.statusBar().showMessage(message, 5000) def printEvent(self): pass From d9b685eebb20b4494eedd30af3c6f67258457ad4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Feb 2015 14:46:18 +0100 Subject: [PATCH 0243/1144] display more detailed status messages --- QtPyLoT.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a7388d67..b5918fe9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -412,7 +412,8 @@ class MainWindow(QMainWindow): emsg = QErrorMessage(self) emsg.showMessage('Error: {0}'.format(e)) else: - self.updateStatus('Filter loaded ...') + self.updateStatus('Filter loaded ... ' + '[{0}: {1} Hz]'.format(self.getFilterOptions().getFilterType(), self.getFilterOptions().getFreq())) if self.filterAction.isChecked(): self.filterWaveformData() @@ -424,6 +425,8 @@ class MainWindow(QMainWindow): def setSeismicPhase(self, phase): self.seismicPhase = self.seismicPhaseButtonGroup.getValue() + self.updateStatus('Seismic phase changed to ' + '{0}'.format(self.getSeismicPhase())) def updateStatus(self, message): self.statusBar().showMessage(message, 5000) From c5f9842c37d92c162514d554a5f6578f1bd9eb68 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Feb 2015 14:47:38 +0100 Subject: [PATCH 0244/1144] added a toolbar (work in progress) --- QtPyLoT.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index b5918fe9..4b86514a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -185,11 +185,21 @@ class MainWindow(QMainWindow): editActions = (self.filterAction, filterEditAction, None, self.selectPAction, self.selectSAction, None, printAction) - self.addMenuActions(self.editMenu, editActions) + self.addActions(self.editMenu, editActions) self.helpMenu = self.menuBar().addMenu('&Help') helpActions = (helpAction, ) - self.addMenuActions(self.helpMenu, helpActions) + self.addActions(self.helpMenu, helpActions) + + fileToolBar = self.addToolBar("FileTools") + fileToolActions = (newEventAction, openEventAction, saveEventAction) + fileToolBar.setObjectName("FileTools") + self.addActions(fileToolBar, fileToolActions) + + phaseToolBar = self.addToolBar("PhaseTools") + phaseToolActions = (self.selectPAction, self.selectSAction) + phaseToolBar.setObjectName("PhaseTools") + self.addActions(phaseToolBar, phaseToolActions) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) From 125728f2b1db5433612d8c1eef6d96af4eb25267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 23 Feb 2015 15:04:39 +0100 Subject: [PATCH 0245/1144] Closes Ticket #143, uses now None instead of -1 --- pylot/core/pick/Picker.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index a7c3d154..d489ea8f 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -16,7 +16,9 @@ autoregressive prediction: application ot local and regional distances, Geophys. """ import numpy as np import matplotlib.pyplot as plt -from pylot.core.pick.CharFuns import CharacteristicFunction +from CharFuns import * +#from pylot.core.pick.CharFuns import CharacteristicFunction +import pdb class AutoPicking(object): ''' @@ -52,7 +54,6 @@ class AutoPicking(object): ''' assert isinstance(cf, CharacteristicFunction), "%s is not a CharacteristicFunction object" % str(cf) - #wie kann man hier isinstance benutzen? self.cf = cf.getCF() self.Tcf = cf.getTimeArray() @@ -142,7 +143,7 @@ class AICPicker(AutoPicking): print 'Get onset time (pick) from AIC-CF ...' - self.Pick = -1 + self.Pick = None #taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) @@ -155,7 +156,7 @@ class AICPicker(AutoPicking): if aic[i - 1] >= aic[i]: self.Pick = self.Tcf[i] break - if self.Pick == -1: + if self.Pick == None: print 'AICPicker: Could not find minimum, picking window too short?' return self.Pick @@ -170,7 +171,7 @@ class PragPicker(AutoPicking): if self.getpick1() is not None: print 'Get onset time (pick) from HOS- or AR-CF using pragmatic picking algorithm ...' - self.Pick = -1 + self.Pick = None #smooth CF ismooth = int(round(self.Tsmooth / self.dt)) cfsmooth = np.zeros(len(self.cf)) @@ -238,7 +239,7 @@ class PragPicker(AutoPicking): self.Pick = pick_r else: - self.Pick = -1 + self.Pick = None print 'PragPicker: No initial onset time given! Check input!' return From acd8f70369a8d8d031ee0ba338e9ee49b5db4f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 23 Feb 2015 15:42:35 +0100 Subject: [PATCH 0246/1144] AR-CFs now have same sampling rate as raw seismograms, new attribute getXCF --- pylot/core/pick/CharFuns.py | 80 +++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index d52b7898..9332f828 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -17,12 +17,13 @@ autoregressive prediction: application ot local and regional distances, Geophys. """ import numpy as np from obspy.core import Stream +import pdb class CharacteristicFunction(object): ''' SuperClass for different types of characteristic functions. ''' - def __init__(self, data, cut, t2=None, order=None, t1=None, fnoise=0.001): + def __init__(self, data, cut, t2=None, order=None, t1=None, fnoise=None): ''' Initialize data type object with information from the original Seismogram. @@ -117,12 +118,12 @@ class CharacteristicFunction(object): def getTimeArray(self): if self.getTime1(): - incr = self.getARdetStep()[0] - self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] \ - + self.getTime1() + self.getTime2() + shift = self.getTime2() else: - incr = self.getIncrement() - self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] + shift = 0 + incr = self.getIncrement() + self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] \ + + shift return self.TimeArray def getFnoise(self): @@ -134,6 +135,9 @@ class CharacteristicFunction(object): def getCF(self): return self.cf + def getXCF(self): + return self.xcf + def getDataArray(self, cut=None): ''' If cut times are given, time series is cut from cut[0] (start time) @@ -226,9 +230,8 @@ class AICcf(CharacteristicFunction): cumsumcf = np.cumsum(np.power(xnp, 2)) i = np.where(cumsumcf == 0) cumsumcf[i] = np.finfo(np.float64).eps - cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * - np.log((cumsumcf[datlen - 1] - - cumsumcf[k - 1]) / (datlen - k + 1))) + cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * \ + np.log((cumsumcf[datlen - 1] - cumsumcf[k - 1]) / (datlen - k + 1))) cf[0] = cf[1] inf = np.isinf(cf) ff = np.where(inf == True) @@ -236,6 +239,7 @@ class AICcf(CharacteristicFunction): cf[ff] = 0 self.cf = cf - np.mean(cf) + self.xcf = xnp class HOScf(CharacteristicFunction): @@ -287,6 +291,7 @@ class HOScf(CharacteristicFunction): if len(nn) > 1: LTA[nn] = 0 self.cf = LTA + self.xcf = xnp class ARZcf(CharacteristicFunction): @@ -308,24 +313,24 @@ class ARZcf(CharacteristicFunction): ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] - cf = [] + cf = np.zeros(len(xnp)) loopstep = self.getARdetStep() - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, loopstep[1]): - #determination of AR coefficients - self.arDetZ(xnoise, self.getOrder(), i-ldet, i) + arcalci = ldet + self.getOrder() - 1 #AR-calculation index + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): + if i == arcalci: + #determination of AR coefficients + #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! + self.arDetZ(xnoise, self.getOrder(), i-ldet, i) + arcalci = arcalci + loopstep[1] #AR prediction of waveform using calculated AR coefficients self.arPredZ(xnp, self.arpara, i + 1, lpred) #prediction error = CF - err = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred] - xnp[i:i + lpred], 2)) / lpred) - cf.append(err) - - #convert list to numpy array - cf = np.asarray(cf) + cf[i] = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred] - xnp[i:i + lpred], 2)) / lpred) nn = np.isnan(cf) if len(nn) > 1: cf[nn] = 0 self.cf = cf - + self.xcf = xnp def arDetZ(self, data, order, rind, ldet): ''' @@ -430,23 +435,25 @@ class ARHcf(CharacteristicFunction): ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] - cf = [] + cf = np.zeros(tend - lpred + 1) loopstep = self.getARdetStep() - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, loopstep[1]): - self.arDetH(Xnoise, self.getOrder(), i-ldet, i) + arcalci = ldet + self.getOrder() - 1 #AR-calculation index + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): + if i == arcalci: + #determination of AR coefficients + #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! + self.arDetH(Xnoise, self.getOrder(), i-ldet, i) + arcalci = arcalci + loopstep[1] #AR prediction of waveform using calculated AR coefficients self.arPredH(xnp, self.arpara, i + 1, lpred) #prediction error = CF - err = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + cf[i] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2)) / (2 * lpred)) - cf.append(err) - - #convert list to numpy array - cf = np.asarray(cf) nn = np.isnan(cf) if len(nn) > 1: cf[nn] = 0 self.cf = cf + self.xcf = xnp def arDetH(self, data, order, rind, ldet): ''' @@ -560,24 +567,27 @@ class AR3Ccf(CharacteristicFunction): ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] - cf = [] + cf = np.zeros(tend - lpred + 1) loopstep = self.getARdetStep() - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1, loopstep[1]): - self.arDet3C(Xnoise, self.getOrder(), i-ldet, i) + arcalci = ldet + self.getOrder() - 1 #AR-calculation index + for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): + if i == arcalci: + #determination of AR coefficients + #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! + self.arDet3C(Xnoise, self.getOrder(), i-ldet, i) + arcalci = arcalci + loopstep[1] + #AR prediction of waveform using calculated AR coefficients self.arPred3C(xnp, self.arpara, i + 1, lpred) #prediction error = CF - err = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + cf[i] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2) \ + np.power(self.xpred[2][i:i + lpred] - xnp[2][i:i + lpred], 2)) / (3 * lpred)) - cf.append(err) - - #convert list to numpy array - cf = np.asarray(cf) nn = np.isnan(cf) if len(nn) > 1: cf[nn] = 0 self.cf = cf + self.xcf = xnp def arDet3C(self, data, order, rind, ldet): ''' From 3556a2becce2a329276e39b9ab686313d5566075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 24 Feb 2015 09:08:38 +0100 Subject: [PATCH 0247/1144] Changed index for AR-CF calculation, no more shift in getTimeArray needed. --- pylot/core/pick/CharFuns.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 9332f828..169a5940 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -18,6 +18,7 @@ autoregressive prediction: application ot local and regional distances, Geophys. import numpy as np from obspy.core import Stream import pdb +import matplotlib.pyplot as plt class CharacteristicFunction(object): ''' @@ -117,13 +118,8 @@ class CharacteristicFunction(object): return self.dt def getTimeArray(self): - if self.getTime1(): - shift = self.getTime2() - else: - shift = 0 incr = self.getIncrement() - self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] \ - + shift + self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] return self.TimeArray def getFnoise(self): @@ -316,7 +312,7 @@ class ARZcf(CharacteristicFunction): cf = np.zeros(len(xnp)) loopstep = self.getARdetStep() arcalci = ldet + self.getOrder() - 1 #AR-calculation index - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): + for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): if i == arcalci: #determination of AR coefficients #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! @@ -325,7 +321,7 @@ class ARZcf(CharacteristicFunction): #AR prediction of waveform using calculated AR coefficients self.arPredZ(xnp, self.arpara, i + 1, lpred) #prediction error = CF - cf[i] = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred] - xnp[i:i + lpred], 2)) / lpred) + cf[i + lpred] = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred] - xnp[i:i + lpred], 2)) / lpred) nn = np.isnan(cf) if len(nn) > 1: cf[nn] = 0 @@ -438,7 +434,7 @@ class ARHcf(CharacteristicFunction): cf = np.zeros(tend - lpred + 1) loopstep = self.getARdetStep() arcalci = ldet + self.getOrder() - 1 #AR-calculation index - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): + for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): if i == arcalci: #determination of AR coefficients #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! @@ -447,7 +443,7 @@ class ARHcf(CharacteristicFunction): #AR prediction of waveform using calculated AR coefficients self.arPredH(xnp, self.arpara, i + 1, lpred) #prediction error = CF - cf[i] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + cf[i + lpred] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2)) / (2 * lpred)) nn = np.isnan(cf) if len(nn) > 1: @@ -570,7 +566,7 @@ class AR3Ccf(CharacteristicFunction): cf = np.zeros(tend - lpred + 1) loopstep = self.getARdetStep() arcalci = ldet + self.getOrder() - 1 #AR-calculation index - for i in range(ldet + self.getOrder() - 1, tend - lpred + 1): + for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): if i == arcalci: #determination of AR coefficients #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! @@ -580,7 +576,7 @@ class AR3Ccf(CharacteristicFunction): #AR prediction of waveform using calculated AR coefficients self.arPred3C(xnp, self.arpara, i + 1, lpred) #prediction error = CF - cf[i] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + cf[i + lpred] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2) \ + np.power(self.xpred[2][i:i + lpred] - xnp[2][i:i + lpred], 2)) / (3 * lpred)) nn = np.isnan(cf) From 1966a2b6121de6699c8233be6650e0a2ebbcc0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 25 Feb 2015 09:56:23 +0100 Subject: [PATCH 0248/1144] Extended for applying new class EarlLatePicker and for plotting earliest and lates possible picks --- pylot/core/pick/run_makeCF.py | 95 ++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 56f7f228..41c499b3 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - Script to run autoPyLoT-script "makeCF.py". + Script to run PyLoT modules CharFuns.py and Picker.py. Only for test purposes! """ from obspy.core import read import matplotlib.pyplot as plt import numpy as np -from CharFuns import * -from Picker import * +from pylot.core.pick.CharFuns import CharacteristicFunction +from pylot.core.pick.Picker import AutoPicking import glob import argparse @@ -18,7 +18,7 @@ def run_makeCF(project, database, event, iplot, station=None): #parameters for CF calculation t2 = 7 #length of moving window for HOS calculation [sec] p = 4 #order of statistics - cuttimes = [10, 40] #start and end time for CF calculation + cuttimes = [10, 50] #start and end time for CF calculation bpz = [2, 30] #corner frequencies of bandpass filter, vertical component bph = [2, 15] #corner frequencies of bandpass filter, horizontal components tdetz= 1.2 #length of AR-determination window [sec], vertical component @@ -30,16 +30,16 @@ def run_makeCF(project, database, event, iplot, station=None): arhorder = 4 #chosen order of AR process, horizontal components #get waveform data if station: - dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHZ.msd' % (project, database, event, station) - dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHE.msd' % (project, database, event, station) - dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*EHN.msd' % (project, database, event, station) + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*HZ.msd' % (project, database, event, station) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*HE.msd' % (project, database, event, station) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*HN.msd' % (project, database, event, station) #dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_z.gse' % (project, database, event, station) #dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_e.gse' % (project, database, event, station) #dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_n.gse' % (project, database, event, station) else: - dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHZ.msd' % (project, database, event) - dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHE.msd' % (project, database, event) - dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*EHN.msd' % (project, database, event) + dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*HZ.msd' % (project, database, event) + dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*HE.msd' % (project, database, event) + dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*HN.msd' % (project, database, event) wfzfiles = glob.glob(dpz) wfefiles = glob.glob(dpe) wfnfiles = glob.glob(dpn) @@ -63,13 +63,15 @@ def run_makeCF(project, database, event, iplot, station=None): tr_aic = tr_filt.copy() tr_aic.data = hoscf.getCF() st_copy[0].data = tr_aic.data - aiccf = AICcf(st_copy, cuttimes, t2) #instance of AICcf + aiccf = AICcf(st_copy, cuttimes) #instance of AICcf ############################################################## #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicpick = AICPicker(aiccf, 2, 70, [1, 0.5, 0.2], 3) + aicpick = AICPicker(aiccf, None, [5, 0.5, 1], 3, 10, None, 0.1) ############################################################## #get refined onset time from HOS-CF using class Picker - hospick = PragPicker(hoscf, 2, 70, [1, 0.5, 0.2], 2, 0.001, 0.2, aicpick.getpick()) + hospick = PragPicker(hoscf, None, [5, 0.5, 1], 2, 10, 0.001, 0.2, aicpick.getpick()) + #get earliest and latest possible picks + hosELpick = EarlLatePicker(hoscf, 1.5, [5, 0.5, 1], None, 10, None, None, hospick.getpick()) ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction #get stream object of filtered data @@ -84,10 +86,12 @@ def run_makeCF(project, database, event, iplot, station=None): araiccf = AICcf(st_copy, cuttimes, tpredz, 0, tdetz) #instance of AICcf ############################################################## #get onset time from AIC-ARZ-CF using subclass AICPicker of class AutoPicking - aicarzpick = AICPicker(araiccf, 2, 70, [1, 0.5, 0.2], 2) + aicarzpick = AICPicker(araiccf, 1.5, [5, 0.5, 2], 2, 10, None, 0.1) ############################################################## #get refined onset time from ARZ-CF using class Picker - arzpick = PragPicker(arzcf, 2, 70, [1, 0.5, 0.2], 2, 0, 0.2, aicarzpick.getpick()) + arzpick = PragPicker(arzcf, 1.5, [5, 0.5, 2], 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) + #get earliest and latest possible picks + arzELpick = EarlLatePicker(arzcf, 1.5, [5, 0.5, 1], None, 10, None, None, arzpick.getpick()) elif not wfzfiles: print 'No vertical component data found!' @@ -109,6 +113,7 @@ def run_makeCF(project, database, event, iplot, station=None): trH2_filt.taper(max_percentage=0.05, type='hann') H_copy[0].data = trH1_filt.data H_copy[1].data = trH2_filt.data + ############################################################## #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf @@ -122,8 +127,13 @@ def run_makeCF(project, database, event, iplot, station=None): arhaiccf = AICcf(H_copy, cuttimes, tpredh, 0, tdeth) #instance of AICcf ############################################################## #get onset time from AIC-ARH-CF using subclass AICPicker of class AutoPicking - aicarhpick = AICPicker(arhaiccf, 2, 70, [1, 0.5, 0.2], 2) + aicarhpick = AICPicker(arhaiccf, 1.5, [5, 0.5, 2], 4, 10, None, 0.1) ############################################################### + #get refined onset time from ARH-CF using class Picker + arhpick = PragPicker(arhcf, 1.5, [5, 0.5, 2], 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) + #get earliest and latest possible picks + arhELpick = EarlLatePicker(arhcf, 1.5, [5, 0.5, 1], None, 10, None, None, arhpick.getpick()) + #create stream with 3 traces #merge streams AllC = read('%s' % wfefiles[i]) @@ -144,6 +154,8 @@ def run_makeCF(project, database, event, iplot, station=None): AllC[2].data = All3_filt.data #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf + #get earliest and latest possible pick from initial ARH-pick + ar3cELpick = EarlLatePicker(ar3ccf, 1.5, [5, 0.5, 1], None, 10, None, None, arhpick.getpick()) ############################################################## if iplot: #plot vertical trace @@ -158,16 +170,21 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'b--') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'b') plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'b') - plt.plot([hospick.getpick(), hospick.getpick()], [-1.3, 1.3], 'r--') + plt.plot([hospick.getpick(), hospick.getpick()], [-1.3, 1.3], 'r', linewidth=2) plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [1.3, 1.3], 'r') plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [-1.3, -1.3], 'r') - plt.plot([aicarzpick.getpick(), aicarzpick.getpick()], [-1.2, 1.2], 'y--') + plt.plot([hosELpick.getLpick(), hosELpick.getLpick()], [-1.1, 1.1], 'r--') + plt.plot([hosELpick.getEpick(), hosELpick.getEpick()], [-1.1, 1.1], 'r--') + plt.plot([aicarzpick.getpick(), aicarzpick.getpick()], [-1.2, 1.2], 'y', linewidth=2) plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [1.2, 1.2], 'y') plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [-1.2, -1.2], 'y') - plt.plot([arzpick.getpick(), arzpick.getpick()], [-1.4, 1.4], 'g--') + plt.plot([arzpick.getpick(), arzpick.getpick()], [-1.4, 1.4], 'g', linewidth=2) plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [1.4, 1.4], 'g') plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [-1.4, -1.4], 'g') + plt.plot([arzELpick.getLpick(), arzELpick.getLpick()], [-1.2, 1.2], 'g--') + plt.plot([arzELpick.getEpick(), arzELpick.getEpick()], [-1.2, 1.2], 'g--') plt.yticks([]) + plt.ylim([-1.5, 1.5]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') plt.title([tr.stats.station, tr.stats.channel]) @@ -183,45 +200,73 @@ def run_makeCF(project, database, event, iplot, station=None): p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') p22, = plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') p23, = plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) - plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') + plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') + plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') + plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') plt.yticks([]) + plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) plt.suptitle(trH1_filt.stats.starttime) plt.legend([p21, p22, p23], ['Data', 'ARH-CF', 'ARHAIC-CF']) plt.subplot(2,1,2) plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) - plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b--') + plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') + plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') + plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) plt.yticks([]) + plt.ylim([-1.5, 1.5]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') #plot 3-component window plt.figure(3) - tar3ccf = np.arange(0, len(ar3ccf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdetz +tpredz plt.subplot(3,1,1) p31, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p32, = plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + p32, = plt.plot(ar3ccf.getTimeArray(), ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') + plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') plt.yticks([]) plt.xticks([]) plt.ylabel('Normalized Counts') plt.title([tr.stats.station, tr.stats.channel]) + plt.suptitle(trH1_filt.stats.starttime) plt.legend([p31, p32], ['Data', 'AR3C-CF']) plt.subplot(3,1,2) plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot(ar3ccf.getTimeArray(), ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') + plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') plt.yticks([]) plt.xticks([]) plt.ylabel('Normalized Counts') plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) plt.subplot(3,1,3) plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - plt.plot(tar3ccf, ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot(ar3ccf.getTimeArray(), ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') + plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') plt.yticks([]) plt.ylabel('Normalized Counts') plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) From 4a48874f8847586612cdfe3884440b5c8e02c288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 25 Feb 2015 09:59:59 +0100 Subject: [PATCH 0249/1144] Debuged and cleaned source code --- pylot/core/pick/CharFuns.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 169a5940..fe53a85f 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -17,8 +17,6 @@ autoregressive prediction: application ot local and regional distances, Geophys. """ import numpy as np from obspy.core import Stream -import pdb -import matplotlib.pyplot as plt class CharacteristicFunction(object): ''' @@ -235,7 +233,7 @@ class AICcf(CharacteristicFunction): cf[ff] = 0 self.cf = cf - np.mean(cf) - self.xcf = xnp + self.xcf = x class HOScf(CharacteristicFunction): @@ -287,7 +285,7 @@ class HOScf(CharacteristicFunction): if len(nn) > 1: LTA[nn] = 0 self.cf = LTA - self.xcf = xnp + self.xcf = x class ARZcf(CharacteristicFunction): @@ -326,7 +324,7 @@ class ARZcf(CharacteristicFunction): if len(nn) > 1: cf[nn] = 0 self.cf = cf - self.xcf = xnp + self.xcf = x def arDetZ(self, data, order, rind, ldet): ''' @@ -431,7 +429,7 @@ class ARHcf(CharacteristicFunction): ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] - cf = np.zeros(tend - lpred + 1) + cf = np.zeros(len(xenoise)) loopstep = self.getARdetStep() arcalci = ldet + self.getOrder() - 1 #AR-calculation index for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): @@ -563,7 +561,7 @@ class AR3Ccf(CharacteristicFunction): ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] - cf = np.zeros(tend - lpred + 1) + cf = np.zeros(len(xenoise)) loopstep = self.getARdetStep() arcalci = ldet + self.getOrder() - 1 #AR-calculation index for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): From b953377c58e32c3af6bcc44094cae14df5c82985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 25 Feb 2015 10:07:16 +0100 Subject: [PATCH 0250/1144] Major changes: 1) Implemented new class EarlLatePicker for calculating earliest and lates possible pick from initial (most likely) onset, based on cook book for consistent phase picking by Diehl & Kissling 2) Modified AICPicker, uses now unsmoothed and smoothed CF for not sticking in some local minima 3) Implemented optional plotting of interims results --- pylot/core/pick/Picker.py | 363 ++++++++++++++++++++++++++++++++++---- 1 file changed, 325 insertions(+), 38 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index d489ea8f..275b86a8 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -Created Dec 2014 -Implementation of the picking algorithms published and described in: +Created Dec 2014 to Feb 2015 +Implementation of the automated picking algorithms published and described in: Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, 2010: Automated determination of P-phase arrival times at regional and local distances @@ -12,29 +12,28 @@ Working Group, 2012: Automated determination of S-phase arrival times using autoregressive prediction: application ot local and regional distances, Geophys. J. Int., 188, 687-702. +The picks with the above described algorithms are assumed to be the most likely picks. +For each most likely pick the corresponding earliest and latest possible picks are +calculated after Diehl & Kissling (2009). + :author: MAGS2 EP3 working group / Ludger Kueperkoch """ import numpy as np import matplotlib.pyplot as plt -from CharFuns import * -#from pylot.core.pick.CharFuns import CharacteristicFunction -import pdb +from pylot.core.pick.CharFuns import CharacteristicFunction class AutoPicking(object): ''' Superclass of different, automated picking algorithms applied on a CF determined using AIC, HOS, or AR prediction. ''' - def __init__(self, cf, Tslope, aerr, TSNR, PickWindow, aus=None, Tsmooth=None, Pick1=None): + def __init__(self, cf, nfac, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, Pick1=None): ''' :param: cf, characteristic function, on which the picking algorithm is applied :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object - :param: Tslope, length of time window after pick used to determine slope - for quality estimation [s] - :type: float - - :param: aerr (adjusted error), percentage of maximum of CF to determine slope for quality estimation + :param: nfac (noise factor), nfac times noise level to calculate latest possible pick + in EarlLatePick :type: int :param: TSNR, length of time windows around pick used to determine SNR [s] @@ -43,25 +42,31 @@ class AutoPicking(object): :param: PickWindow, length of pick window [s] :type: float + :param: iplot, no. of figure window for plotting interims results + :type: integer + :param: aus ("artificial uplift of samples"), find local minimum at i if aic(i-1)*(1+aus) >= aic(i) :type: float :param: Tsmooth, length of moving smoothing window to calculate smoothed CF [s] :type: float - :param: Pick1, initial (prelimenary) onset time, starting point for PragPicker + :param: Pick1, initial (prelimenary) onset time, starting point for PragPicker and + EarlLatePick :type: float + ''' assert isinstance(cf, CharacteristicFunction), "%s is not a CharacteristicFunction object" % str(cf) self.cf = cf.getCF() self.Tcf = cf.getTimeArray() + self.Data = cf.getXCF() self.dt = cf.getIncrement() - self.setTslope(Tslope) - self.setaerr(aerr) + self.setnfac(nfac) self.setTSNR(TSNR) self.setPickWindow(PickWindow) + self.setiplot(iplot) self.setaus(aus) self.setTsmooth(Tsmooth) self.setpick1(Pick1) @@ -69,33 +74,25 @@ class AutoPicking(object): def __str__(self): return '''\n\t{name} object:\n - TSlope:\t{Tslope}\n - aerr:\t{aerr}\n + nfac:\t{nfac}\n TSNR:\t\t\t{TSNR}\n PickWindow:\t{PickWindow}\n aus:\t{aus}\n Tsmooth:\t{Tsmooth}\n Pick1:\t{Pick1}\n '''.format(name=type(self).__name__, - Tslope=self.getTslope(), - aerr=self.getaerr(), + nfac=self.getnfac(), TSNR=self.getTSNR(), PickWindow=self.getPickWindow(), aus=self.getaus(), Tsmooth=self.getTsmooth(), Pick1=self.getpick1()) - def getTslope(self): - return self.Tslope - - def setTslope(self, Tslope): - self.Tslope = Tslope - - def getaerr(self): - return self.aerr + def getnfac(self): + return self.nfac - def setaerr(self, aerr): - self.aerr = aerr + def setnfac(self, nfac): + self.nfac = nfac def getTSNR(self): return self.TSNR @@ -124,6 +121,18 @@ class AutoPicking(object): def getpick(self): return self.Pick + def getLpick(self): + return self.LPick + + def getEpick(self): + return self.EPick + + def getiplot(self): + return self.iplot + + def setiplot(self, iplot): + self.iplot = iplot + def getpick1(self): return self.Pick1 @@ -132,7 +141,8 @@ class AutoPicking(object): def calcPick(self): self.Pick = None - + + class AICPicker(AutoPicking): ''' Method to derive onset time of arriving phase based on CF @@ -144,23 +154,59 @@ class AICPicker(AutoPicking): print 'Get onset time (pick) from AIC-CF ...' self.Pick = None + #find NaN's + nn = np.isnan(self.cf) + if len(nn) > 1: + self.cf[nn] = 0 #taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) + #smooth AIC-CF + ismooth = int(round(self.Tsmooth / self.dt)) + aicsmooth = np.zeros(len(aic)) + if len(aic) < ismooth: + print 'AICPicker: Tsmooth larger than CF!' + return + else: + for i in range(1, len(aic)): + if i > ismooth: + ii1 = i - ismooth; + aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth + else: + aicsmooth[i] = np.mean(aic[1 : i]) + #remove offset + offset = abs(min(aic) - min(aicsmooth)) + aicsmooth = aicsmooth - offset #get maximum of CF as starting point icfmax = np.argmax(aic) #find minimum in front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if aic[i - 1] >= aic[i]: + if aicsmooth[i - 1] >= aicsmooth[i]: self.Pick = self.Tcf[i] break + + if self.iplot is not None: + plt.figure(self.iplot) + x = self.Data[0].data + p1, = plt.plot(self.Tcf, x / max(x), 'k') + p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') + p3, = plt.plot([self.Pick, self.Pick], [-1 , 1], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(self.iplot) + if self.Pick == None: print 'AICPicker: Could not find minimum, picking window too short?' return self.Pick + class PragPicker(AutoPicking): ''' Method of pragmatic picking exploiting information given by CF. @@ -169,7 +215,7 @@ class PragPicker(AutoPicking): def calcPick(self): if self.getpick1() is not None: - print 'Get onset time (pick) from HOS- or AR-CF using pragmatic picking algorithm ...' + print 'Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' self.Pick = None #smooth CF @@ -190,9 +236,9 @@ class PragPicker(AutoPicking): #which is centered around tpick1 ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) - cfipick = self.cf[ipick] + cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) Tcfpick = self.Tcf[ipick] - cfsmoothipick = cfsmooth[ipick] + cfsmoothipick = cfsmooth[ipick]- np.mean(self.cf[ipick]) ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) cfpick1 = 2 * self.cf[ipick1] @@ -232,14 +278,255 @@ class PragPicker(AutoPicking): break #now decide which pick: left or right? - if flagpick_l > 0 and flagpick_r > 0: - if cfpick_l <= cfpick_r: - self.Pick = pick_l - else: - self.Pick = pick_r + if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: + self.Pick = pick_l + elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + self.Pick = pick_r + + if self.getiplot() is not None: + plt.figure(self.getiplot()) + p1, = plt.plot(Tcfpick,cfipick, 'k') + p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') + p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(self.getiplot()) else: self.Pick = None print 'PragPicker: No initial onset time given! Check input!' return + +class EarlLatePicker(AutoPicking): + ''' + Method to derive earliest and latest possible pick after Diehl & Kissling (2009) + as reasonable uncertainties. Latest possible pick is based on noise level, + earliest possible pick is half a signal wavelength in front of most likely + pick given by PragPicker. Most likely pick (initial pick) must be given. + ''' + + def calcPick(self): + + self.LPick = None + self.EPick = None + if self.getpick1() is not None: + print 'Get earliest and latest possible pick relative to most likely pick ...' + + ti = self.getpick1() + x = self.Data + t = self.Tcf + #some parmaters needed: + tnoise = self.TSNR[0] #noise window length for calculating noise level + tsignal = self.TSNR[2] #signal window length + tsafety = self.TSNR[1] #safety gap between signal onset and noise window + + #get latest possible pick + #get noise window + inoise = np.where((self.Tcf <= ti - tsafety) & (self.Tcf >= ti - tnoise - tsafety)) + #get signal window + isignal = np.where((self.Tcf <= ti + tsignal) & (self.Tcf >= ti)) + #calculate noise level + if len(x) == 1: + nlevel = max(abs(x[0].data[inoise])) * self.nfac + #get time where signal exceeds nlevel + ilup = np.where(x[0].data[isignal] > nlevel) + ildown = np.where(x[0].data[isignal] < -nlevel) + if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: + print 'EarlLatePick: Signal lower than noise level, misspick?' + return + il = min([ilup[0][0], ildown[0][0]]) + self.LPick = t[isignal][il] + elif len(x) == 2: + nlevel = max(np.sqrt(np.power(x[0].data[inoise], 2) + np.power(x[1].data[inoise], 2))) + #get earliest time where signal exceeds nlevel + ilup1 = np.where(x[0].data[isignal] > nlevel) + ilup2 = np.where(x[1].data[isignal] > nlevel) + ildown1 = np.where(x[0].data[isignal] < -nlevel) + ildown2 = np.where(x[1].data[isignal] < -nlevel) + ilup = min([ilup1[0][0], ilup2[0][0]]) + ildown = min([ildown1[0][0], ildown2[0][0]]) + if np.size(ilup) < 1 and np.size(ildown) < 1: + print 'EarlLatePick: Signal lower than noise level, misspick?' + return + il = min([ilup, ildown]) + self.LPick = t[isignal][il] + elif len(x) == 3: + nlevel = max(np.sqrt(np.power(x[0].data[inoise], 2) + np.power(x[1].data[inoise], 2) + \ + np.power(x[2].data[inoise], 2))) + #get earliest time where signal exceeds nlevel + ilup1 = np.where(x[0].data[isignal] > nlevel) + ilup2 = np.where(x[1].data[isignal] > nlevel) + ilup3 = np.where(x[2].data[isignal] > nlevel) + ildown1 = np.where(x[0].data[isignal] < -nlevel) + ildown2 = np.where(x[1].data[isignal] < -nlevel) + ildown3 = np.where(x[2].data[isignal] < -nlevel) + ilup = min([ilup1[0][0], ilup2[0][0], ilup3[0][0]]) + ildown = min([ildown1[0][0], ildown2[0][0], ildown3[0][0]]) + if np.size(ilup) < 1 and np.size(ildown) < 1: + print 'EarlLatePick: Signal lower than noise level, misspick?' + return + il = min([ilup, ildown]) + self.LPick = t[isignal][il] + + #get earliest possible pick + #get next 2 zero crossings after most likely pick + #if there is one trace in stream + if len(x) == 1: + zc = [] + zc.append(ti) + i = 0 + for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): + i = i+ 1 + if x[0].data[j-1] <= 0 and x[0].data[j] >= 0: + zc.append(t[isignal][i]) + elif x[0].data[j-1] > 0 and x[0].data[j] <= 0: + zc.append(t[isignal][i]) + if len(zc) == 3: + break + #calculate maximum period of signal out of zero crossings + Ts = max(np.diff(zc)) + #if there are two traces in stream + #get maximum of two signal periods + if len(x) == 2: + zc1 = [] + zc2 = [] + zc1.append(ti) + zc2.append(ti) + i = 0 + for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): + i = i+ 1 + if x[0].data[j-1] <= 0 and x[0].data[j] >= 0: + zc1.append(t[isignal][i]) + elif x[0].data[j-1] > 0 and x[0].data[j] <= 0: + zc1.append(t[isignal][i]) + if x[1].data[j-1] <= 0 and x[1].data[j] >= 0: + zc2.append(t[isignal][i]) + elif x[1].data[j-1] > 0 and x[1].data[j] <= 0: + zc2.append(t[isignal][i]) + if len(zc1) >= 3 and len(zc2) >= 3: + break + Ts = max([max(np.diff(zc1)), max(np.diff(zc2))]) + #if there are three traces in stream + #get maximum of three signal periods + if len(x) == 3: + zc1 = [] + zc2 = [] + zc3 = [] + zc1.append(ti) + zc2.append(ti) + zc3.append(ti) + i = 0 + for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): + i = i+ 1 + if x[0].data[j-1] <= 0 and x[0].data[j] >= 0: + zc1.append(t[isignal][i]) + elif x[0].data[j-1] > 0 and x[0].data[j] <= 0: + zc1.append(t[isignal][i]) + if x[1].data[j-1] <= 0 and x[1].data[j] >= 0: + zc2.append(t[isignal][i]) + elif x[1].data[j-1] > 0 and x[1].data[j] <= 0: + zc2.append(t[isignal][i]) + if x[2].data[j-1] <= 0 and x[2].data[j] >= 0: + zc3.append(t[isignal][i]) + elif x[2].data[j-1] > 0 and x[2].data[j] <= 0: + zc3.append(t[isignal][i]) + if len(zc1) >= 3 and len(zc2) >= 3 and len(zc3) >= 3: + break + Ts = max([max(np.diff(zc1)), max(np.diff(zc2)), max(np.diff(zc3))]) + + #Ts/4 is assumed as time difference between most likely and earliest possible pick! + self.EPick = ti - Ts/4 + + if self.iplot is not None: + plt.figure(self.iplot) + if len(x) == 1: + p1, = plt.plot(t, x[0].data, 'k') + p2, = plt.plot(t[inoise], x[0].data[inoise]) + p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings']) + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) + plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title('Earliest-/Latest Possible and Most Likely Pick, %s' % self.Data[0].stats.station) + elif len(x) == 2: + plt.subplot(2,1,1) + p1, = plt.plot(t, x[0].data, 'k') + p2, = plt.plot(t[inoise], x[0].data[inoise]) + p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc1[0:3], [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings']) + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) + plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.plot(zc1[0:3], [0, 0, 0], '*g') + plt.yticks([]) + plt.title('Earliest-/Latest Possible and Most Likely Pick, %s' % self.Data[0].stats.station) + plt.subplot(2,1,2) + plt.plot(t, x[1].data, 'k') + plt.plot(t[inoise], x[1].data[inoise]) + plt.plot(t[isignal], x[1].data[isignal], 'r') + plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([ti, ti], [max(x[1].data), -max(x[1].data)], 'b', linewidth=2) + plt.plot([self.LPick, self.LPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') + plt.plot([self.EPick, self.EPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') + plt.plot(zc2[0:3], [0, 0, 0], '*g', markersize=14) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + elif len(x) == 3: + plt.subplot(3,1,1) + p1, = plt.plot(t, x[0].data, 'k') + p2, = plt.plot(t[inoise], x[0].data[inoise]) + p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc1[0:3], [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings']) + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) + plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.yticks([]) + plt.title('Earliest-/Latest Possible and Most Likely Pick, %s' % self.Data[0].stats.station) + plt.subplot(3,1,2) + plt.plot(t, x[1].data, 'k') + plt.plot(t[inoise], x[1].data[inoise]) + plt.plot(t[isignal], x[1].data[isignal], 'r') + plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([ti, ti], [max(x[1].data), -max(x[1].data)], 'b', linewidth=2) + plt.plot([self.LPick, self.LPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') + plt.plot([self.EPick, self.EPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') + plt.plot(zc2[0:3], [0, 0, 0], '*g', markersize=14) + plt.yticks([]) + plt.subplot(3,1,3) + plt.plot(t, x[2].data, 'k') + plt.plot(t[inoise], x[2].data[inoise]) + plt.plot(t[isignal], x[2].data[isignal], 'r') + plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([ti, ti], [max(x[2].data), -max(x[2].data)], 'b', linewidth=2) + plt.plot([self.LPick, self.LPick], [max(x[2].data)/2, -max(x[2].data)/2], '--k') + plt.plot([self.EPick, self.EPick], [max(x[2].data)/2, -max(x[2].data)/2], '--k') + plt.plot(zc3[0:3], [0, 0, 0], '*g', markersize=14) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.show() + raw_input() + plt.close(self.iplot) + + elif self.getpick1() == None: + print 'EarlLatePick: No initial onset time given! Check input!' + return + From addb8ae815f68bff477c8d41d58b6053579e2187 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sun, 1 Mar 2015 10:31:49 +0100 Subject: [PATCH 0251/1144] try to make filtering work --- QtPyLoT.py | 48 +++++++++++++++++++++----------------- pylot/core/read/inputs.py | 6 ++--- pylot/core/util/widgets.py | 25 +++++++++++++++----- 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 4b86514a..dfffab73 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -34,7 +34,7 @@ from obspy.core import UTCDateTime from pylot.core.read import Data, FilterOptions from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ - checkurl, FormatError, layoutStationButtons, FilterOptionsDialog, \ + checkurl, FormatError, FilterOptionsDialog, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ DatastructureError from pylot.core.util.structure import DATASTRUCTURE @@ -62,8 +62,6 @@ class MainWindow(QMainWindow): self.fnames = None self.dataStructure = DATASTRUCTURE[ settings.value("data/Structure", None)]() - self.setWindowTitle("PyLoT - do seismic processing the python way") - self.setWindowIcon(QIcon(":/icon.ico")) self.seismicPhase = str(settings.value("phase", "P")) self.dispComponent = str(settings.value("plotting/dispComponent", "Z")) if settings.value("data/dataRoot", None) is None: @@ -72,14 +70,9 @@ class MainWindow(QMainWindow): settings.setValue("data/dataRoot", dirname) settings.sync() - # initialize filter parameter - filterOptionsP = FILTERDEFAULTS['P'] - filterOptionsS = FILTERDEFAULTS['S'] + self.filteroptions = {} - self.filterOptionsP = FilterOptions(**filterOptionsP) - self.filterOptionsS = FilterOptions(**filterOptionsS) - - # UI has to be set up before(!) children widgets are + # UI has to be set up before(!) children widgets are about to show up self.setupUi() # initialize event data @@ -105,6 +98,7 @@ class MainWindow(QMainWindow): except: self.startTime = UTCDateTime() + self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(QIcon(":/icon.ico")) xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') @@ -272,9 +266,8 @@ class MainWindow(QMainWindow): action = self.sender() if isinstance(action, QAction): if action.data() is None: - filt = "Supported event formats (*.mat *.qml *.xml " \ - "*.kor *.evt)" - caption = 'Select event to open' + filt = "Supported event formats (*.mat *.qml *.xml *.kor *.evt)" + caption = "Open an event file" fname = QFileDialog().getOpenFileName(self, caption=caption, filter=filt) @@ -399,24 +392,35 @@ class MainWindow(QMainWindow): filterOptions=self.getFilterOptions()) if filterDlg.exec_(): filteroptions = filterDlg.getFilterOptions() - assert isinstance(filteroptions, FilterOptions) self.setFilterOptions(filteroptions) def getFilterOptions(self): + try: + return self.filteroptions[self.getSeismicPhase()] + except AttributeError, e: + print e + return FilterOptions(None, None, None) + + def getFilters(self): return self.filteroptions - def setFilterOptions(self, filterOptions): - cases = {'P': self.filterOptionsP, - 'S': self.filterOptionsS} - cases[self.getSeismicPhase()] = filterOptions - self.updateFilterOptions() + def setFilterOptions(self, filterOptions, seismicPhase=None): + if seismicPhase is None: + self.getFilters()[self.getSeismicPhase()] = filterOptions + else: + self.getFilters()[seismicPhase] = filterOptions + def updateFilterOptions(self): try: - self.filteroptions = [self.filterOptionsP - if not self.seismicPhase == 'S' - else self.filterOptionsS][0] + settings = QSettings() + if settings.value("filterdefaults", None) is None and not self.getFilters(): + for key, value in FILTERDEFAULTS.iteritems(): + self.setFilterOptions(FilterOptions(**value), key) + elif settings.value("filterdefaults", None) is not None: + for key, value in settings.value("filterdefaults"): + self.setFilterOptions(FilterOptions(**value), key) except Exception, e: self.updateStatus('Error ...') emsg = QErrorMessage(self) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 87cd16c3..ae7f5666 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -174,9 +174,9 @@ class FilterOptions(object): Type:\t\t{ftype}\n Frequencies:\t{freq}\n Order:\t\t{order}\n - '''.format(ftype=self.getFilterType, - freq=self.getFreq, - order=self.getOrder) + '''.format(ftype=self.getFilterType(), + freq=self.getFreq(), + order=self.getOrder()) return hrs def getFreq(self): diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a070b9c8..0d0d158e 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -296,33 +296,45 @@ class FilterOptionsDialog(QDialog): """ super(FilterOptionsDialog, self).__init__() - if filterOptions is not None and parent.getSeismicPhase() != "P": - self.filterOptions = filterOptions + if parent is not None: + self.filterOptions = parent.getFilterOptions() else: self.filterOptions = FilterOptions() + _enable = True + if self.getFilterOptions().getFilterType() is None: + _enable = False + self.freqminLabel = QLabel() self.freqminLabel.setText("minimum:") self.freqminSpinBox = QDoubleSpinBox() self.freqminSpinBox.setRange(5e-7, 1e6) self.freqminSpinBox.setDecimals(2) self.freqminSpinBox.setSuffix(' Hz') - self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0]) + self.freqminSpinBox.setEnabled(_enable) + self.freqmaxLabel = QLabel() self.freqmaxLabel.setText("maximum:") self.freqmaxSpinBox = QDoubleSpinBox() self.freqmaxSpinBox.setRange(5e-7, 1e6) self.freqmaxSpinBox.setDecimals(2) self.freqmaxSpinBox.setSuffix(' Hz') - if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: - self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) + self.freqmaxSpinBox.setEnabled(_enable) + if _enable: + self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0]) + if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: + self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) + else: + self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()) + self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()) - typeOptions = ["bandpass", "bandstop", "lowpass", "highpass"] + typeOptions = [None, "bandpass", "bandstop", "lowpass", "highpass"] self.orderLabel = QLabel() self.orderLabel.setText("Order:") self.orderSpinBox = QSpinBox() self.orderSpinBox.setRange(2, 10) + self.orderSpinBox.setEnabled(_enable) self.selectTypeLabel = QLabel() self.selectTypeLabel.setText("Select filter type:") self.selectTypeCombo = QComboBox() @@ -358,6 +370,7 @@ class FilterOptionsDialog(QDialog): self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) + def updateUi(self): if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: self.freqminLabel.setText("cutoff:") From b23c9d1104fa1944c2060d02358b62867d22c80c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sun, 1 Mar 2015 10:33:13 +0100 Subject: [PATCH 0252/1144] initialized new widget and window for picking (work in progress) --- pylot/core/util/widgets.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0d0d158e..6b0058be 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -70,6 +70,26 @@ class MPLWidget(FigureCanvas): self.updateYLabel(ylabel) self.updateTitle(title) + +class multiComponentPlot(FigureCanvas): + + def __init__(self, parent=None, noc=3, xlabel='x', ylabel='y', title='Title'): + super(multiComponentPlot, self).__init__(Figure()) + + self.setParent(parent) + self.figure = Figure() + self.canvas = FigureCanvas(self.figure) + + self.axeslist = [] + + for n in range(noc): + nsub = '{0}1{1}'.format(noc, n) + if n >= 1: + self.axeslist.insert(n, self.figure.add_subplot(nsub, sharex=self.axeslist[0], sharey=self.axeslist[0])) + else: + self.axeslist.insert(n, self.figure.add_subplot(nsub)) + + class PickDlg(QDialog): def __init__(self, station=None, parent=None): From 0dbcca1c6f89ad305cccf59d5c7062af0f210276 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 4 Mar 2015 11:52:04 +0100 Subject: [PATCH 0253/1144] moved function createAction to the widgets module (reused in additional widget) bugfix: on Linux systems os.getlogin raises an exception (reimplementation: getLogin) --- QtPyLoT.py | 31 +++++++------------------------ pylot/core/util/__init__.py | 4 ++-- pylot/core/util/utils.py | 4 +++- pylot/core/util/widgets.py | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index dfffab73..0dae5cb6 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -23,7 +23,6 @@ https://www.iconfinder.com/iconsets/flavour (http://www.gnu.org/copyleft/lesser.html) """ -import os import sys from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, QFileInfo @@ -36,7 +35,7 @@ from pylot.core.read import Data, FilterOptions from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ checkurl, FormatError, FilterOptionsDialog, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ - DatastructureError + DatastructureError, createAction, getLogin from pylot.core.util.structure import DATASTRUCTURE @@ -51,17 +50,19 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) + + self.createAction = createAction self.dirty = False settings = QSettings() if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") settings.setValue("user/FullName", fulluser) - settings.setValue("user/Login", os.getlogin()) + settings.setValue("user/Login", getLogin()) settings.sync() self.recentEvents = settings.value("data/recentEvents", []) self.fnames = None self.dataStructure = DATASTRUCTURE[ - settings.value("data/Structure", None)]() + settings.value("data/Structure", "PILOT")]() self.seismicPhase = str(settings.value("phase", "P")) self.dispComponent = str(settings.value("plotting/dispComponent", "Z")) if settings.value("data/dataRoot", None) is None: @@ -89,7 +90,6 @@ class MainWindow(QMainWindow): self.updateFilterOptions() - def setupUi(self): try: @@ -205,23 +205,6 @@ class MainWindow(QMainWindow): _widget.setLayout(_layout) self.setCentralWidget(_widget) - def createAction(self, text, slot=None, shortcut=None, icon=None, - tip=None, checkable=False): - """ - :rtype : ~PySide.QtGui.QAction - """ - action = QAction(text, self) - if icon is not None: - action.setIcon(icon) - if shortcut is not None: - action.setShortcut(shortcut) - if tip is not None: - action.setToolTip(tip) - if slot is not None: - action.triggered.connect(slot) - if checkable: - action.setCheckable(True) - return action def updateFileMenu(self): @@ -335,7 +318,7 @@ class MainWindow(QMainWindow): def getData(self): return self.data - def getDataWidget(self): + def getPlotWidget(self): return self.DataPlot def addActions(self, target, actions): @@ -359,7 +342,7 @@ class MainWindow(QMainWindow): self.plotWaveformData() def plotWaveformData(self): - self.getData().plotWFData(self.getDataWidget()) + self.getData().plotWFData(self.getPlotWidget()) def filterWaveformData(self): if self.getData(): diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 0d9575d0..da316777 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -4,9 +4,9 @@ from pylot.core.util.errors import OptionsError, FormatError, DatastructureError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ - getHash + getHash, getLogin from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ - PropertiesDlg, NewEventDlg, MPLWidget + PropertiesDlg, NewEventDlg, MPLWidget, createAction from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 805bef25..9ba5f564 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -23,9 +23,11 @@ def fnConstructor(s): return fn +def getLogin(): + return pwd.getpwuid(os.getuid())[0] + def getHash(time): ''' - :param time: time object for which a hash should be calculated :type time: :class: `~obspy.core.utcdatetime.UTCDateTime` object :return: str diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 6b0058be..44ed30a1 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -43,6 +43,24 @@ from pylot.core.read import FilterOptions from pylot.core.util.defaults import OUTPUTFORMATS +def createAction(parent, text, slot=None, shortcut=None, icon=None, + tip=None, checkable=False): + """ + :rtype : ~PySide.QtGui.QAction + """ + action = QAction(text, parent) + if icon is not None: + action.setIcon(icon) + if shortcut is not None: + action.setShortcut(shortcut) + if tip is not None: + action.setToolTip(tip) + if slot is not None: + action.triggered.connect(slot) + if checkable: + action.setCheckable(True) + return action + class MPLWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): From 0ceba15118f4e52a3c0309ce22380a9ebf9517bf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 4 Mar 2015 11:53:15 +0100 Subject: [PATCH 0254/1144] corrected MatLab code remnant semicolon --- pylot/core/pick/Picker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 275b86a8..1a3bd2be 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -170,7 +170,7 @@ class AICPicker(AutoPicking): else: for i in range(1, len(aic)): if i > ismooth: - ii1 = i - ismooth; + ii1 = i - ismooth aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth else: aicsmooth[i] = np.mean(aic[1 : i]) From cc2d823272b91b80f72de31cfa6b3b5dc4ce6037 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 4 Mar 2015 11:54:56 +0100 Subject: [PATCH 0255/1144] user interface setup for picking dialog added --- pylot/core/util/widgets.py | 52 +++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 44ed30a1..24ef63a5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -91,31 +91,71 @@ class MPLWidget(FigureCanvas): class multiComponentPlot(FigureCanvas): - def __init__(self, parent=None, noc=3, xlabel='x', ylabel='y', title='Title'): + def __init__(self, parent=None, components='ZNE', xlabel='x', ylabel='y', title='Title'): super(multiComponentPlot, self).__init__(Figure()) self.setParent(parent) self.figure = Figure() self.canvas = FigureCanvas(self.figure) + self.noc = len(components) self.axeslist = [] - for n in range(noc): - nsub = '{0}1{1}'.format(noc, n) + for n, comp in enumerate(components): + nsub = '{0}1{1}'.format(self.noc, n) if n >= 1: - self.axeslist.insert(n, self.figure.add_subplot(nsub, sharex=self.axeslist[0], sharey=self.axeslist[0])) + self.axeslist.insert(n, + self.figure.add_subplot(nsub, + sharex=self.axeslist[0], + sharey=self.axeslist[0])) else: self.axeslist.insert(n, self.figure.add_subplot(nsub)) class PickDlg(QDialog): - def __init__(self, station=None, parent=None): + def __init__(self, parent=None, station=None, rotate=False): super(PickDlg, self).__init__(parent) - pass + self.station = station + self.rotate = rotate + self.components = 'ZNE' + self.data = parent.getData().getWFData().copy() + self.setupUi() + + def setupUi(self): + + # create actions + self.filterAction = createAction(parent=self, text='Filter', + slot=self.filterWFData, + icon=QIcon(':/filter.png'), + tip='Filter waveforms', + checkable=True) + self.selectPhase = QComboBox() + self.selectPhase.addItems(['Pn', 'Pg', 'P1', 'P2']) + + # layout the outermost appearance of the Pick Dialog + _outerlayout = QVBoxLayout() + _dialtoolbar = QToolBar() + + # fill toolbar with content + + _dialtoolbar.addAction(self.filterAction) + _dialtoolbar.addWidget(self.selectPhase) + + _innerlayout = QHBoxLayout() + _toolslayout = QVBoxLayout() + + def filterWFData(self): + self.data.filterWFData() + self.plotData() + + def plotData(self): + for comp in self.components: + self.getPlotWidget() + class PropertiesDlg(QDialog): def __init__(self, parent=None): From f6922fafef530504bbba7726a19106d99a584874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 4 Mar 2015 13:45:29 +0100 Subject: [PATCH 0256/1144] Implemented quality assessment for AICPicker based on slope and SNR from CF. New attributes getSNR and getSlope. --- pylot/core/pick/Picker.py | 81 ++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 1a3bd2be..525948c0 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -33,7 +33,7 @@ class AutoPicking(object): :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object :param: nfac (noise factor), nfac times noise level to calculate latest possible pick - in EarlLatePick + in EarlLatePicker :type: int :param: TSNR, length of time windows around pick used to determine SNR [s] @@ -121,6 +121,12 @@ class AutoPicking(object): def getpick(self): return self.Pick + def getSNR(self): + return self.SNR + + def getSlope(self): + return self.slope + def getLpick(self): return self.LPick @@ -151,7 +157,7 @@ class AICPicker(AutoPicking): def calcPick(self): - print 'Get onset time (pick) from AIC-CF ...' + print 'AICPicker: Get onset time (pick) from AIC-CF ...' self.Pick = None #find NaN's @@ -187,6 +193,37 @@ class AICPicker(AutoPicking): self.Pick = self.Tcf[i] break + #quality assessment using SNR and slope from CF + if self.Pick is not None: + #some parameters needed: + tnoise = self.TSNR[0] #noise window length for calculating noise level + tsignal = self.TSNR[2] #signal window length + tsafety = self.TSNR[1] #safety gap between signal onset and noise window + tslope = self.TSNR[3] #slope determination window + #get noise window + inoise = np.where((self.Tcf <= max([self.Pick - tsafety, 0])) \ + & (self.Tcf >= max([self.Pick - tnoise - tsafety, 0]))) + #get signal window + isignal = np.where((self.Tcf <= min([self.Pick + tsignal + tsafety, len(self.Data[0].data)])) \ + & (self.Tcf >= self.Pick)) + #calculate SNR from CF + self.SNR = max(abs(self.cf[isignal] - np.mean(self.cf[isignal]))) / max(abs(self.cf[inoise] \ + - np.mean(self.cf[inoise]))) + #calculate slope from CF after initial pick + #get slope window + islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ + & (self.Tcf >= self.Pick)) + dataslope = self.Data[0].data[islope] + #calculate slope as linear regression of order 1 + xslope = np.arange(0, len(dataslope), 1) + P = np.polyfit(xslope, dataslope, 1) + datafit = np.polyval(P, xslope) + self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] + + else: + self.SNR = None + self.slope = None + if self.iplot is not None: plt.figure(self.iplot) x = self.Data[0].data @@ -198,14 +235,26 @@ class AICPicker(AutoPicking): plt.yticks([]) plt.title(self.Data[0].stats.station) plt.show() + + if self.Pick is not None: + plt.figure(self.iplot + 1) + p11, = plt.plot(self.Tcf, x, 'k') + p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) + p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') + p14, = plt.plot(self.Tcf[islope], dataslope, 'g') + p15, = plt.plot(self.Tcf[islope], datafit, 'g--', linewidth=2) + plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope']) + plt.title('SNR and Slope, Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ + self.SNR, self.slope)) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.ylabel('Counts') + raw_input() plt.close(self.iplot) if self.Pick == None: print 'AICPicker: Could not find minimum, picking window too short?' - return self.Pick - class PragPicker(AutoPicking): ''' @@ -215,9 +264,11 @@ class PragPicker(AutoPicking): def calcPick(self): if self.getpick1() is not None: - print 'Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' + print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' self.Pick = None + self.SNR = None + self.slope = None #smooth CF ismooth = int(round(self.Tsmooth / self.dt)) cfsmooth = np.zeros(len(self.cf)) @@ -314,22 +365,26 @@ class EarlLatePicker(AutoPicking): self.LPick = None self.EPick = None + self.SNR = None + self.slope = None if self.getpick1() is not None: - print 'Get earliest and latest possible pick relative to most likely pick ...' + print 'EarlLatePicker: Get earliest and latest possible pick relative to most likely pick ...' ti = self.getpick1() x = self.Data t = self.Tcf - #some parmaters needed: + #some parameters needed: tnoise = self.TSNR[0] #noise window length for calculating noise level tsignal = self.TSNR[2] #signal window length tsafety = self.TSNR[1] #safety gap between signal onset and noise window #get latest possible pick #get noise window - inoise = np.where((self.Tcf <= ti - tsafety) & (self.Tcf >= ti - tnoise - tsafety)) + inoise = np.where((self.Tcf <= max([ti - tsafety, 0])) \ + & (self.Tcf >= max([ti - tnoise - tsafety, 0]))) #get signal window - isignal = np.where((self.Tcf <= ti + tsignal) & (self.Tcf >= ti)) + isignal = np.where((self.Tcf <= min([ti + tsignal + tsafety, len(x[0].data)])) \ + & (self.Tcf >= ti)) #calculate noise level if len(x) == 1: nlevel = max(abs(x[0].data[inoise])) * self.nfac @@ -337,7 +392,7 @@ class EarlLatePicker(AutoPicking): ilup = np.where(x[0].data[isignal] > nlevel) ildown = np.where(x[0].data[isignal] < -nlevel) if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'EarlLatePick: Signal lower than noise level, misspick?' + print 'EarlLatePicker: Signal lower than noise level, misspick?' return il = min([ilup[0][0], ildown[0][0]]) self.LPick = t[isignal][il] @@ -351,7 +406,7 @@ class EarlLatePicker(AutoPicking): ilup = min([ilup1[0][0], ilup2[0][0]]) ildown = min([ildown1[0][0], ildown2[0][0]]) if np.size(ilup) < 1 and np.size(ildown) < 1: - print 'EarlLatePick: Signal lower than noise level, misspick?' + print 'EarlLatePicker: Signal lower than noise level, misspick?' return il = min([ilup, ildown]) self.LPick = t[isignal][il] @@ -368,7 +423,7 @@ class EarlLatePicker(AutoPicking): ilup = min([ilup1[0][0], ilup2[0][0], ilup3[0][0]]) ildown = min([ildown1[0][0], ildown2[0][0], ildown3[0][0]]) if np.size(ilup) < 1 and np.size(ildown) < 1: - print 'EarlLatePick: Signal lower than noise level, misspick?' + print 'EarlLatePicker: Signal lower than noise level, misspick?' return il = min([ilup, ildown]) self.LPick = t[isignal][il] @@ -527,6 +582,6 @@ class EarlLatePicker(AutoPicking): plt.close(self.iplot) elif self.getpick1() == None: - print 'EarlLatePick: No initial onset time given! Check input!' + print 'EarlLatePicker: No initial onset time given! Check input!' return From 714e70de69c234e93c8ea0da88b490ec6818ff46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 4 Mar 2015 13:49:02 +0100 Subject: [PATCH 0257/1144] Modified for improved class Picker.py --- pylot/core/pick/run_makeCF.py | 53 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 41c499b3..a170c467 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ - Script to run PyLoT modules CharFuns.py and Picker.py. + Script to run autoPyLoT-script "makeCF.py". Only for test purposes! """ @@ -10,24 +10,26 @@ from obspy.core import read import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.CharFuns import CharacteristicFunction -from pylot.core.pick.Picker import AutoPicking +from pylot.core.pick.Picker import Picker import glob import argparse def run_makeCF(project, database, event, iplot, station=None): #parameters for CF calculation - t2 = 7 #length of moving window for HOS calculation [sec] - p = 4 #order of statistics - cuttimes = [10, 50] #start and end time for CF calculation - bpz = [2, 30] #corner frequencies of bandpass filter, vertical component - bph = [2, 15] #corner frequencies of bandpass filter, horizontal components - tdetz= 1.2 #length of AR-determination window [sec], vertical component - tdeth= 0.8 #length of AR-determination window [sec], horizontal components - tpredz = 0.4 #length of AR-prediction window [sec], vertical component - tpredh = 0.4 #length of AR-prediction window [sec], horizontal components - addnoise = 0.001 #add noise to seismogram for stable AR prediction - arzorder = 2 #chosen order of AR process, vertical component - arhorder = 4 #chosen order of AR process, horizontal components + t2 = 7 #length of moving window for HOS calculation [sec] + p = 4 #order of HOS + cuttimes = [10, 50] #start and end time for CF calculation + bpz = [2, 30] #corner frequencies of bandpass filter, vertical component + bph = [2, 15] #corner frequencies of bandpass filter, horizontal components + tdetz= 1.2 #length of AR-determination window [sec], vertical component + tdeth= 0.8 #length of AR-determination window [sec], horizontal components + tpredz = 0.4 #length of AR-prediction window [sec], vertical component + tpredh = 0.4 #length of AR-prediction window [sec], horizontal components + addnoise = 0.001 #add noise to seismogram for stable AR prediction + arzorder = 2 #chosen order of AR process, vertical component + arhorder = 4 #chosen order of AR process, horizontal components + TSNR = [5, 0.5, 1, 0.03] #window lengths [s] for calculating SNR for earliest/latest pick and quality assessment + #[noise window, safety gap, signal window, slope determination window] #get waveform data if station: dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*HZ.msd' % (project, database, event, station) @@ -66,12 +68,12 @@ def run_makeCF(project, database, event, iplot, station=None): aiccf = AICcf(st_copy, cuttimes) #instance of AICcf ############################################################## #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicpick = AICPicker(aiccf, None, [5, 0.5, 1], 3, 10, None, 0.1) + aicpick = AICPicker(aiccf, None, TSNR, 3, 10, None, 0.1) ############################################################## #get refined onset time from HOS-CF using class Picker - hospick = PragPicker(hoscf, None, [5, 0.5, 1], 2, 10, 0.001, 0.2, aicpick.getpick()) + hospick = PragPicker(hoscf, None, TSNR, 2, 10, 0.001, 0.2, aicpick.getpick()) #get earliest and latest possible picks - hosELpick = EarlLatePicker(hoscf, 1.5, [5, 0.5, 1], None, 10, None, None, hospick.getpick()) + hosELpick = EarlLatePicker(hoscf, 1.5, TSNR, None, 10, None, None, hospick.getpick()) ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction #get stream object of filtered data @@ -86,12 +88,12 @@ def run_makeCF(project, database, event, iplot, station=None): araiccf = AICcf(st_copy, cuttimes, tpredz, 0, tdetz) #instance of AICcf ############################################################## #get onset time from AIC-ARZ-CF using subclass AICPicker of class AutoPicking - aicarzpick = AICPicker(araiccf, 1.5, [5, 0.5, 2], 2, 10, None, 0.1) + aicarzpick = AICPicker(araiccf, 1.5, TSNR, 2, 10, None, 0.1) ############################################################## #get refined onset time from ARZ-CF using class Picker - arzpick = PragPicker(arzcf, 1.5, [5, 0.5, 2], 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) + arzpick = PragPicker(arzcf, 1.5, TSNR, 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) #get earliest and latest possible picks - arzELpick = EarlLatePicker(arzcf, 1.5, [5, 0.5, 1], None, 10, None, None, arzpick.getpick()) + arzELpick = EarlLatePicker(arzcf, 1.5, TSNR, None, 10, None, None, arzpick.getpick()) elif not wfzfiles: print 'No vertical component data found!' @@ -127,12 +129,12 @@ def run_makeCF(project, database, event, iplot, station=None): arhaiccf = AICcf(H_copy, cuttimes, tpredh, 0, tdeth) #instance of AICcf ############################################################## #get onset time from AIC-ARH-CF using subclass AICPicker of class AutoPicking - aicarhpick = AICPicker(arhaiccf, 1.5, [5, 0.5, 2], 4, 10, None, 0.1) + aicarhpick = AICPicker(arhaiccf, 1.5, TSNR, 4, 10, None, 0.1) ############################################################### #get refined onset time from ARH-CF using class Picker - arhpick = PragPicker(arhcf, 1.5, [5, 0.5, 2], 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) + arhpick = PragPicker(arhcf, 1.5, TSNR, 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) #get earliest and latest possible picks - arhELpick = EarlLatePicker(arhcf, 1.5, [5, 0.5, 1], None, 10, None, None, arhpick.getpick()) + arhELpick = EarlLatePicker(arhcf, 1.5, TSNR, None, 10, None, None, arhpick.getpick()) #create stream with 3 traces #merge streams @@ -155,7 +157,7 @@ def run_makeCF(project, database, event, iplot, station=None): #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf #get earliest and latest possible pick from initial ARH-pick - ar3cELpick = EarlLatePicker(ar3ccf, 1.5, [5, 0.5, 1], None, 10, None, None, arhpick.getpick()) + ar3cELpick = EarlLatePicker(ar3ccf, 1.5, TSNR, None, 10, None, None, arhpick.getpick()) ############################################################## if iplot: #plot vertical trace @@ -187,7 +189,8 @@ def run_makeCF(project, database, event, iplot, station=None): plt.ylim([-1.5, 1.5]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') - plt.title([tr.stats.station, tr.stats.channel]) + plt.title('%s, %s, AIC-SNR=%7.2f, AIC-Slope=%12.2f' % (tr.stats.station, \ + tr.stats.channel, aicpick.getSNR(), aicpick.getSlope())) plt.suptitle(tr.stats.starttime) plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) #plot horizontal traces From 5f0b7fbdc002325b81ba8ba76aeed10135ae132f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 4 Mar 2015 15:40:25 +0100 Subject: [PATCH 0258/1144] bugfix: fixed usage of createAction do to outsourcing --- QtPyLoT.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 0dae5cb6..e94f09e3 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -119,51 +119,51 @@ class MainWindow(QMainWindow): saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) newIcon = self.style().standardIcon(QStyle.SP_FileIcon) - newEventAction = self.createAction("&New event ...", + newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, QKeySequence.New, newIcon, "Create a new event.") - openEventAction = self.createAction("&Open event ...", self.loadData, + openEventAction = self.createAction(self, "&Open event ...", self.loadData, QKeySequence.Open, openIcon, "Open an event.") openEventAction.setData(None) - saveEventAction = self.createAction("&Save event ...", self.saveData, + saveEventAction = self.createAction(self, "&Save event ...", self.saveData, QKeySequence.Save, saveIcon, "Save actual event data.") - openWFDataAction = self.createAction("Open &waveforms ...", + openWFDataAction = self.createAction(self, "Open &waveforms ...", self.loadWaveformData, "Ctrl+W", QIcon(":/wfIcon.png"), """Open waveform data (event will be closed).""") - prefsEventAction = self.createAction("Preferences", self.PyLoTprefs, + prefsEventAction = self.createAction(self, "Preferences", self.PyLoTprefs, QKeySequence.Preferences, QIcon(None), "Edit PyLoT app preferences.") - quitAction = self.createAction("&Quit", + quitAction = self.createAction(self, "&Quit", QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") - self.filterAction = self.createAction("&Filter ...", self.filterWaveformData, + self.filterAction = self.createAction(self, "&Filter ...", self.filterWaveformData, "Ctrl+F", QIcon(":/filter.png"), """Toggle un-/filtered waveforms to be displayed, according to the desired seismic phase.""", True) - filterEditAction = self.createAction("&Filter parameter ...", + filterEditAction = self.createAction(self, "&Filter parameter ...", self.adjustFilterOptions, "Alt+F", QIcon(None), """Adjust filter parameters.""") - self.selectPAction = self.createAction("&P", self.alterPhase, "Alt+P", + self.selectPAction = self.createAction(self, "&P", self.alterPhase, "Alt+P", QIcon(":/picon.png"), "Toggle P phase.", True) - self.selectSAction = self.createAction("&S", self.alterPhase, "Alt+S", + self.selectSAction = self.createAction(self, "&S", self.alterPhase, "Alt+S", QIcon(":/sicon.png"), "Toggle S phase", True) - printAction = self.createAction("&Print event ...", + printAction = self.createAction(self, "&Print event ...", self.printEvent, QKeySequence.Print, QIcon(":/printer.png"), "Print waveform overview.") - helpAction = self.createAction("&Help ...", self.helpHelp, + helpAction = self.createAction(self, "&Help ...", self.helpHelp, QKeySequence.HelpContents, helpIcon, """Show either the documentation homepage (internet connection available), From 567ae16f1da78d3e6c514e75ef93c5154c3660d8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Wed, 4 Mar 2015 15:42:08 +0100 Subject: [PATCH 0259/1144] bugfix: FilterOptionsDialog should only provide logic options --- pylot/core/util/widgets.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 24ef63a5..ed462466 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -377,7 +377,7 @@ class FilterOptionsDialog(QDialog): if parent is not None: self.filterOptions = parent.getFilterOptions() else: - self.filterOptions = FilterOptions() + self.filterOptions = FilterOptions(filterOptions) _enable = True if self.getFilterOptions().getFilterType() is None: @@ -403,8 +403,13 @@ class FilterOptionsDialog(QDialog): if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) else: - self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()) - self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()) + try: + self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()) + self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()) + except TypeError, e: + print e + self.freqmaxSpinBox.setValue(1.) + self.freqminSpinBox.setValue(.1) typeOptions = [None, "bandpass", "bandstop", "lowpass", "highpass"] @@ -450,20 +455,22 @@ class FilterOptionsDialog(QDialog): def updateUi(self): + _enable = False if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: self.freqminLabel.setText("cutoff:") - self.freqmaxLabel.setEnabled(False) - self.freqmaxSpinBox.setEnabled(False) self.freqmaxSpinBox.setValue(self.freqminSpinBox.value()) else: + _enable = True self.freqminLabel.setText("minimum:") - self.freqmaxLabel.setEnabled(True) - self.freqmaxSpinBox.setEnabled(True) + + self.freqmaxLabel.setEnabled(_enable) + self.freqmaxSpinBox.setEnabled(_enable) + self.getFilterOptions().setFilterType(self.selectTypeCombo.currentText()) freq = [] freq.append(self.freqminSpinBox.value()) - if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: + if _enable: if self.freqminSpinBox.value() > self.freqmaxSpinBox.value(): QMessageBox.warning(self, "Value error", "Maximum frequency must be at least the " From 8f712978848728134e1d92e8292065f0e94fce44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 4 Mar 2015 15:52:14 +0100 Subject: [PATCH 0260/1144] Debuged, stable slope determination of CF, modified plotting. --- pylot/core/pick/Picker.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 525948c0..11e1b6a6 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -213,11 +213,19 @@ class AICPicker(AutoPicking): #get slope window islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ & (self.Tcf >= self.Pick)) + #find maximum within slope determination window + #'cause slope should be calculated up to first local minimum only! + imax = np.argmax(self.Data[0].data[islope]) + islope = islope[0][0 :imax] dataslope = self.Data[0].data[islope] - #calculate slope as linear regression of order 1 + #calculate slope as polynomal fit of order 1 xslope = np.arange(0, len(dataslope), 1) P = np.polyfit(xslope, dataslope, 1) datafit = np.polyval(P, xslope) + if datafit[0] >= datafit[len(datafit) - 1]: + print 'AICPicker: Negative slope, bad onset skipped!' + return + self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] else: @@ -241,13 +249,17 @@ class AICPicker(AutoPicking): p11, = plt.plot(self.Tcf, x, 'k') p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') - p14, = plt.plot(self.Tcf[islope], dataslope, 'g') - p15, = plt.plot(self.Tcf[islope], datafit, 'g--', linewidth=2) - plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope']) + p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') + p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) + plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], \ + loc='best') plt.title('SNR and Slope, Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ self.SNR, self.slope)) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.ylabel('Counts') + ax = plt.gca() + ax.set_ylim([-10, max(self.Data[0].data)]) + ax.set_xlim([self.Tcf[inoise[0][0]] - 5, self.Tcf[isignal[0][len(isignal) - 1]] + 5]) raw_input() plt.close(self.iplot) From 77c87067daaa9f6d21bc646ca50f368715f94528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 4 Mar 2015 15:53:18 +0100 Subject: [PATCH 0261/1144] Different time windows for slope determination from AR- and HOS-CF. --- pylot/core/pick/run_makeCF.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index a170c467..e57f80d2 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -10,7 +10,7 @@ from obspy.core import read import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.CharFuns import CharacteristicFunction -from pylot.core.pick.Picker import Picker +from pylot.core.pick.Picker import AutoPicking import glob import argparse @@ -28,8 +28,10 @@ def run_makeCF(project, database, event, iplot, station=None): addnoise = 0.001 #add noise to seismogram for stable AR prediction arzorder = 2 #chosen order of AR process, vertical component arhorder = 4 #chosen order of AR process, horizontal components - TSNR = [5, 0.5, 1, 0.03] #window lengths [s] for calculating SNR for earliest/latest pick and quality assessment - #[noise window, safety gap, signal window, slope determination window] + TSNRhos = [5, 0.5, 1, 0.1] #window lengths [s] for calculating SNR for earliest/latest pick and quality assessment + #from HOS-CF [noise window, safety gap, signal window, slope determination window] + TSNRarz = [5, 0.5, 1, 0.5] #window lengths [s] for calculating SNR for earliest/lates pick and quality assessment + #from ARZ-CF #get waveform data if station: dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*HZ.msd' % (project, database, event, station) @@ -68,12 +70,12 @@ def run_makeCF(project, database, event, iplot, station=None): aiccf = AICcf(st_copy, cuttimes) #instance of AICcf ############################################################## #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicpick = AICPicker(aiccf, None, TSNR, 3, 10, None, 0.1) + aicpick = AICPicker(aiccf, None, TSNRhos, 3, 10, None, 0.1) ############################################################## #get refined onset time from HOS-CF using class Picker - hospick = PragPicker(hoscf, None, TSNR, 2, 10, 0.001, 0.2, aicpick.getpick()) + hospick = PragPicker(hoscf, None, TSNRhos, 2, 10, 0.001, 0.2, aicpick.getpick()) #get earliest and latest possible picks - hosELpick = EarlLatePicker(hoscf, 1.5, TSNR, None, 10, None, None, hospick.getpick()) + hosELpick = EarlLatePicker(hoscf, 1.5, TSNRhos, None, 10, None, None, hospick.getpick()) ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction #get stream object of filtered data @@ -88,12 +90,12 @@ def run_makeCF(project, database, event, iplot, station=None): araiccf = AICcf(st_copy, cuttimes, tpredz, 0, tdetz) #instance of AICcf ############################################################## #get onset time from AIC-ARZ-CF using subclass AICPicker of class AutoPicking - aicarzpick = AICPicker(araiccf, 1.5, TSNR, 2, 10, None, 0.1) + aicarzpick = AICPicker(araiccf, 1.5, TSNRarz, 2, 10, None, 0.1) ############################################################## #get refined onset time from ARZ-CF using class Picker - arzpick = PragPicker(arzcf, 1.5, TSNR, 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) + arzpick = PragPicker(arzcf, 1.5, TSNRarz, 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) #get earliest and latest possible picks - arzELpick = EarlLatePicker(arzcf, 1.5, TSNR, None, 10, None, None, arzpick.getpick()) + arzELpick = EarlLatePicker(arzcf, 1.5, TSNRarz, None, 10, None, None, arzpick.getpick()) elif not wfzfiles: print 'No vertical component data found!' @@ -129,12 +131,12 @@ def run_makeCF(project, database, event, iplot, station=None): arhaiccf = AICcf(H_copy, cuttimes, tpredh, 0, tdeth) #instance of AICcf ############################################################## #get onset time from AIC-ARH-CF using subclass AICPicker of class AutoPicking - aicarhpick = AICPicker(arhaiccf, 1.5, TSNR, 4, 10, None, 0.1) + aicarhpick = AICPicker(arhaiccf, 1.5, TSNRarz, 4, 10, None, 0.1) ############################################################### #get refined onset time from ARH-CF using class Picker - arhpick = PragPicker(arhcf, 1.5, TSNR, 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) + arhpick = PragPicker(arhcf, 1.5, TSNRarz, 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) #get earliest and latest possible picks - arhELpick = EarlLatePicker(arhcf, 1.5, TSNR, None, 10, None, None, arhpick.getpick()) + arhELpick = EarlLatePicker(arhcf, 1.5, TSNRarz, None, 10, None, None, arhpick.getpick()) #create stream with 3 traces #merge streams @@ -157,7 +159,7 @@ def run_makeCF(project, database, event, iplot, station=None): #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf #get earliest and latest possible pick from initial ARH-pick - ar3cELpick = EarlLatePicker(ar3ccf, 1.5, TSNR, None, 10, None, None, arhpick.getpick()) + ar3cELpick = EarlLatePicker(ar3ccf, 1.5, TSNRarz, None, 10, None, None, arhpick.getpick()) ############################################################## if iplot: #plot vertical trace From 3507314955a217aab6099c9e0e3e3ce7189926d0 Mon Sep 17 00:00:00 2001 From: Dennis Wlecklik Date: Thu, 5 Mar 2015 11:44:38 +0100 Subject: [PATCH 0262/1144] initial git import of module trigger which introduces simple triggerlist modification functionality --- pylot/core/analysis/trigger.py | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 pylot/core/analysis/trigger.py diff --git a/pylot/core/analysis/trigger.py b/pylot/core/analysis/trigger.py new file mode 100644 index 00000000..add9a687 --- /dev/null +++ b/pylot/core/analysis/trigger.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from obspy.signal.trigger import recSTALTA, triggerOnset + + +def createSingleTriggerlist(st, station='ZV01', trigcomp='Z', stalta=[1, 10], trigonoff=[6, 1]): + ''' + uses a single-station trigger to create a triggerlist for this station + :param st: + :param station: + :param trigcomp: + :param stalta: + :param trigonoff: + :return: + ''' + tr = st.copy().select(component=trigcomp, station=station)[0] + df = tr.stats.sampling_rate + + cft = recSTALTA(tr.data, int(stalta[0] * df), int(stalta[1] * df)) + triggers = triggerOnset(cft, trigonoff[0], trigonoff[1]) + trigg = [] + for time in triggers: + trigg.append(tr.stats.starttime + time[0] / df) + return trigg + + +def createSubCoincTriggerlist(trig, station='ZV01'): + ''' + makes a triggerlist with the events, that are triggered by the + coincidence trigger and are seen at the demanded station + :param trig: list containing triggers from coincidence trigger + :type trig: list + :param station: station name to get triggers for + :type station: str + :return: list of triggertimes + :rtype: list + ''' + trigg = [] + for tri in trig: + if station in tri['stations']: + trigg.append(tri['time']) + return trigg From 5fbd9d7fa95f9e01cbb7277856b0564328b7497b Mon Sep 17 00:00:00 2001 From: Dennis Wlecklik Date: Thu, 5 Mar 2015 11:49:27 +0100 Subject: [PATCH 0263/1144] initial import from coincidence trigger to generate coincidence triggerlists with obspys coincidenceTrigger --- pylot/core/analysis/coinctimes.py | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 pylot/core/analysis/coinctimes.py diff --git a/pylot/core/analysis/coinctimes.py b/pylot/core/analysis/coinctimes.py new file mode 100644 index 00000000..b1486898 --- /dev/null +++ b/pylot/core/analysis/coinctimes.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from obspy.core import read +from obspy.signal.trigger import coincidenceTrigger + + + +class CoincidenceTimes(): + + def __init__(self, st, comp='Z', coinum=4, sta=1., lta=10., on=5., off=1.): + _type = 'recstalta' + self.coinclist = self.createCoincTriggerlist(data=st, trigcomp=comp, + coinum=coinum, sta=sta, + lta=lta, trigon=on, + trigoff=off, type=_type) + + def __str__(self): + n = 1 + out = '' + for time in self.getCoincTimes(): + out += 'event no. {0}: starttime is {1}\n'.format(n, time) + n += 1 + return out + + def getCoincTimes(self): + timelist = [] + for info in self.getCoincList(): + timelist.append(info['time']) + + return timelist + + def getCoincList(self): + return self.coinclist + + def createCoincTriggerlist(self, data, trigcomp, coinum, sta, lta, + trigon, trigoff, type): + ''' + uses a coincidence trigger to detect all events in the given + dataset + ''' + + triggerlist = coincidenceTrigger(type, trigon, trigoff, + data.select(component=trigcomp), + coinum, sta=sta, lta=lta) + return triggerlist + + +def main(): + data = read('/data/SDS/2014/1A/ZV??/?H?.D/*.365') + data.filter(type='bandpass', freqmin=5., freqmax=30.) + coincs = CoincidenceTimes(data) + print(coincs) + + +if __name__ == '__main__': + main() \ No newline at end of file From a86a2efb8aa9bbdb91d6b47bbcc26f6fc3d9cdf8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Thu, 5 Mar 2015 14:52:34 +0100 Subject: [PATCH 0264/1144] debugging in progress (filter waveform not working) --- QtPyLoT.py | 4 ++-- pylot/core/util/widgets.py | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e94f09e3..77706ead 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -375,8 +375,7 @@ class MainWindow(QMainWindow): filterOptions=self.getFilterOptions()) if filterDlg.exec_(): filteroptions = filterDlg.getFilterOptions() - assert isinstance(filteroptions, FilterOptions) - self.setFilterOptions(filteroptions) + self.setFilterOptions(filteroptions) def getFilterOptions(self): try: @@ -447,6 +446,7 @@ class MainWindow(QMainWindow): new = NewEventDlg() if new.exec_() != QDialog.Rejected: evtpar = new.getValues() + self.data = Data(self, evtdata=createEvent(**evtpar)) self.dirty = True diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index ed462466..46225573 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -326,11 +326,10 @@ class NewEventDlg(QDialog): self.buttonBox.rejected.connect(self.reject) def getValues(self): - if self.accepted(): - return {'origintime' : self.eventTimeEdit.dateTime().toPyDateTime(), - 'latitude' : self.latEdit.text(), - 'longitude' : self.lonEdit.text(), - 'depth' : self.depEdit.text()} + return {'origintime' : self.eventTimeEdit.dateTime().toPython(), + 'latitude' : self.latEdit.text(), + 'longitude' : self.lonEdit.text(), + 'depth' : self.depEdit.text()} def setupUI(self): @@ -376,8 +375,10 @@ class FilterOptionsDialog(QDialog): if parent is not None: self.filterOptions = parent.getFilterOptions() - else: + elif filterOptions is not None: self.filterOptions = FilterOptions(filterOptions) + else: + self.filterOptions = FilterOptions() _enable = True if self.getFilterOptions().getFilterType() is None: @@ -397,7 +398,7 @@ class FilterOptionsDialog(QDialog): self.freqmaxSpinBox.setRange(5e-7, 1e6) self.freqmaxSpinBox.setDecimals(2) self.freqmaxSpinBox.setSuffix(' Hz') - self.freqmaxSpinBox.setEnabled(_enable) + if _enable: self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0]) if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: @@ -436,6 +437,8 @@ class FilterOptionsDialog(QDialog): self.freqGroupLayout.addWidget(self.freqmaxSpinBox, 1, 1) self.freqGroupBox.setLayout(self.freqGroupLayout) + self.freqmaxSpinBox.setEnabled(_enable) + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok| QDialogButtonBox.Cancel) From e321ad26b28ab6c23dfd14dae008ff3ba910fc11 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling Date: Fri, 6 Mar 2015 09:03:04 +0100 Subject: [PATCH 0265/1144] make creating new event work --- QtPyLoT.py | 10 ++++++---- pylot/core/util/__init__.py | 2 +- pylot/core/util/utils.py | 15 +++++++-------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 77706ead..82ab3837 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -35,7 +35,7 @@ from pylot.core.read import Data, FilterOptions from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ checkurl, FormatError, FilterOptionsDialog, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ - DatastructureError, createAction, getLogin + DatastructureError, createAction, getLogin, createCreationInfo from pylot.core.util.structure import DATASTRUCTURE @@ -58,7 +58,8 @@ class MainWindow(QMainWindow): fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") settings.setValue("user/FullName", fulluser) settings.setValue("user/Login", getLogin()) - settings.sync() + if settings.value("agency_id", None) is None: + agency = QInputDialog.getText(self, "Enter authority name (e.g. BUG):", "Authority") self.recentEvents = settings.value("data/recentEvents", []) self.fnames = None self.dataStructure = DATASTRUCTURE[ @@ -69,7 +70,7 @@ class MainWindow(QMainWindow): dirname = QFileDialog().getExistingDirectory( caption='Choose data root ...') settings.setValue("data/dataRoot", dirname) - settings.sync() + settings.sync() self.filteroptions = {} @@ -446,7 +447,8 @@ class MainWindow(QMainWindow): new = NewEventDlg() if new.exec_() != QDialog.Rejected: evtpar = new.getValues() - + cinfo = createCreationInfo(agency_id=self.agency) + event = createEvent(evtpar['origintime']) self.data = Data(self, evtdata=createEvent(**evtpar)) self.dirty = True diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index da316777..5248f170 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -4,7 +4,7 @@ from pylot.core.util.errors import OptionsError, FormatError, DatastructureError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ - getHash, getLogin + getHash, getLogin, createCreationInfo, createResourceID from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ PropertiesDlg, NewEventDlg, MPLWidget, createAction from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 9ba5f564..ab37c2b0 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -22,7 +22,6 @@ def fnConstructor(s): fn = '_' + fn return fn - def getLogin(): return pwd.getpwuid(os.getuid())[0] @@ -36,6 +35,13 @@ def getHash(time): hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) return hg.hexdigest() +def createCreationInfo(agency_id=None, creation_time=None, author=None): + if author is None: + author = getLogin() + if creation_time is None: + creation_time = UTCDateTime() + return ope.CreationInfo(agency_id=agency_id, author=author, + creation_time=creation_time) def createResourceID(timetohash, restype, authority_id=None, hrstr=None): ''' @@ -58,7 +64,6 @@ def createResourceID(timetohash, restype, authority_id=None, hrstr=None): resID.convertIDToQuakeMLURI(authority_id=authority_id) return resID - def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, authority_id=None): ''' @@ -88,7 +93,6 @@ def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, origin.depth = depth return origin - def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): ''' createEvent - funtion to create an ObsPy Event @@ -118,7 +122,6 @@ def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): event.event_type = etype return event - def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, wfseedstr, authority_id): ''' @@ -153,7 +156,6 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') return pick - def createArrival(origintime, pickresID, eventnum, cinfo, phase, station, authority_id, azimuth=None, dist=None): ''' @@ -191,7 +193,6 @@ def createArrival(origintime, pickresID, eventnum, cinfo, phase, station, arrival.distance = None return arrival - def createMagnitude(originID, origintime, cinfo, authority_id=None): ''' createMagnitude - function to create an ObsPy Magnitude object @@ -208,7 +209,6 @@ def createMagnitude(originID, origintime, cinfo, authority_id=None): magnitude.origin_id = originID return magnitude - def createAmplitude(pickID, amp, unit, category, origintime, cinfo, authority_id=None): amplresID = createResourceID(origintime, 'ampl', authority_id) @@ -221,7 +221,6 @@ def createAmplitude(pickID, amp, unit, category, origintime, cinfo, amplitude.pick_id = pickID return amplitude - def getOwner(fn): return pwd.getpwuid(os.stat(fn).st_uid).pw_name From 78b41f3d57e368baee1c3453e4a8a6c2d986aa23 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 6 Mar 2015 09:05:52 +0100 Subject: [PATCH 0266/1144] initialization of a picking window (work doubled due to system crash before commit) --- pylot/core/util/widgets.py | 44 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 6b0058be..20d87647 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -73,7 +73,7 @@ class MPLWidget(FigureCanvas): class multiComponentPlot(FigureCanvas): - def __init__(self, parent=None, noc=3, xlabel='x', ylabel='y', title='Title'): + def __init__(self, parent=None, noc=3, xlabel='time in seconds', ylabel=None, title='Title'): super(multiComponentPlot, self).__init__(Figure()) self.setParent(parent) @@ -82,21 +82,55 @@ class multiComponentPlot(FigureCanvas): self.axeslist = [] + if ylabel is None: + ylabel = [str(n) for n in range(3)] + for n in range(noc): nsub = '{0}1{1}'.format(noc, n) if n >= 1: - self.axeslist.insert(n, self.figure.add_subplot(nsub, sharex=self.axeslist[0], sharey=self.axeslist[0])) + subax = self.figure.add_subplot(nsub, + sharex=self.axeslist[0], + sharey=self.axeslist[0]) else: - self.axeslist.insert(n, self.figure.add_subplot(nsub)) + subax = self.figure.add_subplot(nsub) + subax.autoscale(tight=True) + self.axeslist.insert(n, subax) + self.updateYLabel(n, ylabel[n]) + if n == noc: + self.updateXLabel(noc, xlabel) + else: + self.updateXLabel(n, '') + + def insertLabel(self, pos, text): + subax = self.axeslist[pos] + axann = subax.annotate(text, xy=(.03, .97), xycoords='axes fraction') + axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) + + def updateXLabel(self, pos, text): + self.axeslist[pos].set_xlabel(text) + + def updateYLabel(self, pos, text): + self.axeslist[pos].set_ylabel(text) + class PickDlg(QDialog): - def __init__(self, station=None, parent=None): + def __init__(self, parent=None, station=None, rotate=False): super(PickDlg, self).__init__(parent) - pass + self.station = station + self.rotate = rotate + _toplayout = QVBoxLayout() + _layout = QHBoxLayout() + multicompfig = multiComponentPlot() + _layout.addWidget() + + def plotData(self, data): + data.select(station=self.station) + if self.rotate: + data.rotate class PropertiesDlg(QDialog): From c7aeb1959b49531063d06ee0a1c6e069ee1c4f22 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Mar 2015 11:21:33 +0100 Subject: [PATCH 0267/1144] implement picking window and station selection (tests pending due to not working station selection so far) --- QtPyLoT.py | 19 ++++++- pylot/core/util/widgets.py | 109 +++++++++++++++++++++++++++---------- 2 files changed, 99 insertions(+), 29 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 82ab3837..4765be91 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -35,7 +35,7 @@ from pylot.core.read import Data, FilterOptions from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ checkurl, FormatError, FilterOptionsDialog, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ - DatastructureError, createAction, getLogin, createCreationInfo + DatastructureError, createAction, getLogin, createCreationInfo, PickDlg from pylot.core.util.structure import DATASTRUCTURE @@ -73,6 +73,7 @@ class MainWindow(QMainWindow): settings.sync() self.filteroptions = {} + self.pickDlgs = {} # UI has to be set up before(!) children widgets are about to show up self.setupUi() @@ -322,6 +323,9 @@ class MainWindow(QMainWindow): def getPlotWidget(self): return self.DataPlot + def getWFID(self): + return self.getPlotWidget().getStatID() + def addActions(self, target, actions): for action in actions: if action is None: @@ -417,6 +421,10 @@ class MainWindow(QMainWindow): def getSeismicPhase(self): return self.seismicPhase + def getStationName(self, wfID): + data = self.getData().copy().select(component=self.getComponent()) + return data[wfID].stats.station + def alterPhase(self): pass @@ -425,6 +433,15 @@ class MainWindow(QMainWindow): self.updateStatus('Seismic phase changed to ' '{0}'.format(self.getSeismicPhase())) + def pickOnStation(self): + + wfID = self.getWFID() + + station = self.getStationName(wfID) + self.pickDlgs[wfID] = PickDlg(self, + self.getData().select(station=station), + station) + def updateStatus(self, message): self.statusBar().showMessage(message, 5000) if self.getData() is not None: diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b93bfb5c..d5d5fa1b 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -36,8 +36,8 @@ from PySide.QtGui import (QAction, from PySide.QtCore import (QSettings, Qt, QUrl, - SIGNAL, - SLOT) + Signal, + Slot) from PySide.QtWebKit import QWebView from pylot.core.read import FilterOptions from pylot.core.util.defaults import OUTPUTFORMATS @@ -66,14 +66,35 @@ class MPLWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): super(MPLWidget, self).__init__(Figure()) + self._parent = None self.setParent(parent) self.figure = Figure() self.canvas = FigureCanvas(self.figure) + self.canvas.mpl_connect('button_press_event', self.emitSelection) self.axes = self.figure.add_subplot(111) self.axes.autoscale(tight=True) + self._statID = None self.updateWidget(xlabel, ylabel, title) + def emitSelection(self, event): + + self._statID = round(event.ydata) + if self.getParent(): + self.getParent().pickOnStation() + + def getStatID(self): + if self._statID is None: + return + else: + return self._statID + + def getParent(self): + return self._parent + + def setParent(self, parent): + self._parent = parent + def updateXLabel(self, text): self.axes.set_xlabel(text) @@ -91,7 +112,7 @@ class MPLWidget(FigureCanvas): class multiComponentPlot(FigureCanvas): - def __init__(self, parent=None, components='ZNE', xlabel='x', ylabel='y', title='Title'): + def __init__(self, parent=None, components='ZNE'): super(multiComponentPlot, self).__init__(Figure()) self.setParent(parent) @@ -101,20 +122,27 @@ class multiComponentPlot(FigureCanvas): self.axeslist = [] + def plotData(self, components, data): + if self.axeslist: + self.axeslist = [] + self.figure.clf() + xlabel = 'time since {0} [s]'.format(data[0].stats.starttime) for n, comp in enumerate(components): nsub = '{0}1{1}'.format(self.noc, n) if n >= 1: - self.axeslist.insert(n, - self.figure.add_subplot(nsub, - sharex=self.axeslist[0], - sharey=self.axeslist[0])) + self.axeslist.insert( + n, + self.figure.add_subplot(nsub, + sharex=self.axeslist[0], + sharey=self.axeslist[0]) + ) else: subax = self.figure.add_subplot(nsub) subax.autoscale(tight=True) self.axeslist.insert(n, subax) - self.updateYLabel(n, ylabel[n]) - if n == noc: - self.updateXLabel(noc, xlabel) + self.updateYLabel(n, comp) + if n == self.noc: + self.updateXLabel(self.noc, xlabel) else: self.updateXLabel(n, '') @@ -133,19 +161,26 @@ class multiComponentPlot(FigureCanvas): class PickDlg(QDialog): - def __init__(self, parent=None, station=None, rotate=False): + def __init__(self, parent=None, data=None, station=None, rotate=False): super(PickDlg, self).__init__(parent) self.station = station self.rotate = rotate self.components = 'ZNE' - self.data = parent.getData().getWFData().copy() - - multicompfig = multiComponentPlot() - _layout.addWidget() - def plotData(self, data): + if data is None: + try: + data = parent.getData().getWFData().copy() + self.data = data.select(station=station) + except AttributeError, e: + errmsg = 'You either have to put in a data or an appropriate ' \ + 'parent (PyLoT MainWindow) object: {0}'.format(e) + raise Exception() + else: + self.data = data self.setupUi() + self.multicompfig = multiComponentPlot(parent=self, + components=self.getComponents()) def setupUi(self): @@ -168,15 +203,33 @@ class PickDlg(QDialog): _dialtoolbar.addWidget(self.selectPhase) _innerlayout = QHBoxLayout() + _toolslayout = QVBoxLayout() + _toolslabel = QLabel('Place for Tools') + _toolslayout.addWidget(_toolslabel) + + _innerlayout.addLayout(_toolslayout) + _innerlayout.addWidget(self.multicompfig) + + _outerlayout.addWidget(_dialtoolbar) + _outerlayout.addLayout(_innerlayout) + + + def getComponents(self): + return self.components + + def getPlotWidget(self): + return self.multicompfig + + def getWFData(self): + return self.data def filterWFData(self): self.data.filterWFData() self.plotData() def plotData(self): - for comp in self.components: - self.getPlotWidget() + self.getPlotWidget().plotData(self.getComponents(), self.getWFData()) class PropertiesDlg(QDialog): @@ -201,12 +254,12 @@ class PropertiesDlg(QDialog): layout.addWidget(self.buttonBox) self.setLayout(layout) - self.connect(self.buttonBox, SIGNAL("accepted()"), self, - SLOT("accept()")) + self.connect(self.buttonBox, Signal("accepted()"), self, + Slot("accept()")) self.connect(self.buttonBox.button(QDialogButtonBox.Apply), - SIGNAL("clicked()"), self.apply) - self.connect(self.buttonBox, SIGNAL("rejected()"), - self, SLOT("reject()")) + Signal("clicked()"), self.apply) + self.connect(self.buttonBox, Signal("rejected()"), + self, Slot("reject()")) def accept(self, *args, **kwargs): self.apply() @@ -549,11 +602,11 @@ class HelpForm(QDialog): layout.addWidget(self.webBrowser, 1) self.setLayout(layout) - self.connect(backAction, SIGNAL("triggered()"), - self.webBrowser, SLOT("backward()")) - self.connect(homeAction, SIGNAL("triggered()"), - self.webBrowser, SLOT("home()")) - self.connect(self.webBrowser, SIGNAL("sourceChanged(QUrl)"), + self.connect(backAction, Signal("triggered()"), + self.webBrowser, Slot("backward()")) + self.connect(homeAction, Signal("triggered()"), + self.webBrowser, Slot("home()")) + self.connect(self.webBrowser, Signal("sourceChanged(QUrl)"), self.updatePageTitle) self.resize(400, 600) From a3fb4770c617cf06a1c8ab5a48475334e5d3822d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 9 Mar 2015 16:11:52 +0100 Subject: [PATCH 0268/1144] Modified to apply and show symmetric picking error derived from EarlLatePicker.py with new attribute getPickError. --- pylot/core/pick/run_makeCF.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index e57f80d2..5b801715 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -191,7 +191,7 @@ def run_makeCF(project, database, event, iplot, station=None): plt.ylim([-1.5, 1.5]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') - plt.title('%s, %s, AIC-SNR=%7.2f, AIC-Slope=%12.2f' % (tr.stats.station, \ + plt.title('%s, %s, CF-SNR=%7.2f, CF-Slope=%12.2f' % (tr.stats.station, \ tr.stats.channel, aicpick.getSNR(), aicpick.getSlope())) plt.suptitle(tr.stats.starttime) plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) @@ -213,6 +213,10 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') + plt.plot([arhpick.getpick() + arhELpick.getPickError(), arhpick.getpick() + arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') + plt.plot([arhpick.getpick() - arhELpick.getPickError(), arhpick.getpick() - arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') @@ -231,6 +235,10 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') + plt.plot([arhpick.getpick() + arhELpick.getPickError(), arhpick.getpick() + arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') + plt.plot([arhpick.getpick() - arhELpick.getPickError(), arhpick.getpick() - arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) plt.yticks([]) plt.ylim([-1.5, 1.5]) From 380cccdf16dddca5896ba09a3f8ff56c98ed8554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 9 Mar 2015 16:14:03 +0100 Subject: [PATCH 0269/1144] New attribute getPickError in class EarlLatePicker to derive symmetric picking error out of earliest and latest possible and most probable pick. --- pylot/core/pick/Picker.py | 59 +++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 11e1b6a6..b88dd3ba 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -133,6 +133,9 @@ class AutoPicking(object): def getEpick(self): return self.EPick + def getPickError(self): + return self.PickError + def getiplot(self): return self.iplot @@ -151,15 +154,18 @@ class AutoPicking(object): class AICPicker(AutoPicking): ''' - Method to derive onset time of arriving phase based on CF - derived from AIC. + Method to derive the onset time of an arriving phase based on CF + derived from AIC. In order to get an impression of the quality of this inital pick, + a quality assessment is applied based on SNR and slope determination derived from the CF, + from which the AIC has been calculated. ''' def calcPick(self): - print 'AICPicker: Get onset time (pick) from AIC-CF ...' + print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' self.Pick = None + self.PickError = None #find NaN's nn = np.isnan(self.cf) if len(nn) > 1: @@ -279,6 +285,7 @@ class PragPicker(AutoPicking): print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' self.Pick = None + self.PickError = None self.SNR = None self.slope = None #smooth CF @@ -377,6 +384,7 @@ class EarlLatePicker(AutoPicking): self.LPick = None self.EPick = None + self.PickError = None self.SNR = None self.slope = None if self.getpick1() is not None: @@ -509,6 +517,12 @@ class EarlLatePicker(AutoPicking): #Ts/4 is assumed as time difference between most likely and earliest possible pick! self.EPick = ti - Ts/4 + #get symmetric pick error as mean from earliest and latest possible pick + #by weighting latest possible pick tow times earliest possible pick + diffti_tl = self.LPick - ti + diffti_te = ti - self.EPick + self.PickError = (diffti_te + 2 * diffti_tl) / 3 + if self.iplot is not None: plt.figure(self.iplot) if len(x) == 1: @@ -517,14 +531,19 @@ class EarlLatePicker(AutoPicking): p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings']) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + loc='best') plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') + plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.yticks([]) - plt.title('Earliest-/Latest Possible and Most Likely Pick, %s' % self.Data[0].stats.station) + ax = plt.gca() + ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) + plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % self.Data[0].stats.station) elif len(x) == 2: plt.subplot(2,1,1) p1, = plt.plot(t, x[0].data, 'k') @@ -532,14 +551,19 @@ class EarlLatePicker(AutoPicking): p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') p5, = plt.plot(zc1[0:3], [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings']) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + loc='best') plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') + plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') plt.plot(zc1[0:3], [0, 0, 0], '*g') plt.yticks([]) - plt.title('Earliest-/Latest Possible and Most Likely Pick, %s' % self.Data[0].stats.station) + ax = plt.gca() + ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) + plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % self.Data[0].stats.station) plt.subplot(2,1,2) plt.plot(t, x[1].data, 'k') plt.plot(t[inoise], x[1].data[inoise]) @@ -549,8 +573,12 @@ class EarlLatePicker(AutoPicking): plt.plot([ti, ti], [max(x[1].data), -max(x[1].data)], 'b', linewidth=2) plt.plot([self.LPick, self.LPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') plt.plot([self.EPick, self.EPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') + plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') + plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') plt.plot(zc2[0:3], [0, 0, 0], '*g', markersize=14) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + ax = plt.gca() + ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) plt.yticks([]) elif len(x) == 3: plt.subplot(3,1,1) @@ -559,13 +587,18 @@ class EarlLatePicker(AutoPicking): p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') p5, = plt.plot(zc1[0:3], [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings']) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + loc='best') plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') + plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') + plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') plt.yticks([]) - plt.title('Earliest-/Latest Possible and Most Likely Pick, %s' % self.Data[0].stats.station) + ax = plt.gca() + ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) + plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % self.Data[0].stats.station) plt.subplot(3,1,2) plt.plot(t, x[1].data, 'k') plt.plot(t[inoise], x[1].data[inoise]) @@ -575,8 +608,12 @@ class EarlLatePicker(AutoPicking): plt.plot([ti, ti], [max(x[1].data), -max(x[1].data)], 'b', linewidth=2) plt.plot([self.LPick, self.LPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') plt.plot([self.EPick, self.EPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') + plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') + plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') plt.plot(zc2[0:3], [0, 0, 0], '*g', markersize=14) plt.yticks([]) + ax = plt.gca() + ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) plt.subplot(3,1,3) plt.plot(t, x[2].data, 'k') plt.plot(t[inoise], x[2].data[inoise]) @@ -586,9 +623,13 @@ class EarlLatePicker(AutoPicking): plt.plot([ti, ti], [max(x[2].data), -max(x[2].data)], 'b', linewidth=2) plt.plot([self.LPick, self.LPick], [max(x[2].data)/2, -max(x[2].data)/2], '--k') plt.plot([self.EPick, self.EPick], [max(x[2].data)/2, -max(x[2].data)/2], '--k') + plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') + plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') plt.plot(zc3[0:3], [0, 0, 0], '*g', markersize=14) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.yticks([]) + ax = plt.gca() + ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) plt.show() raw_input() plt.close(self.iplot) From ea68b38f7e95c19825b5581f7617e92b7bacf170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 10 Mar 2015 16:18:32 +0100 Subject: [PATCH 0270/1144] Stabilized AICPicker by introducing 1st derivative of CF to find global maximum. --- pylot/core/pick/Picker.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index b88dd3ba..daf8d7f2 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -52,7 +52,7 @@ class AutoPicking(object): :type: float :param: Pick1, initial (prelimenary) onset time, starting point for PragPicker and - EarlLatePick + EarlLatePicker :type: float ''' @@ -189,8 +189,16 @@ class AICPicker(AutoPicking): #remove offset offset = abs(min(aic) - min(aicsmooth)) aicsmooth = aicsmooth - offset - #get maximum of CF as starting point - icfmax = np.argmax(aic) + #get maximum of 1st derivative of CF (more stable!) as starting point + diffcf = np.diff(aicsmooth) + #find NaN's + nn = np.isnan(diffcf) + if len(nn) > 1: + diffcf[nn] = 0 + #taper CF to get rid off side maxima + tap = np.hanning(len(diffcf)) + diffcf = tap * diffcf * max(abs(aicsmooth)) + icfmax = np.argmax(diffcf) #find minimum in front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) @@ -222,6 +230,11 @@ class AICPicker(AutoPicking): #find maximum within slope determination window #'cause slope should be calculated up to first local minimum only! imax = np.argmax(self.Data[0].data[islope]) + if imax == 0: + print 'AICPicker: Maximum for slope determination right at the beginning of the window!' + print 'Choose longer slope determination window!' + pdb.set_trace() + return islope = islope[0][0 :imax] dataslope = self.Data[0].data[islope] #calculate slope as polynomal fit of order 1 From 85f0445e6b51c87d31e045b062c88a664a596c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 10 Mar 2015 16:48:48 +0100 Subject: [PATCH 0271/1144] Stabilized AICPicker: if no minimum was found, try 1st derivative of AIC-CF. --- pylot/core/pick/Picker.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index daf8d7f2..25ef1af0 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -189,7 +189,7 @@ class AICPicker(AutoPicking): #remove offset offset = abs(min(aic) - min(aicsmooth)) aicsmooth = aicsmooth - offset - #get maximum of 1st derivative of CF (more stable!) as starting point + #get maximum of 1st derivative of AIC-CF (more stable!) as starting point diffcf = np.diff(aicsmooth) #find NaN's nn = np.isnan(diffcf) @@ -200,12 +200,19 @@ class AICPicker(AutoPicking): diffcf = tap * diffcf * max(abs(aicsmooth)) icfmax = np.argmax(diffcf) - #find minimum in front of maximum + #find minimum in AIC-CF front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): if aicsmooth[i - 1] >= aicsmooth[i]: self.Pick = self.Tcf[i] break + #if no minimum could be found: + #search in 1st derivative of AIC-CF + if self.Pick is None: + for i in range(icfmax -1, max([icfmax -lpickwindow, 2]), -1): + if diffcf[i -1] >= diffcf[i]: + self.Pick = self.Tcf[i] + break #quality assessment using SNR and slope from CF if self.Pick is not None: @@ -233,7 +240,6 @@ class AICPicker(AutoPicking): if imax == 0: print 'AICPicker: Maximum for slope determination right at the beginning of the window!' print 'Choose longer slope determination window!' - pdb.set_trace() return islope = islope[0][0 :imax] dataslope = self.Data[0].data[islope] From 486449fbb5c1a92ccc93e6bfb678e7eb937019e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Mar 2015 12:01:06 +0100 Subject: [PATCH 0272/1144] Debuged EarLatePicker noise level exceedance part. --- pylot/core/pick/Picker.py | 61 ++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 25ef1af0..dc928a85 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -262,8 +262,11 @@ class AICPicker(AutoPicking): x = self.Data[0].data p1, = plt.plot(self.Tcf, x / max(x), 'k') p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') - p3, = plt.plot([self.Pick, self.Pick], [-1 , 1], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) + if self.Pick is not None: + p3, = plt.plot([self.Pick, self.Pick], [-1 , 1], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) + else: + plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.yticks([]) plt.title(self.Data[0].stats.station) @@ -442,9 +445,24 @@ class EarlLatePicker(AutoPicking): ilup2 = np.where(x[1].data[isignal] > nlevel) ildown1 = np.where(x[0].data[isignal] < -nlevel) ildown2 = np.where(x[1].data[isignal] < -nlevel) - ilup = min([ilup1[0][0], ilup2[0][0]]) - ildown = min([ildown1[0][0], ildown2[0][0]]) - if np.size(ilup) < 1 and np.size(ildown) < 1: + if np.size(ilup1) < 1 and np.size(ilup2) > 1: + ilup = ilup2 + elif np.size(ilup1) > 1 and np.size(ilup2) < 1: + ilup = ilup1 + elif np.size(ilup1) < 1 and np.size(ilup2) < 1: + ilup = None + else: + ilup = min([ilup1[0][0], ilup2[0][0]]) + + if np.size(ildown1) < 1 and np.size(ildown2) > 1: + ildown = ildown2 + elif np.size(ildown1) > 1 and np.size(ildown2) < 1: + ildown = ildown1 + elif np.size(ildown1) < 1 and np.size(ildown2) < 1: + ildown = None + else: + ildown = min([ildown1[0][0], ildown2[0][0]]) + if ilup == None and ildown == None: print 'EarlLatePicker: Signal lower than noise level, misspick?' return il = min([ilup, ildown]) @@ -459,9 +477,36 @@ class EarlLatePicker(AutoPicking): ildown1 = np.where(x[0].data[isignal] < -nlevel) ildown2 = np.where(x[1].data[isignal] < -nlevel) ildown3 = np.where(x[2].data[isignal] < -nlevel) - ilup = min([ilup1[0][0], ilup2[0][0], ilup3[0][0]]) - ildown = min([ildown1[0][0], ildown2[0][0], ildown3[0][0]]) - if np.size(ilup) < 1 and np.size(ildown) < 1: + if np.size(ilup1) > 1 and np.size(ilup2) < 1 and np.size(ilup3) < 1: + ilup = ilup1 + elif np.size(ilup1) > 1 and np.size(ilup2) > 1 and np.size(ilup3) < 1: + ilup = min([ilup1[0][0], ilup2[0][0]]) + elif np.size(ilup1) > 1 and np.size(ilup2) > 1 and np.size(ilup3) > 1: + ilup = min([ilup1[0][0], ilup2[0][0], ilup3[0][0]]) + elif np.size(ilup1) < 1 and np.size(ilup2) > 1 and np.size(ilup3) > 1: + ilup = min([ilup2[0][0], ilup3[0][0]]) + elif np.size(ilup1) > 1 and np.size(ilup2) < 1 and np.size(ilup3) > 1: + ilup = min([ilup1[0][0], ilup3[0][0]]) + elif np.size(ilup1) < 1 and np.size(ilup2) < 1 and np.size(ilup3) < 1: + ilup = None + else: + ilup = min([ilup1[0][0], ilup2[0][0], ilup3[0][0]]) + + if np.size(ildown1) > 1 and np.size(ildown2) < 1 and np.size(ildown3) < 1: + ildown = ildown1 + elif np.size(ildown1) > 1 and np.size(ildown2) > 1 and np.size(ildown3) < 1: + ildown = min([ildown1[0][0], ildown2[0][0]]) + elif np.size(ildown1) > 1 and np.size(ildown2) > 1 and np.size(ildown3) > 1: + ildown = min([ildown1[0][0], ildown2[0][0], ildown3[0][0]]) + elif np.size(ildown1) < 1 and np.size(ildown2) > 1 and np.size(ildown3) > 1: + ildown = min([ildown2[0][0], ildown3[0][0]]) + elif np.size(ildown1) > 1 and np.size(ildown2) < 1 and np.size(ildown3) > 1: + ildown = min([ildown1[0][0], ildown3[0][0]]) + elif np.size(ildown1) < 1 and np.size(ildown2) < 1 and np.size(ildown3) < 1: + ildown = None + else: + ildown = min([ildown1[0][0], ildown2[0][0], ildown3[0][0]]) + if ilup == None and ildown == None: print 'EarlLatePicker: Signal lower than noise level, misspick?' return il = min([ilup, ildown]) From a0bbe8ca0446363c56679bfe699ab829d501981f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 Mar 2015 12:05:52 +0100 Subject: [PATCH 0273/1144] trying to get the picking of plot coordinates working (pending for poster preparation) --- QtPyLoT.py | 50 +++++++++++++++++++++++++------------ icons/pick.png | Bin 0 -> 22846 bytes pylot/core/read/data.py | 1 + pylot/core/util/widgets.py | 19 +++----------- qrc_resources.py | 8 +++--- resources.qrc | 13 ++++++---- 6 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 icons/pick.png diff --git a/QtPyLoT.py b/QtPyLoT.py index 4765be91..1ecab769 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -25,7 +25,8 @@ https://www.iconfinder.com/iconsets/flavour import sys -from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, QFileInfo +from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ + QFileInfo, Qt from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ QDialog, QErrorMessage, QApplication @@ -37,8 +38,7 @@ from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ DatastructureError, createAction, getLogin, createCreationInfo, PickDlg from pylot.core.util.structure import DATASTRUCTURE - - +import qrc_resources # Version information __version__ = _getVersionString() @@ -60,6 +60,7 @@ class MainWindow(QMainWindow): settings.setValue("user/Login", getLogin()) if settings.value("agency_id", None) is None: agency = QInputDialog.getText(self, "Enter authority name (e.g. BUG):", "Authority") + settings.setValue("agency_id", agency) self.recentEvents = settings.value("data/recentEvents", []) self.fnames = None self.dataStructure = DATASTRUCTURE[ @@ -106,6 +107,7 @@ class MainWindow(QMainWindow): xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') _widget = QWidget() + _widget.setCursor(Qt.CrossCursor) _layout = QHBoxLayout() plottitle = "Overview: {0} components ".format(self.getComponent()) @@ -121,24 +123,29 @@ class MainWindow(QMainWindow): saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) newIcon = self.style().standardIcon(QStyle.SP_FileIcon) + pickIcon = QIcon(':/pick.png') newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, QKeySequence.New, newIcon, "Create a new event.") - openEventAction = self.createAction(self, "&Open event ...", self.loadData, - QKeySequence.Open, openIcon, - "Open an event.") + openEventAction = self.createAction(self, "&Open event ...", + self.loadData, QKeySequence.Open, + openIcon, "Open an event.") openEventAction.setData(None) - saveEventAction = self.createAction(self, "&Save event ...", self.saveData, - QKeySequence.Save, saveIcon, - "Save actual event data.") + saveEventAction = self.createAction(self, "&Save event ...", + self.saveData, QKeySequence.Save, + saveIcon, "Save actual event data.") openWFDataAction = self.createAction(self, "Open &waveforms ...", self.loadWaveformData, "Ctrl+W", QIcon(":/wfIcon.png"), """Open waveform data (event will be closed).""") - - prefsEventAction = self.createAction(self, "Preferences", self.PyLoTprefs, + selectStation = self.createAction(self, "Select station", + self.pickOnStation, "Alt+P", pickIcon, + "Select a station from overview " + "plot for picking") + prefsEventAction = self.createAction(self, "Preferences", + self.PyLoTprefs, QKeySequence.Preferences, QIcon(None), "Edit PyLoT app preferences.") @@ -197,6 +204,11 @@ class MainWindow(QMainWindow): phaseToolBar.setObjectName("PhaseTools") self.addActions(phaseToolBar, phaseToolActions) + pickToolBar = self.addToolBar("PickTools") + pickToolActions = (selectStation, ) + pickToolBar.setObjectName("PickTools") + self.addActions(pickToolBar, pickToolActions) + self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) status = self.statusBar() @@ -205,8 +217,9 @@ class MainWindow(QMainWindow): status.showMessage("Ready", 500) _widget.setLayout(_layout) - self.setCentralWidget(_widget) + _widget.showFullScreen() + self.setCentralWidget(_widget) def updateFileMenu(self): @@ -323,8 +336,13 @@ class MainWindow(QMainWindow): def getPlotWidget(self): return self.DataPlot - def getWFID(self): - return self.getPlotWidget().getStatID() + def getWFID(self, event): + + ycoord = event.ydata + + statID = round(ycoord) + + return statID def addActions(self, target, actions): for action in actions: @@ -433,9 +451,9 @@ class MainWindow(QMainWindow): self.updateStatus('Seismic phase changed to ' '{0}'.format(self.getSeismicPhase())) - def pickOnStation(self): + def pickOnStation(self, event): - wfID = self.getWFID() + wfID = self.getWFID(event) station = self.getStationName(wfID) self.pickDlgs[wfID] = PickDlg(self, diff --git a/icons/pick.png b/icons/pick.png new file mode 100644 index 0000000000000000000000000000000000000000..10f1fc9419746123d357152b94385373795376ae GIT binary patch literal 22846 zcmXtg1yCE$`#uDR;$E~tTHIY*f@^``5~NVvrDzDHXrZ`k(O@Y~DUcLzixmqVyl9aR zQuNRFKl7WLyPMgW+kN-F_uYN=+2=0aKu?{N_$e_K78a?d2FMT#3)}4R)F;G$EFtWj zb$Gnt+iRnyzJjDNF8)_FU$ z=WNUp1STS^CIFR_n0@)xmUdSN*u*+t@-&z+V)@HQ&@%6)kIlU}ikEo!0e;W-?GOuk zF@cLMlDgvvp~fiC68h)LF8xS4mIMG$_>Q?cRhFUxOS2jyhg(cD{3duq%d;|)RUg&g z@-Z~tnebf3t}qZvvUh9t8QO%Is+9bZQNjCH5i>#qJF>IY~k5Gohq& z`&CA*%qmThBXQy8AN7Ocu0zE}JT9ik3cAZ>iLM9{&yQb2Vz`6rEOc?xsHk6#MJ#1i z6}KkdORnCJ@ryI!;=)MZ%GY>05(SV$7P${_P#_8lLdg=(_p+?$nY6tlR56ybVS6G|9j@J|EuIN7sdec zk2|F0CB_;O1$Ghr;Z+m4-|+IC7C=kfXTaV1E0(d!K`1*KOUTfJKoOrg-n``pG0}ex zOYvGBe`)R>4)Cc!v%{?u#-I2M{zgF_Tau>O7sn(b?Q+0dEqXs62;_<7o!U*Ixo}yb zZv#+e1hxxsMiL^Ft$?J)16{v3E{6xkN|^?MqDTubf}oISGF&EuEHhKw zo+n}0W)gVq|9N7ksySXOfmw*X`2N>vlehpGj2UJhd4<371BeO4aAPFF;q@~%s>Ii@ zL;>mAM88La_C#~I%?uT?epAYwBmJ@ryZ}ce@hVUC4A;MS2_{uO+QZQ=JDUg4E4lYH zxkGj%N0GYlYpY-mZ0smVew5jw$O_gffl2K~#cC2EZHEe&KBoQk`v8g}{h6FPwM;rb zGz3V9yO`FWheHO?dtYHV&J{xs)sA8UpHCjs0UjezlB4s8pa7<6Qz(uP-G`FY8tf zJ_fbAL~Pl7HAZYH4O7@j1LfMxiSbAKUv5COUVQ{g6z=XUUTzqB9lTl2nY|d~Vd)wE zV~l#>*&hP*@OxfUX9p}4|j$3 z?SWYhvSFq#S4nZ1@{nCqhgi3C!TNxzzj$LfV+J$krL8!RzGx%_ML$VNk+7Cbu!b7~-=b+FMh~-DC zprNb02G+BhzgRuBaPk-vvKJ!U*8TdwwIty-Ok5WYEhS7e?gj`j<-RY_mxxwGK|0&=N9KFelAo<>qfbd7FHvp6U)r#pu~(B$TEw6#;PchenE&q? z-cCV+Tgod9ohoMBvHq*(i`ZUTZ9nO}nY;B-FI|15W9?Na$D4yZQ2Vh@5{3v=}T z<$&u`VvcAjCKlehFxAB!;K)-@cV0MXq1+jVCC>JYo(3nsz=MpfG0P0yg$Dyow*m&| z1xmoQb-IHm);eWb(z07BNISSJhkp$e3eDm&%h$$GVW13pbBQC^H%5=Cwp_zA?dBjd zIL3P;YFWV^Owq!NN*ET$SU@%y^n#SUJK*<1{+}y3=0>CLgl*XMu^ZR2OiSo|bsPi% z+53-iqza==Erixa)kqY<$&Feu+3-Aa>uR1aw1~oGEIOJ`hfgrHw^W_~3pp*{J6>*| zfco(pZiFqNk4Fg2xHmUoae)-_nsTiNwDx^gcK?d+N6)V=ml!}&{VPPE$i5Y(nnCZf zgln$-RlAF8lJkF@d(_bSLwp3oMp4jnCzKd_Nhi^4+WtFasz|{R6=v8#q?rprbaa%s zL83#P7<@8NJG|3Kk=iByW3U4a*_SNXE6>O$)7>R-8#%va7!Dx3`T%ErI^6mO@$Tc0 zK6|m!ll0`%H`5`f7){D| zI-q3QRImy)0c=#X^IjI;w-DRC0v_~U@V6lYL0$1gr_`V$I1~&zqDhT*hn3p5%-35; z)rVATXKOU9ePIC4k>V~|ZC+PA)em4nc)VEs-L%)*P3GW9kMYIGP|6sB2H#hbR#vyuTlNB&7N`&@2{Im)sgg^F79T#X*==#2FJ7$m)$^r$^X z8GIKt1ou)t`*a{l`t{;R>OZV2VnjhzJiBJmu#C-f3iny25~x+LL7$5ao?;0+l8kxk zG?*&5GiuIuj5sEre^T$lAm=5jGX8TDFPgve9$)&~hpBH*4$9$;xSlR+FWIRwWpau6 z$?IHZ4n720cE4YbHw z&6%pu6IKIAppgp#z@pMxDl4#Y%xZ`8gt*#W;cu0!k(W6V$F?B zYJKx1XPmcnaDj&Vu-U{}{$mTwN2n+_EIzOnzYv*~b-TB=jmP~L71Jh!BX0=-=44Y* zqgDmpjk&ymd5oi#rQ6he!%@6*$_>~)rK@>VN3B|c>hA?6*FeIWkBDRkX$$2uToAMl z{1r$W7B@)a9*u-9J7;k%^t>K?phS|G6J!1O#akQ~MsxL=v?^Hf#T-;NhsqN)PwP;7 zhyBU;)kPpCJ*PD;C9Q`U`?W`dV3&ZKGhr>(6m&-T$c2yoljl6+hq!_iBF^jdxH=X> zNgLmAp~||pzjtbz*C7}vRCbLEte^^BP1JO|Qr9^x>G%TRHd_lqT8z%d0h{Exee^Oq zTtvMKNq=ZO+*K{(^8wR^JI&_KZ?F#QvM+$JuFKppoiQ@Kh#{t{^cq$=DrYov|l zQ+Qdp5jbT4SG>Ti={x-;?w?^InQ4pFcf0t)h2Yc!vammVR@q+yhfo1hx-|ybJqS&Moot$lJH@* zCk)q+UK^;p&_74l%cFQ@GMI&lI`FTORYGlfflPR?0FHusk%$PzxLYeD{f1diZ#r)d z;fj92=i+8DxYKGaGrS7}@M|MC*$%8f@jhXZ^c{RS+|RK+@h`mPJ>{Jy-L%-zdm7HP zH?(IXsht#Pb@1vKR%ZkD5B^>7x;iv&`8Uv598AFK+^la zoizSOnNdIG|8B8BRv!mhc+Rg^i$mUDvxnc3tFIn}2+xV?iJKfG0y5RrL) zUlWEya+V62W`lVL_(KkIDn6}3`-S61NJ4oB{VM5_njQb^Y*8qOuQ(`iE204#WwT## z`GCdP7mrS@g@;5Yj0Cu0B7VePgA>h>Meu76FbijiATjR;pG*hN&&CNIrW1R2T(Bk?Zn+#A|)$)3E34vJENCZ3!<6X;I?ZB~)l zkez>@JGR8^V@1qqw7017qR)K%zzN>jcdk{LkJ!qm@2PE{%PGi5X-E7OtPFR?BgdOEGq zy>r2l#VGzV-IAAyR$Ll^+z6ovyMCo=6qi7V5+zEl|7q$$LKb8w<62|;sq!m6tlnd3 zf68MKaABdLuL7fV*I;sRiUiRTrSuCVLEvDs*q~i6-NA==_<_!3^BqC2)^CTpnv$ci zIYPV}zT2?HWW$*%L#+mNyYElkP{nn=;c^q8sn667uXy=usQz`&*3p3jG{C*lH2?$c z^~1ub)hSJ;!gyY^)uhSpd;{*-dB@T=x@#5YoDwu!s^4;v@mQ`GVpvM=F4I`b@QkZ z_`$X6=Ijw>RPC}g#Ag=33LV=gi)#p^p9D3=3u4+o3vknI*)r6>4^2)T_)$R?EN+W#~$uk9P_gEgl zULuD`+b>5a(le}f!{Q-_??cu;m7@U$qdJxLnkSRjdh57uVjqileEz(`B=T_1WkP%H z41fQFC_0JkFCd)-G#e&6_Fu5iUz9(h%LDB!N4AEwo)lF5SNC(={ntd_(>Vqm3_qLr zDNWj&NQ67r+%(3*?qmi{f6)$A3XDVJbXtDQwBO%a=7a$*@f1nkPE220Cj}u7i>m(g z_g^*UrtNf6_nsPdC1|Vz1SkDt+CVY-IXf)yJh+81P;^l7=U7oyFeQ4%iHO*&$zw<) zjn~_C@)1fY@U86!Ex17Z$zPZDl%FIom*tY?aB`V7zwA<31?1Gg{~G`M#e$s4Ih|9? ziPl`X^L%nh!H8efR4%i)w%lq*SL%eZA$o&-+_Nshx-Z1^MmCWcE5lOB!m&N5tj%C% z&G+DtD9{2mjrJJ;Vn}#4qe$G?=W>zi7KK(GI#*wjH-~MM+`YSdv}B=l>6-lPHd|5< z4qmT5{@V_FQEw`5{)s>Sr6j_cZzFetmq0&X7i;bfrs=?rUF71ddmzW)`(ZlQbv0zS-T^D!;4i|zs%PK}+U%Kz}Os$05014?0&V-_>{~l}v;npAb&0xQYos<-9SdVTfjCkpr*Y*o(=XL!QV`1(D?#No=*|^R|lC!a-r7 z1XvTVQ5DC}0sLu(EgnJR+4+0pUca@Racigk;{G{A$D9vP3#d&Po|d-#bE#mwO6#Ik zCIBQ@qu&(93Nr-nq}s&|->7ajA~`y4s3l2Ik}xY0qP8OvE861#osw8S6TF@RD2$~~ z190G4c6c8jDw=bNLm*30m%)ynIiMqdoXh-|{MyJcJ072CI_p04k{|g5MzPoSe}%(zui1Y5e_g`SOet@wKcs@yIno5SfwMLwjYpaH^eD zR=)SH`%j)nvTMVNG->nIKAc?%e@S(V_Rh6ff;Z?W;W_lVkL5QT#`MU|)mCwfhC$SI z1cYs7l-Vpfwds3=fcYM7Sr>VsP z=R%0ErnOZx#R8Orxq$;rQAa*HGe>Q5%e;m2w`fWDH05Vk7ULZSkqx>|Tw^YGtVeN#3_B;x z6ylwq9FwG|c$v=BioI&ccL3lA#_wkQwJ5XE21h!%O%Y5tK=uv6l1|?eiJk`y6lIWo zuAjxXMBDUe-G=F8m|7dVkNF*Y#)eAo>hI@>zY`B52~OD%37`H_6!LNN`kl^)CJB4# zWqut%@h<#2z2%p3-UR5A8mt}^Hn+yQU)+~*pY^zGpCmM~XGdpyUwSRm8QD~N63>FW z1@Z4cQJv;~s9D`eXf{6YY)LVrjCZB}Sbt*d+I!(vyJy}8@>N!T|162Em2b8$I9Hsi zYQrhfgCMY;1ZB~+f}I>|-2f*s{4JvQT|wknJE>e$TdcE4<6l2YF!-^RTRxr;uX7Xk zb7eUW#+(J>Hu0}6nP01(``G3ZP)|?AIVWYDCUz{^D;@ZaD-vG_FPWYgzIN6I&GvUt zcdowxAL>eo#TDRB=fwshss4LpFC!^j0@Mk-0p2vbB;qe(6MY(ayPj4CnsBV`dA1R= znqT-PN1HJ$yL2#+S62U7ZTC*G=`sO(X~}dJ9lBh+=FXf;(l; zDDxb7(U>1SG~4UhbO=h`wu#wLG0aoluleJ6)#hS|N*&2~?%!GM z7<4hs&Lbt^*C5Qd4-kIt<|S@bheHy^&WC*AcQRJOqY*k!V_w$wSR+Ug?rFIDUppiS z9+Jd;Hr_6IG(NB0tL5P`p-eqRE2*V^hzju7O(rY-{2B34(D&PpBr|=NFWJW)*};@iyzFvRAP*9Kd=;ydB35P05q_0|&CXLGQ#j{*z4Ym7;5 zB_u0G{KXGrsW65VV7d^B1lI@lf#B><& z(ix3AQX|uz2VtPW_~tMrDvB8gX8@aYZlebc4j|idr9D{5>a_Nb(2_l~%aN7Qk6=Vbd+C7<#Aoy*UxH?2lT zy~LNheh-}hSfK?6>Kcx|6Jc!6a_E=p=u3+PAv{FBEmi=hjL+Zb_~_Dyl`MQW<55B%_+lyF8IO zlGccK%(z`aER_VB7_qTYFJRtpP{YOn)_=KA??7N7cJ{*#{N9k)FwHPUnTqjq225U3 z_>S6gorH^xq3I)c-g-FKHz4n=O&q7`t-t46(K=90Fwne4f9qh@ro%1_-sL?mYFh8zIdqc4QnPG{OoV{JB8e%2=u-!u(C^e)OX#+0SeEEDio%b_8|24F?T%-FaA=E-u zm=%W{H~EMBeQao$JhBIQ!4p=Lo8#Kh;{*%Y2pl3`;x&$Is1+CRZtY$9ltv$ds5Mss z?^M0F!jt~FG2!;Oh(_w_MlTAd>azLDAGzxQiwB?!OMt=)FU$E=qBj}VX&p@IekB&} z@kHWrU1V0C89TJ0qYpGKrmegeqj1`~kIcRwS6n;pz#wkBu-ztCR_Qp()j`)2!6Ps? z;CBj%&*e`mY9mi>k1K+Szlg}^+{l{O9a4f~FJ-7vZ46y{tvTdN*)aNlL`zhqzxtN9 z)24^)o^TVz@g*C~1mUnruv|x-d5@cWKj9#;iSg7+9REHp*Dv1*kR9WJ4w+bD{2G*)p>njll$TAb4qif%b57sC?$LwcSd<= zXa%%!BMYyQ)mwtveF+&_7=B-Y zmNnlcx^G#fZDl!{m^Vpt+O1}+W-{1#mb-snx7A}~e1^^%ElE*dzY;AkA5TIK8U@ck z+8E9Z8F{ZBc66C7m%uNnCbTw*D_1hzW@g7XY!1X+I+D9SIz+V3@cXJphW4M_X42LL z_y-XgF(K>xokrz9(ZLqj+MjBxAlq>W+W7A`tL?j>DU}C|MZ=$Rk>@s5DV319z}t0& zTq>#Ore{b6o?Z8b`6j_fUWw=}_}kGlvJ1_5u@2&F+DNRPAN6&?mPro*Yyx;{qHJ|F?LAD-L%QKTYRdN3@&7H@lAO>T?X$9xH~}F2 z;QE5JOjxga@E1Cf-#Msa|mjF$-{y`1bw%y`GVeNbsw&0l^BcMcpf z8{ehKdM(`jCghC1EM14FGb?hlHemPo0X)+MXv8)9b|`)ERsOw3S}-wnuz|pAh2l%9 z3i&5ivgtY{p{#Tqv~+7FHNbhMhr^0rN%Lv1F5{QYf>!k-pe1p%-~*zq zNQos$Nv!r;pzknENG|L*-d!8_`IUcGit)#=kFAVq; zmiVOwGJU@zB5?vAs9Xz3fkU*rBZcJLL5Kz!`Y8$sf_vtsa`NpNG|qp`glB!0W`=}B zLx`pw26Id80WFzB`A`jp8i-nH)>}1VR&+pA?c~LrF0&HhT&yCC0hbk>*MP&|f);UG zMt&AKN>CdG4SI%__oSuAK|OU}>v#wsoeS50DqwA;@JT%~$EztT9O?70Ti!P+s_jji znI1+IXriU%-#g^gy@ej4I@KV&}{woA|OwY>1%dCOWKuU_KJ~zR|sFYWs zvc2TQSeyc1ps`Fl65=4-8YZYffOW1hBIlD5y?6-fEjTvZ)^tXuRER)&okhp>r|YCU zvB_c9P&vbYCp3%#6OKh2|FX`Wp~YXIPm*th#wbudlpSSuQ+@oaU0CC?%wiQIL?#N0 zBJ}Anyqrx3;BXo2kO-G)0^mJzEiNK%TE-24Y90!dL6{0R2Vx+rPwqO_vu zE8gU97OGY_u6*g0`Sof>n*5mw5B*DP9b7HhvJNXI!AIp1A0&{3$~}+vps9g4FbhuR zXn>OZ1%fn3)IaK9O2~+3W3af*TC>g(Yk%%P_iG#`q3t({P||$pK%JxSb)@(FJm^gC zv#3XiFFO6NeMZ`^aASu%6MV3rNKX=Es@&m#ZfVz5|>+$bdfA zf3#AJeJhtHB+sobXdK26RU5k6qzgja;oUWUT=+&<{wp9vc%*&kYYrh0?h!i!c^Pa5ZA(L<*rvQhEJ(wK&rk8mAudttHf)y9FU(Fj z&4dGwT<46rJ9VsQnu4BAY;{C({9Tk#ST&%q$1d`|`-6M_mn{38XQ@bO|3A6!ALdSY)Ob`9~?};H*KLh ze7~X3Qtp_xY_{Q4*L(hr0c=(*oA`^vqypg_UqL&g+5e9DhAI9rBVS#A#R_3CV~&P- zWW8L?x==Lm_ozR?d42UH8qXoT#lZX|XXL6Svu&2}TKtgEpn#Y};g%x74{hrrV?g4n+_3N8Q2{nXK_U0F%hh`&;KaWs+TT$BhIKQe* zwZV+moo;Q$ih%|5A@J3 zrr_Q=uBA+|(uE6$=o%J0Vlmsw<_nPtd<68qLG{0n*1wSG0h2Gu!?EG^0ix>!eLK~R z-*@aoO^~=lchRu=XxQlbIIsg-RG-C zDyPI{eOdu#BiqC?-VKuAtN^L{Oy(3ff12#U>T;6tilV>o{!j(kZ>#)f)c59PcNQcaY7AViS~4@&bUGZWa@D-e3KIv|T zxN-|JeBJw21HE0#E7E61n0tyn#SGe(rHqXb5+mtg<);XpXaM&9Bt_J=~w}d3sNi5c2(Ri z25M)v*2Ul@>V+HLF==;1w9PX78|;u-^;!G-{H)pp>nJF6`?+~`|Ep$|bUZvc3xb-q zr+p!%wvN;?AP!nSuIXKkj{3pWOUe2RQJ}bd6$|e z@fdUhmXywLnSSBcms)+^b*$lWY*BvwCuAd0oO^N1p)B)%l9 z-?V&+hYTT2b0ult;=w#-#bYvy!if>deVtCihQh{tVZ&)Qo3I}wegK{3#iEPVG5q`2 zQ4sCc?7=I~8BG-YBx|IF)C9abAmB3KWLL`9ylIh=K;v=_dU1KMC|9&oTIRR?lfpxB z?ZO1&H{?*%I3f8iYix}6GS*(61Djz6BLI2XK!lG`V!rUe2_!Da)I3vGxD}hyXxJ*U zhNe79Vze3s zZ>~rMEpe#Qvr-?jDZ?y-q^2gc$KRT-qUq^=sh|9etI?ryA;nst(@QN+Q zU+Y+jrERfXMv?^coe`UWorTjn%AVF04@}1Ze`_j3Q3)N3!hf|V@4BcCl4zFBT$68l z@fdA4d6Cx1H-FQf*)JP`Sh4lo?eHvhJKpM!7DP$D=s!xnyv02ZA;Pm9B@SCM8%wR* zHjs%_m5~z_QHpl^eLpu6tcb3Zq>dZ*1fH^ca|~8&y`VHNqJzO_{Zh3xoHR#yMew)rH7}Zi z94qLIzs%$i{)Q`W7$8(Gf|!prhc>(_{wo7>DVY_E^3Qyq36#%+3G~c;$7_)OrS%RX zFywIWaO0^s2V$874;-aa*)OYsJcsySg?VQzcR~N+QqDD9NefTfJ-o!U(JViF?dZ%J zb#Phq;XovB+`pCnb7$iWwfqTf_s?D6?1MDwBUs~Cwc1c&gKH9(S@sPJOq|Ktvo@TIvvxtYUQ z8c+u>;Vw3tvQM%t!1z+eJ}a(qQe>On$#n4!?i)`PA+2`PamKm#A& zvkv;sNAe}b!5uO4Vvv)*bM`T4Q|;$hbgsG`q8l)!Ptpv&G~)F7$R8*mFL4?r6%;>? z$YEf;*o}{N3}RWzvHIz29o;D;-#k>g5Sf5&zcXA|f65wY5M!vBvJvRv0y0q=)Iq|H z?VlbtP6@Y}(*I{(Dzb2vVHjLNOf?u)@2!14r+(hkl?nU}gXv;r%O3UWKlSa0@8u)$ zl4Ap~dMQ(#!cyv!12J!JlC6)OmwY+ra2mFw2o(C&482d-hjCIQ zj1b*Av&rh4eNWif0e)NIH_az>SXB=TKmjoNGZ9lfY(s2Su);rf>m75d`Jb%x7I~nF zS0N@~dMSxV@4s52?;_J2=^n?vAK#cTQr+Z;f;iM@u#@4)@zfCZaafG2~|qReG|@4hX9ef41o z9ftb6I=%6E$0A@YFpXp?U?B7{-OIs*7kW!72R*rEHxI;!iAMm5lmLo0Xs)_^xzQEL z@}4?1_%f5nK(|=0gzfi|$y=U#wU*>M%Fn%P6CQRk+|A>qbTBYuwjoUEj#sx9{7XPD zR7j=hjh3MA%zCB7`;oYp?ttu%_`*pI`CnrxG^oszGc>_IUBs|PLKz5_P{33L9VeY6p&`~>mCle*P5>PDtM z{Jvm^K9&~CB_gEK2Tj#K35>{dt_4U-TeV~KM-`N3QWyEI0GWsDQRK6CbXmrT>Ej4G z?0q@bGbmi}Eh7v(<81boZ1S!2D^O+S=KUFx{?K@UR*5cEUviPJ;uN6M+HWW@VxofZ zu_M8pu@0RBl!RPUTo>7Q;Y|d=-&XS#8uCGcFx3tMwTzZXsji1j)#RI$A%5ZVEshhK zLHggc9>_PqCN^xBp{GcZBDR=BpP>C(f|bNj`$Hx}$881z_x(rmPaTmB@RDXdYZb?3 zrTKL54#~wD+lCWKld6!f(QRU}`Q!B+AcY+NVAi=El{0oDSotlEkJWD1s!@;|RzVk) zK=;-4HKO=fdi%+^TjoA_E6CR)*%CVR)8t7j9SZKMbnGzH#|==gIC$NoLqz|HcY^3v z6{M)6Y%v+FdEpn`Cn6?dgX7XK?sn>pvPSkA{r5y}z>f7T9v+EzDVRu5+5*H~zJ+bv zN{?pC+48w!z{pbmgAA^2yiK%{D~)QPPXU*^ABI$A*_nNJRZ1q=diWxC4UuuHFZ)tU191_=A%2MNNK zN*MOUEoY3x(fiKdl)*8;mado04+X!l)o);C#XZuS6#GWse%qK>{pnvVIPLvy!k(`- z-8uv1t`_6SN_?bl0})aecsi5Bhj#r&n5JCn5V0g^5fcnZw_G2kio9XrDwx-9c`4EM zhtrogZ0YlzepQ>H#FqTCuwozWp*yeU%Hdfqv;E==lA%76elI+wU1u4RtIs=Y9!Me8 z%^l$HtO`@JfoY*#|LAw?x|>!+URbt|;z&2fWj#JJr?d_~G98>iANg$_%!jfNiypQW z>EW4J2wzXSiI!iGXH1Q$Oqf1sR%5N18DM4FccP9h6*nwuXHsOr-@-Jdhn9&; z$zvrQnt6wuYJs`oDpR+d_{>jG@kZ=v~s%FMxlQNY5zQ+AR8dG0|BoOTCd+Tp@ zHO3vJzDheBLVr7|UK3{mJkX_v6KaTs7=?LJpJ;!1R|F@kI54-DAfE<3@}q@ux2z6H zN(5-+{jQ&jz+e^J%VEjR>LgKIF+E`Qn!%GOA@Pg+;KzL`N^{8poOj}j7LPOCc@s*F zIIt9)9-YLGP(_jXDC&!-HB61^EaVb>vu`&&yr3I=<6T5+oX*myK+?gxovSN@XDLv* zP9ILkhO%hAjx?!X@v0IU=oe#Dvh4_2QxDABfVD!Y4X-Ev;9mgl%{OWk3FI?>_b|T`Ql2|S{bLrc{;6E&5mhnysI5q?ObYg(sW_6s3c8|8lS>bfR5MIT7<8@B}tR zCKT5)ZCHD2J*l$9xxzU{#2q|+{L}-L6e#LlIaJyX7_T}`wfed6O(!$)XD2VgkvzQ7 zgmLXti(ufq^H&xnm77kWDA&Mv1^gNZ!@h1FzVf6)1>{MWO3L)_Etof5nwV|YH{2Ff z@%9JbPra^3;B4{~Cnup+Fejzv+b84R7xz-|u(la*+oi7)+iuCaD1a3vnEK~hc3WN1 z755Q}pcar_`F77L`E6srBVLGHrO^?YydW?;Vc3b3eeQJ(7`ADV0NLQ4_w344jJ&1u z5qjKG9LFIDwfbv#gRS|>VER#U4DKd_oF1FR6!b;QW6AFZi>a3vykEi6`%l_m|2LTP z%hH)b+?R9(x+xw%2FqIx;HePhzoa<-*?ph4?t-v#+5`D6RmlOa#xI1b2Xq@}9U#N_ z;p?0j0aN+WG$QL3vha$${Unsax67yC!d+2_6$WnKPMvyLQ4l*e&^WQc2y6oXC>A)) zLzAT^J{wE!CLNS~$croB1=h~xI`Z+L`R#j_AKjyv(G$E<>UP_!8|F{T{dE$|W2<}^ z(6oNQM;Z8EaM77HZ9Iu!^ArPp7|#~2IN_CJ&K}bEiJ9af$6UkhJ=^i4vcsXk4BH6% zl8ds_2Bhdr{}PZ#(u-_dKOD_bEfxPhBY&I(U)k_g?%Gf;6aq$mAML!Npp>xzhyGr8 zl>93)Wd_D=lun7H+7UR~y|3Q01~#RF4~tX&{pW#P31p&5y@9$x-RcwcLPCo8u5^Fk z?~C5Or*yvl^SKR+x)l6lpjyZOS#jHxHZ?fGG#TbfTQ)2M6SfoXR5$s|Q=h@VDuc=KF7Q?~|A=}p6fYIy zG$su90Z$z$Weh--1R^;sal>C(9TdNFv5KvP*aZ#Ug$k1dj8N5COTYVmg5dbQlIm_! zAQZxSZ$vpSt&O7&ZfrK{QJP!x&3E9fx+e;L4O*d0%?EA90-LJh+%XVW7ys(IpFqb)-HbC9FDj=yC$-T!E2 z%;M*_%0k~c^COn;d+ah*Kn0KD@}`GCFG|#_uXJDWiFI(Wy5W0LDBC#E%A?B%BOTsX zrr$^lOb24&uhFzYDeJa3R>Rp4p=O-lZ}5&X*0D1U!>FmBev9Am*$4Der|O&Rzb^om zb(5nlKxF+H$7WqG+eNXBsCT=;Wb2_s8d}|~@wV1qU;ZR@ksxh(cRUekJi|AxKIW$^ zh*U5?Is!IXFUVzq83@){p;%5|_IJ!#jNu`)+&-ssC$$hu{Y~xvwm55lT(3B^R2%Jo zPyVf5)RRXa`#2tD4hKZu-ddxkNkdCvzV+%Og<9JMsCAIO8bw}=CCIG&aytjkyCoz1|S zWZoRUIt;+PIV^P1#reFfH2plS*4XkeKGgSEsTv*>=T*<;5V7v_%Y|`0_n}Yd2MI== z@?Y>Q?MJQ!+v>gR9OW1{qcYZ5Eebb2tFvs9l$C9Jq3pSkzTRkK~S z^ZnumRjpi&28-cw6^(z0N!UJfsSK6kBubjeo+G z7H{;poso}cg5`;0aw(BU3T7AS3B)>v4=~JfaWIQpQsX8I4 z7|?5Npib4zb5Vxx)xxh?+!U|akHs(*_k7#y1ly#_p6Uf52mtu>1bSfXng^{E*ugE2+gDW<`pV#bF=D>nu9o)^xscloc zd~x&rM;O@7%?kY3NkQURPvSJ>D(wei<;TUCuBzc|O!zq!J$YO~twL{@$t_8Y=AQFyPI?_m_t>_!K7LP!KB|m`_msiuYxlCj0opoW z(*#7u_URqI*Sqn&WLavt6`|V@z~pl{iXf`sis@0#3e4&M#_%Fr8uBDQD-06+6Fd$Y za_B)`;c{SMF*N`0zW_qOY`CkJnubGZ+(Gims1USgZ=2CH z^Wbi1tLS1=&Tu~2qNyD5eV4RIO2$9kS0i7Kn6g6+nTdntxfz@|#y1F{IPQ9B_A_St zel+dr9TER8V;G$0U{zIZu@SZ7%{8e@A7)J<7PQAWfbJKOTOH;2s+hR{pJs?CW{Sve zmSk7-IK@=pE)m&ZRc~u$8D$qq-?CfT4~{h@b=tO@YX5n)3Iv;`O1NUWuz%iZ%=An{=FX|6GZtxfp**tS2UiB_ag-zcVBEZi?r61)KG%vZGxak8AG=oPc}o(?Z<9=!xj7Rrl5Yy&B|82^$ev9{8olb6$l>gdS_A zucV@>PQ={>=l!-bWN+lF{t6R|T?;-BTrVQa1uiX1Fr)S5dPj2Aqi?B)i6Dmw> z);v-Rb_X64k!=H~26$pJCb@mBb@2X%2_)#r%7cr@0l+6b4!wee{Hl}82XKm&+%D*} zrh>MNlI$tKk%@tg60&pMUF?n6fR|WC7I+ z>{qmw;>zt9mztYw#|a3igf`%Fn3eO1HJ>-htCR6d z)O>Q5LrPWkub6;~1u>Znb_^Uc>OxPhU`qm}6znXF?___GdW)*b&d0A5e5E!he)i=t zE?E1;pxRGR)o*yZljU&_HYoS+)hO8E>)G_DpYpip$5=8^m1ai4TB7L#4)vHaj|DEu z@)TKB@4=+eeqeJ?919up?lN_{!WMXqQ-N8}k1>_Yqhg#;VzQ;ZHlrSl%<~j zSkpqXk6|j*z7WF{@D$*0BC;Z_iP02n(jl^i8L=Xz(}&3_^$PGBrrda8$?-+_70(=v zoH~IoV7zW7)OgMw;Nz-#agFy?4Ka-tXy8+h=jZr^tY3n$p%=g7{!9_9#rJ^cJ&rp= zM5bk&WM2ff!#@jGAwC4OvWG#X!I!ijY%zfh+8Nqx;MF36eba7>iGLAn7mU4euNclX z5BP|xULOP%oK;O-hv z^($4qmVj2oYM9CazxPyZ#ds6gK~?Y1XnRb=ui`*2CJ>=6e#~{V>8a}9@pGg2u!tPq z1SGovv+(sx&r>`G9Mj??6T6MfQ>AIN5M--5^r`B6Rh?UNku#zuHYd5s@{U zfMoks^(agU3zx-@g}{M!N*d525RqN%boO+hE2fXc>6ie5H#6KcTL61|ng(vF=Z)5t zV6Efw7d+(ovcR<>GOj6PnCf4EO{tIrwFpPXm0>;O>J=JS{4|YZ|st zeF@keICqp?f2*oj1ae*Cw^9~-u+uz7$`eJ=coAk=l|M7?hRHRMRBrX0^5=ldMP$9EVh`0W}ZzVhi91T3#Bu(~yOOihL9<>Mhc%#t?p1+Byo3o1JaT9^- zL}Y7Ko!3MpI{+LjB3G#De*zUPrEX}Tdae1oHVW?;OzH9o3EX3!Rjk*RxZixjZIiEZ zztP;dim?)K944C475~6QdPV1*T_h}{b1t-7?A5K?8%!wkMC3@oCz51c;0h7ht5#pb z6cTM9*QE!ZWYs(%BHLlIT^&q3?pCGnAjZ!4Zc-nJ8VR%!FQ^Z6L{0EZPV zSk1QY!$j9jv;X=sgO~-b*xM+V0y4P)V}J6&?!Xy=e*#iSs?$Z}2O{!3rZT`66C&4{ zm}(klR3O((HX-mWV0F*qzon}GYxF~;r3Tb@tes_dk7JJ$k;hc^$}p^(Ud5(b!HUR4 zOfKLM9!#C8%VhMXqYjUnwPEp~^iJlP$~#7uhmSi!ru@0eo~Z$ zFEv$6Rdv3Id<=9;$uIkXCT-{CF|M0qJdW)K_D!3>YEN4WtLlx``T8`E z!?(v|t2!cZLQ|?&>ZD+EzIs398V6f`eK+7aOtSSCe6xY6s@n2Fv6~1) zWCcuiruSp=UAz~w4$#QNLW_W313y;PnfBRhwe2SDrlnP0e<_l+WE+chN=&=gwA-%5 z(^nCB-A;oSSJ`xq#+uOx?O)sA~BUaCAShffj`wiu5*DyRdrFCA2Ri2pl$ihNLCMw;*5)}aw8Rp4 z8dI?FGF5%O5yL1YVCC=i!&vHNtJc%~^Ti%n+=q_KV2=n2MNd2a*lufs~>jxY4TBn_~=msnta- zN%KH8xd_@ZtNq*6aGj?xA;QoTdtI@XSFot+TOzU(a8DrFK(HyoeA_2)0j^fnIiU+I z#WUZoVfG2e#BwUhhWxfR!CI1iJ&9+q`m`~(e_-DofQ=9G-OaR7E!AC6+Y#`Y{uuXtGrVPt{cKZL1dZxU~xN?nyTu-EO zx$V?KQtYEU`pt{1Zb^2n8cy>o;AmAH4C5iT_wrv%lvGwR7gP246^z~QNsKQxsyeGd zZ8{bhYybCy9;bNL{yt})BUZ&Nh}i###^P$2B;d7}1s(#vZppO`Qxd1Y=s8k7Zrigw zkX7}y0wwE9vR?((6OjXh?JPEIgGrXMPQe!a|B5NO$06V)jBW2lOQ>@(rAE4&@SSL~ zCpSt>kXJFe9DZBF>=Mjk_5t?3+wInE-w#<4MD3aHHSv(_uE0YgvbU-}9r&^4cd`I@ z#BRk(Y?&hBUB?c@shE)0XzTLmpK1r;d`p&d?SIe5lx=XX1c5Ns7ByR#+P>7J)q8+brO4pP;d!Z=CnT`dG2wcBXZ;*KKH zUD0l1F)0LlEuk*9zumwam`wi*F^eKvE>_)UsT!H(reA@k)lDmE&*L#^Mr(->y%*TC zAi8Z)miv2q6Aj!~3zV!c$xZ~W7m*WG^{haL4S%Nt1qu`qnS+Vnr%9*rnS_Xp11<(W zS>vhh0rqO9ybQS}8<4MJ>iN7}W1IX)ME+nG76LLj<};v3>#bs=Tm-S^)D@96Fm3U#1Bnzg7{kK9{(i(Dv!tS`Z(<@}E{oxG#{<`i z$ns&hHC)A&Q*%bWx~S6mOB|bp=dZhh6_L#`<(syz@fq^Kw^j8ss@l`+9X1CrI5^1Q z;2?!$8xd|_U!P*?=j22rtGNq$RrRyLaWPDj_5%JZBA*I;mkN?y5*-L*$yJpKISvn+TY#-p_1RWy%UoYyA3Z%i3=Iv@+uMt((%08ll4uJ$^^koL zStZ$LEXm#;!|5gie-M!yL}ZmPJOeULuA5|`sw_FMcVU|@j3!9j+Gh9WXu zLdljO*<6-YtR>kYRXrM0tu_w7v`N6lB66$ERT~hH1=aciOhWPFi6=kF8^8{#dU>n2 zZEo@6#mt{SpPrr`BC_r6?H#tu_V@P>FMvfPTcjw+x^JkeUJUF4%#7)jy8%y$$d|(K zO;p8}chhoHuhNR`CL-fR1Up^9z+!FG3d50h(mcQ*qA1N8Rx($mvJ zUtix+=7IthOV&*V1tj|qj6wg=m`*tnxIjei5Rqx2tHprHn6>QD*Ry>Hv!%=lHofPQ zn2G^$38agGomBPh&{-|P&Ye4#g$oxlfBt+HFJ3%skL~H{8CI-4Jw2%Eu!;p37#PSm zTc=dj*D#LMSH*bRU4bV>s zELyaP`Sa(qV8H^qySwS>=~=>Dkk997YilbGqire`Ye}|SRX+)Q9+L$>4om=k3_K?y zhlxmA=z1QQgVIBup5XUX+ zbLI@23L4SM>Fn&J+0<(Zl6@1{#|9Fdi?IU6f%Skti^#*k395QW=!(_9 zmQnhh&mvdhu}veMFxlijP3FhW$gb5rtQC0gwcdUY7=ggVIgb5RdeP1Jz6%oda8ADG` z54l{9F=NI=*|iuye*6+d3otY^q)k1t&C2M0BJyeA7r>MlPrnQBpom-pd^c3K4e-Qf zz-OqfTy{A;2^^}bkA!A+m_~GVcCv8cLLPtoac;cv#^D97-rnBf{~jD191c2&Y_O5* z7C=u=Pvlo8&1Q=&L9|x_8vuVz;Pm@pJPv**B5Q{3Sysi4<_DXjwW7H;0L}n@kN~+3 z04D>R2Xgfw*aZs~FmK+xVV~D%p{u*Qo9^yzdV726>+54^XlR&J3zKoTl8P-L*>_ZR ze@vmbSuvl!4dcP^tcaWw5_|_#z}l8nPY2dd;QWsQTdL|0ZH3toTP zMJ!&tm_>^g4VwnWj~_owtOElBw70hp2O9vxze^ZtT7+aR(OxAYcLToyJ`wZj#{g@! zavXOB^$O+V>RsHlq~JFzN_Q+~@oKk(NEJ-j_{qSJRCO?PyBZX1q+I*^`|0cJ8;;=W z?d_$vw|Ce)(AU?8h%6D&1r&$Qw%&+WeOFZv1P%b+iSg8Vj9X1W02t4cGckEpk4}(W z9|AU0)zbpG#z3$G0|U&RJC{X^7A+AT8zp|}>obdmksoZKdMzZ{W-OfONVI>!*upNv zM7Gq5%T@KM&_&Ct*wJzkWF9ud)j=Y1FYq|QP6bw=oBF%j&nS+nTw z?k1{iP?$-pFw0ghmm{Cg51R}EN|NksRXxHI?Vs@X5$*?m7+Tbyo3c!@BC@WC{1}s! z>T+Pm1TQy$37uU>RnJz{q0oI|B-rlm?%`0_C{H>9Rn_4@gn&_r_EA;c15?%ZA5|W<7&tYwURm=%l!IV2H?2%4SP@xA zM9uGOGWUtLQ<_P3-h;xwB}z<&1iO-Wu4bv9hP z0;ctJKfJ5}*o8gstE3Dka?mP7`Y(y#?-i z%dDi@cCmX^Pv{D<^tP){z)lZbnOVIAHi0#tn&n_^6|@JEZV~fp{`3}UB}6c`gxe2^ zRalR!PsBt-mn+BWqtQJp4ryU2fSZMCQ%gg{Xg|2WqWLJif)Wa5Bz_2FQln}pkahY6Me>>lupSX=9d z-xdRQu+xq-LA59fBsoM~LxU@Gy1$FUbubW?^vcZM0c&&I>J#8KF=p=?xK2&GS2L@_ z1RiU34HU9nP172Ji*3>_a1A(1Ol9eC9SdNm9c+SX?qbusJ6YFAG}e+akAQ<}dqPa= zei1lBtV;aFFYU-e1c!)Q%^t`TX)kmlhp)?Z+%Um+GrLnk_JO4Ryl3eepZ)-e3t7~j S)<*7 literal 0 HcmV?d00001 diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 1af5a0ea..b0c90a18 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -97,6 +97,7 @@ class Data(object): def plotWFData(self, widget): wfst = self.getWFData().select(component=self.getComp()) + widget.axes.cla() for n, trace in enumerate(wfst): stime = trace.stats.starttime - self.getCutTimes()[0] etime = trace.stats.endtime - self.getCutTimes()[1] diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d5d5fa1b..8c6225fa 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -13,6 +13,7 @@ matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.widgets import MultiCursor from PySide.QtGui import (QAction, QApplication, QComboBox, @@ -70,25 +71,13 @@ class MPLWidget(FigureCanvas): self.setParent(parent) self.figure = Figure() self.canvas = FigureCanvas(self.figure) - self.canvas.mpl_connect('button_press_event', self.emitSelection) self.axes = self.figure.add_subplot(111) self.axes.autoscale(tight=True) self._statID = None - + self.multiCursor = MultiCursor(self.canvas, (self.axes,), horizOn=True, + color='m', lw=1) self.updateWidget(xlabel, ylabel, title) - def emitSelection(self, event): - - self._statID = round(event.ydata) - if self.getParent(): - self.getParent().pickOnStation() - - def getStatID(self): - if self._statID is None: - return - else: - return self._statID - def getParent(self): return self._parent @@ -119,7 +108,6 @@ class multiComponentPlot(FigureCanvas): self.figure = Figure() self.canvas = FigureCanvas(self.figure) self.noc = len(components) - self.axeslist = [] def plotData(self, components, data): @@ -145,6 +133,7 @@ class multiComponentPlot(FigureCanvas): self.updateXLabel(self.noc, xlabel) else: self.updateXLabel(n, '') + self.multiCursor = MultiCursor(self.canvas, tuple(self.axeslist)) def insertLabel(self, pos, text): subax = self.axeslist[pos] diff --git a/qrc_resources.py b/qrc_resources.py index 9ee115e0..fdaeb26c 100644 --- a/qrc_resources.py +++ b/qrc_resources.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Mo. Dez. 8 09:32:58 2014 +# Created: Di. März 10 11:47:00 2015 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x02\xa2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\x00\x00\x02iIDATx^\xedYM\x8b\xc20\x10\x9d\x96\xbd(\xa8\xa8\x08\xa27\xaf\xfe\x01\xf5\xa8\xff\xdb\xbb\x07\xaf\x1e\x14\xc4\x83\x17\x11\x05\x11?@\xa1K\x02sI\x98}\xc9NYYHaH\xda\xa6\x9d\xf7\xe6\xcd\xb4i\x9a\x15EA\xffy\xcb?\xe8;\x11H\x04\x12\x81D \x11H\x04\x12\x81\xaf\x90A\xdb\xed\xb6\xe0~\x96e\xdc\x8a}n\xdd\xad(\x0a\xb7\x95\xfa\xb6\x1d\x0c\x06\x19\xc2\xc6s!\x08\xbeV\xab10\xdc\xca\x1b\x03\x84\xed\xf5z\x0d\x22\xf1\x15\x0a\xbe\xd3\xe9\xd0\x1fo\x86\x84\xc5 \x90\xc0\x04\x18|\xb5Z\xa5\xdb\xed\xc6\xd1\x95\x22\x1e\xa7\x80\xac\x881\x0e\x18+\xa1.b\x00\xde\xf6\xfd}6\xff\x9c2\x18\x98\x00p&\x163\x9b|/\x7f\xbc\x8aD\x1e\x10u\x09\xbcD\x0c\x19R\xd4%\xa8R \xc6Ih\x0a\xc5(PN\x0aE\xa4\x99\x00\x16\x8c\xf3M\xa9\x00\x96\x16\x83\xc3\xfb\xc0\x8fB\x01l\xb2\xb3\xe7\xf3I\xa7\xd3\x89\x0e\x87\x03m6\x1b:\x9f\xcf\xe6\x98\xa0(\x04\xadV\xc0u\x08#\xb7\xdf\xefi\xb9\x5c\xd2n\xb73\xe0-\x89\xf9|N\x8b\xc5\x82\xde\xef7V\xd2\xef\x97V\xc4\xc1\xe7\x1b\x8d\x06\x8d\xc7c6\x9aN\xa7\xf4z\xbd\x0c1\x10q\xbd\x02\xd8\xe2\xa3d\xdf\xe4\xc3\xe1\xd0\xa6\xd6\xe3\xf1\xd0\xdcKO \xf6\xbd\xc1\xd6n\xb7\xed\xb1\xfb\xfd\x0e\x80~\x80\x00\x00\xc4\xc0Y\x0d\x97\xf0\xe7\x09\x84,O\xaeV+\xaa\xd7\xebT\xa9T\xc0u\xda\x0f\x1a=\x11S\xb0&\xdf9\xf2\xf6\xc9t\xb9\x5ch2\x99(\x81\xeb\x15`\x00b\x14\x19\xf4z\xbd6f\xc1w\xbb]\x9a\xcdffj\x8e\xee\xa5P\x00\x8369\x0b\xcf\xf3ct4\x1a\xa1\xaf\xb1R\xd23\x07\xa0\x8d\x81\x889\xad|\xddO\xe3\x7f\xadH\xae\xccwD\x90-\xf0k\xac|\x05@\xeb\x81\x15#\xee\xefc%\xf55\xc0 \xf1\x18\xb0\x94\x22\xf4\xb1o\x9d\x02X\x0d?\xd2\xadV\x8b\xfa\xfd>\x1fg\x93\x22\x1e\x12}u\x0a\xb1\x05\x15_\xb3\xd9\xa4^\xaf\xc7\xe0D\x228\x85\xf4\x0a\xa0|\x05\xb9.\xde+F\x81\xd2S(\xb6\x88\xd9\xc0X\x00\x1e\x16q\xfc\xcb\x8c\xfbN\x01\x03\xa7\x8a\x02\xc7\x0a\xe8\x9d\xb2!\xd0lj\xf0X\x01\xac\x84\xab\x80b\x9a\xa0\x98\x0b\x81\x05V\x9e\xc7\xbb\xceD\xd0\xf8\x9d\x80\xc9\x1c\x8fG\xf6]\xda\xf2z\xf8\xea\x9d>\xf2\xce\xf2:&\x00ID/A\xea\xd3F\xff\x83\x03[\xfaG\x96\x08|\x03\xf7\x03\x9fA\x22K\x9b\x82\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xa9\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x03KIDATx\xda\xd4W\xcdk\x13A\x14\x7f\x93\x9d\xcdWs\xc8\xc1\xe6RZAj\x03\xb6H\xdb`\x9b\xda\xd8\x8f\x80\x0azmZ\xfc\x0b\xbc+\x88\xff\x83\xf4PDA\xf0\x1f\x90\xeaE\xb1\xa0B/F\x1b?\xaa=\xa8\xa0\xf6b\xa1XTj\x0e%i\xd2d\xc7\xf7\xa6\xd9\xb8\x99\xec\x96\xacn+\xbee\x98\xccn\xe6\xfd~\xf3\xe6\xbd7o\xd8\xf7\x9f\xdf`\xe0\xf8 XE\xd34\xd0u]P\xef\x85T\xabU\xd8\xd9\xd9a\xd4\xab\xc2\xed& 8D\xa3Q\x08\x06\x83\x9e\x10\xd8\xde\xde\x86|>\x0f\xce\x04D\xe3K\x9f\xcf\x07\xc1@\x10\xc2\xe1\xb0'\x04H?\xe9Tq\x1c-@\x7f\x14\xf4\x08\xe1\x11\xbe\xb0\x05\xaf\x13\x10\xcaW\x13\xdc3\x02B\x80\xf9\xb4\xb4\x05\x92\xb0\xc7\x04$\x86\xf8_,@\xde\xfa`\xe1>x)C\x89ag\x0b\xa0\x87vh\x5c\x9b\xd5|Z\x8a1\xd6A\xf1?\x9e\x9a\x00\x1c{\x93\x07\x8c*\xe8\x5c\x17\xbe\x90\x8f\xac\xb1\x8e\xe3l\xb5R\xbdl\x18\xc6:\xeb\xec\xec\x8cc\xb8\xe5\xfc\x01\x7f\xd4\xef\xf7\xcbpa\xf8\xec\x87\xd0\x16 (\x94\xcbe(\x97\xca\xf9B\xa1\x90\xe4\xed\xb1\xf69d\x15\x0d\x85B2\x03\xa2\x05`?\x85\xfc!\x10\x08@\x91\x17\xa3m\x91\xb69\x8e\x80is\xe5^:\xde^BXD\x02\xd3s\x9a\xa3\xc3\xe948\x08`\x95\x04as\xd3\xe4\x07M\x80\x84\xb0\xb9\x99t\xfe\x89 ,7\x84!=\xd3*4^x\xf4\xd0S\xacsg\xcf\xef\x1eHV\x1c\xc4\xe6v\x8e\x87\xce\x01\xa3\xc3)\x86D\x84:\xc9\xad\xd0bP\x07\x0b\xb7\x85\x059\xbb\x1a\x11\xb6\x04(\x13R\xf2\xe0\x9c\x83:\xc9\xadP\xccW*\x15\xa9S\xc5q$@cr\x10*L(B\xfe6\xeeMp{\x02\x86h\xf2\x01z''\xd4\xbeQ{\xb2\xf8\xd8\x15\xf0\xe9\xf4\x99\xdd\xdc\xa2\xe8Rq\xb8]\xe1Q?\x0dk=\xad`4\x99\x92\xa6lEh\xeb\xc8rdEU\x97\x8a\xe3\xb8\x05\xd6\x9a\x80\xda\xb3\x5c\xd6\x95\x05&\xc7\xd2\xbf\xcd\xeeP_\xc8-\x90,m>Xk\x02j#C'm\x8bJ;\xa13\xc5\xcc\xae\xaa\xae\xa6D\x14\x8b\xc5`ss\xb3\x99\x80\xa5\xd1\x0a\x96^>we\x81\x89\xd4d\x93\x1e\x95\x00a\xf3\x99\x0b\xd3?n\x5c\xbfy\xc8z\x0a6M\xc4'yb\xc4\xb5\x0f\xecE\x80~\x136\x9f\xcaL]\xdd\xf8\xbaq\xfb\xee\xfc=h8\x17\x14\x1f\xc8\xbdZre\x81\xb1\xd1qG\x1f\xa0~z&\x03\x84\xcd\xe8f\x842\xbb\xf6e\xed\xd2\xf2\xf2\x1bX\xfd\xbc*\xf7z\xfe\xce<\xc3U\x08,T\xe4\xb8T*\xb9\xf6\x01\xea\xb1\xf0\xa0\xb9,3\x93\x917\xad\xee\xa3\xdd\x90H\x0cB\xd7\xe1\xaek\xbd\xf1\xbe+\x92\xc0\xb1\x9e^\xf8\xf0\xe9}\x1c\xe7\xa6\xb1\xf5\x93\x92\xfe\xbe\x81\x8b\xa1pH\xa6O\x02\xce.=ue\x81\xd4\xc8\xa9]\x02\x98\x09\x8b\x85\x22[y\xf7\xf6V\xed\xd3\x0a\xb6E\xc4\xfcHz%\x81\x9e#qi~\xb3\xc9\xf8\xc4}\x8cD\x22u\x02\xb4\x127\x16 \xcb\x99\x04\xb6\xb6\xb6\x18\xf9\x0f\x95z\xaaoqk\xe2\xb1V\xad\x5c\xe3\x8d\xe1\xa2s\xc0\xc2\xb5\xe5s\xbe\xa1\xb4\x13\xe0H\xde\xfeb\xa2\xdc\x0d$\x01\xce\xff\xec>P[\xe9\x9eW3\xdb\xc9\x98\xa7_\xbc\xceyR\x0b\x90\x8f9\xc9/\x01\x06\x00oO\x87\x87}~\xb3\xc0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\x00\x00\x02\x9dIDATx^\xedY\xc1\xebqA\x14\xbdOoC\xa1H\x14\xa5\xec\xec\xac\x84\x85\x85\x7f\x5c$v\xb2S\x14\x16\x16J,\x08%\xf5\xbe\xee\xab[\xfa\xee\xef}\xc74\xd3\xa7_\xcd\xa9i\xe65c\xe6\x9c{\xee\xbc\xf7\xe6\x09\xa2(\xa2\xdf\x8c\x14}\x0f^\x80\x17\xe0\x05x\x01^\x80\x17\xe0\x05\x84\x84A\x9b\xcd&\x92v\x10\x04R'\xb6\xa5\xfe\x1bQ\x14\xa9:\xa9\xcdh4\x1a\x01\x01\x042\x18\x91\xcff\xb3B\x0c\xd6\x00L\x10\xd6\xd7\xeb\xf5#\x11\xe1\xa7\xe4K\xa5\x12\xfdo\xb0\x08\xe6 \x22\x8c\x05\x08\xf9L&C\xb7\xdbM\xa2\x9b\x14q3\x07\x92\x1dap\xc0D\x84\x93M\x8c\xc8s[_K\xd1}N\x82\x81\x05\xe0\xc5\xa4_\x91N\x9eK\x8f\xc7\x22\xcc\x05$M\xa6k\xdd\x87\x0arT\x09t\x98B\xd8\x01\x9cBd\xe2\x80\x9b\x14\x02PD\x14Y4N\xc3\xd2\x01l-&\x87\xaf\xc1:\x16\x0e`\xe0M\xf7x\xd3v\xbb\xa5~\xbfO\xb9\x5c\x8e\x04\xf7\xfb\x9d\xd2\xe9\xb4<\xb0x|\x5c#`\x07\x8c\x80\xa3\xc4Q\xafT*B^\xc0OsM\xc6<\xe2\xf6\x02\x0c\x9f\x1b \x0d\xed\x91rM\xbcX,\xd2\xe1p`'\x0c\x04\x7fY\x80\xe4\xb2\x08\xa8\xd5j4\x9b\xcdh\xb1X\xf0\x06\x06\xbf\xfb\x82\x00D\xa8\xd5jQ\xb7\xdb\x8d7\xeeh4\xa2\xe9t\xcamD\xdc\xbd\x00\x1c9i\xeb~v\x82E\x0c\x06\x03b\x8c\xc7c\x16\x81\xe6r+\x00\xdb\x8d\x8f\x8b|\xeb\xect:\x94\xcf\xe7i\xb5Z\xa9q\x16\xebB\x01B\x02FL\xd7\xfaw\x85B\x81\x1dH\x1a\x0f\x1c\xb1p\x008\x82\x04J18\x8d\xb9u\x00\xd4\x8a,\xdfu\x14\xf1\xe7\xf3I\xfb\xfd\x9e\xca\xe5\xb2r\x08\xcc\x0b\x11\xda\xe6\xa0\x8c\x91\xfb\xfa|>'F\xb5Z%\xc6\xeb\xf5b\xf2\xf1\x93\xb9^\xaf\x9b\x90\x14\xa1.71\xfe\xb6\xd3\xeb\xf5\x98(\xbf\x8d\xf2{Q\xdc\xd7l6\xa9\xddn\xff4\x17\x88>Fh\x10}y\x09S\xf5{_\x18\x86,\x00\xce\x85S\xc8\xdc\x01l#v@\xca\xbf\xe62q\xc0y\x0a\x81M\xac\xae\xa5\x80\xb1\x98 - icons/pylot.ico - icons/printer.png - icons/picon.png - icons/sicon.png - help/index.html + icons/pylot.ico + icons/printer.png + icons/picon.png + icons/sicon.png + icons/pick.png + + + help/index.html From 474622027e1339316f4486feb9f1e600e18d2218 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 12 Mar 2015 13:59:29 +0100 Subject: [PATCH 0274/1144] now the station selection works fine and a picking window is opened when the waveform has been clicked --- QtPyLoT.py | 15 +++-- pylot/core/read/data.py | 11 +++- pylot/core/util/widgets.py | 131 ++++++++++++++++++++++++++----------- testPickDlg.py | 13 ++++ 4 files changed, 125 insertions(+), 45 deletions(-) create mode 100644 testPickDlg.py diff --git a/QtPyLoT.py b/QtPyLoT.py index 1ecab769..4f71b66e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -115,7 +115,7 @@ class MainWindow(QMainWindow): # create central matplotlib figure canvas widget self.DataPlot = MPLWidget(parent=self, xlabel=xlab, ylabel=None, title=plottitle) - + self.DataPlot.mpl_connect('button_press_event', self.pickOnStation) _layout.addWidget(self.DataPlot) openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) @@ -340,7 +340,7 @@ class MainWindow(QMainWindow): ycoord = event.ydata - statID = round(ycoord) + statID = int(round(ycoord)) return statID @@ -440,8 +440,7 @@ class MainWindow(QMainWindow): return self.seismicPhase def getStationName(self, wfID): - data = self.getData().copy().select(component=self.getComponent()) - return data[wfID].stats.station + return self.getPlotWidget().getPlotDict()[wfID] def alterPhase(self): pass @@ -456,9 +455,11 @@ class MainWindow(QMainWindow): wfID = self.getWFID(event) station = self.getStationName(wfID) - self.pickDlgs[wfID] = PickDlg(self, - self.getData().select(station=station), - station) + data = self.getData().getWFData() + pickDlg = PickDlg(self, data.select(station=station), station) + print 'picking on station {0}'.format(station) + pickDlg.exec_() + def updateStatus(self, message): self.statusBar().showMessage(message, 5000) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index b0c90a18..44249638 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -30,7 +30,8 @@ class Data(object): ''' def __init__(self, parent=None, evtdata=None): - if parent: + self._parent = parent + if self.getParent(): self.comp = parent.getComponent() else: self.comp = 'Z' @@ -50,6 +51,9 @@ class Data(object): self.cuttimes = None self.dirty = False + def getParent(self): + return self._parent + def isNew(self): return self.newevent @@ -104,6 +108,7 @@ class Data(object): srate = trace.stats.sampling_rate nsamp = len(trace.data) tincr = trace.stats.delta + station = trace.stats.station time_ax = np.arange(stime, nsamp / srate, tincr) trace.normalize(trace.data.max() * 2) widget.axes.plot(time_ax, trace.data + n, 'k') @@ -112,6 +117,10 @@ class Data(object): zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'} title = 'overview: {0} components'.format(zne_text[self.getComp()]) widget.updateWidget(xlabel, ylabel, title) + widget.setPlotDict(n, station) + + widget.axes.autoscale(tight=True) + def getComp(self): return self.comp diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8c6225fa..74cbcf37 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -6,13 +6,14 @@ Created on Wed Mar 19 11:27:35 2014 """ import datetime +import numpy as np import matplotlib matplotlib.use('Qt4Agg') matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt4agg import FigureCanvas from matplotlib.widgets import MultiCursor from PySide.QtGui import (QAction, QApplication, @@ -65,19 +66,25 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, class MPLWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): - super(MPLWidget, self).__init__(Figure()) self._parent = None self.setParent(parent) self.figure = Figure() - self.canvas = FigureCanvas(self.figure) + self.plotdict = dict() + self.axes = self.figure.add_subplot(111) - self.axes.autoscale(tight=True) self._statID = None - self.multiCursor = MultiCursor(self.canvas, (self.axes,), horizOn=True, + FigureCanvas.__init__(self, self.figure) + self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True, color='m', lw=1) self.updateWidget(xlabel, ylabel, title) + def getPlotDict(self): + return self.plotdict + + def setPlotDict(self, key, value): + self.plotdict[key] = value + def getParent(self): return self._parent @@ -101,51 +108,100 @@ class MPLWidget(FigureCanvas): class multiComponentPlot(FigureCanvas): - def __init__(self, parent=None, components='ZNE'): - super(multiComponentPlot, self).__init__(Figure()) + def __init__(self, data, parent=None, components='ZNE'): + + self.data = data + self._parent = parent + self.components = components - self.setParent(parent) self.figure = Figure() - self.canvas = FigureCanvas(self.figure) - self.noc = len(components) - self.axeslist = [] - def plotData(self, components, data): - if self.axeslist: - self.axeslist = [] - self.figure.clf() - xlabel = 'time since {0} [s]'.format(data[0].stats.starttime) + self.noc = len(components) + FigureCanvas.__init__(self, self.figure) + self.multiCursor = None + self.resetPlot(components, data) + + def getData(self): + return self.data + + def setData(self, data): + self.data = data + + def getParent(self): + return self._parent + + def setParent(self, parent): + self._parent = parent + + def getComponents(self): + return self.components + + def setComponents(self, components): + self.components = components + + def getNoC(self): + return self.noc + + def setNoC(self, noc): + self.noc = noc + + def resetPlot(self, components=None, data=None): + + # clear figure + self.figure.clf() + + # delete multiCursor if existing + if self.multiCursor is not None: + self.multiCursor = None + + # set new attribute values + if data is not None: + self.setData(data) + if components is not None: + self.setComponents(components) + noc = len(self.getComponents()) + if self.getNoC() != noc: + self.setNoC(noc) + self.axesdict = dict() + + # prepare variables for plotting + trace = data[0] + stime = trace.stats.starttime + srate = trace.stats.sampling_rate + npts = trace.stats.npts + tincr = trace.stats.delta + time_ax = np.arange(0, npts / srate, tincr) + xlabel = 'time since {0} [s]'.format(stime) + + # plot individual component traces in separate subplots for n, comp in enumerate(components): - nsub = '{0}1{1}'.format(self.noc, n) + nsub = '{0}1{1}'.format(self.noc, n+1) if n >= 1: - self.axeslist.insert( - n, - self.figure.add_subplot(nsub, - sharex=self.axeslist[0], - sharey=self.axeslist[0]) - ) + subax = self.figure.add_subplot(nsub, sharex=self.axesdict[0]) else: subax = self.figure.add_subplot(nsub) - subax.autoscale(tight=True) - self.axeslist.insert(n, subax) + subax.autoscale(tight=True) + subset = data.copy().select(component=comp)[0].data + subax.plot(time_ax, subset) + self.axesdict[n] = subax self.updateYLabel(n, comp) if n == self.noc: self.updateXLabel(self.noc, xlabel) else: self.updateXLabel(n, '') - self.multiCursor = MultiCursor(self.canvas, tuple(self.axeslist)) + + self.multiCursor = MultiCursor(self.figure.canvas, tuple(self.axesdict.values()), color='r', lw=1) def insertLabel(self, pos, text): - subax = self.axeslist[pos] + subax = self.axesdict[pos] axann = subax.annotate(text, xy=(.03, .97), xycoords='axes fraction') axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) def updateXLabel(self, pos, text): - self.axeslist[pos].set_xlabel(text) + self.axesdict[pos].set_xlabel(text) def updateYLabel(self, pos, text): - self.axeslist[pos].set_ylabel(text) - + self.axesdict[pos].set_ylabel(text) class PickDlg(QDialog): @@ -153,9 +209,11 @@ class PickDlg(QDialog): def __init__(self, parent=None, data=None, station=None, rotate=False): super(PickDlg, self).__init__(parent) + # initialize attributes self.station = station self.rotate = rotate self.components = 'ZNE' + if data is None: try: data = parent.getData().getWFData().copy() @@ -163,13 +221,13 @@ class PickDlg(QDialog): except AttributeError, e: errmsg = 'You either have to put in a data or an appropriate ' \ 'parent (PyLoT MainWindow) object: {0}'.format(e) - raise Exception() + raise Exception(errmsg) else: self.data = data - self.setupUi() - self.multicompfig = multiComponentPlot(parent=self, + self.multicompfig = multiComponentPlot(data=data, parent=self, components=self.getComponents()) + self.setupUi() def setupUi(self): @@ -202,6 +260,7 @@ class PickDlg(QDialog): _outerlayout.addWidget(_dialtoolbar) _outerlayout.addLayout(_innerlayout) + self.setLayout(_outerlayout) def getComponents(self): @@ -214,11 +273,9 @@ class PickDlg(QDialog): return self.data def filterWFData(self): - self.data.filterWFData() - self.plotData() + self.data.filter(type='bandpass', freqmin=.5, freqmax=15.) + self.getPlotWidget().resetPlot(self.getComponents(), self.getWFData()) - def plotData(self): - self.getPlotWidget().plotData(self.getComponents(), self.getWFData()) class PropertiesDlg(QDialog): diff --git a/testPickDlg.py b/testPickDlg.py new file mode 100644 index 00000000..9c83f206 --- /dev/null +++ b/testPickDlg.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import sys +from PySide.QtGui import QApplication +from obspy.core import read +from pylot.core.util.widgets import PickDlg + +app = QApplication(sys.argv) + +data = read() +win = PickDlg(data=data) +win.show() +app.exec_() From 49a248d139765dce8761b4cff45399ac95d42e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 12 Mar 2015 15:45:54 +0100 Subject: [PATCH 0275/1144] Debuged AICPicker: Now values equal zero are removed from CF to avoid numerical instabilities. --- pylot/core/pick/CharFuns.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index fe53a85f..c6072cba 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -218,12 +218,13 @@ class AICcf(CharacteristicFunction): nn = np.isnan(xnp) if len(nn) > 1: xnp[nn] = 0 + i0 = np.where(xnp == 0) + i = np.where(xnp > 0) + xnp[i0] = xnp[i[0][0]] datlen = len(xnp) k = np.arange(1, datlen) cf = np.zeros(datlen) cumsumcf = np.cumsum(np.power(xnp, 2)) - i = np.where(cumsumcf == 0) - cumsumcf[i] = np.finfo(np.float64).eps cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * \ np.log((cumsumcf[datlen - 1] - cumsumcf[k - 1]) / (datlen - k + 1))) cf[0] = cf[1] From 8ba34db05c6fbdf4e4d1573464b5040020cf55b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 18 Mar 2015 14:44:08 +0100 Subject: [PATCH 0276/1144] New function to calculate earliest and latest possible pick from a given initial (most likely) pick. Replaces class EarlLatePicker of object Picker. --- pylot/core/pick/earllatepicker.py | 138 ++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100755 pylot/core/pick/earllatepicker.py diff --git a/pylot/core/pick/earllatepicker.py b/pylot/core/pick/earllatepicker.py new file mode 100755 index 00000000..1a2663f7 --- /dev/null +++ b/pylot/core/pick/earllatepicker.py @@ -0,0 +1,138 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" + Created Mar 2015 + Transcription of the rezipe of Diehl et al. (2009) for consistent phase + picking. For a given inital (the most likely) pick, the corresponding earliest + and latest possible picks are calculated based on noise measurements in front of + the most likely pick and signal wavelength derived from zero crossings. + + :author: MAGS2 EP3 working group / Ludger Kueperkoch +""" +import numpy as np +import matplotlib.pyplot as plt +from obspy.core import Stream +import argparse + +def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): + ''' + Function to derive earliest and latest possible pick after Diehl & Kissling (2009) + as reasonable uncertainties. Latest possible pick is based on noise level, + earliest possible pick is half a signal wavelength in front of most likely + pick given by PragPicker. Most likely pick (initial pick) must be given. + + :param: x, time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: nfac (noise factor), nfac times noise level to calculate latest possible pick + in EarlLatePicker + :type: int + + :param: TSNR, length of time windows around pick used to determine SNR [s] + :type: tuple (T_noise, T_gap, T_signal) + + :param: Pick1, initial (prelimenary) onset time, starting point for EarlLatePicker + :type: float + + ''' + + assert isinstance(X, Stream), "%s is not a stream object" % str(X) + + LPick = None + EPick = None + PickError = None + if Pick1 is not None: + print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' + + x =X[0].data + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) + #some parameters needed: + tnoise = TSNR[0] #noise window length for calculating noise level + tsignal = TSNR[2] #signal window length + tsafety = TSNR[1] #safety gap between signal onset and noise window + + #get latest possible pick + #get noise window + inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ + & (t >= max([Pick1 - tnoise - tsafety, 0]))) + #get signal window + isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ + & (t >= Pick1)) + #calculate noise level + nlevel = max(abs(x[inoise])) * nfac + #get time where signal exceeds nlevel + ilup = np.where(x[isignal] > nlevel) + ildown = np.where(x[isignal] < -nlevel) + if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: + print 'earllatepicker: Signal lower than noise level, misspick?' + return + il = min([ilup[0][0], ildown[0][0]]) + LPick = t[isignal][il] + + #get earliest possible pick + #get next 2 zero crossings after most likely pick + #if there is one trace in stream + zc = [] + zc.append(Pick1) + i = 0 + for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): + i = i+ 1 + if x[j-1] <= 0 and x[j] >= 0: + zc.append(t[isignal][i]) + elif x[j-1] > 0 and x[j] <= 0: + zc.append(t[isignal][i]) + if len(zc) == 3: + break + #calculate maximum period of signal out of zero crossings + Ts = max(np.diff(zc)) + #Ts/4 is assumed as time difference between most likely and earliest possible pick! + EPick = Pick1 - Ts/4 + + #get symmetric pick error as mean from earliest and latest possible pick + #by weighting latest possible pick tow times earliest possible pick + diffti_tl = LPick -Pick1 + diffti_te = Pick1 - EPick + PickError = (diffti_te + 2 * diffti_tl) / 3 + + if iplot is not None: + plt.figure(iplot) + p1, = plt.plot(t, x, 'k') + p2, = plt.plot(t[inoise], x[inoise]) + p3, = plt.plot(t[isignal], x[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + loc='best') + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) + plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') + plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') + plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') + plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') + plt.xlabel('Time [s] since %s' % X[0].stats.starttime) + plt.yticks([]) + ax = plt.gca() + ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) + plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) + plt.show() + raw_input() + plt.close(iplot) + + elif Pick1 == None: + print 'earllatepicker: No initial onset time given! Check input!' + return + + return EPick, LPick, PickError + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') + parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') + parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ + [s] (Tnoise, Tgap, Tsignal)') + parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) + +#earllatepicker(X, nfac, TSNR, Pick1, iplot) From 16ae4bdfe952f163fd5bc3cdf2d9b3c2c5e0fa02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 18 Mar 2015 14:45:08 +0100 Subject: [PATCH 0277/1144] Modified for using new function earllatepicker instead of removed class EarlLatePicker of object Picker. --- pylot/core/pick/run_makeCF.py | 75 +++++++++++++++++------------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 5b801715..de081e23 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -11,6 +11,7 @@ import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.CharFuns import CharacteristicFunction from pylot.core.pick.Picker import AutoPicking +from earllatepicker import earllatepicker import glob import argparse @@ -28,9 +29,9 @@ def run_makeCF(project, database, event, iplot, station=None): addnoise = 0.001 #add noise to seismogram for stable AR prediction arzorder = 2 #chosen order of AR process, vertical component arhorder = 4 #chosen order of AR process, horizontal components - TSNRhos = [5, 0.5, 1, 0.1] #window lengths [s] for calculating SNR for earliest/latest pick and quality assessment + TSNRhos = [5, 0.5, 1, .6] #window lengths [s] for calculating SNR for earliest/latest pick and quality assessment #from HOS-CF [noise window, safety gap, signal window, slope determination window] - TSNRarz = [5, 0.5, 1, 0.5] #window lengths [s] for calculating SNR for earliest/lates pick and quality assessment + TSNRarz = [5, 0.5, 1, 1.0] #window lengths [s] for calculating SNR for earliest/lates pick and quality assessment #from ARZ-CF #get waveform data if station: @@ -70,17 +71,16 @@ def run_makeCF(project, database, event, iplot, station=None): aiccf = AICcf(st_copy, cuttimes) #instance of AICcf ############################################################## #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicpick = AICPicker(aiccf, None, TSNRhos, 3, 10, None, 0.1) + aicpick = AICPicker(aiccf, TSNRhos, 3, 10, None, 0.1) ############################################################## #get refined onset time from HOS-CF using class Picker - hospick = PragPicker(hoscf, None, TSNRhos, 2, 10, 0.001, 0.2, aicpick.getpick()) + hospick = PragPicker(hoscf, TSNRhos, 2, 10, 0.001, 0.2, aicpick.getpick()) + ############################################################# #get earliest and latest possible picks - hosELpick = EarlLatePicker(hoscf, 1.5, TSNRhos, None, 10, None, None, hospick.getpick()) + [lpickhos, epickhos, pickerrhos] = earllatepicker(st, 1.5, TSNRhos, hospick.getpick(), 10) ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction - #get stream object of filtered data - st_copy[0].data = tr_filt.data - arzcf = ARZcf(st_copy, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf + arzcf = ARZcf(st, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf ############################################################## #calculate AIC-ARZ-CF using subclass AICcf of class CharacteristicFunction #class needs stream object => build it @@ -90,12 +90,12 @@ def run_makeCF(project, database, event, iplot, station=None): araiccf = AICcf(st_copy, cuttimes, tpredz, 0, tdetz) #instance of AICcf ############################################################## #get onset time from AIC-ARZ-CF using subclass AICPicker of class AutoPicking - aicarzpick = AICPicker(araiccf, 1.5, TSNRarz, 2, 10, None, 0.1) + aicarzpick = AICPicker(araiccf, TSNRarz, 2, 10, None, 0.1) ############################################################## #get refined onset time from ARZ-CF using class Picker - arzpick = PragPicker(arzcf, 1.5, TSNRarz, 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) + arzpick = PragPicker(arzcf, TSNRarz, 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) #get earliest and latest possible picks - arzELpick = EarlLatePicker(arzcf, 1.5, TSNRarz, None, 10, None, None, arzpick.getpick()) + [lpickarz, epickarz, pickerrarz] = earllatepicker(st, 1.5, TSNRarz, arzpick.getpick(), 10) elif not wfzfiles: print 'No vertical component data found!' @@ -131,12 +131,23 @@ def run_makeCF(project, database, event, iplot, station=None): arhaiccf = AICcf(H_copy, cuttimes, tpredh, 0, tdeth) #instance of AICcf ############################################################## #get onset time from AIC-ARH-CF using subclass AICPicker of class AutoPicking - aicarhpick = AICPicker(arhaiccf, 1.5, TSNRarz, 4, 10, None, 0.1) + aicarhpick = AICPicker(arhaiccf, TSNRarz, 4, 10, None, 0.1) ############################################################### #get refined onset time from ARH-CF using class Picker - arhpick = PragPicker(arhcf, 1.5, TSNRarz, 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) + arhpick = PragPicker(arhcf, TSNRarz, 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) #get earliest and latest possible picks - arhELpick = EarlLatePicker(arhcf, 1.5, TSNRarz, None, 10, None, None, arhpick.getpick()) + H_copy[0].data = trH1_filt.data + [lpickarh1, epickarh1, pickerrarh1] = earllatepicker(H_copy, 1.5, TSNRarz, arhpick.getpick(), 10) + H_copy[0].data = trH2_filt.data + [lpickarh2, epickarh2, pickerrarh2] = earllatepicker(H_copy, 1.5, TSNRarz, arhpick.getpick(), 10) + #get earliest pick of both earliest possible picks + epick = [epickarh1, epickarh2] + lpick = [lpickarh1, lpickarh2] + pickerr = [pickerrarh1, pickerrarh2] + ipick =np.argmin([epickarh1, epickarh2]) + epickarh = epick[ipick] + lpickarh = lpick[ipick] + pickerrarh = pickerr[ipick] #create stream with 3 traces #merge streams @@ -158,8 +169,6 @@ def run_makeCF(project, database, event, iplot, station=None): AllC[2].data = All3_filt.data #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf - #get earliest and latest possible pick from initial ARH-pick - ar3cELpick = EarlLatePicker(ar3ccf, 1.5, TSNRarz, None, 10, None, None, arhpick.getpick()) ############################################################## if iplot: #plot vertical trace @@ -177,16 +186,16 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([hospick.getpick(), hospick.getpick()], [-1.3, 1.3], 'r', linewidth=2) plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [1.3, 1.3], 'r') plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [-1.3, -1.3], 'r') - plt.plot([hosELpick.getLpick(), hosELpick.getLpick()], [-1.1, 1.1], 'r--') - plt.plot([hosELpick.getEpick(), hosELpick.getEpick()], [-1.1, 1.1], 'r--') + plt.plot([lpickhos, lpickhos], [-1.1, 1.1], 'r--') + plt.plot([epickhos, epickhos], [-1.1, 1.1], 'r--') plt.plot([aicarzpick.getpick(), aicarzpick.getpick()], [-1.2, 1.2], 'y', linewidth=2) plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [1.2, 1.2], 'y') plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [-1.2, -1.2], 'y') plt.plot([arzpick.getpick(), arzpick.getpick()], [-1.4, 1.4], 'g', linewidth=2) plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [1.4, 1.4], 'g') plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [-1.4, -1.4], 'g') - plt.plot([arzELpick.getLpick(), arzELpick.getLpick()], [-1.2, 1.2], 'g--') - plt.plot([arzELpick.getEpick(), arzELpick.getEpick()], [-1.2, 1.2], 'g--') + plt.plot([lpickarz, lpickarz], [-1.2, 1.2], 'g--') + plt.plot([epickarz, epickarz], [-1.2, 1.2], 'g--') plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.xlabel('Time [s]') @@ -211,12 +220,10 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'r') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'r') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') - plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') - plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') - plt.plot([arhpick.getpick() + arhELpick.getPickError(), arhpick.getpick() + arhELpick.getPickError()], \ - [-0.2, 0.2], 'r--') - plt.plot([arhpick.getpick() - arhELpick.getPickError(), arhpick.getpick() - arhELpick.getPickError()], \ - [-0.2, 0.2], 'r--') + plt.plot([lpickarh, lpickarh], [-0.8, 0.8], 'r--') + plt.plot([epickarh, epickarh], [-0.8, 0.8], 'r--') + plt.plot([arhpick.getpick() + pickerrarh, arhpick.getpick() + pickerrarh], [-0.2, 0.2], 'r--') + plt.plot([arhpick.getpick() - pickerrarh, arhpick.getpick() - pickerrarh], [-0.2, 0.2], 'r--') plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') @@ -233,12 +240,10 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'r') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'r') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') - plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') - plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') - plt.plot([arhpick.getpick() + arhELpick.getPickError(), arhpick.getpick() + arhELpick.getPickError()], \ - [-0.2, 0.2], 'r--') - plt.plot([arhpick.getpick() - arhELpick.getPickError(), arhpick.getpick() - arhELpick.getPickError()], \ - [-0.2, 0.2], 'r--') + plt.plot([lpickarh, lpickarh], [-0.8, 0.8], 'r--') + plt.plot([epickarh, epickarh], [-0.8, 0.8], 'r--') + plt.plot([arhpick.getpick() + pickerrarh, arhpick.getpick() + pickerrarh], [-0.2, 0.2], 'r--') + plt.plot([arhpick.getpick() - pickerrarh, arhpick.getpick() - pickerrarh], [-0.2, 0.2], 'r--') plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) plt.yticks([]) plt.ylim([-1.5, 1.5]) @@ -252,8 +257,6 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') - plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') - plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') plt.yticks([]) plt.xticks([]) plt.ylabel('Normalized Counts') @@ -266,8 +269,6 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') - plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') - plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') plt.yticks([]) plt.xticks([]) plt.ylabel('Normalized Counts') @@ -278,8 +279,6 @@ def run_makeCF(project, database, event, iplot, station=None): plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') - plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') - plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') plt.yticks([]) plt.ylabel('Normalized Counts') plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) From 787cac7d686214f66a09eb293a5f69cb68f212bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 18 Mar 2015 14:45:49 +0100 Subject: [PATCH 0278/1144] Removed class EarlLatePicker, replaced by new function earllatepicker. --- pylot/core/pick/Picker.py | 339 +------------------------------------- 1 file changed, 3 insertions(+), 336 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index dc928a85..b3579663 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -27,15 +27,11 @@ class AutoPicking(object): Superclass of different, automated picking algorithms applied on a CF determined using AIC, HOS, or AR prediction. ''' - def __init__(self, cf, nfac, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, Pick1=None): + def __init__(self, cf, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, Pick1=None): ''' :param: cf, characteristic function, on which the picking algorithm is applied :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object - :param: nfac (noise factor), nfac times noise level to calculate latest possible pick - in EarlLatePicker - :type: int - :param: TSNR, length of time windows around pick used to determine SNR [s] :type: tuple (T_noise, T_gap, T_signal) @@ -63,7 +59,6 @@ class AutoPicking(object): self.Tcf = cf.getTimeArray() self.Data = cf.getXCF() self.dt = cf.getIncrement() - self.setnfac(nfac) self.setTSNR(TSNR) self.setPickWindow(PickWindow) self.setiplot(iplot) @@ -74,25 +69,18 @@ class AutoPicking(object): def __str__(self): return '''\n\t{name} object:\n - nfac:\t{nfac}\n TSNR:\t\t\t{TSNR}\n PickWindow:\t{PickWindow}\n aus:\t{aus}\n Tsmooth:\t{Tsmooth}\n Pick1:\t{Pick1}\n '''.format(name=type(self).__name__, - nfac=self.getnfac(), TSNR=self.getTSNR(), PickWindow=self.getPickWindow(), aus=self.getaus(), Tsmooth=self.getTsmooth(), Pick1=self.getpick1()) - def getnfac(self): - return self.nfac - - def setnfac(self, nfac): - self.nfac = nfac def getTSNR(self): return self.TSNR @@ -127,15 +115,6 @@ class AutoPicking(object): def getSlope(self): return self.slope - def getLpick(self): - return self.LPick - - def getEpick(self): - return self.EPick - - def getPickError(self): - return self.PickError - def getiplot(self): return self.iplot @@ -165,7 +144,6 @@ class AICPicker(AutoPicking): print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' self.Pick = None - self.PickError = None #find NaN's nn = np.isnan(self.cf) if len(nn) > 1: @@ -263,7 +241,7 @@ class AICPicker(AutoPicking): p1, = plt.plot(self.Tcf, x / max(x), 'k') p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') if self.Pick is not None: - p3, = plt.plot([self.Pick, self.Pick], [-1 , 1], 'b', linewidth=2) + p3, = plt.plot([self.Pick, self.Pick], [-0.1 , 0.5], 'b', linewidth=2) plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) else: plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) @@ -281,7 +259,7 @@ class AICPicker(AutoPicking): p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], \ loc='best') - plt.title('SNR and Slope, Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ + plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ self.SNR, self.slope)) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.ylabel('Counts') @@ -307,7 +285,6 @@ class PragPicker(AutoPicking): print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' self.Pick = None - self.PickError = None self.SNR = None self.slope = None #smooth CF @@ -392,313 +369,3 @@ class PragPicker(AutoPicking): self.Pick = None print 'PragPicker: No initial onset time given! Check input!' return - - -class EarlLatePicker(AutoPicking): - ''' - Method to derive earliest and latest possible pick after Diehl & Kissling (2009) - as reasonable uncertainties. Latest possible pick is based on noise level, - earliest possible pick is half a signal wavelength in front of most likely - pick given by PragPicker. Most likely pick (initial pick) must be given. - ''' - - def calcPick(self): - - self.LPick = None - self.EPick = None - self.PickError = None - self.SNR = None - self.slope = None - if self.getpick1() is not None: - print 'EarlLatePicker: Get earliest and latest possible pick relative to most likely pick ...' - - ti = self.getpick1() - x = self.Data - t = self.Tcf - #some parameters needed: - tnoise = self.TSNR[0] #noise window length for calculating noise level - tsignal = self.TSNR[2] #signal window length - tsafety = self.TSNR[1] #safety gap between signal onset and noise window - - #get latest possible pick - #get noise window - inoise = np.where((self.Tcf <= max([ti - tsafety, 0])) \ - & (self.Tcf >= max([ti - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((self.Tcf <= min([ti + tsignal + tsafety, len(x[0].data)])) \ - & (self.Tcf >= ti)) - #calculate noise level - if len(x) == 1: - nlevel = max(abs(x[0].data[inoise])) * self.nfac - #get time where signal exceeds nlevel - ilup = np.where(x[0].data[isignal] > nlevel) - ildown = np.where(x[0].data[isignal] < -nlevel) - if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'EarlLatePicker: Signal lower than noise level, misspick?' - return - il = min([ilup[0][0], ildown[0][0]]) - self.LPick = t[isignal][il] - elif len(x) == 2: - nlevel = max(np.sqrt(np.power(x[0].data[inoise], 2) + np.power(x[1].data[inoise], 2))) - #get earliest time where signal exceeds nlevel - ilup1 = np.where(x[0].data[isignal] > nlevel) - ilup2 = np.where(x[1].data[isignal] > nlevel) - ildown1 = np.where(x[0].data[isignal] < -nlevel) - ildown2 = np.where(x[1].data[isignal] < -nlevel) - if np.size(ilup1) < 1 and np.size(ilup2) > 1: - ilup = ilup2 - elif np.size(ilup1) > 1 and np.size(ilup2) < 1: - ilup = ilup1 - elif np.size(ilup1) < 1 and np.size(ilup2) < 1: - ilup = None - else: - ilup = min([ilup1[0][0], ilup2[0][0]]) - - if np.size(ildown1) < 1 and np.size(ildown2) > 1: - ildown = ildown2 - elif np.size(ildown1) > 1 and np.size(ildown2) < 1: - ildown = ildown1 - elif np.size(ildown1) < 1 and np.size(ildown2) < 1: - ildown = None - else: - ildown = min([ildown1[0][0], ildown2[0][0]]) - if ilup == None and ildown == None: - print 'EarlLatePicker: Signal lower than noise level, misspick?' - return - il = min([ilup, ildown]) - self.LPick = t[isignal][il] - elif len(x) == 3: - nlevel = max(np.sqrt(np.power(x[0].data[inoise], 2) + np.power(x[1].data[inoise], 2) + \ - np.power(x[2].data[inoise], 2))) - #get earliest time where signal exceeds nlevel - ilup1 = np.where(x[0].data[isignal] > nlevel) - ilup2 = np.where(x[1].data[isignal] > nlevel) - ilup3 = np.where(x[2].data[isignal] > nlevel) - ildown1 = np.where(x[0].data[isignal] < -nlevel) - ildown2 = np.where(x[1].data[isignal] < -nlevel) - ildown3 = np.where(x[2].data[isignal] < -nlevel) - if np.size(ilup1) > 1 and np.size(ilup2) < 1 and np.size(ilup3) < 1: - ilup = ilup1 - elif np.size(ilup1) > 1 and np.size(ilup2) > 1 and np.size(ilup3) < 1: - ilup = min([ilup1[0][0], ilup2[0][0]]) - elif np.size(ilup1) > 1 and np.size(ilup2) > 1 and np.size(ilup3) > 1: - ilup = min([ilup1[0][0], ilup2[0][0], ilup3[0][0]]) - elif np.size(ilup1) < 1 and np.size(ilup2) > 1 and np.size(ilup3) > 1: - ilup = min([ilup2[0][0], ilup3[0][0]]) - elif np.size(ilup1) > 1 and np.size(ilup2) < 1 and np.size(ilup3) > 1: - ilup = min([ilup1[0][0], ilup3[0][0]]) - elif np.size(ilup1) < 1 and np.size(ilup2) < 1 and np.size(ilup3) < 1: - ilup = None - else: - ilup = min([ilup1[0][0], ilup2[0][0], ilup3[0][0]]) - - if np.size(ildown1) > 1 and np.size(ildown2) < 1 and np.size(ildown3) < 1: - ildown = ildown1 - elif np.size(ildown1) > 1 and np.size(ildown2) > 1 and np.size(ildown3) < 1: - ildown = min([ildown1[0][0], ildown2[0][0]]) - elif np.size(ildown1) > 1 and np.size(ildown2) > 1 and np.size(ildown3) > 1: - ildown = min([ildown1[0][0], ildown2[0][0], ildown3[0][0]]) - elif np.size(ildown1) < 1 and np.size(ildown2) > 1 and np.size(ildown3) > 1: - ildown = min([ildown2[0][0], ildown3[0][0]]) - elif np.size(ildown1) > 1 and np.size(ildown2) < 1 and np.size(ildown3) > 1: - ildown = min([ildown1[0][0], ildown3[0][0]]) - elif np.size(ildown1) < 1 and np.size(ildown2) < 1 and np.size(ildown3) < 1: - ildown = None - else: - ildown = min([ildown1[0][0], ildown2[0][0], ildown3[0][0]]) - if ilup == None and ildown == None: - print 'EarlLatePicker: Signal lower than noise level, misspick?' - return - il = min([ilup, ildown]) - self.LPick = t[isignal][il] - - #get earliest possible pick - #get next 2 zero crossings after most likely pick - #if there is one trace in stream - if len(x) == 1: - zc = [] - zc.append(ti) - i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[0].data[j-1] <= 0 and x[0].data[j] >= 0: - zc.append(t[isignal][i]) - elif x[0].data[j-1] > 0 and x[0].data[j] <= 0: - zc.append(t[isignal][i]) - if len(zc) == 3: - break - #calculate maximum period of signal out of zero crossings - Ts = max(np.diff(zc)) - #if there are two traces in stream - #get maximum of two signal periods - if len(x) == 2: - zc1 = [] - zc2 = [] - zc1.append(ti) - zc2.append(ti) - i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[0].data[j-1] <= 0 and x[0].data[j] >= 0: - zc1.append(t[isignal][i]) - elif x[0].data[j-1] > 0 and x[0].data[j] <= 0: - zc1.append(t[isignal][i]) - if x[1].data[j-1] <= 0 and x[1].data[j] >= 0: - zc2.append(t[isignal][i]) - elif x[1].data[j-1] > 0 and x[1].data[j] <= 0: - zc2.append(t[isignal][i]) - if len(zc1) >= 3 and len(zc2) >= 3: - break - Ts = max([max(np.diff(zc1)), max(np.diff(zc2))]) - #if there are three traces in stream - #get maximum of three signal periods - if len(x) == 3: - zc1 = [] - zc2 = [] - zc3 = [] - zc1.append(ti) - zc2.append(ti) - zc3.append(ti) - i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[0].data[j-1] <= 0 and x[0].data[j] >= 0: - zc1.append(t[isignal][i]) - elif x[0].data[j-1] > 0 and x[0].data[j] <= 0: - zc1.append(t[isignal][i]) - if x[1].data[j-1] <= 0 and x[1].data[j] >= 0: - zc2.append(t[isignal][i]) - elif x[1].data[j-1] > 0 and x[1].data[j] <= 0: - zc2.append(t[isignal][i]) - if x[2].data[j-1] <= 0 and x[2].data[j] >= 0: - zc3.append(t[isignal][i]) - elif x[2].data[j-1] > 0 and x[2].data[j] <= 0: - zc3.append(t[isignal][i]) - if len(zc1) >= 3 and len(zc2) >= 3 and len(zc3) >= 3: - break - Ts = max([max(np.diff(zc1)), max(np.diff(zc2)), max(np.diff(zc3))]) - - #Ts/4 is assumed as time difference between most likely and earliest possible pick! - self.EPick = ti - Ts/4 - - #get symmetric pick error as mean from earliest and latest possible pick - #by weighting latest possible pick tow times earliest possible pick - diffti_tl = self.LPick - ti - diffti_te = ti - self.EPick - self.PickError = (diffti_te + 2 * diffti_tl) / 3 - - if self.iplot is not None: - plt.figure(self.iplot) - if len(x) == 1: - p1, = plt.plot(t, x[0].data, 'k') - p2, = plt.plot(t[inoise], x[0].data[inoise]) - p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ - loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) - plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') - plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') - plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % self.Data[0].stats.station) - elif len(x) == 2: - plt.subplot(2,1,1) - p1, = plt.plot(t, x[0].data, 'k') - p2, = plt.plot(t[inoise], x[0].data[inoise]) - p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc1[0:3], [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ - loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) - plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') - plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') - plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot(zc1[0:3], [0, 0, 0], '*g') - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % self.Data[0].stats.station) - plt.subplot(2,1,2) - plt.plot(t, x[1].data, 'k') - plt.plot(t[inoise], x[1].data[inoise]) - plt.plot(t[isignal], x[1].data[isignal], 'r') - plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([ti, ti], [max(x[1].data), -max(x[1].data)], 'b', linewidth=2) - plt.plot([self.LPick, self.LPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') - plt.plot([self.EPick, self.EPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') - plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot(zc2[0:3], [0, 0, 0], '*g', markersize=14) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - ax = plt.gca() - ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) - plt.yticks([]) - elif len(x) == 3: - plt.subplot(3,1,1) - p1, = plt.plot(t, x[0].data, 'k') - p2, = plt.plot(t[inoise], x[0].data[inoise]) - p3, = plt.plot(t[isignal], x[0].data[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc1[0:3], [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ - loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([ti, ti], [max(x[0].data), -max(x[0].data)], 'b', linewidth=2) - plt.plot([self.LPick, self.LPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') - plt.plot([self.EPick, self.EPick], [max(x[0].data)/2, -max(x[0].data)/2], '--k') - plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % self.Data[0].stats.station) - plt.subplot(3,1,2) - plt.plot(t, x[1].data, 'k') - plt.plot(t[inoise], x[1].data[inoise]) - plt.plot(t[isignal], x[1].data[isignal], 'r') - plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([ti, ti], [max(x[1].data), -max(x[1].data)], 'b', linewidth=2) - plt.plot([self.LPick, self.LPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') - plt.plot([self.EPick, self.EPick], [max(x[1].data)/2, -max(x[1].data)/2], '--k') - plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot(zc2[0:3], [0, 0, 0], '*g', markersize=14) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) - plt.subplot(3,1,3) - plt.plot(t, x[2].data, 'k') - plt.plot(t[inoise], x[2].data[inoise]) - plt.plot(t[isignal], x[2].data[isignal], 'r') - plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([ti, ti], [max(x[2].data), -max(x[2].data)], 'b', linewidth=2) - plt.plot([self.LPick, self.LPick], [max(x[2].data)/2, -max(x[2].data)/2], '--k') - plt.plot([self.EPick, self.EPick], [max(x[2].data)/2, -max(x[2].data)/2], '--k') - plt.plot([ti + self.PickError, ti + self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot([ti - self.PickError, ti - self.PickError], [max(x[0].data)/2, -max(x[0].data)/2], 'r--') - plt.plot(zc3[0:3], [0, 0, 0], '*g', markersize=14) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([self.Tcf[inoise[0][0]] - 2, self.Tcf[isignal[0][len(isignal) - 1]] + 3]) - plt.show() - raw_input() - plt.close(self.iplot) - - elif self.getpick1() == None: - print 'EarlLatePicker: No initial onset time given! Check input!' - return - From a606b030e2f620b557f92975f3a2e0764b7aaf65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 19 Mar 2015 14:32:50 +0100 Subject: [PATCH 0279/1144] New function to derive automatically first motion (polarity) of phase onset based on zero crossings and slope determination. --- pylot/core/pick/fmpicker.py | 183 ++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100755 pylot/core/pick/fmpicker.py diff --git a/pylot/core/pick/fmpicker.py b/pylot/core/pick/fmpicker.py new file mode 100755 index 00000000..6aee8eb3 --- /dev/null +++ b/pylot/core/pick/fmpicker.py @@ -0,0 +1,183 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" + Created Mar 2015 + Function to derive first motion (polarity) for given phase onset based on zero crossings. + + :author: MAGS2 EP3 working group / Ludger Kueperkoch +""" +import numpy as np +import matplotlib.pyplot as plt +from obspy.core import Stream +import argparse + +def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): + ''' + Function to derive first motion (polarity) of given phase onset Pick. + Calculation is based on zero crossings determined within time window pickwin + after given onset time. + + :param: Xraw, unfiltered time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: Xfilt, filtered time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: pickwin, time window after onset Pick within zero crossings are calculated + :type: float + + :param: Pick, initial (most likely) onset time, starting point for fmpicker + :type: float + + :param: iplot, if given, results are plotted in figure(iplot) + :type: int + ''' + + assert isinstance(Xraw, Stream), "%s is not a stream object" % str(Xraw) + assert isinstance(Xfilt, Stream), "%s is not a stream object" % str(Xfilt) + + FM = None + if Pick is not None: + print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' + + xraw = Xraw[0].data + xfilt = Xfilt[0].data + t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, Xraw[0].stats.delta) + #get pick window + ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) + #remove mean + xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) + xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) + + #get next zero crossing after most likely pick + #initial onset is assumed to be the first zero crossing + #first from unfiltered trace + zc1 = [] + zc1.append(Pick) + index1 = [] + i = 0 + for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): + i = i+ 1 + if xraw[j-1] <= 0 and xraw[j] >= 0: + zc1.append(t[ipick][i]) + index1.append(i) + elif xraw[j-1] > 0 and xraw[j] <= 0: + zc1.append(t[ipick][i]) + index1.append(i) + if len(zc1) == 3: + break + + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc1[1] - zc1[0] <= Xraw[0].stats.delta: + li1 = index1[1] + else: + li1 = index1[0] + if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: + print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' + P1 = None + else: + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) + islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) + #calculate slope as polynomal fit of order 1 + xslope1 = np.arange(0, len(xraw[islope1]), 1) + P1 = np.polyfit(xslope1, xraw[islope1], 1) + datafit1 = np.polyval(P1, xslope1) + + #now using filterd trace + #next zero crossing after most likely pick + zc2 = [] + zc2.append(Pick) + index2 = [] + i = 0 + for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): + i = i+ 1 + if xfilt[j-1] <= 0 and xfilt[j] >= 0: + zc2.append(t[ipick][i]) + index2.append(i) + elif xfilt[j-1] > 0 and xfilt[j] <= 0: + zc2.append(t[ipick][i]) + index2.append(i) + if len(zc2) == 3: + break + + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: + li2 = index2[1] + else: + li2 = index2[0] + if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: + print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' + P2 = None + else: + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) + islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) + #calculate slope as polynomal fit of order 1 + xslope2 = np.arange(0, len(xfilt[islope2]), 1) + P2 = np.polyfit(xslope2, xfilt[islope2], 1) + datafit2 = np.polyval(P2, xslope2) + + #compare results + if P1 is not None and P2 is not None: + if P1[0] < 0 and P2[0] < 0: + FM = 'D' + elif P1[0] >= 0 and P2[0] < 0: + FM = '-' + elif P1[0] < 0 and P2[0]>= 0: + FM = '-' + elif P1[0] > 0 and P2[0] > 0: + FM = 'U' + elif P1[0] <= 0 and P2[0] > 0: + FM = '+' + elif P1[0] > 0 and P2[0] <= 0: + FM = '+' + + if iplot is not None: + plt.figure(iplot) + plt.subplot(2,1,1) + plt.plot(t, xraw, 'k') + p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) + if P1 is not None: + p2, = plt.plot(t[islope1], xraw[islope1]) + p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) + p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) + plt.legend([p1, p2, p3, p4], ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ + loc='best') + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim([t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) + plt.yticks([]) + plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[0].stats.station) + + plt.subplot(2,1,2) + plt.title('First-Motion Determination, Filtered Data') + plt.plot(t, xfilt, 'k') + p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', linewidth=2) + if P2 is not None: + p2, = plt.plot(t[islope2], xfilt[islope2]) + p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) + p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim([t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) + plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) + plt.yticks([]) + plt.show() + raw_input() + plt.close(iplot) + + return FM + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') + parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') + parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') + parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + earllatepicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) + From dc78abed09bd5f05322dbfe1c0c967e9c745f70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 19 Mar 2015 14:36:56 +0100 Subject: [PATCH 0280/1144] Modified to handle new function fmpicker. --- pylot/core/pick/run_makeCF.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index de081e23..690fdcff 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -12,6 +12,7 @@ import numpy as np from pylot.core.pick.CharFuns import CharacteristicFunction from pylot.core.pick.Picker import AutoPicking from earllatepicker import earllatepicker +from fmpicker import fmpicker import glob import argparse @@ -77,7 +78,11 @@ def run_makeCF(project, database, event, iplot, station=None): hospick = PragPicker(hoscf, TSNRhos, 2, 10, 0.001, 0.2, aicpick.getpick()) ############################################################# #get earliest and latest possible picks - [lpickhos, epickhos, pickerrhos] = earllatepicker(st, 1.5, TSNRhos, hospick.getpick(), 10) + st_copy[0].data = tr_filt.data + [lpickhos, epickhos, pickerrhos] = earllatepicker(st_copy, 1.5, TSNRhos, hospick.getpick(), 10) + ############################################################# + #get first motion of onset + hosfm = fmpicker(st, st_copy, 0.2, hospick.getpick(), 11) ############################################################## #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction arzcf = ARZcf(st, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf @@ -95,7 +100,8 @@ def run_makeCF(project, database, event, iplot, station=None): #get refined onset time from ARZ-CF using class Picker arzpick = PragPicker(arzcf, TSNRarz, 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) #get earliest and latest possible picks - [lpickarz, epickarz, pickerrarz] = earllatepicker(st, 1.5, TSNRarz, arzpick.getpick(), 10) + st_copy[0].data = tr_filt.data + [lpickarz, epickarz, pickerrarz] = earllatepicker(st_copy, 1.5, TSNRarz, arzpick.getpick(), 10) elif not wfzfiles: print 'No vertical component data found!' From c46b8a78219a4e7ec184bf91230c395104ff8a57 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 19 Mar 2015 15:36:55 +0100 Subject: [PATCH 0281/1144] made a copy and modified run_makeCF.py (now test_autopick.py in the root directory) --- test_autopick.py | 303 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100755 test_autopick.py diff --git a/test_autopick.py b/test_autopick.py new file mode 100755 index 00000000..bc942eab --- /dev/null +++ b/test_autopick.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + Script to run autoPyLoT-script "makeCF.py". + Only for test purposes! +""" + +from obspy.core import read +import matplotlib.pyplot as plt +import numpy as np +from pylot.core.pick.CharFuns import * +from pylot.core.pick.Picker import * +import glob +import argparse + +def run_makeCF(project, database, event, iplot, station=None): + #parameters for CF calculation + t2 = 7 #length of moving window for HOS calculation [sec] + p = 4 #order of HOS + cuttimes = [10, 50] #start and end time for CF calculation + bpz = [2, 30] #corner frequencies of bandpass filter, vertical component + bph = [2, 15] #corner frequencies of bandpass filter, horizontal components + tdetz= 1.2 #length of AR-determination window [sec], vertical component + tdeth= 0.8 #length of AR-determination window [sec], horizontal components + tpredz = 0.4 #length of AR-prediction window [sec], vertical component + tpredh = 0.4 #length of AR-prediction window [sec], horizontal components + addnoise = 0.001 #add noise to seismogram for stable AR prediction + arzorder = 2 #chosen order of AR process, vertical component + arhorder = 4 #chosen order of AR process, horizontal components + TSNRhos = [5, 0.5, 1, 0.1] #window lengths [s] for calculating SNR for earliest/latest pick and quality assessment + #from HOS-CF [noise window, safety gap, signal window, slope determination window] + TSNRarz = [5, 0.5, 1, 0.5] #window lengths [s] for calculating SNR for earliest/lates pick and quality assessment + #from ARZ-CF + #get waveform data + if station: + dpz = '/data/%s/EVENT_DATA/LOCAL/%s/%s/%s*HZ.msd' % (project, database, event, station) + dpe = '/data/%s/EVENT_DATA/LOCAL/%s/%s/%s*HE.msd' % (project, database, event, station) + dpn = '/data/%s/EVENT_DATA/LOCAL/%s/%s/%s*HN.msd' % (project, database, event, station) + #dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_z.gse' % (project, database, event, station) + #dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_e.gse' % (project, database, event, station) + #dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/%s*_n.gse' % (project, database, event, station) + else: + # dpz = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*_z.gse' % (project, database, event) + # dpe = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*_e.gse' % (project, database, event) + # dpn = '/DATA/%s/EVENT_DATA/LOCAL/%s/%s/*_n.gse' % (project, database, event) + dpz = '/data/%s/EVENT_DATA/LOCAL/%s/%s/*HZ.msd' % (project, database, event) + dpe = '/data/%s/EVENT_DATA/LOCAL/%s/%s/*HE.msd' % (project, database, event) + dpn = '/data/%s/EVENT_DATA/LOCAL/%s/%s/*HN.msd' % (project, database, event) + wfzfiles = glob.glob(dpz) + wfefiles = glob.glob(dpe) + wfnfiles = glob.glob(dpn) + if wfzfiles: + for i in range(len(wfzfiles)): + print 'Vertical component data found ...' + print wfzfiles[i] + st = read('%s' % wfzfiles[i]) + st_copy = st.copy() + #filter and taper data + tr_filt = st[0].copy() + tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + st_copy[0].data = tr_filt.data + ############################################################## + #calculate HOS-CF using subclass HOScf of class CharacteristicFunction + hoscf = HOScf(st_copy, cuttimes, t2, p) #instance of HOScf + ############################################################## + #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_aic = tr_filt.copy() + tr_aic.data = hoscf.getCF() + st_copy[0].data = tr_aic.data + aiccf = AICcf(st_copy, cuttimes) #instance of AICcf + ############################################################## + #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking + aicpick = AICPicker(aiccf, None, TSNRhos, 3, 10, None, 0.1) + ############################################################## + #get refined onset time from HOS-CF using class Picker + hospick = PragPicker(hoscf, None, TSNRhos, 2, 10, 0.001, 0.2, aicpick.getpick()) + #get earliest and latest possible picks + hosELpick = EarlLatePicker(hoscf, 1.5, TSNRhos, None, 10, None, None, hospick.getpick()) + ############################################################## + #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction + #get stream object of filtered data + st_copy[0].data = tr_filt.data + arzcf = ARZcf(st_copy, cuttimes, tpredz, arzorder, tdetz, addnoise) #instance of ARZcf + ############################################################## + #calculate AIC-ARZ-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_arzaic = tr_filt.copy() + tr_arzaic.data = arzcf.getCF() + st_copy[0].data = tr_arzaic.data + araiccf = AICcf(st_copy, cuttimes, tpredz, 0, tdetz) #instance of AICcf + ############################################################## + #get onset time from AIC-ARZ-CF using subclass AICPicker of class AutoPicking + aicarzpick = AICPicker(araiccf, 1.5, TSNRarz, 2, 10, None, 0.1) + ############################################################## + #get refined onset time from ARZ-CF using class Picker + arzpick = PragPicker(arzcf, 1.5, TSNRarz, 2.0, 10, 0.1, 0.05, aicarzpick.getpick()) + #get earliest and latest possible picks + arzELpick = EarlLatePicker(arzcf, 1.5, TSNRarz, None, 10, None, None, arzpick.getpick()) + elif not wfzfiles: + print 'No vertical component data found!' + + if wfefiles and wfnfiles: + for i in range(len(wfefiles)): + print 'Horizontal component data found ...' + print wfefiles[i] + print wfnfiles[i] + #merge streams + H = read('%s' % wfefiles[i]) + H += read('%s' % wfnfiles[i]) + H_copy = H.copy() + #filter and taper data + trH1_filt = H[0].copy() + trH2_filt = H[1].copy() + trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + H_copy[0].data = trH1_filt.data + H_copy[1].data = trH2_filt.data + + ############################################################## + #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction + arhcf = ARHcf(H_copy, cuttimes, tpredh, arhorder, tdeth, addnoise) #instance of ARHcf + ############################################################## + #calculate AIC-ARH-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_arhaic = trH1_filt.copy() + tr_arhaic.data = arhcf.getCF() + H_copy[0].data = tr_arhaic.data + #calculate ARH-AIC-CF + arhaiccf = AICcf(H_copy, cuttimes, tpredh, 0, tdeth) #instance of AICcf + ############################################################## + #get onset time from AIC-ARH-CF using subclass AICPicker of class AutoPicking + aicarhpick = AICPicker(arhaiccf, 1.5, TSNRarz, 4, 10, None, 0.1) + ############################################################### + #get refined onset time from ARH-CF using class Picker + arhpick = PragPicker(arhcf, 1.5, TSNRarz, 2.5, 10, 0.1, 0.05, aicarhpick.getpick()) + #get earliest and latest possible picks + arhELpick = EarlLatePicker(arhcf, 1.5, TSNRarz, None, 10, None, None, arhpick.getpick()) + + #create stream with 3 traces + #merge streams + AllC = read('%s' % wfefiles[i]) + AllC += read('%s' % wfnfiles[i]) + AllC += read('%s' % wfzfiles[i]) + #filter and taper data + All1_filt = AllC[0].copy() + All2_filt = AllC[1].copy() + All3_filt = AllC[2].copy() + All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + All1_filt.taper(max_percentage=0.05, type='hann') + All2_filt.taper(max_percentage=0.05, type='hann') + All3_filt.taper(max_percentage=0.05, type='hann') + AllC[0].data = All1_filt.data + AllC[1].data = All2_filt.data + AllC[2].data = All3_filt.data + #calculate AR3C-CF using subclass AR3Ccf of class CharacteristicFunction + ar3ccf = AR3Ccf(AllC, cuttimes, tpredz, arhorder, tdetz, addnoise) #instance of AR3Ccf + #get earliest and latest possible pick from initial ARH-pick + ar3cELpick = EarlLatePicker(ar3ccf, 1.5, TSNRarz, None, 10, None, None, arhpick.getpick()) + ############################################################## + if iplot: + #plot vertical trace + plt.figure() + tr = st[0] + tdata = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) + p1, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p2, = plt.plot(hoscf.getTimeArray(), hoscf.getCF() / max(hoscf.getCF()), 'r') + p3, = plt.plot(aiccf.getTimeArray(), aiccf.getCF()/max(aiccf.getCF()), 'b') + p4, = plt.plot(arzcf.getTimeArray(), arzcf.getCF()/max(arzcf.getCF()), 'g') + p5, = plt.plot(araiccf.getTimeArray(), araiccf.getCF()/max(araiccf.getCF()), 'y') + plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'b--') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'b') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([hospick.getpick(), hospick.getpick()], [-1.3, 1.3], 'r', linewidth=2) + plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [1.3, 1.3], 'r') + plt.plot([hospick.getpick()-0.5, hospick.getpick()+0.5], [-1.3, -1.3], 'r') + plt.plot([hosELpick.getLpick(), hosELpick.getLpick()], [-1.1, 1.1], 'r--') + plt.plot([hosELpick.getEpick(), hosELpick.getEpick()], [-1.1, 1.1], 'r--') + plt.plot([aicarzpick.getpick(), aicarzpick.getpick()], [-1.2, 1.2], 'y', linewidth=2) + plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [1.2, 1.2], 'y') + plt.plot([aicarzpick.getpick()-0.5, aicarzpick.getpick()+0.5], [-1.2, -1.2], 'y') + plt.plot([arzpick.getpick(), arzpick.getpick()], [-1.4, 1.4], 'g', linewidth=2) + plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [1.4, 1.4], 'g') + plt.plot([arzpick.getpick()-0.5, arzpick.getpick()+0.5], [-1.4, -1.4], 'g') + plt.plot([arzELpick.getLpick(), arzELpick.getLpick()], [-1.2, 1.2], 'g--') + plt.plot([arzELpick.getEpick(), arzELpick.getEpick()], [-1.2, 1.2], 'g--') + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + plt.title('%s, %s, CF-SNR=%7.2f, CF-Slope=%12.2f' % (tr.stats.station, \ + tr.stats.channel, aicpick.getSNR(), aicpick.getSlope())) + plt.suptitle(tr.stats.starttime) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) + #plot horizontal traces + plt.figure(2) + plt.subplot(2,1,1) + tsteph = tpredh / 4 + th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) + th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) + tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh + p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + p22, = plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') + p23, = plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) + plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') + plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') + plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') + plt.plot([arhpick.getpick() + arhELpick.getPickError(), arhpick.getpick() + arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') + plt.plot([arhpick.getpick() - arhELpick.getPickError(), arhpick.getpick() - arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.suptitle(trH1_filt.stats.starttime) + plt.legend([p21, p22, p23], ['Data', 'ARH-CF', 'ARHAIC-CF']) + plt.subplot(2,1,2) + plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) + plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'r') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'r') + plt.plot([arhELpick.getLpick(), arhELpick.getLpick()], [-0.8, 0.8], 'r--') + plt.plot([arhELpick.getEpick(), arhELpick.getEpick()], [-0.8, 0.8], 'r--') + plt.plot([arhpick.getpick() + arhELpick.getPickError(), arhpick.getpick() + arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') + plt.plot([arhpick.getpick() - arhELpick.getPickError(), arhpick.getpick() - arhELpick.getPickError()], \ + [-0.2, 0.2], 'r--') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.xlabel('Time [s]') + plt.ylabel('Normalized Counts') + #plot 3-component window + plt.figure(3) + plt.subplot(3,1,1) + p31, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p32, = plt.plot(ar3ccf.getTimeArray(), ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') + plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([tr.stats.station, tr.stats.channel]) + plt.suptitle(trH1_filt.stats.starttime) + plt.legend([p31, p32], ['Data', 'AR3C-CF']) + plt.subplot(3,1,2) + plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + plt.plot(ar3ccf.getTimeArray(), ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') + plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') + plt.yticks([]) + plt.xticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) + plt.subplot(3,1,3) + plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(ar3ccf.getTimeArray(), ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') + plt.plot([arhpick.getpick(), arhpick.getpick()], [-1, 1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [-1, -1], 'b') + plt.plot([arhpick.getpick()-0.5, arhpick.getpick()+0.5], [1, 1], 'b') + plt.plot([ar3cELpick.getLpick(), ar3cELpick.getLpick()], [-0.8, 0.8], 'b--') + plt.plot([ar3cELpick.getEpick(), ar3cELpick.getEpick()], [-0.8, 0.8], 'b--') + plt.yticks([]) + plt.ylabel('Normalized Counts') + plt.title([trH2_filt.stats.station, trH2_filt.stats.channel]) + plt.xlabel('Time [s]') + plt.show() + raw_input() + plt.close() + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--project', type=str, help='project name (e.g. Insheim)') + parser.add_argument('--database', type=str, help='event data base (e.g. 2014.09_Insheim)') + parser.add_argument('--event', type=str, help='event ID (e.g. e0010.015.14)') + parser.add_argument('--iplot', help='anything, if set, figure occurs') + parser.add_argument('--station', type=str, help='Station ID (e.g. INS3) (optional)') + args = parser.parse_args() + + run_makeCF(args.project, args.database, args.event, args.iplot, args.station) From 27ecdb899b6923c67422a64cc0311d6f6646a47a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sun, 29 Mar 2015 08:07:46 +0200 Subject: [PATCH 0282/1144] plotting scheme restructured: the same widgets are used for plotting the overview and the station plots, the plotting method has been moved to the widget side which makes more sense than having it on the data side; new functions in utils module: prepTimeAxis returns a proper time axis array for a given start time and an obspy trace; getGlobalTimes returns the minimum start time and the maximum end time (edges) of a given obspy stream object --- QtPyLoT.py | 18 +++++--- pylot/core/read/data.py | 38 +++-------------- pylot/core/util/__init__.py | 3 +- pylot/core/util/utils.py | 28 ++++++++++++- pylot/core/util/widgets.py | 84 +++++++++++++++++++++++++------------ 5 files changed, 105 insertions(+), 66 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 4f71b66e..909cf8fc 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -104,7 +104,7 @@ class MainWindow(QMainWindow): self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(QIcon(":/icon.ico")) - xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') + xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') _widget = QWidget() _widget.setCursor(Qt.CrossCursor) @@ -365,7 +365,11 @@ class MainWindow(QMainWindow): self.plotWaveformData() def plotWaveformData(self): - self.getData().plotWFData(self.getPlotWidget()) + zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'} + comp = self.getComponent() + title = 'overview: {0} components'.format(zne_text[comp]) + wfst = self.getData().getWFData().select(component=comp) + self.getPlotWidget().plotWFData(wfdata=wfst, title=title) def filterWaveformData(self): if self.getData(): @@ -455,10 +459,14 @@ class MainWindow(QMainWindow): wfID = self.getWFID(event) station = self.getStationName(wfID) - data = self.getData().getWFData() - pickDlg = PickDlg(self, data.select(station=station), station) print 'picking on station {0}'.format(station) - pickDlg.exec_() + data = self.getData().getWFData() + pickDlg = PickDlg(self, data=data.select(station=station), + station=station) + if pickDlg.exec_(): + print 'picks accepted' + else: + print 'picks not saved and closed dialog' def updateStatus(self, message): diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 44249638..874c7aac 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -10,7 +10,8 @@ from obspy.core.event import (Event, Catalog) from pylot.core.read import readPILOTEvent -from pylot.core.util import fnConstructor, createEvent, FormatError +from pylot.core.util import fnConstructor, createEvent, FormatError, \ + prepTimeAxis, getGlobalTimes class Data(object): @@ -63,14 +64,7 @@ class Data(object): return self.cuttimes def updateCutTimes(self): - min_start = UTCDateTime() - max_end = None - for trace in self.getWFData().select(component=self.getComp()): - if trace.stats.starttime < min_start: - min_start = trace.stats.starttime - if max_end is None or trace.stats.endtime > max_end: - max_end = trace.stats.endtime - self.cuttimes = [min_start, max_end] + self.cuttimes = getGlobalTimes(self.getWFData()) def exportEvent(self, fnout=None, evtformat='QUAKEML'): @@ -99,29 +93,6 @@ class Data(object): raise KeyError('''{0} export format not implemented: {1}'''.format(evtformat, e)) - def plotWFData(self, widget): - wfst = self.getWFData().select(component=self.getComp()) - widget.axes.cla() - for n, trace in enumerate(wfst): - stime = trace.stats.starttime - self.getCutTimes()[0] - etime = trace.stats.endtime - self.getCutTimes()[1] - srate = trace.stats.sampling_rate - nsamp = len(trace.data) - tincr = trace.stats.delta - station = trace.stats.station - time_ax = np.arange(stime, nsamp / srate, tincr) - trace.normalize(trace.data.max() * 2) - widget.axes.plot(time_ax, trace.data + n, 'k') - xlabel = 'seconds since {0}'.format(self.getCutTimes()[0]) - ylabel = '' - zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'} - title = 'overview: {0} components'.format(zne_text[self.getComp()]) - widget.updateWidget(xlabel, ylabel, title) - widget.setPlotDict(n, station) - - widget.axes.autoscale(tight=True) - - def getComp(self): return self.comp @@ -146,7 +117,8 @@ class Data(object): def appendWFData(self, fnames): assert isinstance(fnames, list), "input parameter 'fnames' is " \ "supposed to be of type 'list' " \ - "but is actually".format(type(fnames)) + "but is actually {0}".format(type( + fnames)) if self.dirty: self.resetWFData() diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 5248f170..b20bc021 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -4,7 +4,8 @@ from pylot.core.util.errors import OptionsError, FormatError, DatastructureError from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ - getHash, getLogin, createCreationInfo, createResourceID + getHash, getLogin, createCreationInfo, createResourceID, prepTimeAxis, \ + getGlobalTimes from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ PropertiesDlg, NewEventDlg, MPLWidget, createAction from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index ab37c2b0..a173fe40 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -6,6 +6,7 @@ import os import pwd import re import hashlib +import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope @@ -224,5 +225,30 @@ def createAmplitude(pickID, amp, unit, category, origintime, cinfo, def getOwner(fn): return pwd.getpwuid(os.stat(fn).st_uid).pw_name +def prepTimeAxis(stime, trace): + nsamp = trace.stats.npts + srate = trace.stats.sampling_rate + tincr = trace.stats.delta + etime = stime + nsamp / srate + time_ax = np.arange(stime, etime, tincr) + if len(time_ax) < nsamp: + print 'elongate time axes by one datum' + time_ax = np.arange(stime, etime + tincr, tincr) + elif len(time_ax) > nsamp: + print 'shorten time axes by one datum' + time_ax = np.arange(stime, etime - tincr, tincr) + if len(time_ax) != nsamp: + raise ValueError('{0} samples of data \n ' + '{1} length of time vector \n' + 'delta: {2}'.format(nsamp, len(time_ax), tincr)) + return time_ax - +def getGlobalTimes(stream): + min_start = UTCDateTime() + max_end = None + for trace in stream: + if trace.stats.starttime < min_start: + min_start = trace.stats.starttime + if max_end is None or trace.stats.endtime > max_end: + max_end = trace.stats.endtime + return [min_start, max_end] diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 74cbcf37..7dd35262 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -43,6 +43,7 @@ from PySide.QtCore import (QSettings, from PySide.QtWebKit import QWebView from pylot.core.read import FilterOptions from pylot.core.util.defaults import OUTPUTFORMATS +from pylot.core.util import prepTimeAxis, getGlobalTimes def createAction(parent, text, slot=None, shortcut=None, icon=None, @@ -70,13 +71,18 @@ class MPLWidget(FigureCanvas): self._parent = None self.setParent(parent) self.figure = Figure() + # attribute plotdict is an dictionary connecting position and a name self.plotdict = dict() - + # create axes self.axes = self.figure.add_subplot(111) - self._statID = None + # clear axes each time plot is called + self.axes.hold(True) + # initialize super class FigureCanvas.__init__(self, self.figure) + # add an cursor for station selection self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True, color='m', lw=1) + # update labels of the entire widget self.updateWidget(xlabel, ylabel, title) def getPlotDict(self): @@ -91,6 +97,25 @@ class MPLWidget(FigureCanvas): def setParent(self, parent): self._parent = parent + def plotWFData(self, wfdata, title = None): + self.axes.lines = [] + wfstart = getGlobalTimes(wfdata)[0] + for n, trace in enumerate(wfdata): + station = trace.stats.station + print('plotting station: %s' % station) + stime = trace.stats.starttime - wfstart + time_ax = prepTimeAxis(stime, trace) + trace.detrend() + trace.detrend('demean') + trace.normalize(trace.data.max() * 2) + self.axes.plot(time_ax, trace.data + n, 'k') + self.axes.hold(True) + xlabel = 'seconds since {0}'.format(wfstart) + ylabel = '' + self.updateWidget(xlabel, ylabel, title) + self.setPlotDict(n, station) + self.axes.autoscale(tight=True) + def updateXLabel(self, text): self.axes.set_xlabel(text) @@ -105,6 +130,11 @@ class MPLWidget(FigureCanvas): self.updateYLabel(ylabel) self.updateTitle(title) + def insertLabel(self, pos, text): + pos = pos / max(self.axes.ylim) + axann = self.axes.annotate(text, xy=(.03, pos), xycoords='axes fraction') + axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) + class multiComponentPlot(FigureCanvas): @@ -165,12 +195,8 @@ class multiComponentPlot(FigureCanvas): self.axesdict = dict() # prepare variables for plotting - trace = data[0] - stime = trace.stats.starttime - srate = trace.stats.sampling_rate - npts = trace.stats.npts - tincr = trace.stats.delta - time_ax = np.arange(0, npts / srate, tincr) + stime = getGlobalTimes(self.getData())[0] + xlabel = 'time since {0} [s]'.format(stime) # plot individual component traces in separate subplots @@ -181,8 +207,9 @@ class multiComponentPlot(FigureCanvas): else: subax = self.figure.add_subplot(nsub) subax.autoscale(tight=True) - subset = data.copy().select(component=comp)[0].data - subax.plot(time_ax, subset) + subset = data.copy().select(component=comp)[0] + time_ax = prepTimeAxis(subset.stats.starttime - stime, subset) + subax.plot(time_ax, subset.data) self.axesdict[n] = subax self.updateYLabel(n, comp) if n == self.noc: @@ -190,12 +217,9 @@ class multiComponentPlot(FigureCanvas): else: self.updateXLabel(n, '') - self.multiCursor = MultiCursor(self.figure.canvas, tuple(self.axesdict.values()), color='r', lw=1) - - def insertLabel(self, pos, text): - subax = self.axesdict[pos] - axann = subax.annotate(text, xy=(.03, .97), xycoords='axes fraction') - axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) + self.multiCursor = MultiCursor(self.figure.canvas, + tuple(self.axesdict.values()), + color='r', lw=1) def updateXLabel(self, pos, text): self.axesdict[pos].set_xlabel(text) @@ -214,6 +238,7 @@ class PickDlg(QDialog): self.rotate = rotate self.components = 'ZNE' + # set attribute holding data if data is None: try: data = parent.getData().getWFData().copy() @@ -225,10 +250,16 @@ class PickDlg(QDialog): else: self.data = data - self.multicompfig = multiComponentPlot(data=data, parent=self, - components=self.getComponents()) + # initialize plotting widget + self.multicompfig = MPLWidget(self) + + # setup ui self.setupUi() + # plot data + self.getPlotWidget().plotWFData(wfdata=self.getWFData(), + title=self.getStation()) + def setupUi(self): # create actions @@ -266,6 +297,9 @@ class PickDlg(QDialog): def getComponents(self): return self.components + def getStation(self): + return self.station + def getPlotWidget(self): return self.multicompfig @@ -273,8 +307,9 @@ class PickDlg(QDialog): return self.data def filterWFData(self): - self.data.filter(type='bandpass', freqmin=.5, freqmax=15.) - self.getPlotWidget().resetPlot(self.getComponents(), self.getWFData()) + data = self.getWFData().copy().filter(type='bandpass', freqmin=.5, freqmax=15.) + title = self.getStation() + ' (filtered)' + self.getPlotWidget().plotWFData(wfdata=data, title=title) class PropertiesDlg(QDialog): @@ -300,12 +335,9 @@ class PropertiesDlg(QDialog): layout.addWidget(self.buttonBox) self.setLayout(layout) - self.connect(self.buttonBox, Signal("accepted()"), self, - Slot("accept()")) - self.connect(self.buttonBox.button(QDialogButtonBox.Apply), - Signal("clicked()"), self.apply) - self.connect(self.buttonBox, Signal("rejected()"), - self, Slot("reject()")) + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + self.buttonBox.button(QDialogButtonBox.Apply).clicked.connect(self.apply) def accept(self, *args, **kwargs): self.apply() From 814906ef65a6c1e1f1e037131961d165090ced39 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 30 Mar 2015 05:55:54 +0200 Subject: [PATCH 0283/1144] module restructured: beginning with general utils followed by utils concerning obspy event creation --- pylot/core/util/utils.py | 75 +++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index a173fe40..d60f52b3 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -23,9 +23,11 @@ def fnConstructor(s): fn = '_' + fn return fn + def getLogin(): return pwd.getpwuid(os.getuid())[0] + def getHash(time): ''' :param time: time object for which a hash should be calculated @@ -36,6 +38,41 @@ def getHash(time): hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) return hg.hexdigest() + +def getOwner(fn): + return pwd.getpwuid(os.stat(fn).st_uid).pw_name + + +def prepTimeAxis(stime, trace): + nsamp = trace.stats.npts + srate = trace.stats.sampling_rate + tincr = trace.stats.delta + etime = stime + nsamp / srate + time_ax = np.arange(stime, etime, tincr) + if len(time_ax) < nsamp: + print 'elongate time axes by one datum' + time_ax = np.arange(stime, etime + tincr, tincr) + elif len(time_ax) > nsamp: + print 'shorten time axes by one datum' + time_ax = np.arange(stime, etime - tincr, tincr) + if len(time_ax) != nsamp: + raise ValueError('{0} samples of data \n ' + '{1} length of time vector \n' + 'delta: {2}'.format(nsamp, len(time_ax), tincr)) + return time_ax + + +def getGlobalTimes(stream): + min_start = UTCDateTime() + max_end = None + for trace in stream: + if trace.stats.starttime < min_start: + min_start = trace.stats.starttime + if max_end is None or trace.stats.endtime > max_end: + max_end = trace.stats.endtime + return [min_start, max_end] + + def createCreationInfo(agency_id=None, creation_time=None, author=None): if author is None: author = getLogin() @@ -44,6 +81,7 @@ def createCreationInfo(agency_id=None, creation_time=None, author=None): return ope.CreationInfo(agency_id=agency_id, author=author, creation_time=creation_time) + def createResourceID(timetohash, restype, authority_id=None, hrstr=None): ''' @@ -65,6 +103,7 @@ def createResourceID(timetohash, restype, authority_id=None, hrstr=None): resID.convertIDToQuakeMLURI(authority_id=authority_id) return resID + def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, authority_id=None): ''' @@ -94,6 +133,7 @@ def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, origin.depth = depth return origin + def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): ''' createEvent - funtion to create an ObsPy Event @@ -123,6 +163,7 @@ def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): event.event_type = etype return event + def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, wfseedstr, authority_id): ''' @@ -157,6 +198,7 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') return pick + def createArrival(origintime, pickresID, eventnum, cinfo, phase, station, authority_id, azimuth=None, dist=None): ''' @@ -194,6 +236,7 @@ def createArrival(origintime, pickresID, eventnum, cinfo, phase, station, arrival.distance = None return arrival + def createMagnitude(originID, origintime, cinfo, authority_id=None): ''' createMagnitude - function to create an ObsPy Magnitude object @@ -210,6 +253,7 @@ def createMagnitude(originID, origintime, cinfo, authority_id=None): magnitude.origin_id = originID return magnitude + def createAmplitude(pickID, amp, unit, category, origintime, cinfo, authority_id=None): amplresID = createResourceID(origintime, 'ampl', authority_id) @@ -221,34 +265,3 @@ def createAmplitude(pickID, amp, unit, category, origintime, cinfo, amplitude.type = ope.AmplitudeCategory(category) amplitude.pick_id = pickID return amplitude - -def getOwner(fn): - return pwd.getpwuid(os.stat(fn).st_uid).pw_name - -def prepTimeAxis(stime, trace): - nsamp = trace.stats.npts - srate = trace.stats.sampling_rate - tincr = trace.stats.delta - etime = stime + nsamp / srate - time_ax = np.arange(stime, etime, tincr) - if len(time_ax) < nsamp: - print 'elongate time axes by one datum' - time_ax = np.arange(stime, etime + tincr, tincr) - elif len(time_ax) > nsamp: - print 'shorten time axes by one datum' - time_ax = np.arange(stime, etime - tincr, tincr) - if len(time_ax) != nsamp: - raise ValueError('{0} samples of data \n ' - '{1} length of time vector \n' - 'delta: {2}'.format(nsamp, len(time_ax), tincr)) - return time_ax - -def getGlobalTimes(stream): - min_start = UTCDateTime() - max_end = None - for trace in stream: - if trace.stats.starttime < min_start: - min_start = trace.stats.starttime - if max_end is None or trace.stats.endtime > max_end: - max_end = trace.stats.endtime - return [min_start, max_end] From 5b52f718febad0caa88ec00458dcefa599a644cf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 30 Mar 2015 06:04:24 +0200 Subject: [PATCH 0284/1144] mutable default arguments may cause problems as unexpected default behavior --- pylot/core/analysis/trigger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/analysis/trigger.py b/pylot/core/analysis/trigger.py index add9a687..baa4ccd8 100644 --- a/pylot/core/analysis/trigger.py +++ b/pylot/core/analysis/trigger.py @@ -4,7 +4,8 @@ from obspy.signal.trigger import recSTALTA, triggerOnset -def createSingleTriggerlist(st, station='ZV01', trigcomp='Z', stalta=[1, 10], trigonoff=[6, 1]): +def createSingleTriggerlist(st, station='ZV01', trigcomp='Z', stalta=(1, 10), + trigonoff=(6, 1)): ''' uses a single-station trigger to create a triggerlist for this station :param st: From 45eb138717a1cf4b6ece730ef32a669c32116cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Mar 2015 10:58:24 +0200 Subject: [PATCH 0285/1144] New function to derive SNR, returns SNR and SNR[dB]. --- pylot/core/pick/getSNR.py | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 pylot/core/pick/getSNR.py diff --git a/pylot/core/pick/getSNR.py b/pylot/core/pick/getSNR.py new file mode 100644 index 00000000..6c2b6872 --- /dev/null +++ b/pylot/core/pick/getSNR.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" + Created Mar/Apr 2015 + Function to calculate SNR of certain part of seismogram relativ + to given time. Returns SNR and SNR [dB]. + + :author: Ludger Kueperkoch /MAGS EP3 working group +""" +from obspy.core import Stream +import numpy as np + +def getSNR(X, TSNR, t1): + ''' + Function to calculate SNR of certain part of seismogram relative to + given time (onset) out of given noise and signal windows. A safety gap + between noise and signal part can be set. Returns SNR and SNR [dB]. + + :param: X, time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: TSNR, length of time windows [s] around t1 (onset) used to determine SNR + :type: tuple (T_noise, T_gap, T_signal) + + :param: t1, initial time (onset) from which noise and signal windows are calculated + :type: float + ''' + + assert isinstance(X, Stream), "%s is not a stream object" % str(X) + + SNR = None + SNRdB = None + x = X[0].data + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) + #some parameters needed: + tnoise = TSNR[0] #noise window length for calculating noise level + tsignal = TSNR[2] #signal window length + tsafety = TSNR[1] #safety gap between signal onset and noise window + #get noise window + inoise = np.where((t <= max([t1 - tsafety, 0])) \ + & (t >= max([t1 - tnoise - tsafety, 0]))) + #get signal window + isignal = np.where((t <= min([t1 + tsignal + tsafety, len(x)])) \ + & (t >= t1)) + if np.size(inoise) < 1: + print 'getSNR: Empty array inoise, check noise window!' + return + elif np.size(isignal) < 1: + print 'getSNR: Empty array isignal, check signal window!' + return + + #calculate ratios + SNR = max(abs(x[isignal])) / np.mean(abs(x[inoise])) + SNRdB = 20 * np.log10(SNR) + + return SNR, SNRdB + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') + parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ + [s] (Tnoise, Tgap, Tsignal)') + parser.add_argument('--t1', type=float, help='initial time from which noise and signal windows are calculated') + args = parser.parse_args() + getSNR(args.X, args.TSNR, args.t1) + From 0ad7e629ce5a0256572ab449097919d2d42ae105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Mar 2015 10:59:27 +0200 Subject: [PATCH 0286/1144] Marginal changes only. --- pylot/core/pick/earllatepicker.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pylot/core/pick/earllatepicker.py b/pylot/core/pick/earllatepicker.py index 1a2663f7..31c98c66 100755 --- a/pylot/core/pick/earllatepicker.py +++ b/pylot/core/pick/earllatepicker.py @@ -4,10 +4,10 @@ Created Mar 2015 Transcription of the rezipe of Diehl et al. (2009) for consistent phase picking. For a given inital (the most likely) pick, the corresponding earliest - and latest possible picks are calculated based on noise measurements in front of + and latest possible pick is calculated based on noise measurements in front of the most likely pick and signal wavelength derived from zero crossings. - :author: MAGS2 EP3 working group / Ludger Kueperkoch + :author: Ludger Kueperkoch / MAGS2 EP3 working group """ import numpy as np import matplotlib.pyplot as plt @@ -19,21 +19,23 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): Function to derive earliest and latest possible pick after Diehl & Kissling (2009) as reasonable uncertainties. Latest possible pick is based on noise level, earliest possible pick is half a signal wavelength in front of most likely - pick given by PragPicker. Most likely pick (initial pick) must be given. + pick given by PragPicker or manually set by analyst. Most likely pick + (initial pick Pick1) must be given. - :param: x, time series (seismogram) + :param: X, time series (seismogram) :type: `~obspy.core.stream.Stream` :param: nfac (noise factor), nfac times noise level to calculate latest possible pick - in EarlLatePicker :type: int :param: TSNR, length of time windows around pick used to determine SNR [s] :type: tuple (T_noise, T_gap, T_signal) - :param: Pick1, initial (prelimenary) onset time, starting point for EarlLatePicker + :param: Pick1, initial (most likely) onset time, starting point for earllatepicker :type: float + :param: iplot, if given, results are plotted in figure(iplot) + :type: int ''' assert isinstance(X, Stream), "%s is not a stream object" % str(X) @@ -71,7 +73,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): #get earliest possible pick #get next 2 zero crossings after most likely pick - #if there is one trace in stream + #initial onset is assumed to be the first zero crossing zc = [] zc.append(Pick1) i = 0 @@ -89,7 +91,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): EPick = Pick1 - Ts/4 #get symmetric pick error as mean from earliest and latest possible pick - #by weighting latest possible pick tow times earliest possible pick + #by weighting latest possible pick two times earliest possible pick diffti_tl = LPick -Pick1 diffti_te = Pick1 - EPick PickError = (diffti_te + 2 * diffti_tl) / 3 @@ -134,5 +136,3 @@ if __name__ == "__main__": parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') args = parser.parse_args() earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) - -#earllatepicker(X, nfac, TSNR, Pick1, iplot) From bebe3a3c4543a88879bc5dfc84086433eda4afee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Mar 2015 14:18:19 +0200 Subject: [PATCH 0287/1144] Cleaned up source code, debuged: calculates now T/4 instead of T/8 out of zero crossings. --- pylot/core/pick/earllatepicker.py | 145 +++++++++++++++--------------- 1 file changed, 70 insertions(+), 75 deletions(-) diff --git a/pylot/core/pick/earllatepicker.py b/pylot/core/pick/earllatepicker.py index 31c98c66..f41a0503 100755 --- a/pylot/core/pick/earllatepicker.py +++ b/pylot/core/pick/earllatepicker.py @@ -43,86 +43,81 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): LPick = None EPick = None PickError = None - if Pick1 is not None: - print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' + print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' - x =X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window + x = X[0].data + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) + #some parameters needed: + tnoise = TSNR[0] #noise window length for calculating noise level + tsignal = TSNR[2] #signal window length + tsafety = TSNR[1] #safety gap between signal onset and noise window - #get latest possible pick - #get noise window - inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ - & (t >= max([Pick1 - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ - & (t >= Pick1)) - #calculate noise level - nlevel = max(abs(x[inoise])) * nfac - #get time where signal exceeds nlevel - ilup = np.where(x[isignal] > nlevel) - ildown = np.where(x[isignal] < -nlevel) - if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'earllatepicker: Signal lower than noise level, misspick?' - return - il = min([ilup[0][0], ildown[0][0]]) - LPick = t[isignal][il] + #get latest possible pick + #get noise window + inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ + & (t >= max([Pick1 - tnoise - tsafety, 0]))) + #get signal window + isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ + & (t >= Pick1)) + #calculate noise level + nlevel = max(abs(x[inoise])) * nfac + #get time where signal exceeds nlevel + ilup = np.where(x[isignal] > nlevel) + ildown = np.where(x[isignal] < -nlevel) + if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: + print 'earllatepicker: Signal lower than noise level, misspick?' + return + il = min([ilup[0][0], ildown[0][0]]) + LPick = t[isignal][il] - #get earliest possible pick - #get next 2 zero crossings after most likely pick - #initial onset is assumed to be the first zero crossing - zc = [] - zc.append(Pick1) - i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[j-1] <= 0 and x[j] >= 0: - zc.append(t[isignal][i]) - elif x[j-1] > 0 and x[j] <= 0: - zc.append(t[isignal][i]) - if len(zc) == 3: - break - #calculate maximum period of signal out of zero crossings - Ts = max(np.diff(zc)) - #Ts/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - Ts/4 + #get earliest possible pick + #get next 2 zero crossings after most likely pick + #initial onset is assumed to be the first zero crossing + zc = [] + zc.append(Pick1) + i = 0 + for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): + i = i+ 1 + if x[j-1] <= 0 and x[j] >= 0: + zc.append(t[isignal][i]) + elif x[j-1] > 0 and x[j] <= 0: + zc.append(t[isignal][i]) + if len(zc) == 3: + break + #calculate maximum period T0 of signal out of zero crossings + T0 = max(np.diff(zc)) #this is half wave length! + #T0/4 is assumed as time difference between most likely and earliest possible pick! + EPick = Pick1 - T0/2 - #get symmetric pick error as mean from earliest and latest possible pick - #by weighting latest possible pick two times earliest possible pick - diffti_tl = LPick -Pick1 - diffti_te = Pick1 - EPick - PickError = (diffti_te + 2 * diffti_tl) / 3 + #get symmetric pick error as mean from earliest and latest possible pick + #by weighting latest possible pick two times earliest possible pick + diffti_tl = LPick - Pick1 + diffti_te = Pick1 - EPick + PickError = (diffti_te + 2 * diffti_tl) / 3 - if iplot is not None: - plt.figure(iplot) - p1, = plt.plot(t, x, 'k') - p2, = plt.plot(t[inoise], x[inoise]) - p3, = plt.plot(t[isignal], x[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ - loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) - plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') - plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') - plt.xlabel('Time [s] since %s' % X[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) - plt.show() - raw_input() - plt.close(iplot) - - elif Pick1 == None: - print 'earllatepicker: No initial onset time given! Check input!' - return + if iplot is not None: + plt.figure(iplot) + p1, = plt.plot(t, x, 'k') + p2, = plt.plot(t[inoise], x[inoise]) + p3, = plt.plot(t[isignal], x[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + loc='best') + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) + plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') + plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') + plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') + plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') + plt.xlabel('Time [s] since %s' % X[0].stats.starttime) + plt.yticks([]) + ax = plt.gca() + ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) + plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) + plt.show() + raw_input() + plt.close(iplot) return EPick, LPick, PickError From 1cdda3652f5fd00dfcd2a9ce806e8d3dd8056dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Mar 2015 14:35:21 +0200 Subject: [PATCH 0288/1144] New module containing some helpful functions, replaces getSNR, fmpicker, and earllatepicker. --- pylot/core/pick/utils.py | 359 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 pylot/core/pick/utils.py diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py new file mode 100644 index 00000000..727429c0 --- /dev/null +++ b/pylot/core/pick/utils.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python +# +# -*- coding: utf-8 -*- +""" + Created Mar/Apr 2015 + Collection of helpful functions for manual and automatic picking. + + :author: Ludger Kueperkoch / MAGS2 EP3 working group +""" + +import numpy as np +import matplotlib.pyplot as plt +from obspy.core import Stream +import argparse + +def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): + ''' + Function to derive earliest and latest possible pick after Diehl & Kissling (2009) + as reasonable uncertainties. Latest possible pick is based on noise level, + earliest possible pick is half a signal wavelength in front of most likely + pick given by PragPicker or manually set by analyst. Most likely pick + (initial pick Pick1) must be given. + + :param: X, time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: nfac (noise factor), nfac times noise level to calculate latest possible pick + :type: int + + :param: TSNR, length of time windows around pick used to determine SNR [s] + :type: tuple (T_noise, T_gap, T_signal) + + :param: Pick1, initial (most likely) onset time, starting point for earllatepicker + :type: float + + :param: iplot, if given, results are plotted in figure(iplot) + :type: int + ''' + + assert isinstance(X, Stream), "%s is not a stream object" % str(X) + + LPick = None + EPick = None + PickError = None + print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' + + x = X[0].data + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) + #some parameters needed: + tnoise = TSNR[0] #noise window length for calculating noise level + tsignal = TSNR[2] #signal window length + tsafety = TSNR[1] #safety gap between signal onset and noise window + + #get latest possible pick + #get noise window + inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ + & (t >= max([Pick1 - tnoise - tsafety, 0]))) + #get signal window + isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ + & (t >= Pick1)) + #calculate noise level + nlevel = max(abs(x[inoise])) * nfac + #get time where signal exceeds nlevel + ilup = np.where(x[isignal] > nlevel) + ildown = np.where(x[isignal] < -nlevel) + if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: + print 'earllatepicker: Signal lower than noise level, misspick?' + return + il = min([ilup[0][0], ildown[0][0]]) + LPick = t[isignal][il] + + #get earliest possible pick + #get next 2 zero crossings after most likely pick + #initial onset is assumed to be the first zero crossing + zc = [] + zc.append(Pick1) + i = 0 + for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): + i = i+ 1 + if x[j-1] <= 0 and x[j] >= 0: + zc.append(t[isignal][i]) + elif x[j-1] > 0 and x[j] <= 0: + zc.append(t[isignal][i]) + if len(zc) == 3: + break + #calculate maximum period T0 of signal out of zero crossings + T0 = max(np.diff(zc)) #this is half wave length! + #T0/4 is assumed as time difference between most likely and earliest possible pick! + EPick = Pick1 - T0/2 + + #get symmetric pick error as mean from earliest and latest possible pick + #by weighting latest possible pick two times earliest possible pick + diffti_tl = LPick - Pick1 + diffti_te = Pick1 - EPick + PickError = (diffti_te + 2 * diffti_tl) / 3 + + if iplot is not None: + plt.figure(iplot) + p1, = plt.plot(t, x, 'k') + p2, = plt.plot(t[inoise], x[inoise]) + p3, = plt.plot(t[isignal], x[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + loc='best') + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) + plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') + plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') + plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') + plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') + plt.xlabel('Time [s] since %s' % X[0].stats.starttime) + plt.yticks([]) + ax = plt.gca() + ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) + plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) + plt.show() + raw_input() + plt.close(iplot) + + return EPick, LPick, PickError + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') + parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') + parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ + [s] (Tnoise, Tgap, Tsignal)') + parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) + + +def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): + ''' + Function to derive first motion (polarity) of given phase onset Pick. + Calculation is based on zero crossings determined within time window pickwin + after given onset time. + + :param: Xraw, unfiltered time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: Xfilt, filtered time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: pickwin, time window after onset Pick within zero crossings are calculated + :type: float + + :param: Pick, initial (most likely) onset time, starting point for fmpicker + :type: float + + :param: iplot, if given, results are plotted in figure(iplot) + :type: int + ''' + + assert isinstance(Xraw, Stream), "%s is not a stream object" % str(Xraw) + assert isinstance(Xfilt, Stream), "%s is not a stream object" % str(Xfilt) + + FM = None + if Pick is not None: + print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' + + xraw = Xraw[0].data + xfilt = Xfilt[0].data + t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, Xraw[0].stats.delta) + #get pick window + ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) + #remove mean + xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) + xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) + + #get next zero crossing after most likely pick + #initial onset is assumed to be the first zero crossing + #first from unfiltered trace + zc1 = [] + zc1.append(Pick) + index1 = [] + i = 0 + for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): + i = i+ 1 + if xraw[j-1] <= 0 and xraw[j] >= 0: + zc1.append(t[ipick][i]) + index1.append(i) + elif xraw[j-1] > 0 and xraw[j] <= 0: + zc1.append(t[ipick][i]) + index1.append(i) + if len(zc1) == 3: + break + + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc1[1] - zc1[0] <= Xraw[0].stats.delta: + li1 = index1[1] + else: + li1 = index1[0] + if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: + print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' + P1 = None + else: + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) + islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) + #calculate slope as polynomal fit of order 1 + xslope1 = np.arange(0, len(xraw[islope1]), 1) + P1 = np.polyfit(xslope1, xraw[islope1], 1) + datafit1 = np.polyval(P1, xslope1) + + #now using filterd trace + #next zero crossing after most likely pick + zc2 = [] + zc2.append(Pick) + index2 = [] + i = 0 + for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): + i = i+ 1 + if xfilt[j-1] <= 0 and xfilt[j] >= 0: + zc2.append(t[ipick][i]) + index2.append(i) + elif xfilt[j-1] > 0 and xfilt[j] <= 0: + zc2.append(t[ipick][i]) + index2.append(i) + if len(zc2) == 3: + break + + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: + li2 = index2[1] + else: + li2 = index2[0] + if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: + print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' + P2 = None + else: + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) + islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) + #calculate slope as polynomal fit of order 1 + xslope2 = np.arange(0, len(xfilt[islope2]), 1) + P2 = np.polyfit(xslope2, xfilt[islope2], 1) + datafit2 = np.polyval(P2, xslope2) + + #compare results + if P1 is not None and P2 is not None: + if P1[0] < 0 and P2[0] < 0: + FM = 'D' + elif P1[0] >= 0 and P2[0] < 0: + FM = '-' + elif P1[0] < 0 and P2[0]>= 0: + FM = '-' + elif P1[0] > 0 and P2[0] > 0: + FM = 'U' + elif P1[0] <= 0 and P2[0] > 0: + FM = '+' + elif P1[0] > 0 and P2[0] <= 0: + FM = '+' + + if iplot is not None: + plt.figure(iplot) + plt.subplot(2,1,1) + plt.plot(t, xraw, 'k') + p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) + if P1 is not None: + p2, = plt.plot(t[islope1], xraw[islope1]) + p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) + p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) + plt.legend([p1, p2, p3, p4], ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ + loc='best') + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim([t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) + plt.yticks([]) + plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[0].stats.station) + + plt.subplot(2,1,2) + plt.title('First-Motion Determination, Filtered Data') + plt.plot(t, xfilt, 'k') + p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', linewidth=2) + if P2 is not None: + p2, = plt.plot(t[islope2], xfilt[islope2]) + p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) + p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim([t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) + plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) + plt.yticks([]) + plt.show() + raw_input() + plt.close(iplot) + + return FM + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') + parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') + parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') + parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + earllatepicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) + + +def getSNR(X, TSNR, t1): + ''' + Function to calculate SNR of certain part of seismogram relative to + given time (onset) out of given noise and signal windows. A safety gap + between noise and signal part can be set. Returns SNR and SNR [dB]. + + :param: X, time series (seismogram) + :type: `~obspy.core.stream.Stream` + + :param: TSNR, length of time windows [s] around t1 (onset) used to determine SNR + :type: tuple (T_noise, T_gap, T_signal) + + :param: t1, initial time (onset) from which noise and signal windows are calculated + :type: float + ''' + + assert isinstance(X, Stream), "%s is not a stream object" % str(X) + + SNR = None + SNRdB = None + x = X[0].data + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) + #some parameters needed: + tnoise = TSNR[0] #noise window length for calculating noise level + tsignal = TSNR[2] #signal window length + tsafety = TSNR[1] #safety gap between signal onset and noise window + #get noise window + inoise = np.where((t <= max([t1 - tsafety, 0])) \ + & (t >= max([t1 - tnoise - tsafety, 0]))) + #get signal window + isignal = np.where((t <= min([t1 + tsignal + tsafety, len(x)])) \ + & (t >= t1)) + if np.size(inoise) < 1: + print 'getSNR: Empty array inoise, check noise window!' + return + elif np.size(isignal) < 1: + print 'getSNR: Empty array isignal, check signal window!' + return + + #calculate ratios + SNR = max(abs(x[isignal])) / np.mean(abs(x[inoise])) + SNRdB = 20 * np.log10(SNR) + + return SNR, SNRdB + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') + parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ + [s] (Tnoise, Tgap, Tsignal)') + parser.add_argument('--t1', type=float, help='initial time from which noise and signal windows are calculated') + args = parser.parse_args() + getSNR(args.X, args.TSNR, args.t1) + From e8aa9de060e130ddadff66ee5663a5851f458704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Mar 2015 16:20:41 +0200 Subject: [PATCH 0289/1144] Modified to handle new module utils.py. --- pylot/core/pick/run_makeCF.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 690fdcff..d1748d67 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ - Script to run autoPyLoT-script "makeCF.py". + Script to run autoPyLoT-script "run_makeCF.py". Only for test purposes! """ @@ -11,8 +11,7 @@ import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.CharFuns import CharacteristicFunction from pylot.core.pick.Picker import AutoPicking -from earllatepicker import earllatepicker -from fmpicker import fmpicker +from pylot.core.pick.utils import * import glob import argparse @@ -81,6 +80,10 @@ def run_makeCF(project, database, event, iplot, station=None): st_copy[0].data = tr_filt.data [lpickhos, epickhos, pickerrhos] = earllatepicker(st_copy, 1.5, TSNRhos, hospick.getpick(), 10) ############################################################# + #get SNR + [SNR, SNRdB] = getSNR(st_copy, TSNRhos, hospick.getpick()) + print 'SNR:', SNR, 'SNR[dB]:', SNRdB + ########################################################## #get first motion of onset hosfm = fmpicker(st, st_copy, 0.2, hospick.getpick(), 11) ############################################################## From a015b0c90d47be23317a18ca2fc8ea980f1bec5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Mar 2015 16:22:20 +0200 Subject: [PATCH 0290/1144] New functions in module: getnoisewin and getsignalwin to extract noise and signal parts. --- pylot/core/pick/utils.py | 90 ++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 727429c0..9bd5dfc9 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -46,18 +46,11 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window - #get latest possible pick #get noise window - inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ - & (t >= max([Pick1 - tnoise - tsafety, 0]))) + inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1]) #get signal window - isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ - & (t >= Pick1)) + isignal = getsignalwin(t, Pick1, TSNR[2]) #calculate noise level nlevel = max(abs(x[inoise])) * nfac #get time where signal exceeds nlevel @@ -325,16 +318,10 @@ def getSNR(X, TSNR, t1): SNRdB = None x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window #get noise window - inoise = np.where((t <= max([t1 - tsafety, 0])) \ - & (t >= max([t1 - tnoise - tsafety, 0]))) + inoise = getnoisewin(t, t1, TSNR[0], TSNR[1]) #get signal window - isignal = np.where((t <= min([t1 + tsignal + tsafety, len(x)])) \ - & (t >= t1)) + isignal = getsignalwin(t, t1, TSNR[2]) if np.size(inoise) < 1: print 'getSNR: Empty array inoise, check noise window!' return @@ -357,3 +344,72 @@ if __name__ == "__main__": args = parser.parse_args() getSNR(args.X, args.TSNR, args.t1) + +def getnoisewin(t, t1, tnoise, tgap): + ''' + Function to extract indeces of data out of time series for noise calculation. + Returns an array of indeces. + + :param: t, array of time stamps + :type: numpy array + + :param: t1, time from which relativ to it noise window is extracted + :type: float + + :param: tnoise, length of time window [s] for noise part extraction + :type: float + + :param: tgap, safety gap between t1 (onset) and noise window to + ensure, that noise window contains no signal + :type: float + ''' + + inoise = None + #get noise window + inoise = np.where((t <= max([t1 - tgap, 0])) \ + & (t >= max([t1 - tnoise - tgap, 0]))) + if np.size(inoise) < 1: + print 'getnoisewin: Empty array inoise, check noise window!' + + return inoise + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--t', type=array, help='numpy array of time stamps') + parser.add_argument('--t1', type=float, help='time from which relativ to it noise window is extracted') + parser.add_argument('--tnoise', type=float, help='length of time window [s] for noise part extraction') + parser.add_argument('--tgap', type=float, help='safety gap between signal (t1=onset) and noise') + args = parser.parse_args() + getnoisewin(args.t, args.t1, args.tnoise, args.tgap) + +def getsignalwin(t, t1, tsignal): + ''' + Function to extract data out of time series for signal level calculation. + Returns an array of indeces. + + :param: t, array of time stamps + :type: numpy array + + :param: t1, time from which relativ to it signal window is extracted + :type: float + + :param: tsignal, length of time window [s] for signal level calculation + :type: float + ''' + + inoise = None + #get signal window + isignal = np.where((t <= min([t1 + tsignal, len(t)])) \ + & (t >= t1)) + if np.size(isignal) < 1: + print 'getsignalwin: Empty array isignal, check signal window!' + + return isignal + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--t', type=array, help='numpy array of time stamps') + parser.add_argument('--t1', type=float, help='time from which relativ to it signal window is extracted') + parser.add_argument('--tsignal', type=float, help='length of time window [s] for signal part extraction') + args = parser.parse_args() + getsignalwin(args.t, args.t1, args.tsignal) From f6f1c9cf2fb9e437bd69be4e88371f9294f32a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Mar 2015 16:25:04 +0200 Subject: [PATCH 0291/1144] Modified to handle new module utils. --- pylot/core/pick/Picker.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index b3579663..9c3595b9 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -20,6 +20,7 @@ calculated after Diehl & Kissling (2009). """ import numpy as np import matplotlib.pyplot as plt +from pylot.core.pick.utils import * from pylot.core.pick.CharFuns import CharacteristicFunction class AutoPicking(object): @@ -194,22 +195,16 @@ class AICPicker(AutoPicking): #quality assessment using SNR and slope from CF if self.Pick is not None: - #some parameters needed: - tnoise = self.TSNR[0] #noise window length for calculating noise level - tsignal = self.TSNR[2] #signal window length - tsafety = self.TSNR[1] #safety gap between signal onset and noise window - tslope = self.TSNR[3] #slope determination window #get noise window - inoise = np.where((self.Tcf <= max([self.Pick - tsafety, 0])) \ - & (self.Tcf >= max([self.Pick - tnoise - tsafety, 0]))) + inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1]) #get signal window - isignal = np.where((self.Tcf <= min([self.Pick + tsignal + tsafety, len(self.Data[0].data)])) \ - & (self.Tcf >= self.Pick)) + isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) #calculate SNR from CF self.SNR = max(abs(self.cf[isignal] - np.mean(self.cf[isignal]))) / max(abs(self.cf[inoise] \ - np.mean(self.cf[inoise]))) #calculate slope from CF after initial pick #get slope window + tslope = self.TSNR[3] #slope determination window islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ & (self.Tcf >= self.Pick)) #find maximum within slope determination window From 08240261598ef37472fc9f8df4d1c113db9f6b42 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Apr 2015 15:45:38 +0200 Subject: [PATCH 0292/1144] standalone python files now import definitions from module pylot.core.pick.utils and parse arguments when called from shell (deleted "if __name__ == '__main__' :" clauses from the module) --- pylot/core/pick/earllatepicker.py | 132 +-------- pylot/core/pick/fmpicker.py | 184 +----------- pylot/core/pick/getSNR.py | 74 ++--- pylot/core/pick/getnoisewin.py | 15 + pylot/core/pick/getsignalwin.py | 14 + pylot/core/pick/utils.py | 446 ++++++++++++++---------------- 6 files changed, 282 insertions(+), 583 deletions(-) create mode 100644 pylot/core/pick/getnoisewin.py create mode 100644 pylot/core/pick/getsignalwin.py diff --git a/pylot/core/pick/earllatepicker.py b/pylot/core/pick/earllatepicker.py index f41a0503..ab241880 100755 --- a/pylot/core/pick/earllatepicker.py +++ b/pylot/core/pick/earllatepicker.py @@ -7,127 +7,21 @@ and latest possible pick is calculated based on noise measurements in front of the most likely pick and signal wavelength derived from zero crossings. - :author: Ludger Kueperkoch / MAGS2 EP3 working group + :author: Ludger Kueperkoch / MAGS2 EP3 working group """ -import numpy as np -import matplotlib.pyplot as plt -from obspy.core import Stream + import argparse +import obspy +from pylot.core.pick.utils import earllatepicker -def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): - ''' - Function to derive earliest and latest possible pick after Diehl & Kissling (2009) - as reasonable uncertainties. Latest possible pick is based on noise level, - earliest possible pick is half a signal wavelength in front of most likely - pick given by PragPicker or manually set by analyst. Most likely pick - (initial pick Pick1) must be given. - - :param: X, time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: nfac (noise factor), nfac times noise level to calculate latest possible pick - :type: int - - :param: TSNR, length of time windows around pick used to determine SNR [s] - :type: tuple (T_noise, T_gap, T_signal) - - :param: Pick1, initial (most likely) onset time, starting point for earllatepicker - :type: float - - :param: iplot, if given, results are plotted in figure(iplot) - :type: int - ''' - - assert isinstance(X, Stream), "%s is not a stream object" % str(X) - - LPick = None - EPick = None - PickError = None - print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' - - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window - - #get latest possible pick - #get noise window - inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ - & (t >= max([Pick1 - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ - & (t >= Pick1)) - #calculate noise level - nlevel = max(abs(x[inoise])) * nfac - #get time where signal exceeds nlevel - ilup = np.where(x[isignal] > nlevel) - ildown = np.where(x[isignal] < -nlevel) - if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'earllatepicker: Signal lower than noise level, misspick?' - return - il = min([ilup[0][0], ildown[0][0]]) - LPick = t[isignal][il] - - #get earliest possible pick - #get next 2 zero crossings after most likely pick - #initial onset is assumed to be the first zero crossing - zc = [] - zc.append(Pick1) - i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[j-1] <= 0 and x[j] >= 0: - zc.append(t[isignal][i]) - elif x[j-1] > 0 and x[j] <= 0: - zc.append(t[isignal][i]) - if len(zc) == 3: - break - #calculate maximum period T0 of signal out of zero crossings - T0 = max(np.diff(zc)) #this is half wave length! - #T0/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - T0/2 - - #get symmetric pick error as mean from earliest and latest possible pick - #by weighting latest possible pick two times earliest possible pick - diffti_tl = LPick - Pick1 - diffti_te = Pick1 - EPick - PickError = (diffti_te + 2 * diffti_tl) / 3 - - if iplot is not None: - plt.figure(iplot) - p1, = plt.plot(t, x, 'k') - p2, = plt.plot(t[inoise], x[inoise]) - p3, = plt.plot(t[isignal], x[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ - loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) - plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') - plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') - plt.xlabel('Time [s] since %s' % X[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) - plt.show() - raw_input() - plt.close(iplot) - - return EPick, LPick, PickError if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) + parser = argparse.ArgumentParser() + parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') + parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') + parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ + [s] (Tnoise, Tgap, Tsignal)') + parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) diff --git a/pylot/core/pick/fmpicker.py b/pylot/core/pick/fmpicker.py index 6aee8eb3..0ccbc1d1 100755 --- a/pylot/core/pick/fmpicker.py +++ b/pylot/core/pick/fmpicker.py @@ -4,180 +4,20 @@ Created Mar 2015 Function to derive first motion (polarity) for given phase onset based on zero crossings. - :author: MAGS2 EP3 working group / Ludger Kueperkoch + :author: MAGS2 EP3 working group / Ludger Kueperkoch """ -import numpy as np -import matplotlib.pyplot as plt -from obspy.core import Stream + import argparse - -def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): - ''' - Function to derive first motion (polarity) of given phase onset Pick. - Calculation is based on zero crossings determined within time window pickwin - after given onset time. - - :param: Xraw, unfiltered time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: Xfilt, filtered time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: pickwin, time window after onset Pick within zero crossings are calculated - :type: float - - :param: Pick, initial (most likely) onset time, starting point for fmpicker - :type: float - - :param: iplot, if given, results are plotted in figure(iplot) - :type: int - ''' - - assert isinstance(Xraw, Stream), "%s is not a stream object" % str(Xraw) - assert isinstance(Xfilt, Stream), "%s is not a stream object" % str(Xfilt) - - FM = None - if Pick is not None: - print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' - - xraw = Xraw[0].data - xfilt = Xfilt[0].data - t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, Xraw[0].stats.delta) - #get pick window - ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) - #remove mean - xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) - xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) - - #get next zero crossing after most likely pick - #initial onset is assumed to be the first zero crossing - #first from unfiltered trace - zc1 = [] - zc1.append(Pick) - index1 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xraw[j-1] <= 0 and xraw[j] >= 0: - zc1.append(t[ipick][i]) - index1.append(i) - elif xraw[j-1] > 0 and xraw[j] <= 0: - zc1.append(t[ipick][i]) - index1.append(i) - if len(zc1) == 3: - break - - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc1[1] - zc1[0] <= Xraw[0].stats.delta: - li1 = index1[1] - else: - li1 = index1[0] - if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: - print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' - P1 = None - else: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) - islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) - #calculate slope as polynomal fit of order 1 - xslope1 = np.arange(0, len(xraw[islope1]), 1) - P1 = np.polyfit(xslope1, xraw[islope1], 1) - datafit1 = np.polyval(P1, xslope1) - - #now using filterd trace - #next zero crossing after most likely pick - zc2 = [] - zc2.append(Pick) - index2 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xfilt[j-1] <= 0 and xfilt[j] >= 0: - zc2.append(t[ipick][i]) - index2.append(i) - elif xfilt[j-1] > 0 and xfilt[j] <= 0: - zc2.append(t[ipick][i]) - index2.append(i) - if len(zc2) == 3: - break - - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: - li2 = index2[1] - else: - li2 = index2[0] - if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: - print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' - P2 = None - else: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) - islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) - #calculate slope as polynomal fit of order 1 - xslope2 = np.arange(0, len(xfilt[islope2]), 1) - P2 = np.polyfit(xslope2, xfilt[islope2], 1) - datafit2 = np.polyval(P2, xslope2) - - #compare results - if P1 is not None and P2 is not None: - if P1[0] < 0 and P2[0] < 0: - FM = 'D' - elif P1[0] >= 0 and P2[0] < 0: - FM = '-' - elif P1[0] < 0 and P2[0]>= 0: - FM = '-' - elif P1[0] > 0 and P2[0] > 0: - FM = 'U' - elif P1[0] <= 0 and P2[0] > 0: - FM = '+' - elif P1[0] > 0 and P2[0] <= 0: - FM = '+' - - if iplot is not None: - plt.figure(iplot) - plt.subplot(2,1,1) - plt.plot(t, xraw, 'k') - p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) - if P1 is not None: - p2, = plt.plot(t[islope1], xraw[islope1]) - p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) - p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) - plt.legend([p1, p2, p3, p4], ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ - loc='best') - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) - plt.yticks([]) - plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[0].stats.station) - - plt.subplot(2,1,2) - plt.title('First-Motion Determination, Filtered Data') - plt.plot(t, xfilt, 'k') - p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', linewidth=2) - if P2 is not None: - p2, = plt.plot(t[islope2], xfilt[islope2]) - p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) - p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) - plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) - plt.yticks([]) - plt.show() - raw_input() - plt.close(iplot) - - return FM +import obspy +from pylot.core.pick.utils import fmpicker if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') - parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') - parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') - parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) + parser = argparse.ArgumentParser() + parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') + parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') + parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') + parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + fmpicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) diff --git a/pylot/core/pick/getSNR.py b/pylot/core/pick/getSNR.py index 6c2b6872..bbfbdba5 100644 --- a/pylot/core/pick/getSNR.py +++ b/pylot/core/pick/getSNR.py @@ -2,65 +2,29 @@ # -*- coding: utf-8 -*- """ Created Mar/Apr 2015 - Function to calculate SNR of certain part of seismogram relativ + Function to calculate SNR of certain part of seismogram relative to given time. Returns SNR and SNR [dB]. :author: Ludger Kueperkoch /MAGS EP3 working group """ -from obspy.core import Stream -import numpy as np -def getSNR(X, TSNR, t1): - ''' - Function to calculate SNR of certain part of seismogram relative to - given time (onset) out of given noise and signal windows. A safety gap - between noise and signal part can be set. Returns SNR and SNR [dB]. - - :param: X, time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: TSNR, length of time windows [s] around t1 (onset) used to determine SNR - :type: tuple (T_noise, T_gap, T_signal) - - :param: t1, initial time (onset) from which noise and signal windows are calculated - :type: float - ''' - - assert isinstance(X, Stream), "%s is not a stream object" % str(X) - - SNR = None - SNRdB = None - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window - #get noise window - inoise = np.where((t <= max([t1 - tsafety, 0])) \ - & (t >= max([t1 - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((t <= min([t1 + tsignal + tsafety, len(x)])) \ - & (t >= t1)) - if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' - return - elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' - return - - #calculate ratios - SNR = max(abs(x[isignal])) / np.mean(abs(x[inoise])) - SNRdB = 20 * np.log10(SNR) - - return SNR, SNRdB +import argparse +import obspy +from pylot.core.pick.utils import getSNR if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--t1', type=float, help='initial time from which noise and signal windows are calculated') - args = parser.parse_args() - getSNR(args.X, args.TSNR, args.t1) - + parser = argparse.ArgumentParser() + parser.add_argument('--data', '-d', type=~obspy.core.stream.Stream, + help='time series (seismogram) read with obspy module ' + 'read', + dest='data') + parser.add_argument('--tsnr', '-s', type=tuple, + help='length of time windows around pick used to ' + 'determine SNR [s] (Tnoise, Tgap, Tsignal)', + dest='tsnr') + parser.add_argument('--time', '-t', type=float, + help='initial time from which noise and signal windows ' + 'are calculated', + dest='time') + args = parser.parse_args() + print getSNR(args.data, args.tsnr, args.time) diff --git a/pylot/core/pick/getnoisewin.py b/pylot/core/pick/getnoisewin.py new file mode 100644 index 00000000..8c21913f --- /dev/null +++ b/pylot/core/pick/getnoisewin.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse +import numpy +from pylot.core.pick.utils import getnoisewin + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--t', type=~numpy.array, help='numpy array of time stamps') + parser.add_argument('--t1', type=float, help='time from which relativ to it noise window is extracted') + parser.add_argument('--tnoise', type=float, help='length of time window [s] for noise part extraction') + parser.add_argument('--tgap', type=float, help='safety gap between signal (t1=onset) and noise') + args = parser.parse_args() + getnoisewin(args.t, args.t1, args.tnoise, args.tgap) diff --git a/pylot/core/pick/getsignalwin.py b/pylot/core/pick/getsignalwin.py new file mode 100644 index 00000000..4b3013b8 --- /dev/null +++ b/pylot/core/pick/getsignalwin.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse +import numpy +from pylot.core.pick.utils import getsignalwin + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--t', type=~numpy.array, help='numpy array of time stamps') + parser.add_argument('--t1', type=float, help='time from which relativ to it signal window is extracted') + parser.add_argument('--tsignal', type=float, help='length of time window [s] for signal part extraction') + args = parser.parse_args() + getsignalwin(args.t, args.t1, args.tsignal) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9bd5dfc9..15de2312 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -11,25 +11,25 @@ import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream -import argparse + def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' - Function to derive earliest and latest possible pick after Diehl & Kissling (2009) + Function to derive earliest and latest possible pick after Diehl & Kissling (2009) as reasonable uncertainties. Latest possible pick is based on noise level, - earliest possible pick is half a signal wavelength in front of most likely + earliest possible pick is half a signal wavelength in front of most likely pick given by PragPicker or manually set by analyst. Most likely pick - (initial pick Pick1) must be given. - + (initial pick Pick1) must be given. + :param: X, time series (seismogram) :type: `~obspy.core.stream.Stream` - + :param: nfac (noise factor), nfac times noise level to calculate latest possible pick :type: int :param: TSNR, length of time windows around pick used to determine SNR [s] :type: tuple (T_noise, T_gap, T_signal) - + :param: Pick1, initial (most likely) onset time, starting point for earllatepicker :type: float @@ -39,14 +39,15 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): assert isinstance(X, Stream), "%s is not a stream object" % str(X) - LPick = None + LPick = None EPick = None PickError = None print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #get latest possible pick + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, + X[0].stats.delta) + # get latest possible pick #get noise window inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1]) #get signal window @@ -57,8 +58,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ilup = np.where(x[isignal] > nlevel) ildown = np.where(x[isignal] < -nlevel) if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'earllatepicker: Signal lower than noise level, misspick?' - return + print 'earllatepicker: Signal lower than noise level, misspick?' + return il = min([ilup[0][0], ildown[0][0]]) LPick = t[isignal][il] @@ -68,72 +69,67 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): zc = [] zc.append(Pick1) i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[j-1] <= 0 and x[j] >= 0: - zc.append(t[isignal][i]) - elif x[j-1] > 0 and x[j] <= 0: - zc.append(t[isignal][i]) + for j in range(isignal[0][1], isignal[0][len(t[isignal]) - 1]): + i = i + 1 + if x[j - 1] <= 0 and x[j] >= 0: + zc.append(t[isignal][i]) + elif x[j - 1] > 0 and x[j] <= 0: + zc.append(t[isignal][i]) if len(zc) == 3: break #calculate maximum period T0 of signal out of zero crossings - T0 = max(np.diff(zc)) #this is half wave length! + T0 = max(np.diff(zc)) #this is half wave length! #T0/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - T0/2 + EPick = Pick1 - T0 / 2 #get symmetric pick error as mean from earliest and latest possible pick #by weighting latest possible pick two times earliest possible pick - diffti_tl = LPick - Pick1 + diffti_tl = LPick - Pick1 diffti_te = Pick1 - EPick - PickError = (diffti_te + 2 * diffti_tl) / 3 + PickError = (diffti_te + 2 * diffti_tl) / 3 if iplot is not None: - plt.figure(iplot) - p1, = plt.plot(t, x, 'k') - p2, = plt.plot(t[inoise], x[inoise]) - p3, = plt.plot(t[isignal], x[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + plt.figure(iplot) + p1, = plt.plot(t, x, 'k') + p2, = plt.plot(t[inoise], x[inoise]) + p3, = plt.plot(t[isignal], x[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], + ['Data', 'Noise Window', 'Signal Window', 'Noise Level', + 'Zero Crossings'], \ loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) - plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') - plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') - plt.xlabel('Time [s] since %s' % X[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) - plt.show() - raw_input() - plt.close(iplot) + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) + plt.plot([LPick, LPick], [max(x) / 2, -max(x) / 2], '--k') + plt.plot([EPick, EPick], [max(x) / 2, -max(x) / 2], '--k') + plt.plot([Pick1 + PickError, Pick1 + PickError], + [max(x) / 2, -max(x) / 2], 'r--') + plt.plot([Pick1 - PickError, Pick1 - PickError], + [max(x) / 2, -max(x) / 2], 'r--') + plt.xlabel('Time [s] since %s' % X[0].stats.starttime) + plt.yticks([]) + ax = plt.gca() + ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) + plt.title( + 'Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % + X[0].stats.station) + plt.show() + raw_input() + plt.close(iplot) return EPick, LPick, PickError -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) - def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): ''' Function to derive first motion (polarity) of given phase onset Pick. Calculation is based on zero crossings determined within time window pickwin - after given onset time. - + after given onset time. + :param: Xraw, unfiltered time series (seismogram) :type: `~obspy.core.stream.Stream` - + :param: Xfilt, filtered time series (seismogram) :type: `~obspy.core.stream.Stream` @@ -152,149 +148,146 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): FM = None if Pick is not None: - print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' + print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' - xraw = Xraw[0].data - xfilt = Xfilt[0].data - t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, Xraw[0].stats.delta) - #get pick window - ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) - #remove mean - xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) - xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) + xraw = Xraw[0].data + xfilt = Xfilt[0].data + t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, + Xraw[0].stats.delta) + # get pick window + ipick = np.where( + (t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) + #remove mean + xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) + xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) - #get next zero crossing after most likely pick - #initial onset is assumed to be the first zero crossing - #first from unfiltered trace - zc1 = [] - zc1.append(Pick) - index1 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xraw[j-1] <= 0 and xraw[j] >= 0: - zc1.append(t[ipick][i]) - index1.append(i) - elif xraw[j-1] > 0 and xraw[j] <= 0: - zc1.append(t[ipick][i]) - index1.append(i) - if len(zc1) == 3: - break + #get next zero crossing after most likely pick + #initial onset is assumed to be the first zero crossing + #first from unfiltered trace + zc1 = [] + zc1.append(Pick) + index1 = [] + i = 0 + for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): + i = i + 1 + if xraw[j - 1] <= 0 and xraw[j] >= 0: + zc1.append(t[ipick][i]) + index1.append(i) + elif xraw[j - 1] > 0 and xraw[j] <= 0: + zc1.append(t[ipick][i]) + index1.append(i) + if len(zc1) == 3: + break - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc1[1] - zc1[0] <= Xraw[0].stats.delta: - li1 = index1[1] - else: - li1 = index1[0] - if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: - print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' - P1 = None - else: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) - islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) - #calculate slope as polynomal fit of order 1 - xslope1 = np.arange(0, len(xraw[islope1]), 1) - P1 = np.polyfit(xslope1, xraw[islope1], 1) - datafit1 = np.polyval(P1, xslope1) + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc1[1] - zc1[0] <= Xraw[0].stats.delta: + li1 = index1[1] + else: + li1 = index1[0] + if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: + print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' + P1 = None + else: + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) + islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) + #calculate slope as polynomal fit of order 1 + xslope1 = np.arange(0, len(xraw[islope1]), 1) + P1 = np.polyfit(xslope1, xraw[islope1], 1) + datafit1 = np.polyval(P1, xslope1) - #now using filterd trace - #next zero crossing after most likely pick - zc2 = [] - zc2.append(Pick) - index2 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xfilt[j-1] <= 0 and xfilt[j] >= 0: - zc2.append(t[ipick][i]) - index2.append(i) - elif xfilt[j-1] > 0 and xfilt[j] <= 0: - zc2.append(t[ipick][i]) - index2.append(i) - if len(zc2) == 3: - break + #now using filterd trace + #next zero crossing after most likely pick + zc2 = [] + zc2.append(Pick) + index2 = [] + i = 0 + for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): + i = i + 1 + if xfilt[j - 1] <= 0 and xfilt[j] >= 0: + zc2.append(t[ipick][i]) + index2.append(i) + elif xfilt[j - 1] > 0 and xfilt[j] <= 0: + zc2.append(t[ipick][i]) + index2.append(i) + if len(zc2) == 3: + break - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: - li2 = index2[1] - else: - li2 = index2[0] - if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: - print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' - P2 = None - else: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) - islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) - #calculate slope as polynomal fit of order 1 - xslope2 = np.arange(0, len(xfilt[islope2]), 1) - P2 = np.polyfit(xslope2, xfilt[islope2], 1) - datafit2 = np.polyval(P2, xslope2) - - #compare results - if P1 is not None and P2 is not None: - if P1[0] < 0 and P2[0] < 0: - FM = 'D' - elif P1[0] >= 0 and P2[0] < 0: - FM = '-' - elif P1[0] < 0 and P2[0]>= 0: - FM = '-' - elif P1[0] > 0 and P2[0] > 0: - FM = 'U' - elif P1[0] <= 0 and P2[0] > 0: - FM = '+' - elif P1[0] > 0 and P2[0] <= 0: - FM = '+' + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: + li2 = index2[1] + else: + li2 = index2[0] + if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: + print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' + P2 = None + else: + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) + islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) + #calculate slope as polynomal fit of order 1 + xslope2 = np.arange(0, len(xfilt[islope2]), 1) + P2 = np.polyfit(xslope2, xfilt[islope2], 1) + datafit2 = np.polyval(P2, xslope2) + + #compare results + if P1 is not None and P2 is not None: + if P1[0] < 0 and P2[0] < 0: + FM = 'D' + elif P1[0] >= 0 and P2[0] < 0: + FM = '-' + elif P1[0] < 0 and P2[0] >= 0: + FM = '-' + elif P1[0] > 0 and P2[0] > 0: + FM = 'U' + elif P1[0] <= 0 and P2[0] > 0: + FM = '+' + elif P1[0] > 0 and P2[0] <= 0: + FM = '+' if iplot is not None: - plt.figure(iplot) - plt.subplot(2,1,1) - plt.plot(t, xraw, 'k') - p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) - if P1 is not None: - p2, = plt.plot(t[islope1], xraw[islope1]) - p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) - p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) - plt.legend([p1, p2, p3, p4], ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ - loc='best') - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) - plt.yticks([]) - plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[0].stats.station) + plt.figure(iplot) + plt.subplot(2, 1, 1) + plt.plot(t, xraw, 'k') + p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) + if P1 is not None: + p2, = plt.plot(t[islope1], xraw[islope1]) + p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) + p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) + plt.legend([p1, p2, p3, p4], + ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ + loc='best') + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim( + [t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) + plt.yticks([]) + plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[ + 0].stats.station) - plt.subplot(2,1,2) - plt.title('First-Motion Determination, Filtered Data') - plt.plot(t, xfilt, 'k') - p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', linewidth=2) - if P2 is not None: - p2, = plt.plot(t[islope2], xfilt[islope2]) - p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) - p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) - plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) - plt.yticks([]) - plt.show() - raw_input() - plt.close(iplot) + plt.subplot(2, 1, 2) + plt.title('First-Motion Determination, Filtered Data') + plt.plot(t, xfilt, 'k') + p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', + linewidth=2) + if P2 is not None: + p2, = plt.plot(t[islope2], xfilt[islope2]) + p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) + p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim( + [t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) + plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) + plt.yticks([]) + plt.show() + raw_input() + plt.close(iplot) return FM -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') - parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') - parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') - parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) - def getSNR(X, TSNR, t1): ''' @@ -314,35 +307,28 @@ def getSNR(X, TSNR, t1): assert isinstance(X, Stream), "%s is not a stream object" % str(X) - SNR = None - SNRdB = None x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #get noise window + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, + X[0].stats.delta) + + # get noise window inoise = getnoisewin(t, t1, TSNR[0], TSNR[1]) + #get signal window isignal = getsignalwin(t, t1, TSNR[2]) if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' - return + print 'getSNR: Empty array inoise, check noise window!' + return elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' - return + print 'getSNR: Empty array isignal, check signal window!' + return #calculate ratios - SNR = max(abs(x[isignal])) / np.mean(abs(x[inoise])) + noiselevel = np.mean(abs(x[inoise])) + SNR = max(abs(x[isignal])) / noiselevel SNRdB = 20 * np.log10(SNR) - - return SNR, SNRdB -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--t1', type=float, help='initial time from which noise and signal windows are calculated') - args = parser.parse_args() - getSNR(args.X, args.TSNR, args.t1) + return SNR, SNRdB, noiselevel def getnoisewin(t, t1, tnoise, tgap): @@ -352,7 +338,7 @@ def getnoisewin(t, t1, tnoise, tgap): :param: t, array of time stamps :type: numpy array - + :param: t1, time from which relativ to it noise window is extracted :type: float @@ -361,26 +347,18 @@ def getnoisewin(t, t1, tnoise, tgap): :param: tgap, safety gap between t1 (onset) and noise window to ensure, that noise window contains no signal - :type: float + :type: float ''' - + inoise = None - #get noise window + # get noise window inoise = np.where((t <= max([t1 - tgap, 0])) \ - & (t >= max([t1 - tnoise - tgap, 0]))) + & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: - print 'getnoisewin: Empty array inoise, check noise window!' - + print 'getnoisewin: Empty array inoise, check noise window!' + return inoise -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--t', type=array, help='numpy array of time stamps') - parser.add_argument('--t1', type=float, help='time from which relativ to it noise window is extracted') - parser.add_argument('--tnoise', type=float, help='length of time window [s] for noise part extraction') - parser.add_argument('--tgap', type=float, help='safety gap between signal (t1=onset) and noise') - args = parser.parse_args() - getnoisewin(args.t, args.t1, args.tnoise, args.tgap) def getsignalwin(t, t1, tsignal): ''' @@ -389,27 +367,21 @@ def getsignalwin(t, t1, tsignal): :param: t, array of time stamps :type: numpy array - + :param: t1, time from which relativ to it signal window is extracted :type: float :param: tsignal, length of time window [s] for signal level calculation :type: float ''' - + inoise = None - #get signal window + # get signal window isignal = np.where((t <= min([t1 + tsignal, len(t)])) \ - & (t >= t1)) + & (t >= t1)) if np.size(isignal) < 1: - print 'getsignalwin: Empty array isignal, check signal window!' - + print 'getsignalwin: Empty array isignal, check signal window!' + return isignal - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--t', type=array, help='numpy array of time stamps') - parser.add_argument('--t1', type=float, help='time from which relativ to it signal window is extracted') - parser.add_argument('--tsignal', type=float, help='length of time window [s] for signal part extraction') - args = parser.parse_args() - getsignalwin(args.t, args.t1, args.tsignal) + + From e35d6b9371a05b2727f620f16a291da0c07de2f5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Apr 2015 18:36:21 +0200 Subject: [PATCH 0293/1144] bugfix: modified icon management and corrected usage of references --- QtPyLoT.py | 33 +++++++++++++++++++++++---------- resources.qrc => icons.qrc | 1 + icons/filter.png | Bin 0 -> 4700 bytes icons_rc.py | 21 +++++++++++++++++++++ pylot/core/util/widgets.py | 6 +++++- qrc_resources.py | 21 --------------------- 6 files changed, 50 insertions(+), 32 deletions(-) rename resources.qrc => icons.qrc (88%) create mode 100644 icons/filter.png create mode 100644 icons_rc.py delete mode 100644 qrc_resources.py diff --git a/QtPyLoT.py b/QtPyLoT.py index 909cf8fc..24d55da8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -29,7 +29,7 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ QFileInfo, Qt from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ - QDialog, QErrorMessage, QApplication + QDialog, QErrorMessage, QApplication, QPixmap from obspy.core import UTCDateTime from pylot.core.read import Data, FilterOptions @@ -38,12 +38,11 @@ from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ DatastructureError, createAction, getLogin, createCreationInfo, PickDlg from pylot.core.util.structure import DATASTRUCTURE -import qrc_resources +import icons_rc # Version information __version__ = _getVersionString() - class MainWindow(QMainWindow): closing = Signal() @@ -101,8 +100,11 @@ class MainWindow(QMainWindow): except: self.startTime = UTCDateTime() + pylot_icon = QIcon() + pylot_icon.addPixmap(QPixmap(':/icons/pylot.ico')) + self.setWindowTitle("PyLoT - do seismic processing the python way") - self.setWindowIcon(QIcon(":/icon.ico")) + self.setWindowIcon(pylot_icon) xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') @@ -123,7 +125,15 @@ class MainWindow(QMainWindow): saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) newIcon = self.style().standardIcon(QStyle.SP_FileIcon) - pickIcon = QIcon(':/pick.png') + + # create resource icons + p_icon = QIcon() + p_icon.addPixmap(QPixmap(':/icons/picon.png')) + s_icon = QIcon() + s_icon.addPixmap(QPixmap(':/icons/sicon.png')) + print_icon = QIcon() + print_icon.addPixmap(QPixmap(':/icons/printer.png')) + newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, QKeySequence.New, newIcon, @@ -163,14 +173,14 @@ class MainWindow(QMainWindow): "Alt+F", QIcon(None), """Adjust filter parameters.""") self.selectPAction = self.createAction(self, "&P", self.alterPhase, "Alt+P", - QIcon(":/picon.png"), + p_icon, "Toggle P phase.", True) self.selectSAction = self.createAction(self, "&S", self.alterPhase, "Alt+S", - QIcon(":/sicon.png"), + s_icon, "Toggle S phase", True) printAction = self.createAction(self, "&Print event ...", self.printEvent, QKeySequence.Print, - QIcon(":/printer.png"), + print_icon, "Print waveform overview.") helpAction = self.createAction(self, "&Help ...", self.helpHelp, QKeySequence.HelpContents, helpIcon, @@ -517,13 +527,16 @@ class MainWindow(QMainWindow): def main(): # create the Qt application - pylot_app = QApplication(sys.argv[0]) + pylot_app = QApplication(sys.argv) + + app_icon = QIcon() + app_icon.addPixmap(QPixmap(':/icons/pick.png')) # set Application Information pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") pylot_app.setOrganizationDomain("rub.de") pylot_app.setApplicationName("PyLoT") - pylot_app.setWindowIcon(QIcon(":/icon.ico")) + pylot_app.setWindowIcon(app_icon) # create the main window pylot_form = MainWindow() diff --git a/resources.qrc b/icons.qrc similarity index 88% rename from resources.qrc rename to icons.qrc index c15c7560..7ec95b9c 100644 --- a/resources.qrc +++ b/icons.qrc @@ -5,6 +5,7 @@ icons/picon.png icons/sicon.png icons/pick.png + icons/filter.png help/index.html diff --git a/icons/filter.png b/icons/filter.png new file mode 100644 index 0000000000000000000000000000000000000000..1098d3ac27a2c86136b60e2fb5e1b5e49bac6dfe GIT binary patch literal 4700 zcmV-i5~J;jP)M&dkV5~%`nA9 zp6m5>1-k<=FG23{%*8BM{QWQH!(07m5a6@VJ|h#q{d5G1SpH9JSZ7ER5|D9g{jAr-aJ;Y~&=JVXcG9PQ!j&04#Veav3 z;WL^%v&H?ijqp1B*dyWl=rzyd*F0W#={47NAVGs;FThVG7k>GJ^}~k`?|Tl-c98U; zhaM)xu(B|~6X~I0V!+^<=?u+{REpIBA!yw+hzXNw7zV2&vS7k8%(ltfaI#H$O=G{; z84|HN+{bfKXDR%RPM?Fn3!kkU{5pRJ&bA8!>v}#NOplj6tc&)bj`xIZE>NQ9O82_h z#_qv3&u7ncr03!NuDIWC@=p2rLBBkH4JI70!>!d=yIrBVUD}+0M`d9S9*;}}3_tho zQ(AgBYr@RFc;qno?&uIuzQL$X*QH#dYE5sTda{N3!+Y!7<|8Wz#44E!(TE`#IH6d3{p@c!bz=p{|zF5M#qm)Tu>&w>`P^O zPmAlEYU}RtcY2=BIT+-6lly~y;rMCvKF%sAr={1?+5)aXjP|BbSN7Jy22%||-?(f- zcS;9cDvARr4$!X03e6Lgt1c8lI(ZK90Zo0k2JMjJCCoubpi+*SVWOR1YbWuDn18h0Wp$s{J=U%` zz(I8vN6!Irh>@tB`c4gvZQ|FefSnq3o}kw40Y{+Pw`&kjXD(u_yN&@GEH&DxA3-xT z?oWUm?(q%e60Gh+-GjOZbswLJiPwc0=t*WDdj4EJ^d=g-t~Z#0UY+-i)R}GdUcq|v zEi?m#cM#fJn1S9FFJI_o^kkDzDtiv&03N4!uU2D}Z&w-Ds8wh`e7ls3i~}sbIDrm` z1?rnt&{N>PfQ4J{JwG2A2N)O_KqvL2MVy6d(H}?Za;jD8hD8q`Bkpa2LAut`ts8j% zBzLa3t~c{2ZV4=rZM}(vNS?d8Kuq)CO{14@s~fmQaEQB+gX=`-Ev*AaK{*HpG5x^> zPKyMWa?3fJn3L^>^V>pzk&#jIwE52~(24Q?cq3$`mIn4~aC3!*^H@$oUp;jP2X$uN zXxjFPO`I+vj2^VIF3$bvz~jvaf^ zG|el;%`&kpi(!<-aLParAf-Gbm`D~J5_NeZSyYcrs1Zz*A#$2@id^_y6Wz*?Q};5q znI^GJi?u@;-Om=bSr)BhZdoZvCP}4IbRugto7gpn)NC8dHBxi1j5$ykmf@P?u!Ipg z$8m{+kvWp^&(BLco}ZEcNw_|Zp{d*K{JX$53cDQmWja-Ypz!14{f znF!d51od@!vb8{W2>O{cSNk>j(NC@D1@H?brogsndX&eExfQsq6J&z_I z?qktAcs|~T7K!h%Xx&ciUkVA!ti9!gH?n@NuWNvyf_RXlRjbu(WFnwYSSK?xCqFJ% zDigw>uz;;0GqRZAp#t1mqb7y29V8j5x`dFxJ^ZXtXH%W0U^tmlt)hK;+kOn4rl!+ zt_Tkp4P?>_$B!R>yqy(6Z99UX!?P(a|9wL0ls7$)^sy-%Wp11$FhTi8D*UJ2ym=%%wKrng+=6gk-LI{olXI@FXN(% z&}{06r&0moB7%z(Tz&@}VHwB#(4j+QbaXU21Q08o-u?A&)*+h&nN%>jA3#ADB6pzs z3}i9|9P784ZL*ejqB*08X6cNY8y!B0LOA@XaJS!D;F+Yye<Y?7^y{NtmBFhB%fkjOonGsS}-kU$@o+OoGk5EX+d% z`-e33U077^cjNgTQ34y&g^RDRm&@fzLU++P)bAd00-vZ2sjXx_)18p=(Yb^UpGu`N zF*|pDeY^3Fg%a49KvtomGBr4mVeVc9dxl|<`#6R$`Agk;M^~4Gp~6zFR6>LxWRPMvf;MmR1&RuUsXt7fQ8v)G2qlT{M&^%PII~dHQG47E&cW(A*x8K#1 zMWLlqX#(U~fyLEDa7A%~W?Da6W$hJk%={L5RxEUY-FEQpciu%$;RI^5x;iLgTfpe& z08l?_{FmQ+YrPlm?3xnTn9iO(w*<0n8ubB3=mLQ8f&vb_y>Hue<13EQsncf`d)1Gg z?h62=@G5YCYbi_ZNNa?FfiwnrQ0cq67s4V@E(+ZbfK59M**K_#<28(J^QBv21raU? zNjl}DM-JAZ*j4oyQZD1Cqs%QSFt2le!wJvY0) zpE2zl5g-7N;3j$s?P`Us&b4b&^e~y8Lh-+Z%(tsH)rD~HA8g zk1L6$jRVRBaCuiIox*$;CVw%ienh%C6p-L*rBcpTD1C1?y<89YTt|CvX=q1w0@}$iXoMO}5bmH+4=$-Ity#fsN_h?A&^(RGO+*Duqh9 z!YRbzyK4I%DMEYSWgbw6Qa^h7rcR$ZyWWX0>~{`8-hAtA8abqgOC)&F2wFY3xC8&| zr8h1w#iCD9DS?gY^yxG6K>f3oGU@|WYA0jrS5ILxPamF@0Oi?}Cuin6Id*-|0VJgm z6{4)mcfTYF47O2bfhv7*(ns3W0Xhv}6Qg33GfnH!R60$!f*~d0)-a83DeE7~7hBLU z!UtS!8J#_Gvuk2^Mi456I8e1(m4=TKA^UZWdiqf5BMJd_hF4!Zxl}Hfr`PhUq+BVJ zYPAx`(zxmbVk4;NXSG_TsZuEs{4X&1OR+@GxF~^*>DaMj*WnFU%A1>g>k!g>T+~Uf zfP#Cyn`4zY4j=#q?!@M1abb0JC7`Id#SG*%eYkC0cr?qUBI+{>_#a{|o}zXHwQ_9m z@+6p0%V-|<3((KH_ zec1QdE2#ZIQ5RpoL_LLtwR~_^PAn-OKjl-isi%+knIszlx~A0lTb}>wtFK)L%D;B! z&TV3YE+8n*i-;2lYlQB9goOIZ=EgTzzlMG7s?v8*l)y$=ck1++qcGUHJeY@Z1KX}( z93zK)?1kL#lnxL(PvB0^#eH&+mInuiMn^_R*}+gii76}96aRor`N(;gby6Xv%}rGL zCi>1v)o&fUw&}|?ClbAg>a=!m;3|b ztbbg?i0`SwJMq2w{0d#^41HQ8eF*X904p3H53G`F*q5Fv zeR0_r&}~25PR~a!u)4ZB?)m=sV~>5G>H{gZ(2{iea0+o&aJ9;O5Gx>pz~;|kANqOa zVk|kpZU^-Ig^M(DxR%cs*4BDw8I;Q)-Zkd1>q6TGf7T-w{0P*Ai z{@xMnl~;asb#rqw`}ybpCe;qc3S_$6Lr;sEO8T zK=!}!%{TNNz4r7S=xE?x^#Sz7-&|e}WOloL0l`U-`qAu6g4|Cm0&uv}jaWgcv`L?x zw82V*5-7tjoUjUN*(&Fl)&!jfPTA5;FeFoc4D1?QUVny4$w>J1jRP{h_m7VeS!dS zJ4`x>hg-)}3FMD;+OglIzIBRn9fbhBQUW=}Iq^ClAa4Chya>Q`c&a}KS_g1ce-sCZ z){;;hAPTdHKi7#F#W{)t^vM2?6~*~6>(4>b0hASRz;%Ea*xkM_izLMXdaMLGsy_!& z3Dk8Kqrzj=aW7splrO~)xEA2XPzOk-Q_151228wVSp>g3yjhCR>0oiR0>%dJOQ0DD zFf=qw*W%qjHcD1j*Y>}%m+UVZSRX|s{Chv~;Q+`bAADdeS`N_F=$NLF;o)x)4gM%{ zf^rvj`|K6IbLfHCAizK-6TJ@5b*&gCe+ZBOPvJ_X(tFq51E<37DPOE2603A{8|~F^^25`dkfI)!;{L38ym~Vj{kgqzyDX)NNoKGZ7PAu<^YHcU){Zf z#p!nbYsxfYJ6?(90Js}Soa})hb_tKz;a5M<; z$tRzZd#m}_ih;j;n0?AL&2joiH7)zYBR}f-5ij3<;C_k(Pkgwxli|8em2Z?4PAr!Z zoNu$(h+p0y`S*`Ug8&Np9~1&81W*W|5I})K00jyG6et8xpb$WTLI4E{0Td_%P@oV% eLE_>60t^5tGhZa2hdEpT0000 Date: Thu, 2 Apr 2015 18:48:06 +0200 Subject: [PATCH 0294/1144] task: implementation of picking (work in progress) --- QtPyLoT.py | 25 +++-- pylot/core/util/widgets.py | 188 ++++++++++++++++++++++++++++++------- 2 files changed, 164 insertions(+), 49 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 24d55da8..87e49811 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -150,10 +150,6 @@ class MainWindow(QMainWindow): "Ctrl+W", QIcon(":/wfIcon.png"), """Open waveform data (event will be closed).""") - selectStation = self.createAction(self, "Select station", - self.pickOnStation, "Alt+P", pickIcon, - "Select a station from overview " - "plot for picking") prefsEventAction = self.createAction(self, "Preferences", self.PyLoTprefs, QKeySequence.Preferences, @@ -214,10 +210,10 @@ class MainWindow(QMainWindow): phaseToolBar.setObjectName("PhaseTools") self.addActions(phaseToolBar, phaseToolActions) - pickToolBar = self.addToolBar("PickTools") - pickToolActions = (selectStation, ) - pickToolBar.setObjectName("PickTools") - self.addActions(pickToolBar, pickToolActions) + # pickToolBar = self.addToolBar("PickTools") + # pickToolActions = (selectStation, ) + # pickToolBar.setObjectName("PickTools") + # self.addActions(pickToolBar, pickToolActions) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) @@ -346,9 +342,9 @@ class MainWindow(QMainWindow): def getPlotWidget(self): return self.DataPlot - def getWFID(self, event): + def getWFID(self, gui_event): - ycoord = event.ydata + ycoord = gui_event.ydata statID = int(round(ycoord)) @@ -380,6 +376,9 @@ class MainWindow(QMainWindow): title = 'overview: {0} components'.format(zne_text[comp]) wfst = self.getData().getWFData().select(component=comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title) + pos = self.getPlotWidget().getPlotDict().keys() + labels = [int(act) for act in pos] + self.getPlotWidget().setYTickLabels(pos, labels) def filterWaveformData(self): if self.getData(): @@ -454,7 +453,7 @@ class MainWindow(QMainWindow): return self.seismicPhase def getStationName(self, wfID): - return self.getPlotWidget().getPlotDict()[wfID] + return self.getPlotWidget().getPlotDict()[wfID][0] def alterPhase(self): pass @@ -464,9 +463,9 @@ class MainWindow(QMainWindow): self.updateStatus('Seismic phase changed to ' '{0}'.format(self.getSeismicPhase())) - def pickOnStation(self, event): + def pickOnStation(self, gui_event): - wfID = self.getWFID(event) + wfID = self.getWFID(gui_event) station = self.getStationName(wfID) print 'picking on station {0}'.format(station) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 3c91fc4d..25889981 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -15,33 +15,16 @@ matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvas from matplotlib.widgets import MultiCursor -from PySide.QtGui import (QAction, - QApplication, - QComboBox, - QDateTimeEdit, - QDialog, - QDialogButtonBox, - QDoubleSpinBox, - QGroupBox, - QGridLayout, - QHBoxLayout, - QIcon, - QKeySequence, - QLabel, - QLineEdit, - QMessageBox, - QSpinBox, - QTabWidget, - QToolBar, - QVBoxLayout, - QWidget) -from PySide.QtCore import (QSettings, - Qt, - QUrl, - Signal, - Slot) +from PySide.QtGui import QAction, QApplication,QComboBox, QDateTimeEdit,\ + QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout,\ + QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, QPixmap, QSpinBox,\ + QTabWidget, QToolBar, QVBoxLayout, QWidget +from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView +from obspy import Stream, UTCDateTime +from obspy.core.event import Pick from pylot.core.read import FilterOptions +from pylot.core.pick.utils import getSNR from pylot.core.util.defaults import OUTPUTFORMATS from pylot.core.util import prepTimeAxis, getGlobalTimes @@ -91,39 +74,58 @@ class MPLWidget(FigureCanvas): def setPlotDict(self, key, value): self.plotdict[key] = value + def clearPlotDict(self): + self.plotdict = dict() + def getParent(self): return self._parent def setParent(self, parent): self._parent = parent - def plotWFData(self, wfdata, title = None): - self.axes.lines = [] + def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None): + self.axes.cla() + self.clearPlotDict() wfstart = getGlobalTimes(wfdata)[0] for n, trace in enumerate(wfdata): + channel = trace.stats.channel station = trace.stats.station - print('plotting station: %s' % station) + msg = 'plotting %s channel of station %s' % (channel, station) + print(msg) stime = trace.stats.starttime - wfstart time_ax = prepTimeAxis(stime, trace) trace.detrend() trace.detrend('demean') trace.normalize(trace.data.max() * 2) self.axes.plot(time_ax, trace.data + n, 'k') - self.axes.hold(True) xlabel = 'seconds since {0}'.format(wfstart) ylabel = '' self.updateWidget(xlabel, ylabel, title) - self.setPlotDict(n, station) + self.setPlotDict(n, (station, channel)) self.axes.autoscale(tight=True) + if zoomx: + self.axes.set_xlim(zoomx) + if zoomy: + self.axes.set_ylim(zoomy) + self.draw() + + def setYTickLabels(self, pos, labels): + self.axes.set_yticks(pos) + self.axes.set_yticklabels(labels) + self.draw() def updateXLabel(self, text): self.axes.set_xlabel(text) + self.draw() + def updateYLabel(self, text): self.axes.set_ylabel(text) + self.draw() def updateTitle(self, text): self.axes.set_title(text) + self.draw() def updateWidget(self, xlabel, ylabel, title): self.updateXLabel(xlabel) @@ -237,6 +239,7 @@ class PickDlg(QDialog): self.station = station self.rotate = rotate self.components = 'ZNE' + self.picks = {} # set attribute holding data if data is None: @@ -260,6 +263,12 @@ class PickDlg(QDialog): self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) + # set plot labels + self.setPlotLabels() + + # connect button press event to an action + self.cid = self.getPlotWidget().mpl_connect('button_press_event', self.setPick) + def setupUi(self): # create icons @@ -284,19 +293,22 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.filterAction) _dialtoolbar.addWidget(self.selectPhase) - _innerlayout = QHBoxLayout() + _innerlayout = QVBoxLayout() - _toolslayout = QVBoxLayout() - _toolslabel = QLabel('Place for Tools') - _toolslayout.addWidget(_toolslabel) - - _innerlayout.addLayout(_toolslayout) _innerlayout.addWidget(self.multicompfig) + _buttonbox = QDialogButtonBox(QDialogButtonBox.Apply | + QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) + + _innerlayout.addWidget(_buttonbox) _outerlayout.addWidget(_dialtoolbar) _outerlayout.addLayout(_innerlayout) self.setLayout(_outerlayout) + def reconnect(self, event_name, slot): + self.getPlotWidget().mpl_disconnect(self.cid) + self.cid = self.getPlotWidget().mpl_connect(event_name, slot) def getComponents(self): return self.components @@ -307,14 +319,118 @@ class PickDlg(QDialog): def getPlotWidget(self): return self.multicompfig + def getChannelID(self, key): + return self.getPlotWidget().getPlotDict()[int(key)][1] + def getWFData(self): return self.data + def selectWFData(self, channel): + component = channel[-1].upper() + wfdata = Stream() + def selectTrace(trace, components): + if trace.stats.channel[-1].upper() in components: + return trace + + if component == 'E' or component == 'N': + for trace in self.getWFData(): + trace = selectTrace(trace, 'NE') + if trace: + wfdata.append(trace) + elif component == 'Z': + wfdata = self.getWFData().select(component=component) + return wfdata + + def getPicks(self): + return self.picks + + def setPick(self, gui_event): + channel = self.getChannelID(round(gui_event.ydata)) + wfdata = self.selectWFData(channel) + + ini_pick = gui_event.xdata + + # calculate the resolution window width from SNR + # SNR >= 3 -> 2 sec HRW + # 3 > SNR >= 2 -> 5 sec MRW + # 2 > SNR >= 1.5 -> 10 sec LRW + # 1.5 > SNR -> 15 sec VLRW + + res_wins = { + 'HRW' : 2., + 'MRW' : 5., + 'LRW' : 10., + 'VLRW' : 15. + } + + result = getSNR(wfdata, (5,.5,1), ini_pick) + + snr = result[0] + noiselevel = result[2] * 1.5 + + if snr < 1.5: + x_res = res_wins['VLRW'] + elif snr < 2.: + x_res = res_wins['LRW'] + elif snr < 3.: + x_res = res_wins['MRW'] + else: + x_res = res_wins['HRW'] + x_res /= 2 + + zoomx = [ini_pick - x_res, ini_pick + x_res] + zoomy = [noiselevel * 1.5, -noiselevel * 1.5] + self.getPlotWidget().plotWFData(wfdata=wfdata, + title=self.getStation() + + ' picking mode', + zoomx=zoomx, + zoomy=zoomy) + + self.getPlotWidget().axes.plot() + + # reset labels + self.setPlotLabels() + + self.reconnect('button_press_event', self.plotPick) + + def plotPick(self, gui_event): + pick = gui_event.xdata + ax = self.getPlotWidget().axes + + ylims = ax.get_ylim() + + ax.plot([pick, pick], ylims, 'r--') + self.getPlotWidget().draw() + def filterWFData(self): data = self.getWFData().copy().filter(type='bandpass', freqmin=.5, freqmax=15.) title = self.getStation() + ' (filtered)' self.getPlotWidget().plotWFData(wfdata=data, title=title) + self.setPlotLabels() + def setPlotLabels(self): + + # get channel labels + pos = self.getPlotWidget().getPlotDict().keys() + labels = [self.getPlotWidget().getPlotDict()[key][1] for key in pos] + + # set channel labels + self.getPlotWidget().setYTickLabels(pos, labels) + + def apply(self): + if self.getPicks(): + for phase, time in self.getPicks().iteritems(): + ope_pick = Pick() + ope_pick.time = time + ope_pick.phase_hint = phase + print ope_pick + + def reject(self): + QDialog.reject(self) + + def accept(self): + self.apply() + QDialog.accept(self) class PropertiesDlg(QDialog): From 043c45e02cf018c1aa92bb3a81306697da2a7730 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 6 Apr 2015 11:42:21 +0200 Subject: [PATCH 0295/1144] bugfix: undo filtering when checkbox is unchecked code improvement: class PickDlg -> distinguish between setting the initial pick (for zooming) and setting the actual pick (phase onset); methods renamed -> setPick is now setIniPick and plotPick became setPick --- QtPyLoT.py | 1 + pylot/core/util/widgets.py | 47 +++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 87e49811..5cb074be 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -376,6 +376,7 @@ class MainWindow(QMainWindow): title = 'overview: {0} components'.format(zne_text[comp]) wfst = self.getData().getWFData().select(component=comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title) + self.getPlotWidget().draw() pos = self.getPlotWidget().getPlotDict().keys() labels = [int(act) for act in pos] self.getPlotWidget().setYTickLabels(pos, labels) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 25889981..2ce0cd49 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -22,7 +22,7 @@ from PySide.QtGui import QAction, QApplication,QComboBox, QDateTimeEdit,\ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime -from obspy.core.event import Pick +from obspy.core.event import Pick, WaveformStreamID from pylot.core.read import FilterOptions from pylot.core.pick.utils import getSNR from pylot.core.util.defaults import OUTPUTFORMATS @@ -262,12 +262,13 @@ class PickDlg(QDialog): # plot data self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) + self.apd = self.getWFData() # set plot labels self.setPlotLabels() # connect button press event to an action - self.cid = self.getPlotWidget().mpl_connect('button_press_event', self.setPick) + self.cid = self.getPlotWidget().mpl_connect('button_press_event', self.setIniPick) def setupUi(self): @@ -279,7 +280,8 @@ class PickDlg(QDialog): self.filterAction = createAction(parent=self, text='Filter', slot=self.filterWFData, icon=filter_icon, - tip='Filter waveforms', + tip='Toggle filtered/original' + ' waveforms', checkable=True) self.selectPhase = QComboBox() self.selectPhase.addItems(['Pn', 'Pg', 'P1', 'P2']) @@ -344,7 +346,13 @@ class PickDlg(QDialog): def getPicks(self): return self.picks - def setPick(self, gui_event): + def getAPD(self): + return self.apd + + def updateAPD(self, wfdata): + self.apd = wfdata + + def setIniPick(self, gui_event): channel = self.getChannelID(round(gui_event.ydata)) wfdata = self.selectWFData(channel) @@ -363,7 +371,7 @@ class PickDlg(QDialog): 'VLRW' : 15. } - result = getSNR(wfdata, (5,.5,1), ini_pick) + result = getSNR(wfdata, (5, .5, 1), ini_pick) snr = result[0] noiselevel = result[2] * 1.5 @@ -385,15 +393,16 @@ class PickDlg(QDialog): ' picking mode', zoomx=zoomx, zoomy=zoomy) + self.updateAPD(wfdata) self.getPlotWidget().axes.plot() # reset labels self.setPlotLabels() - self.reconnect('button_press_event', self.plotPick) + self.reconnect('button_press_event', self.setPick) - def plotPick(self, gui_event): + def setPick(self, gui_event): pick = gui_event.xdata ax = self.getPlotWidget().axes @@ -403,9 +412,18 @@ class PickDlg(QDialog): self.getPlotWidget().draw() def filterWFData(self): - data = self.getWFData().copy().filter(type='bandpass', freqmin=.5, freqmax=15.) - title = self.getStation() + ' (filtered)' - self.getPlotWidget().plotWFData(wfdata=data, title=title) + ax = self.getPlotWidget().axes + ylims = ax.get_ylim() + xlims = ax.get_xlim() + if self.filterAction.isChecked(): + data = self.getAPD().copy() + data.filter(type='bandpass', freqmin=.5, freqmax=15.) + title = self.getStation() + ' (filtered)' + else: + data = self.getAPD().copy() + title = self.getStation() + self.getPlotWidget().plotWFData(wfdata=data, title=title, zoomx=xlims, + zoomy=ylims) self.setPlotLabels() def setPlotLabels(self): @@ -418,12 +436,9 @@ class PickDlg(QDialog): self.getPlotWidget().setYTickLabels(pos, labels) def apply(self): - if self.getPicks(): - for phase, time in self.getPicks().iteritems(): - ope_pick = Pick() - ope_pick.time = time - ope_pick.phase_hint = phase - print ope_pick + picks = self.getPicks() + for pick in picks: + print pick def reject(self): QDialog.reject(self) From d21798f633ed5508878605ea4349227e7ea9a0ef Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Apr 2015 15:00:00 +0200 Subject: [PATCH 0296/1144] test pickingdialog and prepare figures for the poster --- icons.qrc | 1 + icons/zoom.png | Bin 0 -> 7635 bytes icons_rc.py | 8 ++-- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 84 +++++++++++++++++++++++++++++++++---- 5 files changed, 83 insertions(+), 12 deletions(-) create mode 100755 icons/zoom.png diff --git a/icons.qrc b/icons.qrc index 7ec95b9c..4f9c34a9 100644 --- a/icons.qrc +++ b/icons.qrc @@ -6,6 +6,7 @@ icons/sicon.png icons/pick.png icons/filter.png + icons/zoom.png help/index.html diff --git a/icons/zoom.png b/icons/zoom.png new file mode 100755 index 0000000000000000000000000000000000000000..d8faba4a0f4ac9cdda160815df8a20d55bf11aaa GIT binary patch literal 7635 zcmeHMcQ~7G_peRt5n>lbsM@1ek)rk*rz=bZC7=REg)@?5!-%}nml1GoTJu3Vwl*VDGRa)p%e zmGlZV89`vGt_>3e862W-MNLipdChc{pwa~C*}<<|pF604nV4Bv*?{aEAWklB9$r5Fs{(>T*Mvnx!D8aqC2mMc zNz2H}$tx%--BealRa3vEp{WJAt*xVbM^E3t(8$=t)Xdz%(#qP#7HVhj;OOM+;tF$f zzw6<7&&%7#*Uvv7Feo?#9(q44JmNuQ)WhhQM~`FU;t>gnPm+>TQq!KM|C5pVEGs+b zd2U{QL19sGNog6fyrL3SRbBI|gTPEF6u&V8K!wD5UxX?bOJZT-vFZ@BLpn?JUGZtv{w?f*K!|2{nW^Y{4V z^z3|#`jY}-bg1ZSYgvIfxBFM_Er{7mO4K-&V+u*NXgbh(6NTR)v@5cTMiWb zHg#V=*-gSohgJOOTB>mFVoIe*h`ug|pg=|H&XGk_(@{7M{yra`p6}F9a&*@8`+Vi- zZwv`4+rmKXzy3%|vVQRqpQrded?)5$dqn0#m%R7sFpMlRY&+;s|BgbVC8v0^PjTq% zlGMYVkh-z%VZXn@L+w2r3QbplTnZ)|A>ZMp0bkFDPxszq+8l$tn6t;lTX^xy%^f?-Xi&)6*|41*zq8*lT^$Ebdg2E~_e~@PKCB3774?^|qdA zw1|>*6cn`LMCzeuesQC@SM|y5{Gd6v1=vw(9pvx!>PYju+C~|$hBfF_yiGn_BKy%o z^qtRlht4TS`Hfd9pUxdgAbvIeQhz&P;nE!Yt9E+D`{j2o%?JC1AHCX)i_5+)oN8}9 zZ6Fu6l&(w@K%*(Lwa+-|o@_564_6-eg1EA3wYE#ksT>oBr@oXOz7r^wb_`nrSp-S!2pYh4U z>+?MTVoH>3OEQq+^r{7gLY zIOlyQNNl5a${sp zw()h-8qR+(3&q$cItpbI$`~Gd8TiACR0rOWA7ppy8dn}|2DrB!&!79YEC?@0|L3KmV0EhDMK@f;sBZp?!*QTJ9rLss)tSxV zsmyRfTzctLc$<*D_>IaBtry>zzGA%tr|)@G3s?+Ixu_<7gqZLuB%IsZaWGFMpBFro z4%z7v#?RtOwRJ0u@zb{V1o=}~9X6|xNr3O}$6oMK!LRxleY`o2PFwrgd2s2ACu%Al zx6TgW>e~IEB%|eakA|E2ow+v(k7$`me%nk-IYhEu-Ssgp3rrL`b=#eiDpkM3)1Qmwau|)&LNO`r=xhg!rg!bvI)$TmNfX#(Bi{4XMfdF7gIWH&OW>Cn$U>C{}4dDhvR(${Gsbz z{`i`DAf$@EyTAcBck_%RQeG+1OsvTCpXW1)Iw~F;lPKyOXS|n(5ELb@35$nIGI8F`%Bk zMOpW>Vo`xQwS186gh{ckHOsAZ$aTIi_lQ?!^V3P-CX^$wSf_&>JwM9^cfi!(Fg>dp z9=K}^apqE%e%B-p83s!b`t&8kd$utT$|&i4nzJl?Q(+BnJhB(8sBi-TpWi=veTyM} zrxJP(TBkG^kf@w9E%9*!0j*X9U=MY2C#? zeBm<|dn~hS;d4_$A<06jH*Y?5F;$~44~Js2ccxaoQ9g0F(w|4NSeFXJQX4o?U+2|- zft0Q~wAq;rACyKNivWz{a&}M?b6nCmfgOyoG|m~|4@M-oje+G^hEBscj$r=4Jd0fp zz!?d(G3_h*v{-!)hJ|sCN<5A(QpgT==*?5%+?l4N z8tO7a#=927!FVpL_Xmk)gp8=gMkMsRIiQ#v9w|%v9aChN{eXAw?=#ugu!$f~q$;-K zv8<|izif-+s3Ube-#vCpOwqjHXc6OBXc@K~L}wsFs}GvVk!8flGFnto8C~jDU73Po zuK6FtUUiCGB6;|m9_f#*Ywv-7QKk`1@5$5XaVtbu|crYy%Ed3K*LaHG^ zWr2mT;FfffvtS7Y*?B2EdPK@_WnmQzpg~NAOF4s>(r$zSJ{H(msU7%Llh~FnGO!Gb z4M;Z`os2>{+i_Hb^-Ms55w>zZy z)dGNxqYyD)^&WmW+!X~9bRx8r9b_6ilA$lR8}Nh>;13-;NI%v)<3ZAckMx3PVDuGs zkZ!DU2Ip{>DwSZkg)6g!^kY)mt8kVBe_Ms5w6b0vXqhNQ&l96QAKa(U8Ju@wm0V*R z*`TyY(Y$-=SLKpPKlGFGg3+vyYU#JFb8%QUKw2Wj`ziz(QYp{&wgCPX#ZDZBN%;T? zlaKk3+HnUt1TGdNClMqs%z&8D!rz5ftBo92<2IefwcqPnoS+)gi z+-VN(`Uq6|SghZLmH~v1Z#PZ~;szb81?CD^Zn(zg8OHAKmHpVl#%CsEoCp!_Kc$11 zG?_EoVo#28YdLKfVF9L~`%KyX&@9hzcBd*g!!(5>o|X49Kw%;jiw1i&u-zkreAdq* zE_U>M;}yLC=$ecH56!Mm(f&J*T+}27 zKg`_#^%Es5WSPW8PI6#^95_SwGiOKe5O)QObd##^8w>#G1i(TVD)SP^v7MzXq8J7Z z3s<-Rap%$G9oZV3QikQ3B)h|$3HzqEJ*5+jW_TWfU}@_m?!?Ez)#mGD&~+PwF46L_h!i5ghU#G`^c!dtpI}Z*Dr>ASzt^4um@SPX_fkl7}!*p zTJufvfrHZD`8<)V*uMzbl|B_3z##1XQ*8f;G)^Ay6mg#cIk65(+w3L$8p;G()+qdx zdji>p8Ik3lG|N(2rdLfIq0OWK(d6)3xLZ#XWU|27h=d>6<{NCFtkqk^w3ZBF6=sI> zUg8PM2$`pb9gKA;uu%{on*REI)J&$dr`i#c%IHORJS4o&z&hN*3y|iGj*t4?LT1T_ zw1Urz8NWDi#X2q49l#96=n~j_Epp9#^kt01B2uGdc!nNozR*HjB^YOOQM=bhkEll~ zP+B%tLRsg3&=~mpQ@e9TkE`c7lUmj`R?av=x9C!gFT|nrBkKCrg-J?Fw0SO>chwkw*y+(1TyQdZ|9wlaaz-0MOt>%*pP158JddR{YT^c z%WNVo)r-RDPl74_H58ciH?TjffySIsce3@s5_-Lu&ztP-~Og}&i{n3$L0Vr_M|{K6U4K@=y#8-3Gg=cQu8 z6W;vJfJI~w+fSu*zItEkPV0+UzOGcJoAS3AJ%Rl`Qd6mLW=g5aR8`pPt&PEQQ2%N~ zWTrCkP{oa^dm}w4qSU$GhumS-&Oj=r@o~`2+!Bcw^r|@zbxkRSByiJNriQK+<=*(a z)-?U=29a_N)Q8FHhJlSbUN^@!#s@(4aE)#|Xwu$>(LcJqift1Wa3&GC4f99y-(+)_ zjdW^w%nbq;8_^@2og2oVc9|p@rz)lnT>p7qxH%btF#0`<_xikv))_0^oSciKE)2l? z*xs8*ZhJ;5fRL;wdCne@eU!Y9khWvjg&wAlO}y@8eyA>o|0#}`jEFpn5Fib>*?ppx zC|%nUyWDLelPZitU?JSKgMNjulSioXPsq%g1T(go$!vivJ^>}IEcuoFH0@??(s`AW3D zOMA7z;?NX-C)EJcRrRn5ej(N;)zKm}o$?z}Ougsz)uy2O&6v1ubnyGXwV?-Z`Ea@38MmSX6U-(uQtU4``mJ zWY8X8_|DN#H7V({*IdDf62Ik@7jYGV1zm}MiJ=%L+l>PmPNSI*ZP$XMtNmP;D|xkV zOAI>B?=SC3iq2?;G2A>)K?VlGVhHKo-VeoL-~1y&*KykZV?OIggCw^s?7clDp;mdT zvm)hI&+{kK&XLDbN6RsmUDFh~&P!1f-HfIY9>=0C4X`p|?4{kB(X22gdShG596|@U z3eNPbOPb@oyxNm$)x4=D&AyP;{vsNp#H_yQt0qx?7rWH8pAj!J2iF-|rxWXYRanbI z)OQqXLkQ;DTlcNL=@H7rJgOAGLukoNiLP~eX#6vfAktMYu~sdAe2nAmLY*?CXw$3S=Cq1>?deJbX$Wc`%dpdNY2bIHM~1z(PaH{K*6}{}bq_??V?NRSV_HCB zQgEvfN&~|!ewXDj*bUlaBm&_~knR58bn#LSYj2gkkNx}|5DR^eP`?*5f%~QegX`Og zW~qZ8Cj-VzFc)7cH|3PlgX(^>`5#&-oTg+|eOTO~?y&4BZ?JJmd-RQPxlI+)=rzG$ zzlmD-6Hf4lcMARFdTV@gx*eK5raygeM}fNAJr}%8%Qg zGYUtDaAc2gsq1Lg#*I+UUl%nl?f58o>c-14LAl#9&~wxK7~9Rj%(T`1SgWm__th%V zK}B1u{tGGPrv+}_yMqLd07h*H5tuz*o6WpI457|M|1zBxq-_{blXgb*X`>PpoC_w2 z_r&SQ{N%r65fXRf{Vt~&W~#=1@`0G?@1%4MweI3R^nMSSz&eqLQd{pgH%{iFXUmCH zjOe6v)gnFmF6+Ta@htKRA2GS@WUm*C_Z1I30^Y{2W@c5bL<;&aJaVOn2lkbT?8Op* z0PU}A_a8pE4&U7BEWn7G6vT8gGh02x)d*vs?q=Os6B0Fk`x_$98##t|lK&!XcpK z#(BIeQuC;G>StPGJYW8=zdpGqX01D)y&0D#T5pB;%F(xT{%`^EPvL&W96jZc+Znm% z67l!p1D%X@v=&jETeL_&rf~0} zOf#FLPWg2nu!LHL2Pjn^g5 zX30jygL8X)s62Ap4K-TgUKuT-9vRf#{v@(9r6h;^$r`?y8)L?v(uvoX19J~L9j`lp zRS9A(25dO!6xr4wncc81sm{&d(bR7E&zyDqE9fFhsSRRBXk#i0SoV{h@R;!}+^jXV zGkdf-{*}ujYC^L{I;oTG0lPTQx+F!F&0|1v``DRci=dU)fYVO(3m_75Z71ZdQ3ExQ zklG2%N2ewuxf8oElVXNs4bMjr4Qjq9dVb3Xm?1;)vO5lT{59xWg{+fTYkoC*A?NEM zP3-asfEM@VuO&B9IUos3BXV{ Date: Tue, 7 Apr 2015 15:00:53 +0200 Subject: [PATCH 0297/1144] new zoom icon added --- icons/zoom_minus.png | Bin 0 -> 7037 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 icons/zoom_minus.png diff --git a/icons/zoom_minus.png b/icons/zoom_minus.png new file mode 100755 index 0000000000000000000000000000000000000000..5207ae7758e76f2a76eeba58fbdd6aff7081098a GIT binary patch literal 7037 zcmeHMc{r3``^RK3#%}BpC1Z~nB1;&=*v8oRA^VaE5!v^pk__3(zGNLCglt0^Ymr?c zWQnp1@s98N&+q;3{r~qq*Y!M~=f1Dc{WS!`D z3eqbD88v_e-6hxhNC5Cv(>A82rk+_am?xPuSS<@*GBWDfi<>-m)lQM*(g*f}`4xOpz~@?GHiqhz5&V*ZDed>YG!U>X=QC=YlpFS zxa;WT?BeR??&0Zm&l`K+$Jg(Hf55}Qphv+Wp<&?>kx|hxv2pPUiAj&2JWYO<@;nun zmi{6mGb=mi<*VGh{DQ)w;*!#`*X0$JRn;~4+BbFe4R0Hpn%}jww!Lrf=Z9NA7)xs4Pf+ zj>+H09=`(5x_=!Uozkq$ZLH|6hFOgIpoXA|RsHd9I{v@0MeU)xwKEd(vJ6CL$TWRm z&?i%3TIHXOZkr8+McGcwVVjZe)%Te}cUhoG#wUXa9UZr^660T~K6wSd`i8zI2FE&l z@BkR><`sz6{4E;htZwtBR|JV9`V75ldXhOW`P1UEh=Zka{i{r)or!N|v5AkrYX%SL z&YYhv|3NXK8qcW)o?cGQO$DTxe7SdOm1VIvH#wU3X62}qL3+c~Hk&Gqu03?n0lwP8MW{lR*YjhT!x_dKXNx^vpuY%G3FSu(ookj%b9-%M(n73z9);xoFw79}vO0|K;7CxR78pH+P7a3cSbC#Mz&$6$N zJou{pW1yV=fvbpaQPOxW&j$%5tA`AR4PY zDE7balze(%We|5PMd`cxKIDZ;T2(K)S5^CF*&DcdcSaLn{oJEArFI)jv2IsC)58%c z!MC34C*fQonUjWVXdEWIulB zfluMLQdmCzekFCL{Jdg@_8@%bh+XU_ zf57xSwRg*&%R-#$Xhg ztHhc4Z1g_OlNOe3v^^!=%;Bn3Uq;=xJo{potERo9m46G%uNLa(XxF{3h~ZdMud`L; z;tcrY&Lwk`s@5<}Nbh*h+7WTK`N;J%mJoBOOgH^SlEDHUaElI^8IiQpU2#tDC}-9~FdM@qIUaf?^J@7=nQ>j1P#a+^)>PaeNOe*ZnL z=gNFTEm0+p2k)rEEj1Cc6A|A-E!1Fd8}9fmB^UF1cY58aEas%$;73c$Az|-2++eQi zLBynCUQQ{YVVSSqu^Ww+mtoR&@hT!emviBYva|%GH@QjzM7(G| z)Z3$UM7y~*Loq5kELh~053v3nQ0%ktV0b8P>7Ikiw=Z!fLDG54G;&49{=m9jb@P>b zz%g?A%nl5F4$X!x<_4e8{Npf%J{6>7T!@dY!ws_z&byogba=z&Kj5XpOFV{Nk^oMS zg2~^E2y6N~;Ja78V=AWYq~aMCVnNxQgaID&iZxr{ea;*#ZPd|QF0t_^s$x;JKJ5-< z#a@_(HE@9v4;B)|0c*+sg|`g@;xCP7-;qBT zn3UWG0^}g+9zA5d(X{Ttcbd2HvtXk5$0(K|Z6nVBLG#T?c#pqK_|>gq>zn3Y6wB3d zqC2LBKW!!}iR6jm+8vxf468ZC{4e*Mj$`|DSh)d7ZQ8X$3(t?Kpbdh2mt;tK^$4RHw7PqZ$808#5H% zy@FnmG5^6r6pQX3DjYc5!RtQAm44CX)(`cS5*v+>7G=Rs|AOvvi0ujy_rhmd2U#s> z`-8PoqQ1;GeSrEZR-$F}QY^4G+(dAsiA4Uu7p%=9ccNIh32$ECdnEQNpYRTX*K>zg z<5kRwe!ecPc@dgMLjnN~)=$~O_cgOly-Mb2$`(lO6u2rZO>=)FTHsf=04?-0eeG{4 z!Gj-NT+8&R#TFhy7bX!NCo_#&k|H2j60K=Hz$IEj1j2QsB87);iIzBl07kabdbA=E zOyZN2ag*5qE6)PLC4vTRB0Ng49JS<5&={geS_5?Ii?nPAy<*76F@n_v1orr3Rh&7Q z&a6g)5EEaik;7H7ZLnaqM)1;RVR$Z;LaRZ-aePWMPL9ieQZvGvNhrW*(G0I9gB;Mz zvDQc!!mD{86(i-`G!jDbT{5`UCvx(U%%nM#f@>nHeG@|=*#R0x7m!DaetYxxN%x&N zch|!ttB2J0m?+sgL42q@Id*c$*;!B9K>TGR1;~}WQeiy3>7+`833UmE!0SBn_H3E4 z4+X+4bpTt0Z?HsIpDV#Uy@W1pDWDVg0sit%Jbr5-VkY})2@oGT@*ae+ER-{n8N zbq@Dr;!W}i5WI>wvOdPV)5rN^fe+)1dW0=r$X}kz=9%PLdFYTDatI#6HBFD6x z4R33;AtFxfBV=)9m-02KGBr-z5w5b7ypfa=1uD15^I7ZMnWLRi^M>YmFiL+!nM_?p zJ*VS!BaEWhv!adwMy_EoZo14n!oRPHjl@ypBCgj)SR4pDZWVSIo6qZm#G-UXo#$#9 zIn^TAF%c)iH~6Fh84o59MGTa-v6Q`r7&gZAjxI%+6&gvYiN-KF0%03LDogsH%4kZUVIH-#%Z-gJRYy2!JSdcee?uI7 zWTpDYRaT@x1Cf;P&=}h2SD19AOPRu~4}ol}DiHw2Td7majo|~LtPd}-$>lQhMmtMY zl&GzGfoADwb5s+qUKy*zS?qOxVzg9?u(FRxg&H!T?`CKOsJcTa-*r*Y34h{2f3+IO zdcpi`8X0kLF+SGxwOwBZo+pc_mOo=|kE9eabhxs#UgKygRz+7k3jG9T(>h|!h$j$kIzv!ywUByr@`1QY{)Bnluq$YAR{Z86&6K*n$H-aTig+9 zZm&#v3ZgE%_p4DSgQ=&^yF4bG%?Jaiypg~>$Yi}B-_8g34=(Ek|NLAHik#7pfZ)5X zApKNwj0*|6<_kVZux3tu34sq2^IdpbZgGhV=@$nfp0JvS%W-Na*mcrxK=GpNQajfL zL&+5eiV4E;$*paylOUbbOy)*@W&Kiu0$$w?+4}g1LpMPPlb|D9D;P0(Bq|8(Vtyj* zqqry#z%D2eCAfnWz7Jb05Y%`YCH_8-P#2#Rij!OIWb;Nj02UWbkd=`#y!l!P0>VK- z&^A`4x=`yTp*w-KnQz=xCB=n_Aplhb_*wgk`H?6;n<=WRhW?RmX`e}a6btkXGh1_l zKn*|rR$-rHhwc*oV z+V{|W*^#&xw?SGQ)9}b4{Y0^-jaNmwUX#lYi164M;$~5wySX!?(^lL}b^gFL^A$hh z&oXc+#Bhbn{JJ-v6R^XH-KdP$6ZS%yduIVpiTlGm-uzNcyJ^D@^d#cJ)BKV0OF8?} zF_zuRNl-s|>o0`T0W&;G64XXzrlg(!36B~?gI?0y{32e0qMdY=jj@f(<}Dg=!K2NP zGI2>yC-w4M?2Cz1P&kVO7vgmZ-Y5~x-l;T*gc@Jcu~B{fte0V>&+bRVPnL_^#LWyNxcPO}8Id&(`*tL9S6 ze&OtvCRTNy4%fnvUeOxm)4#J2Zh;f6rkk1TC+qDBZIlWx#k5&cadZ^p=Y)H}^Xik{ z6o5%fc7IXxL9(xrfTwz}aLQ)7XDmQ{Zm?t zYnP*3<&Zwtwwj5$UXM21!3)w<$ufBKPNrZv+Q2nBOm9ltln$Bwp_@C%AByx)QghS>4Gkvu$L&hlYj#N2vf@j&^_ zW~BgE-6Vp?_)p|YYcTlD>#ucim$18p?$ z{LB0jv7_~iAGoi=xK96&A)fCrvdHhcS|1mWQ-Aw7oF=U(%pi520E)$&X;4qddrMoB zY{P)d-S|^?2K$d2d*ld`DvRD*X;!NbxoP2n@-BLly}DX~@d=}cQr9;4Zb6(Ig0V_4 z!DasI62kVb##^4>#>{E_$mkRIw$qG zwJCGz8HvUK^HZ?nJMCBGsh%}D8J-=g)DDb~Blr$P|MgB@_p#)sA7rYk<~{4Z=WIqq zC@u{<^?HVQ6Y&-LRf&8?4a;fWC1TTETBNMvh&+s=oSKkC0YJ$UyO zu@&Y??Du64dzYC)ssTg8`yvq?k`#Pe-6S0s_t3TVX}Op%!!{~F(8L1GyO{s`a`~Gg zMFJuw)7XiW43)R^h2Oye{tpz?7?{N3;xbl!o|S_yD!5=f~Mj7#< zXEH0OG=7uPD-q^>oarSZIzbc1CN+ERpdSr$TKnH+b=NGJ?kV{2eleI*Y$DU|zq~vu z#qBosHs55vp7!mC;K`#O#jfK|zhW)tV*EZV_J+SGus0IzaQX^Ro>N=^YIGytb-E=inNE9cP95EDWu@Cq#aiuu zte;vEITEr>jQumW&;+|(TqXM#cuv&$bfaB?=fzFO+g0#F;Mr|b_~{nbChDeZZt`@# z?gf3KJMg9Khy-_ae`oShm_zM9oDYDxL*DEN{t|fBRmv@2w8Lc8g7F-L&BY|33;$_n)nr6QDCbNpXn9V%LmsfZ7X3*|coA z2z?q!GL(UUD7ed1=(;}$xzBN-@#xBR=F3SOGP{yG^S?>GUilW?q~&KlL@h73&&s2G zk#{yDUwf!KU!na6!2XpB*)W(x(fVM2Mu^>kL^$pu)4}*OqqEl3sU^wPXHjJT=nB}1 z_}lb(THKn4URo-4wKO2;rQW{hyaJ(fqXx9H>a|eTrcP`ZD-9G4Z`}hS{_#lR!nP*A z3_PyUYz$YmO)S}0Q@s3WX4WD0XTMaHea4Fdw>CzzGzG=l6nhJ$wX7iOhzl2!T|#<* zxJ0pAMX#L&xBWI6UUk!r(eEq@tjIex`l8 zTz=J$5?;eF_H8-W=dT^xuX+2K=A*S4N$=-z16oCHwtQt_Wn)J<)rd32-EKYQFwM1b z8DHM_&0b!P;}Ws+re$GiVY=BBK*bx*v(ZAifoH`N&h2f#|2k>piUoyCHyQqYZk7}B z`I8BAU*bNvy9$+V-h-M2I>niBJe|p_97*C|qjicZ0qfX7xjixF$Ep?>5Mq=;(%Z&Mbk6ZW6%CtC*;rRH<1sp?44|5Zb=qa{s7hJ<#vqC=GO_73_{)_t z*9!dOd@eA=>yDRw84#giHNzac?qBA*u>v!0B$=OG%C^E2-xdv{=Exyl+PV2$6#p0A zv4~7RS4KP#GOia4iFJqm2XOq``Zuggl)360gTL6IKBez*mM{DVg#Kfdd?ualiLb$Q zXSmfaTFx+KRrv>PqZ6BohHuzVK<;bj@*hLV|G{-I#gqrk2 zRGkuCgXLA399d+L;*vM} zY)|+`3lHwR*t(V>g&a<4=Y>w85L?>pJ>&@8CdGC#EvFD-{sQ&At bqV_qt9w1`}BjVb1@zX Date: Wed, 8 Apr 2015 11:29:50 +0200 Subject: [PATCH 0298/1144] Deleted: --- pylot/core/pick/earllatepicker.py | 133 ------------------------------ 1 file changed, 133 deletions(-) delete mode 100755 pylot/core/pick/earllatepicker.py diff --git a/pylot/core/pick/earllatepicker.py b/pylot/core/pick/earllatepicker.py deleted file mode 100755 index f41a0503..00000000 --- a/pylot/core/pick/earllatepicker.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - Created Mar 2015 - Transcription of the rezipe of Diehl et al. (2009) for consistent phase - picking. For a given inital (the most likely) pick, the corresponding earliest - and latest possible pick is calculated based on noise measurements in front of - the most likely pick and signal wavelength derived from zero crossings. - - :author: Ludger Kueperkoch / MAGS2 EP3 working group -""" -import numpy as np -import matplotlib.pyplot as plt -from obspy.core import Stream -import argparse - -def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): - ''' - Function to derive earliest and latest possible pick after Diehl & Kissling (2009) - as reasonable uncertainties. Latest possible pick is based on noise level, - earliest possible pick is half a signal wavelength in front of most likely - pick given by PragPicker or manually set by analyst. Most likely pick - (initial pick Pick1) must be given. - - :param: X, time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: nfac (noise factor), nfac times noise level to calculate latest possible pick - :type: int - - :param: TSNR, length of time windows around pick used to determine SNR [s] - :type: tuple (T_noise, T_gap, T_signal) - - :param: Pick1, initial (most likely) onset time, starting point for earllatepicker - :type: float - - :param: iplot, if given, results are plotted in figure(iplot) - :type: int - ''' - - assert isinstance(X, Stream), "%s is not a stream object" % str(X) - - LPick = None - EPick = None - PickError = None - print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' - - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window - - #get latest possible pick - #get noise window - inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ - & (t >= max([Pick1 - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ - & (t >= Pick1)) - #calculate noise level - nlevel = max(abs(x[inoise])) * nfac - #get time where signal exceeds nlevel - ilup = np.where(x[isignal] > nlevel) - ildown = np.where(x[isignal] < -nlevel) - if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'earllatepicker: Signal lower than noise level, misspick?' - return - il = min([ilup[0][0], ildown[0][0]]) - LPick = t[isignal][il] - - #get earliest possible pick - #get next 2 zero crossings after most likely pick - #initial onset is assumed to be the first zero crossing - zc = [] - zc.append(Pick1) - i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[j-1] <= 0 and x[j] >= 0: - zc.append(t[isignal][i]) - elif x[j-1] > 0 and x[j] <= 0: - zc.append(t[isignal][i]) - if len(zc) == 3: - break - #calculate maximum period T0 of signal out of zero crossings - T0 = max(np.diff(zc)) #this is half wave length! - #T0/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - T0/2 - - #get symmetric pick error as mean from earliest and latest possible pick - #by weighting latest possible pick two times earliest possible pick - diffti_tl = LPick - Pick1 - diffti_te = Pick1 - EPick - PickError = (diffti_te + 2 * diffti_tl) / 3 - - if iplot is not None: - plt.figure(iplot) - p1, = plt.plot(t, x, 'k') - p2, = plt.plot(t[inoise], x[inoise]) - p3, = plt.plot(t[isignal], x[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ - loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) - plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') - plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') - plt.xlabel('Time [s] since %s' % X[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) - plt.show() - raw_input() - plt.close(iplot) - - return EPick, LPick, PickError - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) From 62139626542cc40be40d6947668f44cdbbdd2e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 8 Apr 2015 11:32:02 +0200 Subject: [PATCH 0299/1144] Deleted. --- pylot/core/pick/fmpicker.py | 183 ------------------------------------ pylot/core/pick/getSNR.py | 66 ------------- 2 files changed, 249 deletions(-) delete mode 100755 pylot/core/pick/fmpicker.py delete mode 100644 pylot/core/pick/getSNR.py diff --git a/pylot/core/pick/fmpicker.py b/pylot/core/pick/fmpicker.py deleted file mode 100755 index 6aee8eb3..00000000 --- a/pylot/core/pick/fmpicker.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - Created Mar 2015 - Function to derive first motion (polarity) for given phase onset based on zero crossings. - - :author: MAGS2 EP3 working group / Ludger Kueperkoch -""" -import numpy as np -import matplotlib.pyplot as plt -from obspy.core import Stream -import argparse - -def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): - ''' - Function to derive first motion (polarity) of given phase onset Pick. - Calculation is based on zero crossings determined within time window pickwin - after given onset time. - - :param: Xraw, unfiltered time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: Xfilt, filtered time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: pickwin, time window after onset Pick within zero crossings are calculated - :type: float - - :param: Pick, initial (most likely) onset time, starting point for fmpicker - :type: float - - :param: iplot, if given, results are plotted in figure(iplot) - :type: int - ''' - - assert isinstance(Xraw, Stream), "%s is not a stream object" % str(Xraw) - assert isinstance(Xfilt, Stream), "%s is not a stream object" % str(Xfilt) - - FM = None - if Pick is not None: - print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' - - xraw = Xraw[0].data - xfilt = Xfilt[0].data - t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, Xraw[0].stats.delta) - #get pick window - ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) - #remove mean - xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) - xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) - - #get next zero crossing after most likely pick - #initial onset is assumed to be the first zero crossing - #first from unfiltered trace - zc1 = [] - zc1.append(Pick) - index1 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xraw[j-1] <= 0 and xraw[j] >= 0: - zc1.append(t[ipick][i]) - index1.append(i) - elif xraw[j-1] > 0 and xraw[j] <= 0: - zc1.append(t[ipick][i]) - index1.append(i) - if len(zc1) == 3: - break - - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc1[1] - zc1[0] <= Xraw[0].stats.delta: - li1 = index1[1] - else: - li1 = index1[0] - if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: - print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' - P1 = None - else: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) - islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) - #calculate slope as polynomal fit of order 1 - xslope1 = np.arange(0, len(xraw[islope1]), 1) - P1 = np.polyfit(xslope1, xraw[islope1], 1) - datafit1 = np.polyval(P1, xslope1) - - #now using filterd trace - #next zero crossing after most likely pick - zc2 = [] - zc2.append(Pick) - index2 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xfilt[j-1] <= 0 and xfilt[j] >= 0: - zc2.append(t[ipick][i]) - index2.append(i) - elif xfilt[j-1] > 0 and xfilt[j] <= 0: - zc2.append(t[ipick][i]) - index2.append(i) - if len(zc2) == 3: - break - - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: - li2 = index2[1] - else: - li2 = index2[0] - if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: - print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' - P2 = None - else: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) - islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) - #calculate slope as polynomal fit of order 1 - xslope2 = np.arange(0, len(xfilt[islope2]), 1) - P2 = np.polyfit(xslope2, xfilt[islope2], 1) - datafit2 = np.polyval(P2, xslope2) - - #compare results - if P1 is not None and P2 is not None: - if P1[0] < 0 and P2[0] < 0: - FM = 'D' - elif P1[0] >= 0 and P2[0] < 0: - FM = '-' - elif P1[0] < 0 and P2[0]>= 0: - FM = '-' - elif P1[0] > 0 and P2[0] > 0: - FM = 'U' - elif P1[0] <= 0 and P2[0] > 0: - FM = '+' - elif P1[0] > 0 and P2[0] <= 0: - FM = '+' - - if iplot is not None: - plt.figure(iplot) - plt.subplot(2,1,1) - plt.plot(t, xraw, 'k') - p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) - if P1 is not None: - p2, = plt.plot(t[islope1], xraw[islope1]) - p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) - p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) - plt.legend([p1, p2, p3, p4], ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ - loc='best') - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) - plt.yticks([]) - plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[0].stats.station) - - plt.subplot(2,1,2) - plt.title('First-Motion Determination, Filtered Data') - plt.plot(t, xfilt, 'k') - p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', linewidth=2) - if P2 is not None: - p2, = plt.plot(t[islope2], xfilt[islope2]) - p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) - p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) - plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) - plt.yticks([]) - plt.show() - raw_input() - plt.close(iplot) - - return FM - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') - parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') - parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') - parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) - diff --git a/pylot/core/pick/getSNR.py b/pylot/core/pick/getSNR.py deleted file mode 100644 index 6c2b6872..00000000 --- a/pylot/core/pick/getSNR.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - Created Mar/Apr 2015 - Function to calculate SNR of certain part of seismogram relativ - to given time. Returns SNR and SNR [dB]. - - :author: Ludger Kueperkoch /MAGS EP3 working group -""" -from obspy.core import Stream -import numpy as np - -def getSNR(X, TSNR, t1): - ''' - Function to calculate SNR of certain part of seismogram relative to - given time (onset) out of given noise and signal windows. A safety gap - between noise and signal part can be set. Returns SNR and SNR [dB]. - - :param: X, time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: TSNR, length of time windows [s] around t1 (onset) used to determine SNR - :type: tuple (T_noise, T_gap, T_signal) - - :param: t1, initial time (onset) from which noise and signal windows are calculated - :type: float - ''' - - assert isinstance(X, Stream), "%s is not a stream object" % str(X) - - SNR = None - SNRdB = None - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window - #get noise window - inoise = np.where((t <= max([t1 - tsafety, 0])) \ - & (t >= max([t1 - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((t <= min([t1 + tsignal + tsafety, len(x)])) \ - & (t >= t1)) - if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' - return - elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' - return - - #calculate ratios - SNR = max(abs(x[isignal])) / np.mean(abs(x[inoise])) - SNRdB = 20 * np.log10(SNR) - - return SNR, SNRdB - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--t1', type=float, help='initial time from which noise and signal windows are calculated') - args = parser.parse_args() - getSNR(args.X, args.TSNR, args.t1) - From 67dd66535a213ba5c7cfe2be52aa6d5a7e8b7324 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 9 Apr 2015 08:49:11 +0200 Subject: [PATCH 0300/1144] bugfix: zooming with scroll wheel now working properly task: panning now available while in exploration mode, if a phase is selected initial pick can be set and afterwards the onset itself (procedure questionable as two picks are needed) panning not tested yet! --- pylot/core/util/widgets.py | 107 +++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 21 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 904011c5..1d62c65e 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -243,6 +243,11 @@ class PickDlg(QDialog): self.components = 'ZNE' self.picks = {} + # initialize panning attributes + self.press = None + self.xpress = None + self.ypress = None + # set attribute holding data if data is None: try: @@ -269,12 +274,14 @@ class PickDlg(QDialog): self.apd = self.getWFData() # set plot labels + self.setPlotLabels() # connect button press event to an action - self.cidpress = self.connectPressEvent(self.setIniPick) - self.cidscroll = self.getPlotWidget().mpl_connect('scroll_event', - self.scrollZoom) + self.cidpress = self.connectPressEvent(self.panPress) + self.cidmotion = self.connectMotionEvent() + self.cidrelease = self.connectReleaseEvent() + self.cidscroll = self.connectScrollEvent() def setupUi(self): @@ -315,7 +322,6 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.filterAction) _dialtoolbar.addWidget(self.selectPhase) - #_dialtoolbar.addAction(self.zoomAction) _innerlayout = QVBoxLayout() @@ -328,6 +334,9 @@ class PickDlg(QDialog): _outerlayout.addWidget(_dialtoolbar) _outerlayout.addLayout(_innerlayout) + + self.selectPhase.currentIndexChanged.connect(self.verifyPhaseSelection) + self.setLayout(_outerlayout) def disconnectPressEvent(self): @@ -335,11 +344,43 @@ class PickDlg(QDialog): def connectPressEvent(self, slot): widget = self.getPlotWidget() - self.cidpress = widget.mpl_connect('button_press_event', slot) + return widget.mpl_connect('button_press_event', slot) def reconnectPressEvent(self, slot): self.disconnectPressEvent() - self.cidpress = self.connectPressEvent(slot) + return self.connectPressEvent(slot) + + def disconnectScrollEvent(self): + widget = self.getPlotWidget() + widget.mpl_disconnect(self.cidscroll) + + def connectScrollEvent(self): + widget = self.getPlotWidget() + return widget.mpl_connect('scroll_event', self.scrollZoom) + + def disconnectMotionEvent(self): + widget = self.getPlotWidget() + widget.mpl_disconnect(self.cidmotion) + + def connectMotionEvent(self): + widget = self.getPlotWidget() + return widget.mpl_connect('motion_notify_event', self.panMotion) + + def disconnectReleaseEvent(self): + widget = self.getPlotWidget() + widget.mpl_disconnect(self.cidrelease) + + def connectReleaseEvent(self): + widget = self.getPlotWidget() + return widget.mpl_connect('button_release_event', self.panRelease) + + def verifyPhaseSelection(self): + phase = self.selectPhase.currentText() + if phase: + self.disconnectReleaseEvent() + self.disconnectScrollEvent() + self.disconnectMotionEvent() + self.reconnectPressEvent(self.setIniPick) def getComponents(self): return self.components @@ -385,7 +426,9 @@ class PickDlg(QDialog): channel = self.getChannelID(round(gui_event.ydata)) wfdata = self.selectWFData(channel) - self.getPlotWidget().mpl_disconnect(self.cidscroll) + self.disconnectScrollEvent() + + self.cidpress = self.reconnectPressEvent(self.setPick) ini_pick = gui_event.xdata @@ -394,6 +437,7 @@ class PickDlg(QDialog): # 3 > SNR >= 2 -> 5 sec MRW # 2 > SNR >= 1.5 -> 10 sec LRW # 1.5 > SNR -> 15 sec VLRW + # see also Diehl et al. 2009 res_wins = { 'HRW' : 2., @@ -426,13 +470,9 @@ class PickDlg(QDialog): zoomy=zoomy) self.updateAPD(wfdata) - self.getPlotWidget().axes.plot() - # reset labels self.setPlotLabels() - self.reconnectPressEvent(self.setPick) - def setPick(self, gui_event): pick = gui_event.xdata ax = self.getPlotWidget().axes @@ -442,6 +482,32 @@ class PickDlg(QDialog): ax.plot([pick, pick], ylims, 'r--') self.getPlotWidget().draw() + def panPress(self, gui_event): + ax = self.getPlotWidget().axes + if gui_event.inaxes != ax: return + self.cur_xlim = ax.get_xlim() + self.cur_ylim = ax.get_ylim() + self.press = gui_event.xdata, gui_event.ydata + self.xpress, self.ypress = self.press + + def panRelease(self, gui_event): + ax = self.getPlotWidget().axes + self.press = None + ax.figure.canvas.draw() + + def panMotion(self, gui_event): + ax = self.getPlotWidget().axes + if self.press is None: return + if gui_event.inaxes != ax: return + dx = gui_event.xdata - self.xpress + dy = gui_event.ydata - self.ypress + self.cur_xlim -= dx + self.cur_ylim -= dy + ax.set_xlim(self.cur_xlim) + ax.set_ylim(self.cur_ylim) + + ax.figure.canvas.draw() + def filterWFData(self): ax = self.getPlotWidget().axes ylims = ax.get_ylim() @@ -473,7 +539,7 @@ class PickDlg(QDialog): else: self.connectPressEvent(self.setIniPick) - def scrollZoom(self, gui_event, factor=1.2): + def scrollZoom(self, gui_event, factor=2.): widget = self.getPlotWidget() @@ -490,16 +556,15 @@ class PickDlg(QDialog): scale_factor = 1 print gui_event.button - act_width = (curr_xlim[1]-curr_xlim[0])*.5 - act_height = (curr_ylim[1]-curr_ylim[0])*.5 + new_xlim = gui_event.xdata - scale_factor * (gui_event.xdata - curr_xlim) + new_ylim = gui_event.ydata - scale_factor * (gui_event.ydata - curr_ylim) - new_width = act_width*scale_factor - new_height= act_height*scale_factor - - new_xlim = [max(gui_event.xdata - new_width, self.limits['xlims'][0]), - min(gui_event.xdata + new_width, self.limits['xlims'][1])] - new_ylim = [max(gui_event.ydata - new_height, self.limits['ylims'][0]), - min(gui_event.ydata + new_height, self.limits['ylims'][1])] + new_xlim.sort() + new_xlim[0] = max(new_xlim[0], self.limits['xlims'][0]) + new_xlim[1] = min(new_xlim[1], self.limits['xlims'][1]) + new_ylim.sort() + new_ylim[0] = max(new_ylim[0], self.limits['ylims'][0]) + new_ylim[1] = min(new_ylim[1], self.limits['ylims'][1]) widget.axes.set_xlim(new_xlim) widget.axes.set_ylim(new_ylim) From 492aac831a0fc0cd748aa9c15a68efc8d778c45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 13 Apr 2015 09:27:26 +0200 Subject: [PATCH 0301/1144] Commited to pull changes --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index d97c6890..3ba07d90 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0.0.0-gbe0b +043c-dirty From 7816e6342f3abe3b402381bd1629a0f2822b6ec6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Apr 2015 09:42:17 +0200 Subject: [PATCH 0302/1144] zooming for 3-component window changed now zooming is done by using the mouse wheel bugfix: calculation of the snr corrected --- pylot/core/pick/utils.py | 10 ++++++---- pylot/core/util/widgets.py | 34 +++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 15de2312..e97dab3f 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -293,7 +293,8 @@ def getSNR(X, TSNR, t1): ''' Function to calculate SNR of certain part of seismogram relative to given time (onset) out of given noise and signal windows. A safety gap - between noise and signal part can be set. Returns SNR and SNR [dB]. + between noise and signal part can be set. Returns SNR and SNR [dB] and + noiselevel. :param: X, time series (seismogram) :type: `~obspy.core.stream.Stream` @@ -324,9 +325,10 @@ def getSNR(X, TSNR, t1): return #calculate ratios - noiselevel = np.mean(abs(x[inoise])) - SNR = max(abs(x[isignal])) / noiselevel - SNRdB = 20 * np.log10(SNR) + noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) + signallevel = np.sqrt(np.mean(np.square(x[isignal]))) + SNR = signallevel / noiselevel + SNRdB = 10 * np.log10(SNR) return SNR, SNRdB, noiselevel diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1d62c65e..7124f565 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -25,7 +25,7 @@ from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime from obspy.core.event import Pick, WaveformStreamID from pylot.core.read import FilterOptions -from pylot.core.pick.utils import getSNR +from pylot.core.pick.utils import getSNR, earllatepicker from pylot.core.util.defaults import OUTPUTFORMATS from pylot.core.util import prepTimeAxis, getGlobalTimes @@ -85,7 +85,8 @@ class MPLWidget(FigureCanvas): def setParent(self, parent): self._parent = parent - def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None): + def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, + noiselevel=None): self.axes.cla() self.clearPlotDict() wfstart = getGlobalTimes(wfdata)[0] @@ -100,6 +101,11 @@ class MPLWidget(FigureCanvas): trace.detrend('demean') trace.normalize(trace.data.max() * 2) self.axes.plot(time_ax, trace.data + n, 'k') + if noiselevel is not None: + self.axes.plot([time_ax[0], time_ax[-1]], + [noiselevel, noiselevel], '--k') + self.axes.plot([time_ax[0], time_ax[-1]], + [-noiselevel, -noiselevel], '--k') xlabel = 'seconds since {0}'.format(wfstart) ylabel = '' self.updateWidget(xlabel, ylabel, title) @@ -381,6 +387,11 @@ class PickDlg(QDialog): self.disconnectScrollEvent() self.disconnectMotionEvent() self.reconnectPressEvent(self.setIniPick) + else: + self.cidpress = self.connectPressEvent(self.panPress) + self.cidmotion = self.connectMotionEvent() + self.cidrelease = self.connectReleaseEvent() + self.cidscroll = self.connectScrollEvent() def getComponents(self): return self.components @@ -446,7 +457,7 @@ class PickDlg(QDialog): 'VLRW' : 15. } - result = getSNR(wfdata, (10., 2., 1.5), ini_pick) + result = getSNR(wfdata, (5., .5, 1.), ini_pick) snr = result[0] noiselevel = result[2] * 1.5 @@ -467,19 +478,32 @@ class PickDlg(QDialog): title=self.getStation() + ' picking mode', zoomx=zoomx, - zoomy=zoomy) + zoomy=zoomy, + noiselevel=noiselevel) + self.updateAPD(wfdata) # reset labels self.setPlotLabels() def setPick(self, gui_event): - pick = gui_event.xdata + # setting pick + pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global + channel = self.getChannelID(round(gui_event.ydata)) + + wfdata = self.getAPD().copy().select(channel=channel) + # get earliest and latest possible pick + [epp, lpp, pickerror] = earllatepicker(wfdata, 1.5, (5., .5, 1.), pick, 1) + + # plotting picks ax = self.getPlotWidget().axes ylims = ax.get_ylim() ax.plot([pick, pick], ylims, 'r--') + ax.plot([epp, epp], ylims, 'c--') + ax.plot([lpp, lpp], ylims, 'm--') + self.getPlotWidget().draw() def panPress(self, gui_event): From 4d268da4359695f3a846b3f97aec5a813f2445ed Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Apr 2015 09:52:45 +0200 Subject: [PATCH 0303/1144] suppress output of earllatepicker --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 7124f565..524c768c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -493,7 +493,7 @@ class PickDlg(QDialog): wfdata = self.getAPD().copy().select(channel=channel) # get earliest and latest possible pick - [epp, lpp, pickerror] = earllatepicker(wfdata, 1.5, (5., .5, 1.), pick, 1) + [epp, lpp, pickerror] = earllatepicker(wfdata, 1.5, (5., .5, 1.), pick) # plotting picks ax = self.getPlotWidget().axes From bb84e27e54b3bc54d31809a4f7a2fa96c19e6b75 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 21 Apr 2015 08:14:26 +0200 Subject: [PATCH 0304/1144] implemented a much faster calculation of the zero crossing beside an average calculation over the whole signal window of the dominant period --- pylot/core/pick/utils.py | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index e97dab3f..9cdacb49 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -53,32 +53,22 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): #get signal window isignal = getsignalwin(t, Pick1, TSNR[2]) #calculate noise level - nlevel = max(abs(x[inoise])) * nfac + nlevel = np.sqrt(np.mean(np.square(x[inoise]))) * nfac #get time where signal exceeds nlevel - ilup = np.where(x[isignal] > nlevel) - ildown = np.where(x[isignal] < -nlevel) - if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'earllatepicker: Signal lower than noise level, misspick?' - return - il = min([ilup[0][0], ildown[0][0]]) + ilup, = np.where(x[isignal] > nlevel) + ildown, = np.where(x[isignal] < -nlevel) + if not ilup.size and not ildown.size: + raise ValueError('earllatepicker: Signal lower than noise level') + il = min(np.min(ilup) if ilup.size else float('inf'), + np.min(ildown) if ildown.size else float('inf')) LPick = t[isignal][il] #get earliest possible pick - #get next 2 zero crossings after most likely pick - #initial onset is assumed to be the first zero crossing - zc = [] - zc.append(Pick1) - i = 0 - for j in range(isignal[0][1], isignal[0][len(t[isignal]) - 1]): - i = i + 1 - if x[j - 1] <= 0 and x[j] >= 0: - zc.append(t[isignal][i]) - elif x[j - 1] > 0 and x[j] <= 0: - zc.append(t[isignal][i]) - if len(zc) == 3: - break - #calculate maximum period T0 of signal out of zero crossings - T0 = max(np.diff(zc)) #this is half wave length! + + #determine all zero crossings in signal window + zc = crossings_nonzero_all(x[isignal]) + #calculate mean half period T0 of signal as the average of the + T0 = np.mean(np.diff(zc)) * X[0].stats.delta #this is half wave length! #T0/4 is assumed as time difference between most likely and earliest possible pick! EPick = Pick1 - T0 / 2 @@ -288,6 +278,10 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): return FM +def crossings_nonzero_all(data): + pos = data > 0 + npos = ~pos + return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0] def getSNR(X, TSNR, t1): ''' From 463535f01a9fc297d5b15c1cf938c5045ce25ff0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 21 Apr 2015 08:16:32 +0200 Subject: [PATCH 0305/1144] improved flexibility of the GUI actions (Matplotlib) implementation --- pylot/core/util/widgets.py | 53 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 524c768c..021615ab 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -285,9 +285,9 @@ class PickDlg(QDialog): # connect button press event to an action self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent() - self.cidrelease = self.connectReleaseEvent() - self.cidscroll = self.connectScrollEvent() + self.cidmotion = self.connectMotionEvent(self.panMotion) + self.cidrelease = self.connectReleaseEvent(self.panRelease) + self.cidscroll = self.connectScrollEvent(self.scrollZoom) def setupUi(self): @@ -328,6 +328,7 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.filterAction) _dialtoolbar.addWidget(self.selectPhase) + _dialtoolbar.addAction(self.zoomAction) _innerlayout = QVBoxLayout() @@ -346,39 +347,40 @@ class PickDlg(QDialog): self.setLayout(_outerlayout) def disconnectPressEvent(self): - self.getPlotWidget().mpl_disconnect(self.cidpress) + widget = self.getPlotWidget() + widget.mpl_disconnect(self.cidpress) + self.cidpress = None def connectPressEvent(self, slot): widget = self.getPlotWidget() return widget.mpl_connect('button_press_event', slot) - def reconnectPressEvent(self, slot): - self.disconnectPressEvent() - return self.connectPressEvent(slot) - def disconnectScrollEvent(self): widget = self.getPlotWidget() widget.mpl_disconnect(self.cidscroll) + self.cidscroll = None - def connectScrollEvent(self): + def connectScrollEvent(self, slot): widget = self.getPlotWidget() - return widget.mpl_connect('scroll_event', self.scrollZoom) + return widget.mpl_connect('scroll_event', slot) def disconnectMotionEvent(self): widget = self.getPlotWidget() widget.mpl_disconnect(self.cidmotion) + self.cidmotion = None - def connectMotionEvent(self): + def connectMotionEvent(self, slot): widget = self.getPlotWidget() - return widget.mpl_connect('motion_notify_event', self.panMotion) + return widget.mpl_connect('motion_notify_event', slot) def disconnectReleaseEvent(self): widget = self.getPlotWidget() widget.mpl_disconnect(self.cidrelease) + self.cidrelease = None - def connectReleaseEvent(self): + def connectReleaseEvent(self, slot): widget = self.getPlotWidget() - return widget.mpl_connect('button_release_event', self.panRelease) + return widget.mpl_connect('button_release_event', slot) def verifyPhaseSelection(self): phase = self.selectPhase.currentText() @@ -386,12 +388,13 @@ class PickDlg(QDialog): self.disconnectReleaseEvent() self.disconnectScrollEvent() self.disconnectMotionEvent() - self.reconnectPressEvent(self.setIniPick) + self.disconnectPressEvent() + self.cidpress = self.connectPressEvent(self.setIniPick) else: self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent() - self.cidrelease = self.connectReleaseEvent() - self.cidscroll = self.connectScrollEvent() + self.cidmotion = self.connectMotionEvent(self.panMotion) + self.cidrelease = self.connectReleaseEvent(self.panRelease) + self.cidscroll = self.connectScrollEvent(self.scrollZoom) def getComponents(self): return self.components @@ -438,8 +441,10 @@ class PickDlg(QDialog): wfdata = self.selectWFData(channel) self.disconnectScrollEvent() - - self.cidpress = self.reconnectPressEvent(self.setPick) + self.disconnectPressEvent() + self.disconnectReleaseEvent() + self.disconnectMotionEvent() + self.cidpress = self.connectPressEvent(self.setPick) ini_pick = gui_event.xdata @@ -457,7 +462,7 @@ class PickDlg(QDialog): 'VLRW' : 15. } - result = getSNR(wfdata, (5., .5, 1.), ini_pick) + result = getSNR(wfdata, (5., .5, 2.), ini_pick) snr = result[0] noiselevel = result[2] * 1.5 @@ -493,7 +498,7 @@ class PickDlg(QDialog): wfdata = self.getAPD().copy().select(channel=channel) # get earliest and latest possible pick - [epp, lpp, pickerror] = earllatepicker(wfdata, 1.5, (5., .5, 1.), pick) + [epp, lpp, pickerror] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) # plotting picks ax = self.getPlotWidget().axes @@ -559,9 +564,11 @@ class PickDlg(QDialog): def zoom(self): if self.zoomAction.isChecked(): self.disconnectPressEvent() + self.disconnectMotionEvent() + self.disconnectReleaseEvent() self.figToolBar.zoom() else: - self.connectPressEvent(self.setIniPick) + self.cidpress = self.connectPressEvent(self.setIniPick) def scrollZoom(self, gui_event, factor=2.): From 4b641f1b3c3192d7d0c75bc4e65665c9269d45d7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 21 Apr 2015 08:21:59 +0200 Subject: [PATCH 0306/1144] reversed polarity in picking mode --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 3ba07d90..8ef7b6d4 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -043c-dirty +4635-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 021615ab..0dcea588 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -478,7 +478,7 @@ class PickDlg(QDialog): x_res /= 2 zoomx = [ini_pick - x_res, ini_pick + x_res] - zoomy = [noiselevel * 1.5, -noiselevel * 1.5] + zoomy = [-noiselevel * 1.5, noiselevel * 1.5] self.getPlotWidget().plotWFData(wfdata=wfdata, title=self.getStation() + ' picking mode', From 58aad840c891453051692421160e5a47aa95bb8a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 22 Apr 2015 12:22:34 +0200 Subject: [PATCH 0307/1144] task: read also old autoPILOT parameter files --- pylot/core/read/inputs.py | 43 ++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index ae7f5666..abaa038d 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -5,7 +5,7 @@ class AutoPickParameter(object): ''' AutoPickParameters is a parameter type object capable to read and/or write - parameter ASCII and binary files. + parameter ASCII. :param fn str: Filename of the input file @@ -50,27 +50,32 @@ class AutoPickParameter(object): for line in lines: parspl = line.split('\t')[:2] parFileCont[parspl[0].strip()] = parspl[1] - for key, value in parFileCont.iteritems(): - try: - val = int(value) - except: - try: - val = float(value) - except: - if value.find(';') > 0: - vallist = value.strip().split(';') - val = [] - for val0 in vallist: - val0 = float(val0) - val.append(val0) - else: - val = str(value.strip()) - parFileCont[key] = val else: parFileCont = {} except Exception, e: self._printParameterError(e) - parFileCont = {} + inputFile.seek(0) + lines = inputFile.readlines() + for line in lines: + if not line.startswith(('#', '%', '\n', ' ')): + parspl = line.split('#')[:2] + parFileCont[parspl[1].strip()] = parspl[0].strip() + for key, value in parFileCont.iteritems(): + try: + val = int(value) + except: + try: + val = float(value) + except: + if len(value.split(' ')) > 1: + vallist = value.strip().split(' ') + val = [] + for val0 in vallist: + val0 = float(val0) + val.append(val0) + else: + val = str(value.strip()) + parFileCont[key] = val self.__parameter = parFileCont # Human-readable string representation of the object @@ -129,7 +134,7 @@ class AutoPickParameter(object): print self def _printParameterError(self, errmsg): - print 'ParameterError:\n non-existent parameter %s' % errmsg + print 'ParameterReadError:\n non-existent parameter %s' % errmsg class FilterOptions(object): From ca8081b7aad97289acc898e705b5c4c28b7d4637 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 22 Apr 2015 12:38:10 +0200 Subject: [PATCH 0308/1144] debugging: return the exception text whenever an error occurs - makes debugging much easier --- pylot/core/read/data.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 874c7aac..a67cc8a9 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -3,15 +3,13 @@ import os -import numpy as np from obspy.core import (read, Stream, UTCDateTime) from obspy import readEvents from obspy.core.event import (Event, Catalog) from pylot.core.read import readPILOTEvent - -from pylot.core.util import fnConstructor, createEvent, FormatError, \ - prepTimeAxis, getGlobalTimes +from pylot.core.util import fnConstructor, FormatError, \ + getGlobalTimes class Data(object): @@ -129,8 +127,8 @@ class Data(object): except TypeError: try: self.wfdata += read(fname, format='GSE2') - except Exception: - warnmsg += '{0}\n'.format(fname) + except Exception, e: + warnmsg += '{0}\n{1}\n'.format(fname, e) if warnmsg: warnmsg = 'WARNING: unable to read\n' + warnmsg print warnmsg From 8bf9b5528864d3e3a381b1d22f7d2b84e46298ae Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 22 Apr 2015 12:46:24 +0200 Subject: [PATCH 0309/1144] task: create development frame for autoPyLoT --- autoPyLoT.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 autoPyLoT.py diff --git a/autoPyLoT.py b/autoPyLoT.py new file mode 100644 index 00000000..a073cc7a --- /dev/null +++ b/autoPyLoT.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +import os +import argparse + +from pylot.core.util import _getVersionString +from pylot.core.read import Data, AutoPickParameter + + +__version__ = _getVersionString() + + +def autoPyLoT(fnames, inputfile): + parameter = AutoPickParameter(inputfile) + data = Data() + data.setWFData(fnames) + + print parameter.getParam('algoP') + + print(parameter) + + print(data) + + +if __name__ == "__main__": + # parse arguments + parser = argparse.ArgumentParser( + description='''This program visualizes Probabilistic Power Spectral Densities.''') + + parser.add_argument('-d', '-D', '--data', type=str, action='store', + help='''path or url to the data to be picked''', + default='http://examples.obspy.org/BW.KW1..EHZ.D.2011.037' + ) + parser.add_argument('-i', '-I', '--inputfile', type=str, + action='store', + help='''full path to the file containing the input + parameters for autoPyLoT''', + default=os.path.join([os.path.expanduser('~'), + 'autoPyLoT.in']) + ) + parser.add_argument('-v', '-V', '--version', action='version', + version='autoPyLoT ' + __version__, + help='show version information and exit') + + cla = parser.parse_args() + + autoPyLoT(cla.data, cla.input) From 0760c2fe3a107655141753a385637eda61c35105 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 29 Apr 2015 06:29:08 +0200 Subject: [PATCH 0310/1144] introducing new attribute __name to the datastructure objects. Used in autoPyLoT to distinguish between structure types. --- autoPyLoT.py | 35 +++++++++++++++++++++++++++++++---- pylot/core/read/data.py | 18 +++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index a073cc7a..c3962138 100644 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -7,22 +7,49 @@ import argparse from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter +from pylot.core.pick.CharFuns import HOScf, AICcf +from pylot.core.util.structure import DATASTRUCTURE __version__ = _getVersionString() +METHOD = {'HOS':HOScf, 'AIC':AICcf} def autoPyLoT(fnames, inputfile): + ''' + Determine phase onsets automatically utilizing the automatic picking + algorithm by Kueperkoch et al. 2011. + + :param fnames: list of strings containing the paths or urls to the + waveform data to be picked + :type fnames: list + :param inputfile: path to the input file containing all parameter + information for automatic picking (for formatting details, see. + `~pylot.core.read.input.AutoPickParameter` + :type inputfile: str + :return: + + .. rubric:: Example + + ''' parameter = AutoPickParameter(inputfile) + data = Data() - data.setWFData(fnames) - print parameter.getParam('algoP') + cfP = METHOD[parameter.getParam('algoP')]() - print(parameter) + if parameter.hasParam('datastructure'): + datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() - print(data) + def expandSDS(datastructure): + return datastructure.expandDataPath() + def expandPDS(datastructure): + return os.path.join(datastructure.expandDataPath(),'*') + + dsem = {'PDS':expandPDS, 'SDS':expandSDS} + + expandMethod = dsem[datastructure.getName()] if __name__ == "__main__": # parse arguments diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index a67cc8a9..fe68d3fb 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -155,6 +155,7 @@ class GenericDataStructure(object): def __init__(self, structexp='$R/$D/$E', folderdepth=2, **kwargs): structureOptions = ('$R', '$D', '$E') + self.__name = 'GDS' structExpression = [] depth = 0 while structexp is not os.path.sep: @@ -180,6 +181,9 @@ class GenericDataStructure(object): key = str(key).upper() self.__gdsFields[key] = value + def getName(self): + return self.__name + def getFields(self): return self.__gdsFields @@ -196,7 +200,9 @@ class PilotDataStructure(object): def __init__(self, dataformat='GSE2', fsuffix='gse', root='/data/Egelados/EVENT_DATA/LOCAL/', database='2006.01', **kwargs): - self.dataType = dataformat + self.__name = 'PDS' + self.dataFormat = dataformat + self.dataType = 'waveform' self.__pdsFields = {'ROOT': root, 'DATABASE': database, 'SUFFIX': fsuffix @@ -231,6 +237,9 @@ class PilotDataStructure(object): def getFields(self): return self.__pdsFields + def getName(self): + return self.__name + def expandDataPath(self): datapath = os.path.join(self.getFields()['ROOT'], self.getFields()['DATABASE']) @@ -258,8 +267,8 @@ class SeiscompDataStructure(object): } def __init__(self, dataType='waveform', sdate=None, edate=None, **kwargs): - # imports - from obspy.core import UTCDateTime + + self.__name = 'SDS' def checkDate(date): if not isinstance(date, UTCDateTime): @@ -335,6 +344,9 @@ class SeiscompDataStructure(object): def getFields(self): return self.__sdsFields + def getName(self): + return self.__name + def expandDataPath(self): fullChan = '{0}.{1}'.format(self.getFields()['CHAN'], self.getType()) dataPath = os.path.join(self.getFields()['SDSdir'], From 426c2d0d4cdc725766cc46c2966745c1c06fb7d5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 29 Apr 2015 07:57:52 +0200 Subject: [PATCH 0311/1144] TODO: restructuring the datastructure classes; suggestion: write a Superclass GenericDataStructure which is than inherited by PilotDataStructure and SeiscompDataStructure --- autoPyLoT.py | 6 ++++++ pylot/core/read/data.py | 1 + 2 files changed, 7 insertions(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index c3962138..bd6a3e79 100644 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -41,6 +41,10 @@ def autoPyLoT(fnames, inputfile): if parameter.hasParam('datastructure'): datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() + dsfields = {'root':parameter.rootpath} #see TODO: in data.py + + datastructure.modifyFields(dsfields) + def expandSDS(datastructure): return datastructure.expandDataPath() @@ -51,6 +55,8 @@ def autoPyLoT(fnames, inputfile): expandMethod = dsem[datastructure.getName()] + + if __name__ == "__main__": # parse arguments parser = argparse.ArgumentParser( diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index fe68d3fb..4a78dd91 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -146,6 +146,7 @@ class Data(object): def getEvtData(self): return self.evtdata +#TODO: write superclass DataStructure instead of three fully featured individual classes class GenericDataStructure(object): ''' From 367610d0325c4fb15515f0829822a9c4349aa83a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 4 May 2015 05:25:40 +0200 Subject: [PATCH 0312/1144] made AutoPickParameter class more flexible in reading and handling parameters; export routine defined: exports in the new autoPyLoT.in format (see docstring) --- pylot/core/read/inputs.py | 63 +++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index abaa038d..08dd2b32 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -34,24 +34,29 @@ class AutoPickParameter(object): ========== ========== ======================================= ''' - def __init__(self, fn=None): + def __init__(self, fnin=None, fnout=None, **kwargs): ''' Initialize parameter object: read content of an ASCII file an form a type consistent dictionary contain all parameters. ''' - self.__filename = fn + + self.__filename = fnin parFileCont = {} - try: - if self.__filename is not None: + # read from parsed arguments alternatively + for key, val in kwargs.iteritems(): + parFileCont[key] = val + + if self.__filename is not None: inputFile = open(self.__filename, 'r') - lines = inputFile.readlines() - for line in lines: - parspl = line.split('\t')[:2] - parFileCont[parspl[0].strip()] = parspl[1] - else: - parFileCont = {} + else: + return + try: + lines = inputFile.readlines() + for line in lines: + parspl = line.split('\t')[:2] + parFileCont[parspl[0].strip()] = parspl[1] except Exception, e: self._printParameterError(e) inputFile.seek(0) @@ -78,12 +83,15 @@ class AutoPickParameter(object): parFileCont[key] = val self.__parameter = parFileCont + if fnout: + self.export2File(fnout) + # Human-readable string representation of the object def __str__(self): string = '' string += 'Automated picking parameter:\n\n' if self.__parameter: - for key, value in self.__parameter.iteritems(): + for key, value in self.iteritems(): string += '%s:\t\t%s\n' % (key, value) else: string += 'Empty parameter dictionary.' @@ -112,6 +120,25 @@ class AutoPickParameter(object): def __len__(self): return len(self.__parameter.keys()) + def iteritems(self): + for key, value in self.__parameter.iteritems(): + yield key, value + + def hasParam(self, *args): + + def test(param): + try: + self.__parameter[param] + return True + except KeyError: + return False + + try: + for param in args: + test(param) + except TypeError: + test(args) + def getParam(self, *args): try: for param in args: @@ -127,14 +154,18 @@ class AutoPickParameter(object): def setParam(self, **kwargs): for param, value in kwargs.iteritems(): - try: - self.__setitem__(param, value) - except KeyError, e: - self._printParameterError(e) + self.__setitem__(param, value) print self def _printParameterError(self, errmsg): - print 'ParameterReadError:\n non-existent parameter %s' % errmsg + print 'ParameterError:\n non-existent parameter %s' % errmsg + + def export2File(self, fnout): + fid_out = open(fnout, 'w') + lines = [] + for key, value in self.iteritems(): + lines.append('{key}\t{value}'.format(key=key, value=value)) + fid_out.writelines(lines) class FilterOptions(object): From 15bdc47f84ce2965bcc09e535905620ea03e6958 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 4 May 2015 05:31:10 +0200 Subject: [PATCH 0313/1144] data reading interface not clear yet, major changes have to be done to the data structure and the data reading part of PyLoT (DataStructure superclass is needed providing generalized methods to use by the Data object) --- autoPyLoT.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index bd6a3e79..36cbf696 100644 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -36,26 +36,34 @@ def autoPyLoT(fnames, inputfile): data = Data() - cfP = METHOD[parameter.getParam('algoP')]() + meth = parameter.getParam('algoP') + tsnr1 = parameter.getParam('tsnr1') + tsnr2 = parameter.getParam('tsnr2') + tnoise = parameter.getParam('pnoiselen') + tsignal = parameter.getParam('tlim') + order = parameter.getParam('hosorder') + thosmw = parameter.getParam('tlta') if parameter.hasParam('datastructure'): datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() + dsfields = {'root':parameter.getParam('rootpath')} #see TODO: in data.py + datastructure.modifyFields(dsfields) - dsfields = {'root':parameter.rootpath} #see TODO: in data.py + def expandSDS(datastructure): + return datastructure.expandDataPath() - datastructure.modifyFields(dsfields) + def expandPDS(datastructure): + return os.path.join(datastructure.expandDataPath(),'*') - def expandSDS(datastructure): - return datastructure.expandDataPath() + dsem = {'PDS':expandPDS, 'SDS':expandSDS} - def expandPDS(datastructure): - return os.path.join(datastructure.expandDataPath(),'*') - - dsem = {'PDS':expandPDS, 'SDS':expandSDS} - - expandMethod = dsem[datastructure.getName()] + expandMethod = dsem[datastructure.getName()] + data.setWFData(expandMethod()) + else: + data.setWFData() + cfP = METHOD[meth](data.getWFData(), (tnoise, tsignal), thosmw, order) if __name__ == "__main__": # parse arguments @@ -70,8 +78,8 @@ if __name__ == "__main__": action='store', help='''full path to the file containing the input parameters for autoPyLoT''', - default=os.path.join([os.path.expanduser('~'), - 'autoPyLoT.in']) + default=os.path.join(os.path.expanduser('~'), '.pylot', + 'autoPyLoT.in') ) parser.add_argument('-v', '-V', '--version', action='version', version='autoPyLoT ' + __version__, @@ -79,4 +87,4 @@ if __name__ == "__main__": cla = parser.parse_args() - autoPyLoT(cla.data, cla.input) + autoPyLoT(cla.data, str(cla.inputfile)) From 7cab62b97eb2c0edf53e8ee7c933dd6fd412061d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 4 May 2015 12:05:14 +0200 Subject: [PATCH 0314/1144] release version committed to avoid conflicting files while update --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 8ef7b6d4..22818ef8 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -4635-dirty +694a-dirty From c4b6b4c6486654d2e04ec8646beb908eff2884fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 4 May 2015 12:11:32 +0200 Subject: [PATCH 0315/1144] Changed permitions. --- autoPyLoT.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 autoPyLoT.py diff --git a/autoPyLoT.py b/autoPyLoT.py old mode 100644 new mode 100755 From f177901883d732a510f74c61daaf3a7a09cf96b9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 11 May 2015 13:41:08 +0200 Subject: [PATCH 0316/1144] rearranging methods to provide a superclass to all other structure classes --- pylot/core/read/data.py | 148 +++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 71 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 4a78dd91..1c403237 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -154,42 +154,61 @@ class GenericDataStructure(object): base working on. ''' - def __init__(self, structexp='$R/$D/$E', folderdepth=2, **kwargs): - structureOptions = ('$R', '$D', '$E') - self.__name = 'GDS' - structExpression = [] - depth = 0 - while structexp is not os.path.sep: - try: - [structexp, tlexp] = os.path.split(structexp) - except AttributeError: - rootExpression = None - structExpression = None - break - structExpression.append(tlexp) - depth += 1 - if depth is folderdepth: - rootExpression = structexp - break - structExpression.reverse() + def __init__(self, rootpath='/data', dataformat=None, filesuffix=None, + **kwargs): + self.__allowExtraFields = True + self.__expandFields = ['root'] + self.__dsFields = {'root': rootpath} - self.folderDepth = folderdepth - self.__gdsFields = {'ROOT': rootExpression} self.modifyFields(**kwargs) def modifyFields(self, **kwargs): - for key, value in kwargs.iteritems(): - key = str(key).upper() - self.__gdsFields[key] = value - def getName(self): - return self.__name + assert isinstance(kwargs, dict), 'dictionary type object expected' + + for key, value in kwargs.iteritems(): + key = str(key).lower() + if type(value) not in (str, int, float): + for n, val in enumerate(value): + value[n] = str(val) + else: + value = str(value) + try: + self.setFieldValue(key, value) + except KeyError, e: + errmsg = '' + errmsg += 'WARNING:\n' + errmsg += 'unable to set values for datastructure fields\n' + errmsg += '%s; desired value was: %s\n' % (e, value) + print errmsg + + def isField(self, key): + return key in self.getFields().keys() + + def getFieldValue(self, key): + if self.isField(key): + return self.getFields()[key] + else: + return + + def setFieldValue(self, key, value): + if self.isField(key): + self.getFields()[key] = value + else: + print('Warning: trying to set value of non-existent field ' + '{field}'.format(field=key)) def getFields(self): - return self.__gdsFields + return self.__dsFields + + def getExpandFields(self): + return self.__expandFields def expandDataPath(self): - return os.path.join(*self.getFields().values()) + expandList = [] + for item in self.getExpandFields(): + expandList.append(self.getFieldValue(item)) + return os.path.join(*self.getExpandFields()) class PilotDataStructure(object): @@ -204,14 +223,14 @@ class PilotDataStructure(object): self.__name = 'PDS' self.dataFormat = dataformat self.dataType = 'waveform' - self.__pdsFields = {'ROOT': root, - 'DATABASE': database, - 'SUFFIX': fsuffix + self.__pdsFields = {'root': root, + 'database': database, + 'suffix': fsuffix } - self.modifiyFields(**kwargs) + self.modifyFields(**kwargs) - def modifiyFields(self, **kwargs): + def modifyFields(self, **kwargs): if kwargs and isinstance(kwargs, dict): for key, value in kwargs.iteritems(): key = str(key) @@ -247,7 +266,7 @@ class PilotDataStructure(object): return datapath -class SeiscompDataStructure(object): +class SeiscompDataStructure(GenericDataStructure): ''' Dictionary containing the data access information for an SDS data archive: @@ -257,35 +276,16 @@ class SeiscompDataStructure(object): :type sdate, edate: str or UTCDateTime or None ''' - # Data type options - __typeOptions = {'waveform': 'D', # Waveform data - 'detect': 'E', # Detection data - 'log': 'L', # Log data - 'timing': 'T', # Timing data - 'calib': 'C', # Calibration data - 'resp': 'R', # Response data - 'opaque': 'O' # Opaque data - } + def __init__(self, rootpath='/data/SDS', dataformat='MSEED', + filesuffix=None, **kwargs): + super(GenericDataStructure, self).__init__(rootpath, dataformat, + filesuffix, **kwargs) - def __init__(self, dataType='waveform', sdate=None, edate=None, **kwargs): - self.__name = 'SDS' - - def checkDate(date): - if not isinstance(date, UTCDateTime): - return True - return False - - try: - if checkDate(sdate): - sdate = UTCDateTime(sdate) - if checkDate(edate): - edate = UTCDateTime(edate) - except TypeError: - edate = UTCDateTime() - halfyear = UTCDateTime('1970-07-01') - sdate = UTCDateTime(edate - halfyear) - del halfyear + edate = UTCDateTime() + halfyear = UTCDateTime('1970-07-01') + sdate = UTCDateTime(edate - halfyear) + del halfyear year = '' if not edate.year == sdate.year: @@ -296,13 +296,6 @@ class SeiscompDataStructure(object): else: year = '{0:04d}'.format(sdate.year) - if dataType in self.__typeOptions.keys(): - self.dataType = dataType - else: - self.dataType = 'waveform' # default value for dataType - print '''Warning: Selected datatype ('%s') not available.\n - Using 'waveform' instead!'''.format(dataType) - # SDS fields' default values # definitions from # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS @@ -328,10 +321,7 @@ class SeiscompDataStructure(object): else: value = str(value) try: - if key in self.getFields().keys(): - self.getFields()[key] = value - else: - raise KeyError('unknown SDS wildcard: %s.' % key) + self.setField(key, value) except KeyError, e: errmsg = '' errmsg += 'WARNING:\n' @@ -342,6 +332,22 @@ class SeiscompDataStructure(object): def getType(self): return self.__typeOptions[self.dataType] + def isField(self, key): + return key in self.getFields().keys() + + def getFieldValue(self, key): + if self.isField(key): + return self.getFields()[key] + else: + return + + def setFieldValue(self, key, value): + if self.isField(key): + self.getFields()[key] = value + else: + print('Warning: trying to set value of non-existent field ' + '{field}'.format(field=key)) + def getFields(self): return self.__sdsFields From 56a0563709224af74f799fa1e4409ab77b3db974 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 18 May 2015 13:12:12 +0200 Subject: [PATCH 0317/1144] implementation of PilotDataStructure as subclass to GenericDataStructure --- pylot/core/read/data.py | 149 ++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 84 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 1c403237..5ea1dfe2 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -154,11 +154,11 @@ class GenericDataStructure(object): base working on. ''' - def __init__(self, rootpath='/data', dataformat=None, filesuffix=None, - **kwargs): - self.__allowExtraFields = True - self.__expandFields = ['root'] - self.__dsFields = {'root': rootpath} + def __init__(self, **kwargs): + + self.allowedFields = [] + self.expandFields = ['root'] + self.dsFields = {} self.modifyFields(**kwargs) @@ -166,13 +166,17 @@ class GenericDataStructure(object): assert isinstance(kwargs, dict), 'dictionary type object expected' + if not self.extraAllowed(): + kwargs = self.updateNotAllowed(kwargs) + for key, value in kwargs.iteritems(): key = str(key).lower() - if type(value) not in (str, int, float): - for n, val in enumerate(value): - value[n] = str(val) - else: - value = str(value) + if value is not None: + if type(value) not in (str, int, float): + for n, val in enumerate(value): + value[n] = str(val) + else: + value = str(value) try: self.setFieldValue(key, value) except KeyError, e: @@ -192,78 +196,73 @@ class GenericDataStructure(object): return def setFieldValue(self, key, value): - if self.isField(key): - self.getFields()[key] = value + if not self.extraAllowed() and key not in self.getAllowed(): + raise KeyError else: - print('Warning: trying to set value of non-existent field ' - '{field}'.format(field=key)) + if not self.isField(key): + print 'creating new field "%s"' % key + self.getFields()[key] = value def getFields(self): - return self.__dsFields + return self.dsFields def getExpandFields(self): - return self.__expandFields + return self.expandFields + + def setExpandFields(self, keys): + expandFields = [] + for key in keys: + if self.isField(key): + expandFields.append(key) + self.expandFields = expandFields + + def getAllowed(self): + return self.allowedFields + + def extraAllowed(self): + return not self.allowedFields + + def updateNotAllowed(self, kwargs): + for key in kwargs: + if key not in self.getAllowed(): + kwargs.__delitem__(key) + return kwargs + + def hasSuffix(self): + try: + self.getFieldValue('suffix') + except KeyError: + return False + else: + if self.getFieldValue('suffix'): + return True + return False def expandDataPath(self): expandList = [] for item in self.getExpandFields(): expandList.append(self.getFieldValue(item)) - return os.path.join(*self.getExpandFields()) + if self.hasSuffix(): + expandList.append('*%s' % self.getFieldValue('suffix')) + return os.path.join(*expandList) -class PilotDataStructure(object): +class PilotDataStructure(GenericDataStructure): ''' Object containing the data access information for the old PILOT data structure. ''' - def __init__(self, dataformat='GSE2', fsuffix='gse', - root='/data/Egelados/EVENT_DATA/LOCAL/', database='2006.01', - **kwargs): - self.__name = 'PDS' - self.dataFormat = dataformat - self.dataType = 'waveform' - self.__pdsFields = {'root': root, - 'database': database, - 'suffix': fsuffix - } + def __init__(self, **fields): - self.modifyFields(**kwargs) + if not fields: + fields = {'database':'2006.01', + 'root':'/data/Egelados/EVENT_DATA/LOCAL', 'suffix':'gse', + 'format':'GSE2'} - def modifyFields(self, **kwargs): - if kwargs and isinstance(kwargs, dict): - for key, value in kwargs.iteritems(): - key = str(key) - if type(value) not in (str, int, float): - for n, val in enumerate(value): - value[n] = str(val) - else: - value = str(value) - try: - if key in self.getFields().keys(): - self.getFields()[key] = value - else: - raise KeyError('unknown PDS wildcard: %s.' % key) - except KeyError, e: - errmsg = '' - errmsg += 'WARNING:\n' - errmsg += 'unable to set values for PDS fields\n' - errmsg += '%s; desired value was: %s\n' % (e, value) - print errmsg + GenericDataStructure.__init__(self, **fields) - def getType(self): - return self.dataType - - def getFields(self): - return self.__pdsFields - - def getName(self): - return self.__name - - def expandDataPath(self): - datapath = os.path.join(self.getFields()['ROOT'], - self.getFields()['DATABASE']) - return datapath + self.setExpandFields(['root','database']) class SeiscompDataStructure(GenericDataStructure): @@ -278,8 +277,7 @@ class SeiscompDataStructure(GenericDataStructure): def __init__(self, rootpath='/data/SDS', dataformat='MSEED', filesuffix=None, **kwargs): - super(GenericDataStructure, self).__init__(rootpath, dataformat, - filesuffix, **kwargs) + super(GenericDataStructure, self).__init__() edate = UTCDateTime() @@ -300,14 +298,9 @@ class SeiscompDataStructure(GenericDataStructure): # definitions from # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS - self.__sdsFields = {'SDSdir': '/data/SDS', # base directory - 'YEAR': year, # 4 digits - 'NET': '??', # up to 8 characters - 'STA': '????', # up to 8 characters - 'CHAN': 'HH?', # up to 8 characters - 'TYPE': self.getType(), # 1 character - 'LOC': '', # up to 8 characters - 'DAY': '{0:03d}'.format(sdate.julday) # 3 digits + self.dsFields = {'root': '/data/SDS', 'YEAR': year, 'NET': '??', + 'STA': '????', 'CHAN': 'HH?', 'TYPE': 'D', 'LOC': '', + 'DAY': '{0:03d}'.format(sdate.julday) } self.modifiyFields(**kwargs) @@ -329,18 +322,6 @@ class SeiscompDataStructure(GenericDataStructure): errmsg += '%s; desired value was: %s\n' % (e, value) print errmsg - def getType(self): - return self.__typeOptions[self.dataType] - - def isField(self, key): - return key in self.getFields().keys() - - def getFieldValue(self, key): - if self.isField(key): - return self.getFields()[key] - else: - return - def setFieldValue(self, key, value): if self.isField(key): self.getFields()[key] = value From 5badf2ba34d09b7f1bac6967ff80fee33b344dc3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 09:38:25 +0200 Subject: [PATCH 0318/1144] implementation of different data structure type handling --- autoPyLoT.py | 33 +++++++++++++++++++-------------- pylot/core/read/data.py | 3 +++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 36cbf696..d10f1853 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -4,6 +4,7 @@ import os import argparse +import glob from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter @@ -32,10 +33,15 @@ def autoPyLoT(fnames, inputfile): .. rubric:: Example ''' + + # reading parameter file + parameter = AutoPickParameter(inputfile) data = Data() + # declaring parameter variables (only for convenience) + meth = parameter.getParam('algoP') tsnr1 = parameter.getParam('tsnr1') tsnr2 = parameter.getParam('tsnr2') @@ -44,26 +50,25 @@ def autoPyLoT(fnames, inputfile): order = parameter.getParam('hosorder') thosmw = parameter.getParam('tlta') + # getting information on data structure + if parameter.hasParam('datastructure'): datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() - dsfields = {'root':parameter.getParam('rootpath')} #see TODO: in data.py - datastructure.modifyFields(dsfields) + dsfields = {'root':parameter.getParam('rootpath'), + 'dpath':parameter.getParam('datapath'), + 'dbase':parameter.getParam('database')} - def expandSDS(datastructure): - return datastructure.expandDataPath() + datastructure.modifyFields(**dsfields) - def expandPDS(datastructure): - return os.path.join(datastructure.expandDataPath(),'*') + datastructure.setExpandFields(['root', 'dpath', 'dbase']) - dsem = {'PDS':expandPDS, 'SDS':expandSDS} + # process each event in database + datapath = datastructure.expandDataPath() + for event in glob.glob(datapath): + data.setWFData(os.path.join(datapath, event, '*')) + print data - expandMethod = dsem[datastructure.getName()] - - data.setWFData(expandMethod()) - else: - data.setWFData() - - cfP = METHOD[meth](data.getWFData(), (tnoise, tsignal), thosmw, order) + cfP = METHOD[meth](data.getWFData(), (tnoise, tsignal), thosmw, order) if __name__ == "__main__": # parse arguments diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 5ea1dfe2..126310d5 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -50,6 +50,9 @@ class Data(object): self.cuttimes = None self.dirty = False + def __str__(self): + return str(self.evtdata), '\n', str(self.wfdata) + def getParent(self): return self._parent From 4994168247975b148f4e352557e17ec62cbe6a02 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 09:43:32 +0200 Subject: [PATCH 0319/1144] number of arguments changed, filenames not necessary for starting --- autoPyLoT.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index d10f1853..9177cca1 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -16,14 +16,11 @@ __version__ = _getVersionString() METHOD = {'HOS':HOScf, 'AIC':AICcf} -def autoPyLoT(fnames, inputfile): +def autoPyLoT(inputfile): ''' Determine phase onsets automatically utilizing the automatic picking algorithm by Kueperkoch et al. 2011. - :param fnames: list of strings containing the paths or urls to the - waveform data to be picked - :type fnames: list :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. `~pylot.core.read.input.AutoPickParameter` From b46f085a745f0ad3e6124252cd1fcf100d7ee01f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 09:59:06 +0200 Subject: [PATCH 0320/1144] added option to process single event --- autoPyLoT.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 9177cca1..5fd9975d 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -55,15 +55,23 @@ def autoPyLoT(inputfile): 'dpath':parameter.getParam('datapath'), 'dbase':parameter.getParam('database')} + exf = ['root', 'dpath', 'dbase'] + + if parameter.hasParam('eventID'): + dsfields['eventID'] = parameter.getParam('eventID') + exf.append('eventID') datastructure.modifyFields(**dsfields) - datastructure.setExpandFields(['root', 'dpath', 'dbase']) + datastructure.setExpandFields(exf) # process each event in database datapath = datastructure.expandDataPath() - for event in glob.glob(datapath): - data.setWFData(os.path.join(datapath, event, '*')) - print data + if not parameter.hasParam('eventID'): + for event in glob.glob(datapath): + data.setWFData(os.path.join(datapath, event, '*')) + print data + else: + data.setWFData(os.path.join(datapath, '*')) cfP = METHOD[meth](data.getWFData(), (tnoise, tsignal), thosmw, order) From 82540c50af642171bbfdffb5c861ceab5172ba1b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 10:01:19 +0200 Subject: [PATCH 0321/1144] changed argument parser and glob call to search directory --- autoPyLoT.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 5fd9975d..a3e82aa3 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -67,23 +67,18 @@ def autoPyLoT(inputfile): # process each event in database datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): - for event in glob.glob(datapath): + for event in glob.glob(os.path.join(datapath, '*')): data.setWFData(os.path.join(datapath, event, '*')) print data else: data.setWFData(os.path.join(datapath, '*')) - cfP = METHOD[meth](data.getWFData(), (tnoise, tsignal), thosmw, order) if __name__ == "__main__": # parse arguments parser = argparse.ArgumentParser( description='''This program visualizes Probabilistic Power Spectral Densities.''') - parser.add_argument('-d', '-D', '--data', type=str, action='store', - help='''path or url to the data to be picked''', - default='http://examples.obspy.org/BW.KW1..EHZ.D.2011.037' - ) parser.add_argument('-i', '-I', '--inputfile', type=str, action='store', help='''full path to the file containing the input @@ -97,4 +92,4 @@ if __name__ == "__main__": cla = parser.parse_args() - autoPyLoT(cla.data, str(cla.inputfile)) + autoPyLoT(str(cla.inputfile)) From 5f8b6de1ec003dcc45d5a0946ae29d1b33ced892 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 10:23:47 +0200 Subject: [PATCH 0322/1144] bugfix: method hasParam did not return any value --- pylot/core/read/inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 08dd2b32..740bd15a 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -135,9 +135,9 @@ class AutoPickParameter(object): try: for param in args: - test(param) + return test(param) except TypeError: - test(args) + return test(args) def getParam(self, *args): try: From 9a4677c7945ae51661a979ceef819afeb1a6cf7e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 10:33:59 +0200 Subject: [PATCH 0323/1144] bugfix: deleted default value for suffix --- pylot/core/read/data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 126310d5..c571e3df 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -260,8 +260,7 @@ class PilotDataStructure(GenericDataStructure): if not fields: fields = {'database':'2006.01', - 'root':'/data/Egelados/EVENT_DATA/LOCAL', 'suffix':'gse', - 'format':'GSE2'} + 'root':'/data/Egelados/EVENT_DATA/LOCAL'} GenericDataStructure.__init__(self, **fields) From b588c29513af04f8f2ece4d5d89811039e2759bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 20 May 2015 10:56:53 +0200 Subject: [PATCH 0324/1144] Dirty revision --- autoPyLoT.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index a3e82aa3..38b273bc 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -34,7 +34,7 @@ def autoPyLoT(inputfile): # reading parameter file parameter = AutoPickParameter(inputfile) - + data = Data() # declaring parameter variables (only for convenience) @@ -48,7 +48,6 @@ def autoPyLoT(inputfile): thosmw = parameter.getParam('tlta') # getting information on data structure - if parameter.hasParam('datastructure'): datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() dsfields = {'root':parameter.getParam('rootpath'), @@ -64,20 +63,23 @@ def autoPyLoT(inputfile): datastructure.setExpandFields(exf) + # process each event in database # process each event in database datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): - for event in glob.glob(os.path.join(datapath, '*')): +- #for event in glob.glob(os.path.join(datapath, '*')): ++ for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: data.setWFData(os.path.join(datapath, event, '*')) print data else: - data.setWFData(os.path.join(datapath, '*')) + data.setWFData([os.path.join(datapath, '*')]) + print data if __name__ == "__main__": # parse arguments parser = argparse.ArgumentParser( - description='''This program visualizes Probabilistic Power Spectral Densities.''') + description='''This program ''') parser.add_argument('-i', '-I', '--inputfile', type=str, action='store', From bd10f7b61ebf13d185d4d722cc2ba3e0ec9d13a4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 10:58:48 +0200 Subject: [PATCH 0325/1144] bugfix: searching event directory --- autoPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index a3e82aa3..c3e3dfcb 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -67,7 +67,7 @@ def autoPyLoT(inputfile): # process each event in database datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): - for event in glob.glob(os.path.join(datapath, '*')): + for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: data.setWFData(os.path.join(datapath, event, '*')) print data else: From 6dc0b206e9e0215688941366bbab527288d136d1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 11:20:37 +0200 Subject: [PATCH 0326/1144] changed string representation of data object --- pylot/core/read/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index c571e3df..62505a03 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -51,7 +51,7 @@ class Data(object): self.dirty = False def __str__(self): - return str(self.evtdata), '\n', str(self.wfdata) + return str(self.wfdata) def getParent(self): return self._parent From b9ce5b4f41c41e0f9ccfe1df12348a8e5b05a545 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 20 May 2015 11:29:55 +0200 Subject: [PATCH 0327/1144] try to read lists of files not wildcards --- autoPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 560453e8..9128cfb1 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -69,10 +69,10 @@ def autoPyLoT(inputfile): datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: - data.setWFData([os.path.join(datapath, event, '*')]) + data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) print data else: - data.setWFData([os.path.join(datapath, '*')]) + data.setWFData(glob.glob(os.path.join(datapath, '*'))) print data From a239de26e8894aad0032fbdc0decc01e67b19c51 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 27 May 2015 08:48:07 +0200 Subject: [PATCH 0328/1144] bugfix: eventID is now appended to the path in autoPyLoT if eventID is given in autoPyLoT.in --- autoPyLoT.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 9128cfb1..a2c83d8c 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -68,11 +68,15 @@ def autoPyLoT(inputfile): # process each event in database datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): - for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: + for event in [events for events in + glob.glob(os.path.join(datapath, '*')) + if os.path.isdir(events)]: data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) print data else: - data.setWFData(glob.glob(os.path.join(datapath, '*'))) + data.setWFData(glob.glob(os.path.join(datapath, + parameter.getParam('eventID'), + '*'))) print data From caa0dce637dac45d2094344a27579b88141a2c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 27 May 2015 09:36:12 +0200 Subject: [PATCH 0329/1144] Adopted zero-crossing plotting to output of new function for calculating zero-crossings. --- pylot/core/pick/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9cdacb49..b3d3656b 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -11,6 +11,7 @@ import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream +import pdb def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): @@ -84,7 +85,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): p2, = plt.plot(t[inoise], x[inoise]) p3, = plt.plot(t[isignal], x[isignal], 'r') p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) + p5, = plt.plot(t[isignal[0][zc]], np.zeros(len(zc)), '*g', markersize=14) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ From 2da7861b6f6e65b802bf0a2d96b0525df3d65fdb Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 28 May 2015 11:54:16 +0200 Subject: [PATCH 0330/1144] todo mark deleted --- pylot/core/read/data.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 62505a03..5790ac3f 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -149,8 +149,6 @@ class Data(object): def getEvtData(self): return self.evtdata -#TODO: write superclass DataStructure instead of three fully featured individual classes - class GenericDataStructure(object): ''' GenericDataBase type holds all information about the current data- From c86adb4e6e7fec4c7d9843ae1c1d96bb65a219ec Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 28 May 2015 12:14:16 +0200 Subject: [PATCH 0331/1144] preparing storing the picks and making the GUI work more intuitively --- pylot/core/util/widgets.py | 65 +++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0dcea588..15c6a11c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -344,6 +344,10 @@ class PickDlg(QDialog): self.selectPhase.currentIndexChanged.connect(self.verifyPhaseSelection) + _buttonbox.accepted.connect(self.accept) + _buttonbox.rejected.connect(self.reject) + _buttonbox.clicked(QDialogButtonBox.Apply).connect(self.apply) + self.setLayout(_outerlayout) def disconnectPressEvent(self): @@ -486,6 +490,8 @@ class PickDlg(QDialog): zoomy=zoomy, noiselevel=noiselevel) + self.zoomAction.enabled = False + self.updateAPD(wfdata) # reset labels @@ -500,12 +506,66 @@ class PickDlg(QDialog): # get earliest and latest possible pick [epp, lpp, pickerror] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) + # get name of phase actually picked + phase = self.selectPhase.currentText() + + # save pick times for actual phase + phasepicks = {} + + phasepicks['epp'] = epp + phasepicks['lpp'] = lpp + phasepicks['mpp'] = pick + phasepicks['spe'] = pickerror + + try: + oldphasepick = self.picks[phase] + except KeyError: + self.picks[phase] = phasepicks + else: + self.picks[phase] = phasepicks + oepp = oldphasepick['epp'] + ompp = oldphasepick['mpp'] + olpp = oldphasepick['lpp'] + msg = """Warning old phase information for phase {phase} has been + altered.\n + New phase times:\n + earliest possible pick: {epp}\n + most probable pick: {mpp}\n + latest possible pick: {lpp}\n + \n + Old phase times (overwritten):\n + earliest possible pick: {oepp}\n + most probable pick: {ompp}\n + latest possible pick: {olpp}\n""".format(phase=phase, + epp=epp, + mpp=pick, + lpp=lpp, + oepp=oepp, + ompp=ompp, + olpp=olpp) + self.drawPicks(phase) + self.disconnectPressEvent() + self.selectPhase.setCurrentIndex(-1) + + self.cidpress = self.connectPressEvent(self.panPress) + self.cidmotion = self.connectMotionEvent(self.panMotion) + self.cidrelease = self.connectReleaseEvent(self.panRelease) + self.cidscroll = self.connectScrollEvent(self.scrollZoom) + + + def drawPicks(self, phase): # plotting picks ax = self.getPlotWidget().axes ylims = ax.get_ylim() - ax.plot([pick, pick], ylims, 'r--') + picks = self.getPicks() + + mpp = picks['mpp'] + epp = picks['lpp'] + lpp = picks['epp'] + + ax.plot([mpp, mpp], ylims, 'r--') ax.plot([epp, epp], ylims, 'c--') ax.plot([lpp, lpp], ylims, 'm--') @@ -606,9 +666,6 @@ class PickDlg(QDialog): for pick in picks: print pick - def reject(self): - QDialog.reject(self) - def accept(self): self.apply() QDialog.accept(self) From 6c73e30754a1beb1d83894b24db327d78cbc81a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 29 May 2015 16:26:31 +0200 Subject: [PATCH 0332/1144] Modified to run new function run_autopicking. --- autoPyLoT.py | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index a2c83d8c..af1e8423 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -6,15 +6,17 @@ import os import argparse import glob +import matplotlib.pyplot as plt +from obspy.core import read from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter -from pylot.core.pick.CharFuns import HOScf, AICcf +from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE __version__ = _getVersionString() -METHOD = {'HOS':HOScf, 'AIC':AICcf} +#METHOD = {'HOS':HOScf, 'AIC':AICcf} def autoPyLoT(inputfile): ''' @@ -37,16 +39,6 @@ def autoPyLoT(inputfile): data = Data() - # declaring parameter variables (only for convenience) - - meth = parameter.getParam('algoP') - tsnr1 = parameter.getParam('tsnr1') - tsnr2 = parameter.getParam('tsnr2') - tnoise = parameter.getParam('pnoiselen') - tsignal = parameter.getParam('tlim') - order = parameter.getParam('hosorder') - thosmw = parameter.getParam('tlta') - # getting information on data structure if parameter.hasParam('datastructure'): @@ -60,30 +52,38 @@ def autoPyLoT(inputfile): if parameter.hasParam('eventID'): dsfields['eventID'] = parameter.getParam('eventID') exf.append('eventID') - datastructure.modifyFields(**dsfields) + datastructure.modifyFields(**dsfields) datastructure.setExpandFields(exf) - # process each event in database - # process each event in database + # get streams + # read each event in database datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): - for event in [events for events in - glob.glob(os.path.join(datapath, '*')) - if os.path.isdir(events)]: + for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: + print 'Reading %s' %event data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) - print data + #for single event processing else: - data.setWFData(glob.glob(os.path.join(datapath, - parameter.getParam('eventID'), - '*'))) + data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) print data + + wfdat = data.getWFData() # all available streams + ########################################################## + # !automated picking starts here! + for i in range(len(wfdat) - 2): + stationID = wfdat[i].stats.station + #find corresponding streams + statdat = wfdat.select(station=stationID) + run_autopicking(statdat, parameter) + if __name__ == "__main__": # parse arguments parser = argparse.ArgumentParser( - description='''This program ''') + description='''autoPyLoT automatically picks phase onset times using higher order statistics, + autoregressive prediction and AIC''') parser.add_argument('-i', '-I', '--inputfile', type=str, action='store', From 0b6b7a7aeddc5ebe178214d8147efa01f81131dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 29 May 2015 16:28:50 +0200 Subject: [PATCH 0333/1144] Marginal changes only. --- pylot/core/pick/utils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index b3d3656b..1e190ca2 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -11,7 +11,6 @@ import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream -import pdb def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): @@ -79,8 +78,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): diffti_te = Pick1 - EPick PickError = (diffti_te + 2 * diffti_tl) / 3 - if iplot is not None: - plt.figure(iplot) + if iplot > 1: + p = plt.figure(iplot) p1, = plt.plot(t, x, 'k') p2, = plt.plot(t[inoise], x[inoise]) p3, = plt.plot(t[isignal], x[isignal], 'r') @@ -107,7 +106,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): X[0].stats.station) plt.show() raw_input() - plt.close(iplot) + plt.close(p) return EPick, LPick, PickError @@ -238,7 +237,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): elif P1[0] > 0 and P2[0] <= 0: FM = '+' - if iplot is not None: + if iplot > 1: plt.figure(iplot) plt.subplot(2, 1, 1) plt.plot(t, xraw, 'k') From 1c71286fd896d6e109cd58f7ace35e06c8323b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 29 May 2015 16:30:12 +0200 Subject: [PATCH 0334/1144] Modified picking parameter input file for new function run_autopicking. --- autoPyLoT.in | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 autoPyLoT.in diff --git a/autoPyLoT.in b/autoPyLoT.in new file mode 100644 index 00000000..37a5a1eb --- /dev/null +++ b/autoPyLoT.in @@ -0,0 +1,95 @@ +%This is a parameter input file for autoPyLoT. +%All main and special settings regarding data handling +%and picking are to be set here! +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#main settings# +/DATA/Insheim #rootpath# %project path +EVENT_DATA/LOCAL #datapath# %data path +2013.02_Insheim #database# %name of data base +e0019.048.13 #eventID# %certain evnt ID for processing +PILOT #datastructure# %choose data structure +2 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything +AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file +AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file +AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities +HYPOSAT #locrt# %location routine used ("HYPOINVERSE" or "HYPOSAT") +6 #pmin# %minimum required P picks for location +4 #p0min# %minimum required P picks for location if at least + %3 excellent P picks are found +2 #smin# %minimum required S picks for location +/home/ludger/bin/run_HYPOSAT4autoPILOT.csh #cshellp# %path and name of c-shell script to run location routine +7.6 8.5 #blon# %longitude bounding for location map +49 49.4 #blat# %lattitude bounding for location map +#parameters for moment magnitude estimation# +5000 #vp# %average P-wave velocity +2800 #vs# %average S-wave velocity +2200 #rho# %rock density [kg/m^3] +300 #Qp# %quality factor for P waves +100 #Qs# %quality factor for S waves +#common settings picker# +15 #pstart# %start time [s] for calculating CF for P-picking +40 #pstop# %end time [s] for calculating CF for P-picking +-1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking +7 #sstop# %end time [s] after P-onset for calculating CF for S-picking +2 20 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] +2 30 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] +2 15 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] +2 20 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz] +#special settings for calculating CF# +%!!Be careful when editing the following!! +#Z-component# +HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3) +7 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s] +4 #hosorder# %for HOS-picker, order of Higher Order Statistics +2 #Parorder# %for AR-picker, order of AR process of Z-component +1.2 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick +0.4 #tpred1z# %for AR-picker, length of AR prediction window [s] for Z-component, 1st pick +0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick +0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick +0.001 #addnoise# %add noise to seismogram for stable AR prediction +3 0.1 0.5 0.1 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +3 #pickwinP# %for initial AIC pick, length of P-pick window [s] +8 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) +0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) +0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] +0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] +0.001 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P) +1.3 #nfacP# %for HOS/AR, noise factor for noise level determination (P) +#H-components# +ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3) +0.8 #tdet1h# %for HOS/AR, length of AR-determination window [s], H-components, 1st pick +0.4 #tpred1h# %for HOS/AR, length of AR-prediction window [s], H-components, 1st pick +0.6 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick +0.3 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick +4 #Sarorder# %for AR-picker, order of AR process of H-components +6 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) +3 #pickwinS# %for initial AIC pick, length of S-pick window [s] +2 0.2 1.5 0.5 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +0.05 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] +0.02 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) +0.2 #pepsS# %for AR-picker, artificial uplift of samples of CF (S) +0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) +1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) +%quality assessment% +#inital AIC onset# +0.01 0.02 0.04 0.08 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P +0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S +200 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +1.2 #minAICPSNR# %below this SNR the initial P pick is rejected +100 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +1.5 #minAICSSNR# %below this SNR the initial S pick is rejected +#check duration of signal using envelope function# +1.5 #prepickwin# %pre-signal window length [s] for noise level estimation +0.7 #minsiglength# %minimum required length of signal [s] +0.2 #sgap# %safety gap between noise and signal window [s] +2 #noisefactor# %noiselevel*noisefactor=threshold +60 #minpercent# %per cent of samples required higher than threshold +#check for spuriously picked S-onsets# +3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude +#jackknife-processing for P-picks# +3 #thresholdweight#%minimum required weight of picks +3 #dttolerance# %maximum allowed deviation of P picks from median [s] +4 #minstats# %minimum number of stations with reliable P picks +3 #Sdttolerance# %maximum allowed deviation from Wadati-diagram + From 6e51c05c948c829e4ed61c0d729d6d986edb4158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 29 May 2015 16:35:00 +0200 Subject: [PATCH 0335/1144] Running indicies changed after kai Olbert to calculate equal CF as done in MatLab, implemented some tools to compensate numerical artefacts. --- pylot/core/pick/CharFuns.py | 63 ++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index c6072cba..b6fb2f41 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -218,13 +218,12 @@ class AICcf(CharacteristicFunction): nn = np.isnan(xnp) if len(nn) > 1: xnp[nn] = 0 - i0 = np.where(xnp == 0) - i = np.where(xnp > 0) - xnp[i0] = xnp[i[0][0]] datlen = len(xnp) k = np.arange(1, datlen) cf = np.zeros(datlen) cumsumcf = np.cumsum(np.power(xnp, 2)) + i = np.where(cumsumcf == 0) + cumsumcf[i] = np.finfo(np.float64).eps cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * \ np.log((cumsumcf[datlen - 1] - cumsumcf[k - 1]) / (datlen - k + 1))) cf[0] = cf[1] @@ -236,7 +235,6 @@ class AICcf(CharacteristicFunction): self.cf = cf - np.mean(cf) self.xcf = x - class HOScf(CharacteristicFunction): ''' Function to calculate skewness (statistics of order 3) or kurtosis @@ -310,8 +308,8 @@ class ARZcf(CharacteristicFunction): cf = np.zeros(len(xnp)) loopstep = self.getARdetStep() - arcalci = ldet + self.getOrder() - 1 #AR-calculation index - for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): + arcalci = ldet + self.getOrder() #AR-calculation index + for i in range(ldet + self.getOrder(), tend - lpred - 1): if i == arcalci: #determination of AR coefficients #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! @@ -320,10 +318,17 @@ class ARZcf(CharacteristicFunction): #AR prediction of waveform using calculated AR coefficients self.arPredZ(xnp, self.arpara, i + 1, lpred) #prediction error = CF - cf[i + lpred] = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred] - xnp[i:i + lpred], 2)) / lpred) + cf[i + lpred-1] = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred-1] - xnp[i:i + lpred-1], 2)) / lpred) nn = np.isnan(cf) if len(nn) > 1: cf[nn] = 0 + #remove zeros and artefacts + tap = np.hanning(len(cf)) + cf = tap * cf + io = np.where(cf == 0) + ino = np.where(cf > 0) + cf[io] = cf[ino[0][0]] + self.cf = cf self.xcf = x @@ -350,17 +355,18 @@ class ARZcf(CharacteristicFunction): #recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) rhs = np.zeros(self.getOrder()) for k in range(0, self.getOrder()): - for i in range(rind, ldet): - rhs[k] = rhs[k] + data[i] * data[i - k] + for i in range(rind, ldet+1): + ki = k + 1 + rhs[k] = rhs[k] + data[i] * data[i - ki] #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) - A = np.zeros((2,2)) + A = np.zeros((self.getOrder(),self.getOrder())) for k in range(1, self.getOrder() + 1): for j in range(1, k + 1): - for i in range(rind, ldet): + for i in range(rind, ldet+1): ki = k - 1 ji = j - 1 - A[ki,ji] = A[ki,ji] + data[i - ji] * data[i - ki] + A[ki,ji] = A[ki,ji] + data[i - j] * data[i - k] A[ji,ki] = A[ki,ji] @@ -387,20 +393,20 @@ class ARZcf(CharacteristicFunction): Output: predicted waveform z ''' #be sure of the summation indeces - if rind < len(arpara) + 1: - rind = len(arpara) + 1 - if rind > len(data) - lpred + 1: - rind = len(data) - lpred + 1 + if rind < len(arpara): + rind = len(arpara) + if rind > len(data) - lpred : + rind = len(data) - lpred if lpred < 1: lpred = 1 - if lpred > len(data) - 1: - lpred = len(data) - 1 + if lpred > len(data) - 2: + lpred = len(data) - 2 z = np.append(data[0:rind], np.zeros(lpred)) for i in range(rind, rind + lpred): for j in range(1, len(arpara) + 1): ji = j - 1 - z[i] = z[i] + arpara[ji] * z[i - ji] + z[i] = z[i] + arpara[ji] * z[i - j] self.xpred = z @@ -432,8 +438,9 @@ class ARHcf(CharacteristicFunction): cf = np.zeros(len(xenoise)) loopstep = self.getARdetStep() - arcalci = ldet + self.getOrder() - 1 #AR-calculation index - for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): + arcalci = lpred + self.getOrder() - 1 #AR-calculation index + #arcalci = ldet + self.getOrder() - 1 #AR-calculation index + for i in range(lpred + self.getOrder() - 1, tend - 2 * lpred + 1): if i == arcalci: #determination of AR coefficients #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! @@ -447,6 +454,13 @@ class ARHcf(CharacteristicFunction): nn = np.isnan(cf) if len(nn) > 1: cf[nn] = 0 + #remove zeros and artefacts + tap = np.hanning(len(cf)) + cf = tap * cf + io = np.where(cf == 0) + ino = np.where(cf > 0) + cf[io] = cf[ino[0][0]] + self.cf = cf self.xcf = xnp @@ -581,6 +595,13 @@ class AR3Ccf(CharacteristicFunction): nn = np.isnan(cf) if len(nn) > 1: cf[nn] = 0 + #remove zeros and artefacts + tap = np.hanning(len(cf)) + cf = tap * cf + io = np.where(cf == 0) + ino = np.where(cf > 0) + cf[io] = cf[ino[0][0]] + self.cf = cf self.xcf = xnp From 5be662524fac27e940718f7a60ac56ed375c06e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 29 May 2015 16:43:32 +0200 Subject: [PATCH 0336/1144] Debuged AIC picker for plotting smoothed CF instead of unsmoothed CF, implemented quick and dirty a temporary solution to process restituted data in order to calculate apropriate slope (line 204). --- pylot/core/pick/Picker.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 9c3595b9..c2e20304 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -145,6 +145,8 @@ class AICPicker(AutoPicking): print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' self.Pick = None + self.slope = None + self.SNR = None #find NaN's nn = np.isnan(self.cf) if len(nn) > 1: @@ -173,7 +175,7 @@ class AICPicker(AutoPicking): #find NaN's nn = np.isnan(diffcf) if len(nn) > 1: - diffcf[nn] = 0 + diffcf[nn] = 0 #taper CF to get rid off side maxima tap = np.hanning(len(diffcf)) diffcf = tap * diffcf * max(abs(aicsmooth)) @@ -197,11 +199,15 @@ class AICPicker(AutoPicking): if self.Pick is not None: #get noise window inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1]) + #check, if these are counts or m/s, important for slope estimation! + #this is quick and dirty, better solution? + if max(self.Data[0].data < 1e-3): + self.Data[0].data = self.Data[0].data * 1000000 #get signal window isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) #calculate SNR from CF - self.SNR = max(abs(self.cf[isignal] - np.mean(self.cf[isignal]))) / max(abs(self.cf[inoise] \ - - np.mean(self.cf[inoise]))) + self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / max(abs(aic[inoise] \ + - np.mean(aic[inoise]))) #calculate slope from CF after initial pick #get slope window tslope = self.TSNR[3] #slope determination window @@ -230,8 +236,8 @@ class AICPicker(AutoPicking): self.SNR = None self.slope = None - if self.iplot is not None: - plt.figure(self.iplot) + if self.iplot > 1: + p = plt.figure(self.iplot) x = self.Data[0].data p1, = plt.plot(self.Tcf, x / max(x), 'k') p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') @@ -243,7 +249,6 @@ class AICPicker(AutoPicking): plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.yticks([]) plt.title(self.Data[0].stats.station) - plt.show() if self.Pick is not None: plt.figure(self.iplot + 1) @@ -259,11 +264,12 @@ class AICPicker(AutoPicking): plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.ylabel('Counts') ax = plt.gca() - ax.set_ylim([-10, max(self.Data[0].data)]) + plt.yticks([]) ax.set_xlim([self.Tcf[inoise[0][0]] - 5, self.Tcf[isignal[0][len(isignal) - 1]] + 5]) + plt.show() raw_input() - plt.close(self.iplot) + plt.close(p) if self.Pick == None: print 'AICPicker: Could not find minimum, picking window too short?' @@ -347,8 +353,8 @@ class PragPicker(AutoPicking): elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: self.Pick = pick_r - if self.getiplot() is not None: - plt.figure(self.getiplot()) + if self.getiplot() > 1: + p = plt.figure(self.getiplot()) p1, = plt.plot(Tcfpick,cfipick, 'k') p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) @@ -358,7 +364,7 @@ class PragPicker(AutoPicking): plt.title(self.Data[0].stats.station) plt.show() raw_input() - plt.close(self.getiplot()) + plt.close(p) else: self.Pick = None From 74682952e7f6d49979c5300bbae20acb5b4e49e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 29 May 2015 16:48:58 +0200 Subject: [PATCH 0337/1144] New function invoked by autoPyLoT for automated picking of onset times. Main tool for automatic picking! --- pylot/core/pick/run_autopicking.py | 401 +++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100755 pylot/core/pick/run_autopicking.py diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py new file mode 100755 index 00000000..c7b0a436 --- /dev/null +++ b/pylot/core/pick/run_autopicking.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Function to run automated picking algorithms using AIC, +HOS and AR prediction. Uses object CharFuns and Picker and +function conglomerate utils. + +:author: MAGS2 EP3 working group / Ludger Kueperkoch +""" + +from obspy.core import read +import matplotlib.pyplot as plt +import numpy as np +from pylot.core.pick.CharFuns import * +from pylot.core.pick.Picker import * +from pylot.core.pick.CharFuns import * +from pylot.core.pick import utils + +import pdb + +def run_autopicking(wfstream, pickparam): + + ''' + param: wfstream + :type: `~obspy.core.stream.Stream` + + param: pickparam + :type: container of picking parameters from input file, + usually autoPyLoT.in + ''' + + # declaring pickparam variables (only for convenience) + # read your autoPyLoT.in for details! + + #special parameters for P picking + algoP = pickparam.getParam('algoP') + iplot = pickparam.getParam('iplot') + pstart = pickparam.getParam('pstart') + pstop = pickparam.getParam('pstop') + thosmw = pickparam.getParam('tlta') + hosorder = pickparam.getParam('hosorder') + tsnrz = pickparam.getParam('tsnrz') + hosorder = pickparam.getParam('hosorder') + bpz1 = pickparam.getParam('bpz1') + bpz2 = pickparam.getParam('bpz2') + pickwinP = pickparam.getParam('pickwinP') + tsmoothP = pickparam.getParam('tsmoothP') + ausP = pickparam.getParam('ausP') + nfacP = pickparam.getParam('nfacP') + tpred1z = pickparam.getParam('tpred1z') + tdet1z = pickparam.getParam('tdet1z') + Parorder = pickparam.getParam('Parorder') + addnoise = pickparam.getParam('addnoise') + Precalcwin = pickparam.getParam('Precalcwin') + minAICPslope = pickparam.getParam('minAICPslope') + minAICPSNR = pickparam.getParam('minAICPSNR') + timeerrorsP = pickparam.getParam('timeerrorsP') + #special parameters for S picking + algoS = pickparam.getParam('algoS') + sstart = pickparam.getParam('sstart') + sstop = pickparam.getParam('sstop') + bph1 = pickparam.getParam('bph1') + bph2 = pickparam.getParam('bph2') + tsnrh = pickparam.getParam('tsnrh') + pickwinS = pickparam.getParam('pickwinS') + tpred1h = pickparam.getParam('tpred1h') + tdet1h = pickparam.getParam('tdet1h') + tpred2h = pickparam.getParam('tpred2h') + tdet2h = pickparam.getParam('tdet2h') + Sarorder = pickparam.getParam('Sarorder') + aictsmoothS = pickparam.getParam('aictsmoothS') + tsmoothS = pickparam.getParam('tsmoothS') + ausS = pickparam.getParam('ausS') + minAICSslope = pickparam.getParam('minAICSslope') + minAICSSNR = pickparam.getParam('minAICSSNR') + Srecalcwin = pickparam.getParam('Srecalcwin') + nfacS = pickparam.getParam('nfacS') + timeerrorsS = pickparam.getParam('timeerrorsS') + + + # split components + zdat = wfstream.select(component="Z") + edat = wfstream.select(component="E") + ndat = wfstream.select(component="N") + + if algoP == 'HOS' or algoP == 'ARZ' and zdat is not None: + print '##########################################' + print 'run_autopicking: Working on P onset of station %s' % zdat[0].stats.station + print 'Filtering vertical trace ...' + print zdat + z_copy = zdat.copy() + #filter and taper data + tr_filt = zdat[0].copy() + tr_filt.filter('bandpass', freqmin=bpz1[0], freqmax=bpz1[1], zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + z_copy[0].data = tr_filt.data + ############################################################## + #check length of waveform and compare with cut times + Lc = pstop - pstart + Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime + Ldiff = Lwf - Lc + if Ldiff < 0: + print 'run_autopicking: Cutting times are too large for actual waveform!' + print 'Use entire waveform instead!' + pstart = 0 + pstop = len(zdat[0].data) * zdat[0].stats.delta + cuttimes = [pstart, pstop] + if algoP == 'HOS': + #calculate HOS-CF using subclass HOScf of class CharacteristicFunction + cf1 = HOScf(z_copy, cuttimes, thosmw, hosorder) #instance of HOScf + elif algoP == 'ARZ': + #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction + cf1 = ARZcf(z_copy, cuttimes, tpred1z, Parorder, tdet1z, addnoise) #instance of ARZcf + ############################################################## + #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_aic = tr_filt.copy() + tr_aic.data =cf1.getCF() + z_copy[0].data = tr_aic.data + aiccf = AICcf(z_copy, cuttimes) #instance of AICcf + ############################################################## + #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking + aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, tsmoothP) + ############################################################## + #go on with processing if AIC onset passes quality control + if aicpick.getSlope() >= minAICPslope and aicpick.getSNR() >= minAICPSNR: + print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ + (aicpick.getSlope(), aicpick.getSNR()) + print 'Go on with refined picking ...' + #re-filter waveform with larger bandpass + print 'run_autopicking: re-filtering vertical trace ...' + z_copy = zdat.copy() + tr_filt = zdat[0].copy() + tr_filt.filter('bandpass', freqmin=bpz2[0], freqmax=bpz2[1], zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + z_copy[0].data = tr_filt.data + ############################################################# + #re-calculate CF from re-filtered trace in vicinity of initial onset + cuttimes2 = [round(max([aicpick.getpick() - Precalcwin, 0])), \ + round(min([len(zdat[0].data) * zdat[0].stats.delta, \ + aicpick.getpick() + Precalcwin]))] + if algoP == 'HOS': + #calculate HOS-CF using subclass HOScf of class CharacteristicFunction + cf2 = HOScf(z_copy, cuttimes2, thosmw, hosorder) #instance of HOScf + elif algoP == 'ARZ': + #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction + cf2 = ARZcf(z_copy, cuttimes2, tpred1z, Parorder, tdet1z, addnoise) #instance of ARZcf + ############################################################## + #get refined onset time from CF2 using class Picker + refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, aicpick.getpick()) + ############################################################# + #quality assessment + #get earliest and latest possible pick and symmetrized uncertainty + [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, refPpick.getpick(), iplot) + + #get SNR + [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, refPpick.getpick()) + + #weight P-onset using symmetric error + if Perror <= timeerrorsP[0]: + Pweight = 0 + elif Perror > timeerrorsP[0] and Perror <= timeerrorsP[1]: + Pweight = 1 + elif Perror > timeerrorsP[1] and Perror <= timeerrorsP[2]: + Pweight = 2 + elif Perror > timeerrorsP[2] and Perror <= timeerrorsP[3]: + Pweight = 3 + elif Perror > timeerrorsP[3]: + Pweight = 4 + + print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f' % (Pweight, SNRP, SNRPdB) + + else: + print 'Bad initial (AIC) P-pick, skip this onset!' + Pweight = 4 + else: + print 'run_autopicking: No vertical component data available, skipping station!' + return + + if edat is not None and ndat is not None and Pweight < 4: + print 'Go on picking S onset ...' + print '##################################################' + print 'Working on S onset of station %s' % edat[0].stats.station + print 'Filtering horizontal traces ...' + + #determine time window for calculating CF after P onset + #cuttimesh = [round(refPpick.getpick() + sstart), round(refPpick.getpick() + sstop)] + cuttimesh = [round(max([refPpick.getpick() + sstart, 0])), \ + round(min([refPpick.getpick() + sstop, Lwf]))] + + if algoS == 'ARH': + print edat, ndat + #re-create stream object including both horizontal components + hdat = edat.copy() + hdat += ndat + h_copy = hdat.copy() + #filter and taper data + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + elif algoS == 'AR3': + print zdat, edat, ndat + #re-create stream object including both horizontal components + hdat = zdat.copy() + hdat += edat + hdat += ndat + h_copy = hdat.copy() + #filter and taper data + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH3_filt = hdat[2].copy() + trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH3_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + trH3_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + h_copy[2].data = trH3_filt.data + ############################################################## + if algoS == 'ARH': + #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction + arhcf1 = ARHcf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, addnoise) #instance of ARHcf + elif algoS == 'AR3': + #calculate ARH-CF using subclass AR3cf of class CharcteristicFunction + arhcf1 = AR3Ccf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, addnoise) #instance of ARHcf + ############################################################## + #calculate AIC-ARH-CF using subclass AICcf of class CharacteristicFunction + #class needs stream object => build it + tr_arhaic = trH1_filt.copy() + tr_arhaic.data = arhcf1.getCF() + h_copy[0].data = tr_arhaic.data + #calculate ARH-AIC-CF + haiccf = AICcf(h_copy, cuttimesh) #instance of AICcf + ############################################################## + #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking + aicarhpick = AICPicker(haiccf, tsnrh, pickwinS, iplot, None, aictsmoothS) + ############################################################### + #go on with processing if AIC onset passes quality control + if aicarhpick.getSlope() >= minAICSslope and aicarhpick.getSNR() >= minAICSSNR: + print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ + % (aicarhpick.getSlope(), aicarhpick.getSNR()) + print 'Go on with refined picking ...' + #re-calculate CF from re-filtered trace in vicinity of initial onset + cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), \ + round(aicarhpick.getpick() + Srecalcwin)] + #re-filter waveform with larger bandpass + print 'run_autopicking: re-filtering horizontal traces...' + h_copy = hdat.copy() + #filter and taper data + if algoS == 'ARH': + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + ############################################################# + arhcf2 = ARHcf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, addnoise) #instance of ARHcf + elif algoS == 'AR3': + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH3_filt = hdat[2].copy() + trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) + trH3_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + trH3_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + h_copy[2].data = trH3_filt.data + ############################################################# + arhcf2 = AR3Ccf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, addnoise) #instance of ARHcf + + #get refined onset time from CF2 using class Picker + refSpick = PragPicker(arhcf2, tsnrh, pickwinS, iplot, ausS, tsmoothS, aicarhpick.getpick()) + ############################################################# + #quality assessment + #get earliest and latest possible pick and symmetrized uncertainty + h_copy[0].data = trH1_filt.data + [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) + h_copy[0].data = trH2_filt.data + [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) + if algoS == 'ARH': + #get earliest pick of both earliest possible picks + epick = [epickS1, epickS2] + lpick = [lpickS1, lpickS2] + pickerr = [Serror1, Serror2] + ipick =np.argmin([epickS1, epickS2]) + elif algoS == 'AR3': + [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) + #get earliest pick of all three picks + epick = [epickS1, epickS2, epickS3] + lpick = [lpickS1, lpickS2, lpickS3] + pickerr = [Serror1, Serror2, Serror3] + ipick =np.argmin([epickS1, epickS2, epickS3]) + epickS = epick[ipick] + lpickS = lpick[ipick] + Serror = pickerr[ipick] + + #get SNR + [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, refSpick.getpick()) + + #weight S-onset using symmetric error + if Serror <= timeerrorsS[0]: + Sweight = 0 + elif Serror > timeerrorsS[0] and Serror <= timeerrorsS[1]: + Sweight = 1 + elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: + Sweight = 2 + elif Serror > timeerrorsS[2] and Serror <= timeerrorsS[3]: + Sweight = 3 + elif Serror > timeerrorsS[3]: + Sweight = 4 + + print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % (Sweight, SNRS, SNRSdB) + + else: + print 'Bad initial (AIC) S-pick, skip this onset!' + Sweight = 4 + + else: + print 'run_autopicking: No horizontal component data available, skipping S picking!' + return + + ############################################################## + if iplot > 0: + #plot vertical trace + plt.figure() + plt.subplot(3,1,1) + tdata = np.arange(0, tr_filt.stats.npts / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + p1, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') + p2, = plt.plot(cf1.getTimeArray(), cf1.getCF() / max(cf1.getCF()), 'b') + p3, = plt.plot(cf2.getTimeArray(), cf2.getCF() / max(cf2.getCF()), 'm') + p4, = plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'r') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'r') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'r') + p5, = plt.plot([refPpick.getpick(), refPpick.getpick()], [-1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [-1.3, -1.3], 'r', linewidth=2) + plt.plot([lpickP, lpickP], [-1.1, 1.1], 'r--') + plt.plot([epickP, epickP], [-1.1, 1.1], 'r--') + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.title('%s, %s, P Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % (tr_filt.stats.station, \ + tr_filt.stats.channel, Pweight, SNRP, SNRPdB)) + plt.suptitle(tr_filt.stats.starttime) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'CF1', 'CF2', 'Initial P Onset', 'Final P Pick']) + #plot horizontal traces + plt.subplot(3,1,2) + th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) + p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') + p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') + p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % (trH1_filt.stats.channel, \ + Sweight, SNRS, SNRSdB)) + plt.suptitle(trH1_filt.stats.starttime) + plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) + plt.subplot(3,1,3) + th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) + plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') + plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') + plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') + plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') + plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) + plt.ylabel('Normalized Counts') + plt.title(trH2_filt.stats.channel) + plt.show() + raw_input() + plt.close() From c676fc5762154a2bb66b41a377dd55121bebcc04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 1 Jun 2015 14:15:37 +0200 Subject: [PATCH 0338/1144] Modified loop over all available stations to prevent processing of one station several times. --- autoPyLoT.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index af1e8423..8cbc49af 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -12,6 +12,7 @@ from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE +import pdb __version__ = _getVersionString() @@ -72,11 +73,15 @@ def autoPyLoT(inputfile): ########################################################## # !automated picking starts here! - for i in range(len(wfdat) - 2): + procstats = [] + for i in range(len(wfdat)): stationID = wfdat[i].stats.station - #find corresponding streams - statdat = wfdat.select(station=stationID) - run_autopicking(statdat, parameter) + #check if station has already been processed + if stationID not in procstats: + procstats.append(stationID) + #find corresponding streams + statdat = wfdat.select(station=stationID) + run_autopicking(statdat, parameter) if __name__ == "__main__": From ab9a49a727033a30a87c14d80dfc8c53a34b420e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 1 Jun 2015 14:16:52 +0200 Subject: [PATCH 0339/1144] Additional parameters for first-motion picker. --- autoPyLoT.in | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/autoPyLoT.in b/autoPyLoT.in index 37a5a1eb..bddee769 100644 --- a/autoPyLoT.in +++ b/autoPyLoT.in @@ -9,7 +9,7 @@ EVENT_DATA/LOCAL #datapath# %data path 2013.02_Insheim #database# %name of data base e0019.048.13 #eventID# %certain evnt ID for processing PILOT #datastructure# %choose data structure -2 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything +0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities @@ -71,13 +71,17 @@ ARH #algoS# %choose algorithm for S-onset 0.2 #pepsS# %for AR-picker, artificial uplift of samples of CF (S) 0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) 1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) +%first-motion picker% +1 #minfmweight# %minimum required p weight for first-motion determination +2 #minFMSNR# %miniumum required SNR for first-motion determination +0.2 #fmpickwin# %pick window around P onset for calculating zero crossings %quality assessment% #inital AIC onset# 0.01 0.02 0.04 0.08 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P 0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S -200 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +80 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected 1.2 #minAICPSNR# %below this SNR the initial P pick is rejected -100 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +50 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# 1.5 #prepickwin# %pre-signal window length [s] for noise level estimation From 85f0717f10d500dc1c26a4b186031ede7ca71cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 1 Jun 2015 14:18:18 +0200 Subject: [PATCH 0340/1144] Implemented first-motion picker, some debugging. --- pylot/core/pick/run_autopicking.py | 133 ++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 39 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index c7b0a436..7660cdec 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -77,7 +77,10 @@ def run_autopicking(wfstream, pickparam): Srecalcwin = pickparam.getParam('Srecalcwin') nfacS = pickparam.getParam('nfacS') timeerrorsS = pickparam.getParam('timeerrorsS') - + #parameters for first-motion determination + minFMSNR = pickparam.getParam('minFMSNR') + fmpickwin = pickparam.getParam('fmpickwin') + minfmweight = pickparam.getParam('minfmweight') # split components zdat = wfstream.select(component="Z") @@ -125,6 +128,7 @@ def run_autopicking(wfstream, pickparam): ############################################################## #go on with processing if AIC onset passes quality control if aicpick.getSlope() >= minAICPslope and aicpick.getSNR() >= minAICPSNR: + aicPflag = 1 print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ (aicpick.getSlope(), aicpick.getSNR()) print 'Go on with refined picking ...' @@ -169,11 +173,28 @@ def run_autopicking(wfstream, pickparam): elif Perror > timeerrorsP[3]: Pweight = 4 - print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f' % (Pweight, SNRP, SNRPdB) + ############################################################## + #get first motion of P onset + #certain quality required + if Pweight <= minfmweight and SNRP >= minFMSNR: + FM = fmpicker(zdat, z_copy, fmpickwin, refPpick.getpick(), iplot) + else: + FM = 'N' + + print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) else: print 'Bad initial (AIC) P-pick, skip this onset!' + print 'AIC-SNR=%f, AIC-Slope=%f' % (aicpick.getSNR(), aicpick.getSlope()) Pweight = 4 + Sweight = 4 + FM = 'N' + SNRP = None + SNRPdB = None + SNRS = None + SNRSdB = None + aicSflag = 0 + aicPflag = 0 else: print 'run_autopicking: No vertical component data available, skipping station!' return @@ -245,6 +266,7 @@ def run_autopicking(wfstream, pickparam): ############################################################### #go on with processing if AIC onset passes quality control if aicarhpick.getSlope() >= minAICSslope and aicarhpick.getSNR() >= minAICSSNR: + aicSflag = 1 print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ % (aicarhpick.getSlope(), aicarhpick.getSNR()) print 'Go on with refined picking ...' @@ -327,7 +349,11 @@ def run_autopicking(wfstream, pickparam): else: print 'Bad initial (AIC) S-pick, skip this onset!' + print 'AIC-SNR=%f, AIC-Slope=%f' % (aicarhpick.getSNR(), aicarhpick.getSlope()) Sweight = 4 + SNRS = None + SNRSdB = None + aicSflag = 0 else: print 'run_autopicking: No horizontal component data available, skipping S picking!' @@ -338,59 +364,88 @@ def run_autopicking(wfstream, pickparam): #plot vertical trace plt.figure() plt.subplot(3,1,1) - tdata = np.arange(0, tr_filt.stats.npts / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + tdata = np.arange(0, zdat[0].stats.npts / tr_filt.stats.sampling_rate, tr_filt.stats.delta) + #check equal length of arrays, sometimes they are different!? + wfldiff = len(tr_filt.data) - len(tdata) + if wfldiff < 0: + tdata = tdata[0:len(tdata) - abs(wfldiff)] p1, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - p2, = plt.plot(cf1.getTimeArray(), cf1.getCF() / max(cf1.getCF()), 'b') - p3, = plt.plot(cf2.getTimeArray(), cf2.getCF() / max(cf2.getCF()), 'm') - p4, = plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'r') - plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'r') - plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'r') - p5, = plt.plot([refPpick.getpick(), refPpick.getpick()], [-1.3, 1.3], 'r', linewidth=2) - plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [1.3, 1.3], 'r', linewidth=2) - plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [-1.3, -1.3], 'r', linewidth=2) - plt.plot([lpickP, lpickP], [-1.1, 1.1], 'r--') - plt.plot([epickP, epickP], [-1.1, 1.1], 'r--') + if Pweight < 4: + p2, = plt.plot(cf1.getTimeArray(), cf1.getCF() / max(cf1.getCF()), 'b') + if aicPflag == 1: + p3, = plt.plot(cf2.getTimeArray(), cf2.getCF() / max(cf2.getCF()), 'm') + p4, = plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'r') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'r') + plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'r') + p5, = plt.plot([refPpick.getpick(), refPpick.getpick()], [-1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [-1.3, -1.3], 'r', linewidth=2) + plt.plot([lpickP, lpickP], [-1.1, 1.1], 'r--') + plt.plot([epickP, epickP], [-1.1, 1.1], 'r--') + plt.legend([p1, p2, p3, p4, p5], ['Data', 'CF1', 'CF2', 'Initial P Onset', 'Final P Pick']) + plt.title('%s, %s, P Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f Polarity: %s' % (tr_filt.stats.station, \ + tr_filt.stats.channel, Pweight, SNRP, SNRPdB, FM)) + else: + plt.legend([p1, p2], ['Data', 'CF1']) + plt.title('%s, P Weight=%d, SNR=None, SNRdB=None' % (tr_filt.stats.channel, Pweight)) plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') - plt.title('%s, %s, P Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % (tr_filt.stats.station, \ - tr_filt.stats.channel, Pweight, SNRP, SNRPdB)) plt.suptitle(tr_filt.stats.starttime) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'CF1', 'CF2', 'Initial P Onset', 'Final P Pick']) + #plot horizontal traces plt.subplot(3,1,2) th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) + #check equal length of arrays, sometimes they are different!? + wfldiff = len(trH1_filt.data) - len(th1data) + if wfldiff < 0: + th1data = th1data[0:len(th1data) - abs(wfldiff)] p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') - p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') - p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) + plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % (trH1_filt.stats.channel, \ + Sweight, SNRS, SNRSdB)) + else: + plt.legend([p21, p22], ['Data', 'CF1']) + plt.title('%s, S Weight=%d, SNR=None, SNRdB=None' % (trH1_filt.stats.channel, Sweight)) plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') - plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % (trH1_filt.stats.channel, \ - Sweight, SNRS, SNRSdB)) plt.suptitle(trH1_filt.stats.starttime) - plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) + plt.subplot(3,1,3) th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) + #check equal length of arrays, sometimes they are different!? + wfldiff = len(trH2_filt.data) - len(th2data) + if wfldiff < 0: + th2data = th2data[0:len(th2data) - abs(wfldiff)] plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') - plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') - plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') - plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') + plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) + else: + plt.legend([p21, p22], ['Data', 'CF1']) plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) From 5488d4e0fcf8c1f3253d6c5ae26d8b00c65fe423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 1 Jun 2015 16:28:27 +0200 Subject: [PATCH 0341/1144] Debuged loop, seperate loops for single event or complete data structure processing, respectively. --- autoPyLoT.py | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 8cbc49af..59aa53da 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -12,7 +12,6 @@ from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE -import pdb __version__ = _getVersionString() @@ -53,8 +52,8 @@ def autoPyLoT(inputfile): if parameter.hasParam('eventID'): dsfields['eventID'] = parameter.getParam('eventID') exf.append('eventID') - datastructure.modifyFields(**dsfields) + datastructure.modifyFields(**dsfields) datastructure.setExpandFields(exf) # get streams @@ -62,26 +61,41 @@ def autoPyLoT(inputfile): datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: - print 'Reading %s' %event data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) + print 'Working on event %s' %event + print data + + wfdat = data.getWFData() # all available streams + ########################################################## + # !automated picking starts here! + procstats = [] + for i in range(len(wfdat)): + stationID = wfdat[i].stats.station + #check if station has already been processed + if stationID not in procstats: + procstats.append(stationID) + #find corresponding streams + statdat = wfdat.select(station=stationID) + run_autopicking(statdat, parameter) + #for single event processing else: data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) + print 'Working on event ', parameter.getParam('eventID') print data - wfdat = data.getWFData() # all available streams - - ########################################################## - # !automated picking starts here! - procstats = [] - for i in range(len(wfdat)): - stationID = wfdat[i].stats.station - #check if station has already been processed - if stationID not in procstats: - procstats.append(stationID) - #find corresponding streams - statdat = wfdat.select(station=stationID) - run_autopicking(statdat, parameter) + wfdat = data.getWFData() # all available streams + ########################################################## + # !automated picking starts here! + procstats = [] + for i in range(len(wfdat)): + stationID = wfdat[i].stats.station + #check if station has already been processed + if stationID not in procstats: + procstats.append(stationID) + #find corresponding streams + statdat = wfdat.select(station=stationID) + run_autopicking(statdat, parameter) if __name__ == "__main__": From 15b245c2e82c26351cbc73ea0deb5d4b2dcc7b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 1 Jun 2015 16:30:22 +0200 Subject: [PATCH 0342/1144] Debuging, temporary solved problems occuring when reading channels of not oriented sondes (channels 1/2). --- pylot/core/pick/run_autopicking.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 7660cdec..ea54490a 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -17,8 +17,6 @@ from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * from pylot.core.pick import utils -import pdb - def run_autopicking(wfstream, pickparam): ''' @@ -185,7 +183,7 @@ def run_autopicking(wfstream, pickparam): else: print 'Bad initial (AIC) P-pick, skip this onset!' - print 'AIC-SNR=%f, AIC-Slope=%f' % (aicpick.getSNR(), aicpick.getSlope()) + print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() Pweight = 4 Sweight = 4 FM = 'N' @@ -199,7 +197,7 @@ def run_autopicking(wfstream, pickparam): print 'run_autopicking: No vertical component data available, skipping station!' return - if edat is not None and ndat is not None and Pweight < 4: + if edat is not None and ndat is not None and len(edat) > 0 and len(ndat) > 0 and Pweight < 4: print 'Go on picking S onset ...' print '##################################################' print 'Working on S onset of station %s' % edat[0].stats.station @@ -349,14 +347,14 @@ def run_autopicking(wfstream, pickparam): else: print 'Bad initial (AIC) S-pick, skip this onset!' - print 'AIC-SNR=%f, AIC-Slope=%f' % (aicarhpick.getSNR(), aicarhpick.getSlope()) + print 'AIC-SNR=', aicarhpick.getSNR(), 'AIC-Slope=', aicarhpick.getSlope() Sweight = 4 SNRS = None SNRSdB = None aicSflag = 0 else: - print 'run_autopicking: No horizontal component data available, skipping S picking!' + print 'run_autopicking: No horizontal component data available or bad P onset, skipping S picking!' return ############################################################## From ce75a82f5bdf081b5668ce362ad812ace97ca0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 2 Jun 2015 13:42:44 +0200 Subject: [PATCH 0343/1144] Some cosmetics. --- autoPyLoT.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 59aa53da..1249a18d 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -12,6 +12,7 @@ from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE +import pdb __version__ = _getVersionString() @@ -77,10 +78,13 @@ def autoPyLoT(inputfile): #find corresponding streams statdat = wfdat.select(station=stationID) run_autopicking(statdat, parameter) + print '------------------------------------------' + print '-----Finished event %s!-----' % event + print '------------------------------------------' #for single event processing else: - data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) + data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), 'ROTT*'))) print 'Working on event ', parameter.getParam('eventID') print data @@ -96,6 +100,9 @@ def autoPyLoT(inputfile): #find corresponding streams statdat = wfdat.select(station=stationID) run_autopicking(statdat, parameter) + print '------------------------------------------' + print '-------Finished event %s!-------' % parameter.getParam('eventID') + print '------------------------------------------' if __name__ == "__main__": From 0dce58ede408b6816e7ead1d05889c3656dcc66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 2 Jun 2015 13:43:39 +0200 Subject: [PATCH 0344/1144] Marginal changes. --- autoPyLoT.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 1249a18d..66024dd6 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -12,7 +12,6 @@ from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE -import pdb __version__ = _getVersionString() @@ -84,7 +83,7 @@ def autoPyLoT(inputfile): #for single event processing else: - data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), 'ROTT*'))) + data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) print 'Working on event ', parameter.getParam('eventID') print data From b0acee71367a34b744a5fcb60f81aac35243d813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 2 Jun 2015 13:46:10 +0200 Subject: [PATCH 0345/1144] Added component splitting also for channels 1 and 2. --- pylot/core/pick/run_autopicking.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index ea54490a..b8c9389b 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -17,6 +17,7 @@ from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * from pylot.core.pick import utils + def run_autopicking(wfstream, pickparam): ''' @@ -83,7 +84,11 @@ def run_autopicking(wfstream, pickparam): # split components zdat = wfstream.select(component="Z") edat = wfstream.select(component="E") + if len(edat) == 0: #check for other components + edat = wfstream.select(component="2") ndat = wfstream.select(component="N") + if len(ndat) == 0: #check for other components + ndat = wfstream.select(component="1") if algoP == 'HOS' or algoP == 'ARZ' and zdat is not None: print '##########################################' From e0568e3849c7d8eba44b33032b5ed3c1c627f221 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:36:37 +0200 Subject: [PATCH 0346/1144] write picks into data object (work in progress) --- QtPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 5cb074be..448a672a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -475,6 +475,7 @@ class MainWindow(QMainWindow): station=station) if pickDlg.exec_(): print 'picks accepted' + self.getData().applyEVTData(pickDlg.getPicks()) else: print 'picks not saved and closed dialog' From e6b0c2b470860182f7ff7812764c2ba286b90c65 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:37:58 +0200 Subject: [PATCH 0347/1144] zero xings are now be derived from demeaned data --- pylot/core/pick/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index b3d3656b..e3fb6eca 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -67,7 +67,9 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): #get earliest possible pick #determine all zero crossings in signal window - zc = crossings_nonzero_all(x[isignal]) + # remove mean from signal window + signal = x[isignal] - x[isignal].mean() + zc = crossings_nonzero_all(signal) #calculate mean half period T0 of signal as the average of the T0 = np.mean(np.diff(zc)) * X[0].stats.delta #this is half wave length! #T0/4 is assumed as time difference between most likely and earliest possible pick! From 0721e225a75f47be7a4cc24a5d03f26bec5b5b63 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:40:36 +0200 Subject: [PATCH 0348/1144] never return values for without a meaning --- pylot/core/read/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 5790ac3f..07002e0f 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -101,7 +101,7 @@ class Data(object): try: return self.evtdata.get('resource_id').id except: - return 'smi:bug/pylot/1234' + return None def filterWFData(self, kwargs): self.getWFData().filter(**kwargs) From 3cb73fb9483d8a3f77bc01c798303f87af4890ed Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:41:37 +0200 Subject: [PATCH 0349/1144] alternative for storing event data (discussable) --- pylot/core/read/data.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 07002e0f..1640a764 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -247,6 +247,9 @@ class GenericDataStructure(object): expandList.append('*%s' % self.getFieldValue('suffix')) return os.path.join(*expandList) + def getCatalogName(self): + return os.path.join(self.getFieldValue('root'), 'catalog.qml') + class PilotDataStructure(GenericDataStructure): ''' From ba7ab07473cf171a10489c5783994a9e5f1be271 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:43:43 +0200 Subject: [PATCH 0350/1144] catch non string type input for filename construction --- pylot/core/util/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index d60f52b3..5f93b96a 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -12,7 +12,10 @@ import obspy.core.event as ope def fnConstructor(s): - s = s.split('/')[-1] + if type(s) is str: + s = s.split('/')[-1] + else: + s = getHash(UTCDateTime()) badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)') From 1d70a236d134831453135779d2d88fa63094a1a5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:45:49 +0200 Subject: [PATCH 0351/1144] [bugfix] avoid forming invalid pickID strings (line 194) do not use altered resource identifier for all kinds of event objects --- pylot/core/util/utils.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 5f93b96a..3e3f3ac0 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -191,7 +191,7 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, :type authority_id: str :return: An ObsPy :class: `~obspy.core.event.Pick` object ''' - pickID = eventnum + '_' + station + '/{0:3d}'.format(picknum) + pickID = eventnum + '_' + station.strip() + '/{0:03d}'.format(picknum) pickresID = createResourceID(origintime, 'pick', authority_id, pickID) pick = ope.Pick() pick.resource_id = pickresID @@ -202,8 +202,7 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, return pick -def createArrival(origintime, pickresID, eventnum, cinfo, phase, station, - authority_id, azimuth=None, dist=None): +def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): ''' createArrival - function to create an Obspy Arrival :param pickresID: Resource identifier of the created pick @@ -226,42 +225,34 @@ def createArrival(origintime, pickresID, eventnum, cinfo, phase, station, :type dist: float or int, optional :return: An ObsPy :class: `~obspy.core.event.Arrival` object ''' - arriresID = createResourceID(origintime, 'arrival', authority_id, eventnum) arrival = ope.Arrival() - arrival.resource_id = arriresID arrival.creation_info = cinfo arrival.pick_id = pickresID arrival.phase = phase if azimuth is not None: - arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360 + arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360. else: arrival.azimuth = azimuth - arrival.distance = None + arrival.distance = dist return arrival -def createMagnitude(originID, origintime, cinfo, authority_id=None): +def createMagnitude(originID, cinfo): ''' createMagnitude - function to create an ObsPy Magnitude object :param originID: - :param origintime: :param cinfo: :param authority_id: :return: ''' - magnresID = createResourceID(origintime, 'mag', authority_id) magnitude = ope.Magnitude() - magnitude.resource_id = magnresID magnitude.creation_info = cinfo magnitude.origin_id = originID return magnitude -def createAmplitude(pickID, amp, unit, category, origintime, cinfo, - authority_id=None): - amplresID = createResourceID(origintime, 'ampl', authority_id) +def createAmplitude(pickID, amp, unit, category, cinfo): amplitude = ope.Amplitude() - amplitude.resource_id = amplresID amplitude.creation_info = cinfo amplitude.generic_amplitude = amp amplitude.unit = ope.AmplitudeUnit(unit) From b3ea4229a42a1a2677dc86d5cc6655c18a06de86 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:46:37 +0200 Subject: [PATCH 0352/1144] implement opportunity to apply different kind of event data to the data object --- pylot/core/read/data.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 1640a764..95f3ee63 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -149,6 +149,21 @@ class Data(object): def getEvtData(self): return self.evtdata + def applyEVTData(self, data, type='pick'): + + def applyPicks(picks): + pass + def applyArrivals(arrivals): + pass + def applyEvent(event): + pass + + applydata = {'pick':applyPicks, + 'arrival':applyArrivals, + 'event':applyEvent} + + applydata[type](data) + class GenericDataStructure(object): ''' GenericDataBase type holds all information about the current data- From 864cb112bc34741859419a35a6bf4b5c053b8210 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:47:40 +0200 Subject: [PATCH 0353/1144] usage changed due to release [1d70a23] --- pylot/core/read/io.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index 10a390ba..58a0b10b 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -118,13 +118,11 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): phase, stations[n], wffn, authority_id) event.picks.append(pick) pickID = pick.get('id') - arrival = createArrival(eventDate, pickID, eventNum, pickcinfo, - phase, stations[n], authority_id) + arrival = createArrival(pickID, pickcinfo, phase) origin.arrivals.append(arrival) np += 1 - magnitude = createMagnitude(origin.get('id'), eventDate, loccinfo, - authority_id) + magnitude = createMagnitude(origin.get('id'), loccinfo) magnitude.mag = float(loc['Mnet']) magnitude.magnitude_type = 'Ml' From 93dfe76b7ead4d1af1f7aad91f92617f5ebe3f25 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 10 Jun 2015 15:49:06 +0200 Subject: [PATCH 0354/1144] fixed some bugs concerning plotting and implemented a more flexible way of drawing the picks including error plotting --- pylot/core/util/widgets.py | 66 +++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 15c6a11c..aad8a424 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -312,6 +312,7 @@ class PickDlg(QDialog): checkable=True) self.selectPhase = QComboBox() self.selectPhase.addItems([None, 'Pn', 'Pg', 'P1', 'P2']) + self.selectPhase.setEditable(True) @@ -346,7 +347,7 @@ class PickDlg(QDialog): _buttonbox.accepted.connect(self.accept) _buttonbox.rejected.connect(self.reject) - _buttonbox.clicked(QDialogButtonBox.Apply).connect(self.apply) + _buttonbox.button(QDialogButtonBox.Apply).clicked.connect(self.apply) self.setLayout(_outerlayout) @@ -395,10 +396,14 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.setIniPick) else: + self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.panPress) self.cidmotion = self.connectMotionEvent(self.panMotion) self.cidrelease = self.connectReleaseEvent(self.panRelease) self.cidscroll = self.connectScrollEvent(self.scrollZoom) + self.getPlotWidget().plotWFData(wfdata=self.getWFData(), + title=self.getStation()) + self.drawPicks() def getComponents(self): return self.components @@ -490,7 +495,7 @@ class PickDlg(QDialog): zoomy=zoomy, noiselevel=noiselevel) - self.zoomAction.enabled = False + self.zoomAction.setEnabled(False) self.updateAPD(wfdata) @@ -503,8 +508,8 @@ class PickDlg(QDialog): channel = self.getChannelID(round(gui_event.ydata)) wfdata = self.getAPD().copy().select(channel=channel) - # get earliest and latest possible pick - [epp, lpp, pickerror] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) + # get earliest and latest possible pick and symmetric pick error + [epp, lpp, spe] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) # get name of phase actually picked phase = self.selectPhase.currentText() @@ -515,7 +520,7 @@ class PickDlg(QDialog): phasepicks['epp'] = epp phasepicks['lpp'] = lpp phasepicks['mpp'] = pick - phasepicks['spe'] = pickerror + phasepicks['spe'] = spe try: oldphasepick = self.picks[phase] @@ -545,29 +550,33 @@ class PickDlg(QDialog): olpp=olpp) self.drawPicks(phase) self.disconnectPressEvent() + self.zoomAction.setEnabled(True) self.selectPhase.setCurrentIndex(-1) - self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent(self.panMotion) - self.cidrelease = self.connectReleaseEvent(self.panRelease) - self.cidscroll = self.connectScrollEvent(self.scrollZoom) - - - def drawPicks(self, phase): + def drawPicks(self, phase=None): # plotting picks ax = self.getPlotWidget().axes ylims = ax.get_ylim() - - picks = self.getPicks() + if self.getPicks(): + if phase is not None: + picks = self.getPicks()[phase] + else: + for phase in self.getPicks(): + self.drawPicks(phase) + return + else: + return mpp = picks['mpp'] - epp = picks['lpp'] - lpp = picks['epp'] + epp = picks['epp'] + lpp = picks['lpp'] + spe = picks['spe'] - ax.plot([mpp, mpp], ylims, 'r--') - ax.plot([epp, epp], ylims, 'c--') - ax.plot([lpp, lpp], ylims, 'm--') + ax.fill_between([epp, lpp],ylims[0], ylims[1], alpha=.5, color='c') + ax.plot([mpp-spe, mpp-spe], ylims, 'c--', + [mpp, mpp], ylims, 'b-', + [mpp+spe, mpp+spe], ylims, 'c--') self.getPlotWidget().draw() @@ -585,8 +594,8 @@ class PickDlg(QDialog): ax.figure.canvas.draw() def panMotion(self, gui_event): - ax = self.getPlotWidget().axes if self.press is None: return + ax = self.getPlotWidget().axes if gui_event.inaxes != ax: return dx = gui_event.xdata - self.xpress dy = gui_event.ydata - self.ypress @@ -626,9 +635,13 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.disconnectMotionEvent() self.disconnectReleaseEvent() + self.disconnectScrollEvent() self.figToolBar.zoom() else: - self.cidpress = self.connectPressEvent(self.setIniPick) + self.connectPressEvent(self.panPress) + self.connectMotionEvent(self.panMotion) + self.connectReleaseEvent(self.panRelease) + self.connectScrollEvent(self.scrollZoom) def scrollZoom(self, gui_event, factor=2.): @@ -647,8 +660,10 @@ class PickDlg(QDialog): scale_factor = 1 print gui_event.button - new_xlim = gui_event.xdata - scale_factor * (gui_event.xdata - curr_xlim) - new_ylim = gui_event.ydata - scale_factor * (gui_event.ydata - curr_ylim) + new_xlim = gui_event.xdata - \ + scale_factor * (gui_event.xdata - curr_xlim) + new_ylim = gui_event.ydata - \ + scale_factor * (gui_event.ydata - curr_ylim) new_xlim.sort() new_xlim[0] = max(new_xlim[0], self.limits['xlims'][0]) @@ -664,7 +679,7 @@ class PickDlg(QDialog): def apply(self): picks = self.getPicks() for pick in picks: - print pick + print pick, picks[pick] def accept(self): self.apply() @@ -705,7 +720,8 @@ class PropertiesDlg(QDialog): for widint in range(self.tabWidget.count()): curwid = self.tabWidget.widget(widint) values = curwid.getValues() - if values is not None: self.setValues(values) + if values is not None: + self.setValues(values) def setValues(self, tabValues): settings = QSettings() From 77b7d26902c82fd1c4672a3e532a84c0520a85da Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 11 Jun 2015 05:34:07 +0200 Subject: [PATCH 0355/1144] selectable phases should be edited elsewhere not in the combobox itself --- pylot/core/util/widgets.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index aad8a424..e3303780 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -312,9 +312,6 @@ class PickDlg(QDialog): checkable=True) self.selectPhase = QComboBox() self.selectPhase.addItems([None, 'Pn', 'Pg', 'P1', 'P2']) - self.selectPhase.setEditable(True) - - self.zoomAction = createAction(parent=self, text='Zoom', slot=self.zoom, icon=zoom_icon, From 63849177be90bf8ae8c64cfd115ed5e03b95efec Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 11 Jun 2015 05:36:56 +0200 Subject: [PATCH 0356/1144] package active will provide capabilities for processing reflection seismic surveys automatically --- pylot/core/active/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 pylot/core/active/__init__.py diff --git a/pylot/core/active/__init__.py b/pylot/core/active/__init__.py new file mode 100644 index 00000000..ccbcc844 --- /dev/null +++ b/pylot/core/active/__init__.py @@ -0,0 +1 @@ +__author__ = 'sebastianw' From 3d8bea8f7eb634bdd940fffa756f9ed87d606c00 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 11 Jun 2015 10:02:44 +0200 Subject: [PATCH 0357/1144] deleted obsolete module --- pylot/core/util/__init__.py | 3 --- pylot/core/util/layouts.py | 27 --------------------------- 2 files changed, 30 deletions(-) delete mode 100644 pylot/core/util/layouts.py diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index b20bc021..d2cc70cb 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,7 +1,6 @@ from pylot.core.util.connection import checkurl from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import OptionsError, FormatError, DatastructureError -from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ getHash, getLogin, createCreationInfo, createResourceID, prepTimeAxis, \ @@ -9,5 +8,3 @@ from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ PropertiesDlg, NewEventDlg, MPLWidget, createAction from pylot.core.util.version import get_git_version as _getVersionString - - diff --git a/pylot/core/util/layouts.py b/pylot/core/util/layouts.py deleted file mode 100644 index c9011a66..00000000 --- a/pylot/core/util/layouts.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# -*- coding: utf-8 -*- -''' -Created on 10.11.2014 - -@author: sebastianw -''' - -from PySide.QtGui import (QVBoxLayout, - QPushButton) - -def layoutStationButtons(data, comp): - layout = QVBoxLayout() - stationButtons = [] - try: - st = data.select(component=comp) - numStations = len(st) - for n in range(numStations): - stat = st[n].stats.station - stationButtons.append(QPushButton('%s'.format(stat))) - except: - for n in range(5): - stationButtons.append(QPushButton('ST{0:02d}'.format(n+1))) - for button in stationButtons: - layout.addWidget(button) - return layout \ No newline at end of file From 546e919dc959a5e95fcc032e02bcc74286d029e7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 11 Jun 2015 10:07:21 +0200 Subject: [PATCH 0358/1144] code clean-up following several conventions --- QtPyLoT.py | 58 ++++++++++--------- makePyLoT.py | 69 ++++++++++++++-------- pylot/__init__.py | 4 +- pylot/core/pick/utils.py | 4 +- pylot/core/read/__init__.py | 2 - pylot/core/read/inputs.py | 21 ++----- pylot/core/util/errors.py | 2 + pylot/core/util/structure.py | 4 +- pylot/core/util/version.py | 4 +- pylot/core/util/widgets.py | 108 ++++++++++++++++------------------- 10 files changed, 140 insertions(+), 136 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 448a672a..2834e0ca 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -43,13 +43,13 @@ import icons_rc # Version information __version__ = _getVersionString() + class MainWindow(QMainWindow): closing = Signal() def __init__(self, parent=None): super(MainWindow, self).__init__(parent) - self.createAction = createAction self.dirty = False settings = QSettings() @@ -58,7 +58,9 @@ class MainWindow(QMainWindow): settings.setValue("user/FullName", fulluser) settings.setValue("user/Login", getLogin()) if settings.value("agency_id", None) is None: - agency = QInputDialog.getText(self, "Enter authority name (e.g. BUG):", "Authority") + agency = QInputDialog.getText(self, + "Enter authority name (e.g. BUG):", + "Authority") settings.setValue("agency_id", agency) self.recentEvents = settings.value("data/recentEvents", []) self.fnames = None @@ -91,7 +93,6 @@ class MainWindow(QMainWindow): self.loadData() self.updateFilterOptions() - def setupUi(self): try: @@ -159,21 +160,24 @@ class MainWindow(QMainWindow): QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") - self.filterAction = self.createAction(self, "&Filter ...", self.filterWaveformData, - "Ctrl+F", QIcon(":/filter.png"), - """Toggle un-/filtered waveforms + self.filterAction = self.createAction(self, "&Filter ...", + self.filterWaveformData, + "Ctrl+F", QIcon(":/filter.png"), + """Toggle un-/filtered waveforms to be displayed, according to the desired seismic phase.""", True) filterEditAction = self.createAction(self, "&Filter parameter ...", self.adjustFilterOptions, "Alt+F", QIcon(None), """Adjust filter parameters.""") - self.selectPAction = self.createAction(self, "&P", self.alterPhase, "Alt+P", - p_icon, - "Toggle P phase.", True) - self.selectSAction = self.createAction(self, "&S", self.alterPhase, "Alt+S", - s_icon, - "Toggle S phase", True) + self.selectPAction = self.createAction(self, "&P", self.alterPhase, + "Alt+P", + p_icon, + "Toggle P phase.", True) + self.selectSAction = self.createAction(self, "&S", self.alterPhase, + "Alt+S", + s_icon, + "Toggle S phase", True) printAction = self.createAction(self, "&Print event ...", self.printEvent, QKeySequence.Print, print_icon, @@ -197,7 +201,7 @@ class MainWindow(QMainWindow): self.addActions(self.editMenu, editActions) self.helpMenu = self.menuBar().addMenu('&Help') - helpActions = (helpAction, ) + helpActions = (helpAction,) self.addActions(self.helpMenu, helpActions) fileToolBar = self.addToolBar("FileTools") @@ -273,8 +277,8 @@ class MainWindow(QMainWindow): filt = "Supported event formats (*.mat *.qml *.xml *.kor *.evt)" caption = "Open an event file" fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) + caption=caption, + filter=filt) self.fname = fname[0] else: self.fname = unicode(action.data().toString()) @@ -303,9 +307,9 @@ class MainWindow(QMainWindow): if self.dataStructure: searchPath = self.dataStructure.expandDataPath() fnames = QFileDialog.getOpenFileNames(self, - "Select waveform " - "files:", - dir=searchPath) + "Select waveform " + "files:", + dir=searchPath) self.fnames = fnames[0] else: @@ -342,7 +346,8 @@ class MainWindow(QMainWindow): def getPlotWidget(self): return self.DataPlot - def getWFID(self, gui_event): + @staticmethod + def getWFID(gui_event): ycoord = gui_event.ydata @@ -383,11 +388,12 @@ class MainWindow(QMainWindow): def filterWaveformData(self): if self.getData(): - def hasfreq(kwargs): - for key in kwargs.keys(): + def hasfreq(kwdict): + for key in kwdict.keys(): if not key.startswith('freq'): return True return False + if self.filterAction.isChecked(): kwargs = {} freq = self.getFilterOptions().getFreq() @@ -430,11 +436,11 @@ class MainWindow(QMainWindow): else: self.getFilters()[seismicPhase] = filterOptions - def updateFilterOptions(self): try: settings = QSettings() - if settings.value("filterdefaults", None) is None and not self.getFilters(): + if settings.value("filterdefaults", + None) is None and not self.getFilters(): for key, value in FILTERDEFAULTS.iteritems(): self.setFilterOptions(FilterOptions(**value), key) elif settings.value("filterdefaults", None) is not None: @@ -446,7 +452,9 @@ class MainWindow(QMainWindow): emsg.showMessage('Error: {0}'.format(e)) else: self.updateStatus('Filter loaded ... ' - '[{0}: {1} Hz]'.format(self.getFilterOptions().getFilterType(), self.getFilterOptions().getFreq())) + '[{0}: {1} Hz]'.format( + self.getFilterOptions().getFilterType(), + self.getFilterOptions().getFreq())) if self.filterAction.isChecked(): self.filterWaveformData() @@ -479,7 +487,6 @@ class MainWindow(QMainWindow): else: print 'picks not saved and closed dialog' - def updateStatus(self, message): self.statusBar().showMessage(message, 5000) if self.getData() is not None: @@ -493,7 +500,6 @@ class MainWindow(QMainWindow): "PyLoT - seismic processing the python way[*]") self.setWindowModified(self.dirty) - def printEvent(self): pass diff --git a/makePyLoT.py b/makePyLoT.py index 0a37acd4..bce8fd30 100644 --- a/makePyLoT.py +++ b/makePyLoT.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -''' +""" makePyLoT -- build and install PyLoT makePyLoT is a python make file in order to establish the folder structure and @@ -11,16 +11,16 @@ It defines :method main: :author: Sebastian Wehling-Benatelli - + :copyright: 2014 MAGS2 EP3 Working Group. All rights reserved. - + :license: GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) :contact: sebastian.wehling@rub.de updated: Updated -''' +""" import glob import os @@ -38,19 +38,24 @@ DEBUG = 0 TESTRUN = 0 PROFILE = 0 + class CLIError(Exception): - '''Generic exception to raise and log different fatal errors.''' + """Generic exception to raise and log different fatal errors.""" + def __init__(self, msg): super(CLIError).__init__(type(self)) self.msg = "E: %s" % msg + def __str__(self): return self.msg + def __unicode__(self): return self.msg -def main(argv=None): # IGNORE:C0111 + +def main(argv=None): # IGNORE:C0111 '''Command line options.''' - + if argv is None: argv = sys.argv else: @@ -59,16 +64,17 @@ def main(argv=None): # IGNORE:C0111 program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) - program_version_message = '%makePyLoT %s (%s)' % (program_version, program_build_date) + program_version_message = 'makePyLoT %s (%s)' % ( + program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by Sebastian Wehling-Benatelli on %s. Copyright 2014 MAGS2 EP3 Working Group. All rights reserved. - + GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) - + Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. @@ -77,21 +83,28 @@ USAGE try: # Setup argument parser - parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) - parser.add_argument("-b", "--build", dest="build", action="store_true", help="build PyLoT") - parser.add_argument("-v", "--verbose", dest="verbose", action="count", help="set verbosity level") - parser.add_argument("-i", "--install", dest="install", action="store_true", help="install PyLoT on the system") - parser.add_argument("-d", "--directory", dest="directory", help="installation directory", metavar="RE" ) - parser.add_argument('-V', '--version', action='version', version=program_version_message) - + parser = ArgumentParser(description=program_license, + formatter_class=RawDescriptionHelpFormatter) + parser.add_argument("-b", "--build", dest="build", action="store_true", + help="build PyLoT") + parser.add_argument("-v", "--verbose", dest="verbose", action="count", + help="set verbosity level") + parser.add_argument("-i", "--install", dest="install", + action="store_true", + help="install PyLoT on the system") + parser.add_argument("-d", "--directory", dest="directory", + help="installation directory", metavar="RE") + parser.add_argument('-V', '--version', action='version', + version=program_version_message) + # Process arguments args = parser.parse_args() - + verbose = args.verbose build = args.build install = args.install directory = args.directory - + if verbose > 0: print("Verbose mode on") if install and not directory: @@ -112,22 +125,24 @@ USAGE return 0 except Exception, e: if DEBUG or TESTRUN: - raise(e) + raise e indent = len(program_name) * " " sys.stderr.write(program_name + ": " + repr(e) + "\n") sys.stderr.write(indent + " for help use --help") return 2 + def buildPyLoT(verbosity=None): system = sys.platform if verbosity > 1: msg = ("... on system: {0}\n" "\n" " Current working directory: {1}\n" - ).format(system, os.getcwd()) - print msg + ).format(system, os.getcwd()) + print msg if system.startswith(('win', 'microsoft')): - raise CLIError("building on Windows system not tested yet; implementation pending") + raise CLIError( + "building on Windows system not tested yet; implementation pending") elif system == 'darwin': # create a symbolic link to the desired python interpreter in order to # display the right application name @@ -136,24 +151,28 @@ def buildPyLoT(verbosity=None): if found: os.symlink(found, './PyLoT') break - + def installPyLoT(verbosity=None): pass + def cleanUp(verbosity=None): pass + if __name__ == "__main__": if DEBUG: sys.argv.append("-h") sys.argv.append("-v") if TESTRUN: import doctest + doctest.testmod() if PROFILE: import cProfile import pstats + profile_filename = 'makePyLoT_profile.txt' cProfile.run('main()', profile_filename) statsfile = open("profile_stats.txt", "wb") @@ -162,4 +181,4 @@ if __name__ == "__main__": stats.print_stats() statsfile.close() sys.exit(0) - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/pylot/__init__.py b/pylot/__init__.py index 7e113674..e0981d72 100755 --- a/pylot/__init__.py +++ b/pylot/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -#-------------------------------------------------------- +# -------------------------------------------------------- # Purpose: Convience imports for PyLoT # ''' @@ -24,4 +24,4 @@ The development of PyLoT is part of the joint research project MAGS2. :license: GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) -''' \ No newline at end of file +''' diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9bbedf54..1d620f47 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -161,7 +161,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): index1 = [] i = 0 for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): - i = i + 1 + i += 1 if xraw[j - 1] <= 0 and xraw[j] >= 0: zc1.append(t[ipick][i]) index1.append(i) @@ -196,7 +196,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): index2 = [] i = 0 for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): - i = i + 1 + i += 1 if xfilt[j - 1] <= 0 and xfilt[j] >= 0: zc2.append(t[ipick][i]) index2.append(i) diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index 1f7ca5ff..a1918e18 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -2,5 +2,3 @@ from pylot.core.read.inputs import AutoPickParameter, FilterOptions from pylot.core.read.io import readPILOTEvent from pylot.core.read.data import GenericDataStructure, SeiscompDataStructure, \ PilotDataStructure, Data - - diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 740bd15a..2dcfd359 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -124,20 +124,10 @@ class AutoPickParameter(object): for key, value in self.__parameter.iteritems(): yield key, value - def hasParam(self, *args): - - def test(param): - try: - self.__parameter[param] - return True - except KeyError: - return False - - try: - for param in args: - return test(param) - except TypeError: - return test(args) + def hasParam(self, parameter): + if self.__parameter.has_key(parameter): + return True + return False def getParam(self, *args): try: @@ -157,7 +147,8 @@ class AutoPickParameter(object): self.__setitem__(param, value) print self - def _printParameterError(self, errmsg): + @staticmethod + def _printParameterError(errmsg): print 'ParameterError:\n non-existent parameter %s' % errmsg def export2File(self, fnout): diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py index 6ae3b8fe..6239a512 100644 --- a/pylot/core/util/errors.py +++ b/pylot/core/util/errors.py @@ -9,8 +9,10 @@ Created on Thu Mar 20 09:47:04 2014 class OptionsError(Exception): pass + class FormatError(Exception): pass + class DatastructureError(Exception): pass diff --git a/pylot/core/util/structure.py b/pylot/core/util/structure.py index 34c6aaaf..1b39dd4e 100644 --- a/pylot/core/util/structure.py +++ b/pylot/core/util/structure.py @@ -8,5 +8,5 @@ Created on Wed Jan 26 17:47:25 2015 from pylot.core.read import SeiscompDataStructure, PilotDataStructure -DATASTRUCTURE = {'PILOT':PilotDataStructure, 'SeisComP':SeiscompDataStructure, - None:None} +DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure, + None: None} diff --git a/pylot/core/util/version.py b/pylot/core/util/version.py index 5c87cccf..fd298f3e 100755 --- a/pylot/core/util/version.py +++ b/pylot/core/util/version.py @@ -31,7 +31,7 @@ # # include RELEASE-VERSION -__all__ = ("get_git_version") +__all__ = "get_git_version" # NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time) import os @@ -108,4 +108,4 @@ def get_git_version(abbrev=4): if __name__ == "__main__": - print get_git_version() \ No newline at end of file + print get_git_version() diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e3303780..0161fc0e 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -16,9 +16,9 @@ from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvas from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg from matplotlib.widgets import MultiCursor -from PySide.QtGui import QAction, QApplication,QComboBox, QDateTimeEdit,\ - QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout,\ - QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, QPixmap, QSpinBox,\ +from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ + QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout, \ + QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, QPixmap, QSpinBox, \ QTabWidget, QToolBar, QVBoxLayout, QWidget from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView @@ -48,8 +48,8 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, action.setCheckable(True) return action -class MPLWidget(FigureCanvas): +class MPLWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): self._parent = None @@ -65,7 +65,8 @@ class MPLWidget(FigureCanvas): # initialize super class FigureCanvas.__init__(self, self.figure) # add an cursor for station selection - self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True, + self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), + horizOn=True, color='m', lw=1) # update labels of the entire widget self.updateWidget(xlabel, ylabel, title) @@ -126,7 +127,6 @@ class MPLWidget(FigureCanvas): self.axes.set_xlabel(text) self.draw() - def updateYLabel(self, text): self.axes.set_ylabel(text) self.draw() @@ -142,12 +142,12 @@ class MPLWidget(FigureCanvas): def insertLabel(self, pos, text): pos = pos / max(self.axes.ylim) - axann = self.axes.annotate(text, xy=(.03, pos), xycoords='axes fraction') + axann = self.axes.annotate(text, xy=(.03, pos), + xycoords='axes fraction') axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) class multiComponentPlot(FigureCanvas): - def __init__(self, data, parent=None, components='ZNE'): self.data = data @@ -211,7 +211,7 @@ class multiComponentPlot(FigureCanvas): # plot individual component traces in separate subplots for n, comp in enumerate(components): - nsub = '{0}1{1}'.format(self.noc, n+1) + nsub = '{0}1{1}'.format(self.noc, n + 1) if n >= 1: subax = self.figure.add_subplot(nsub, sharex=self.axesdict[0]) else: @@ -239,7 +239,6 @@ class multiComponentPlot(FigureCanvas): class PickDlg(QDialog): - def __init__(self, parent=None, data=None, station=None, rotate=False): super(PickDlg, self).__init__(parent) @@ -275,8 +274,8 @@ class PickDlg(QDialog): # plot data self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) - self.limits = {'xlims' : self.getPlotWidget().axes.get_xlim(), - 'ylims' : self.getPlotWidget().axes.get_ylim()} + self.limits = {'xlims': self.getPlotWidget().axes.get_xlim(), + 'ylims': self.getPlotWidget().axes.get_ylim()} self.apd = self.getWFData() # set plot labels @@ -420,9 +419,10 @@ class PickDlg(QDialog): def selectWFData(self, channel): component = channel[-1].upper() wfdata = Stream() - def selectTrace(trace, components): - if trace.stats.channel[-1].upper() in components: - return trace + + def selectTrace(tr, components): + if tr.stats.channel[-1].upper() in components: + return tr if component == 'E' or component == 'N': for trace in self.getWFData(): @@ -462,10 +462,10 @@ class PickDlg(QDialog): # see also Diehl et al. 2009 res_wins = { - 'HRW' : 2., - 'MRW' : 5., - 'LRW' : 10., - 'VLRW' : 15. + 'HRW': 2., + 'MRW': 5., + 'LRW': 10., + 'VLRW': 15. } result = getSNR(wfdata, (5., .5, 2.), ini_pick) @@ -501,7 +501,7 @@ class PickDlg(QDialog): def setPick(self, gui_event): # setting pick - pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global + pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global channel = self.getChannelID(round(gui_event.ydata)) wfdata = self.getAPD().copy().select(channel=channel) @@ -512,12 +512,7 @@ class PickDlg(QDialog): phase = self.selectPhase.currentText() # save pick times for actual phase - phasepicks = {} - - phasepicks['epp'] = epp - phasepicks['lpp'] = lpp - phasepicks['mpp'] = pick - phasepicks['spe'] = spe + phasepicks = {'epp': epp, 'lpp': lpp, 'mpp': pick, 'spe': spe} try: oldphasepick = self.picks[phase] @@ -570,10 +565,10 @@ class PickDlg(QDialog): lpp = picks['lpp'] spe = picks['spe'] - ax.fill_between([epp, lpp],ylims[0], ylims[1], alpha=.5, color='c') - ax.plot([mpp-spe, mpp-spe], ylims, 'c--', + ax.fill_between([epp, lpp], ylims[0], ylims[1], alpha=.5, color='c') + ax.plot([mpp - spe, mpp - spe], ylims, 'c--', [mpp, mpp], ylims, 'b-', - [mpp+spe, mpp+spe], ylims, 'c--') + [mpp + spe, mpp + spe], ylims, 'c--') self.getPlotWidget().draw() @@ -648,7 +643,7 @@ class PickDlg(QDialog): curr_ylim = widget.axes.get_ylim() if gui_event.button == 'up': - scale_factor = 1/factor + scale_factor = 1 / factor elif gui_event.button == 'down': # deal with zoom out scale_factor = factor @@ -682,8 +677,8 @@ class PickDlg(QDialog): self.apply() QDialog.accept(self) -class PropertiesDlg(QDialog): +class PropertiesDlg(QDialog): def __init__(self, parent=None): super(PropertiesDlg, self).__init__(parent) @@ -707,7 +702,8 @@ class PropertiesDlg(QDialog): self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) - self.buttonBox.button(QDialogButtonBox.Apply).clicked.connect(self.apply) + self.buttonBox.button(QDialogButtonBox.Apply).clicked.connect( + self.apply) def accept(self, *args, **kwargs): self.apply() @@ -728,7 +724,6 @@ class PropertiesDlg(QDialog): class PropTab(QWidget): - def __init__(self, parent=None): super(PropTab, self).__init__(parent) @@ -737,7 +732,6 @@ class PropTab(QWidget): class InputsTab(PropTab): - def __init__(self, parent): super(InputsTab, self).__init__(parent) @@ -775,15 +769,13 @@ class InputsTab(PropTab): self.setLayout(layout) def getValues(self): - values = {} - values["data/dataRoot"] = self.dataDirEdit.text() - values["user/FullName"] = self.fullNameEdit.text() - values["data/Structure"] = self.structureSelect.currentText() + values = {"data/dataRoot": self.dataDirEdit.text(), + "user/FullName": self.fullNameEdit.text(), + "data/Structure": self.structureSelect.currentText()} return values class OutputsTab(PropTab): - def __init__(self, parent=None): super(OutputsTab, self).__init__(parent) @@ -808,12 +800,11 @@ class OutputsTab(PropTab): self.setLayout(layout) def getValues(self): - values = {} - values["output/Format"] = self.eventOutputComboBox.currentText() + values = {"output/Format": self.eventOutputComboBox.currentText()} return values -class PhasesTab(PropTab): +class PhasesTab(PropTab): def __init__(self, parent=None): super(PhasesTab, self).__init__(parent) @@ -821,7 +812,6 @@ class PhasesTab(PropTab): class GraphicsTab(PropTab): - def __init__(self, parent=None): super(GraphicsTab, self).__init__(parent) @@ -850,13 +840,12 @@ class NewEventDlg(QDialog): self.buttonBox.rejected.connect(self.reject) def getValues(self): - return {'origintime' : self.eventTimeEdit.dateTime().toPython(), - 'latitude' : self.latEdit.text(), - 'longitude' : self.lonEdit.text(), - 'depth' : self.depEdit.text()} + return {'origintime': self.eventTimeEdit.dateTime().toPython(), + 'latitude': self.latEdit.text(), + 'longitude': self.lonEdit.text(), + 'depth': self.depEdit.text()} def setupUI(self): - # create widget objects timeLabel = QLabel() timeLabel.setText("Select time: ") @@ -887,8 +876,8 @@ class NewEventDlg(QDialog): self.setLayout(grid) -class FilterOptionsDialog(QDialog): +class FilterOptionsDialog(QDialog): def __init__(self, parent=None, titleString="Filter options", filterOptions=None): """ @@ -925,8 +914,10 @@ class FilterOptionsDialog(QDialog): if _enable: self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0]) - if self.getFilterOptions().getFilterType() in ['bandpass', 'bandstop']: - self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1]) + if self.getFilterOptions().getFilterType() in ['bandpass', + 'bandstop']: + self.freqmaxSpinBox.setValue( + self.getFilterOptions().getFreq()[1]) else: try: self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()) @@ -963,7 +954,7 @@ class FilterOptionsDialog(QDialog): self.freqmaxSpinBox.setEnabled(_enable) - self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok| + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) grid = QGridLayout() @@ -980,7 +971,6 @@ class FilterOptionsDialog(QDialog): self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) - def updateUi(self): _enable = False if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: @@ -993,10 +983,9 @@ class FilterOptionsDialog(QDialog): self.freqmaxLabel.setEnabled(_enable) self.freqmaxSpinBox.setEnabled(_enable) - - self.getFilterOptions().setFilterType(self.selectTypeCombo.currentText()) - freq = [] - freq.append(self.freqminSpinBox.value()) + self.getFilterOptions().setFilterType( + self.selectTypeCombo.currentText()) + freq = [self.freqminSpinBox.value()] if _enable: if self.freqminSpinBox.value() > self.freqmaxSpinBox.value(): QMessageBox.warning(self, "Value error", @@ -1019,7 +1008,6 @@ class FilterOptionsDialog(QDialog): class LoadDataDlg(QDialog): - def __init__(self, parent=None): super(LoadDataDlg, self).__init__(parent) @@ -1027,8 +1015,8 @@ class LoadDataDlg(QDialog): class HelpForm(QDialog): - - def __init__(self, page=QUrl('https://ariadne.geophysik.rub.de/trac/PyLoT'), parent=None): + def __init__(self, page=QUrl('https://ariadne.geophysik.rub.de/trac/PyLoT'), + parent=None): super(HelpForm, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_GroupLeader) From c5da8fd99491236830ad3c46feb348a15dda3dc2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 11 Jun 2015 10:12:50 +0200 Subject: [PATCH 0359/1144] changed usage signature of some function from module utils and the corresponding calls --- QtPyLoT.py | 4 ++-- pylot/core/read/io.py | 4 ++-- pylot/core/util/utils.py | 37 +++++++++++++++++++++++++------------ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 2834e0ca..47ba0d9a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -509,8 +509,8 @@ class MainWindow(QMainWindow): if new.exec_() != QDialog.Rejected: evtpar = new.getValues() cinfo = createCreationInfo(agency_id=self.agency) - event = createEvent(evtpar['origintime']) - self.data = Data(self, evtdata=createEvent(**evtpar)) + event = createEvent(evtpar['origintime'], cinfo) + self.data = Data(self, evtdata=event) self.dirty = True def closeEvent(self, event): diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index 58a0b10b..bbe5b496 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -74,14 +74,14 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): stations = [stat for stat in phases['stat'][0:-1:3]] - event = createEvent(eventDate, loccinfo, 'earthquake', eventNum, + event = createEvent(eventDate, loccinfo, None, 'earthquake', eventNum, authority_id) lat = float(loc['LAT']) lon = float(loc['LON']) dep = float(loc['DEP']) - origin = createOrigin(eventDate, loccinfo, lat, lon, dep, eventNum) + origin = createOrigin(eventDate, loccinfo, lat, lon, dep) for n, pick in enumerate(phases['Ptime']): if pick[0] > 0: kwargs = {'year': int(pick[0]), diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 3e3f3ac0..61b3fafc 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -107,8 +107,7 @@ def createResourceID(timetohash, restype, authority_id=None, hrstr=None): return resID -def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, - authority_id=None): +def createOrigin(origintime, cinfo, latitude, longitude, depth): ''' createOrigin - function to create an ObsPy Origin :param origintime: the origins time of occurence @@ -121,15 +120,14 @@ def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, :type depth: float :return: An ObsPy :class: `~obspy.core.event.Origin` object ''' - if resID is None: - resID = createResourceID(origintime, 'orig', authority_id=authority_id) - elif isinstance(resID, str): - resID = createResourceID(origintime, 'orig', authority_id=authority_id, - hrstr=resID) + + assert isinstance(origintime, UTCDateTime), "origintime has to be " \ + "a UTCDateTime object, but " \ + "actually is of type " \ + "'%s'" % type(origintime) origin = ope.Origin() - origin.resource_id = resID - origin.time = UTCDateTime(origintime) + origin.time = origintime origin.creation_info = cinfo origin.latitude = latitude origin.longitude = longitude @@ -137,33 +135,48 @@ def createOrigin(origintime, cinfo, latitude, longitude, depth, resID=None, return origin -def createEvent(origintime, cinfo, etype, resID=None, authority_id=None): +def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, + authority_id=None): ''' createEvent - funtion to create an ObsPy Event + :param origintime: the events origintime :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object holding information on the creation of the returned object :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param originloc: tuple containing the location of the origin + (LAT, LON, DEP) affiliated with the event which is created + :type originloc: tuple, list :param etype: Event type str object. converted via ObsPy to a valid event type string. :type etype: str :param resID: Resource identifier of the created event - :type resID: :class: `~obspy.core.event.ResourceIdentifier` object + :type resID: :class: `~obspy.core.event.ResourceIdentifier` object, str :param authority_id: name of the institution carrying out the processing :type authority_id: str :return: An ObsPy :class: `~obspy.core.event.Event` object ''' etype = ope.EventType(etype) + if originloc is not None: + o = createOrigin(origintime, cinfo, + originloc[0], originloc[1], originloc[2]) + else: + o = None if etype is None: etype = ope.EventType('earthquake') # defaults to 'earthquake' - if resID is None: + if not resID: resID = createResourceID(origintime, etype, authority_id) elif isinstance(resID, str): resID = createResourceID(origintime, etype, authority_id, resID) + elif not isinstance(resID, ope.ResourceIdentifier): + raise TypeError("unsupported type(resID) for resource identifier " + "generation: %s" % type(resID)) event = ope.Event(resource_id=resID) event.creation_info = cinfo event.event_type = etype + if o: + event.origins = [o] return event From c5ce958a41b94b4636ea8466f4c0ad03f590870e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 12 Jun 2015 09:02:00 +0200 Subject: [PATCH 0360/1144] just cleaning up the code to meet coding conventions --- pylot/core/pick/Picker.py | 455 ++++++++++--------- pylot/core/pick/run_autopicking.py | 706 +++++++++++++++++------------ 2 files changed, 643 insertions(+), 518 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index c2e20304..3fbe2237 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -3,58 +3,65 @@ Created Dec 2014 to Feb 2015 Implementation of the automated picking algorithms published and described in: -Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, 2010: -Automated determination of P-phase arrival times at regional and local distances -using higher order statistics, Geophys. J. Int., 181, 1159-1170 +Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, +2010: Automated determination of P-phase arrival times at regional and local +distances using higher order statistics, Geophys. J. Int., 181, 1159-1170 Kueperkoch, L., Meier, T., Bruestle, A., Lee, J., Friederich, W., & Egelados Working Group, 2012: Automated determination of S-phase arrival times using -autoregressive prediction: application ot local and regional distances, Geophys. J. Int., -188, 687-702. +autoregressive prediction: application ot local and regional distances, +Geophys. J. Int., 188, 687-702. -The picks with the above described algorithms are assumed to be the most likely picks. -For each most likely pick the corresponding earliest and latest possible picks are -calculated after Diehl & Kissling (2009). +The picks with the above described algorithms are assumed to be the most likely +picks. For each most likely pick the corresponding earliest and latest possible +picks are calculated after Diehl & Kissling (2009). -:author: MAGS2 EP3 working group / Ludger Kueperkoch +:author: MAGS2 EP3 working group / Ludger Kueperkoch """ import numpy as np import matplotlib.pyplot as plt from pylot.core.pick.utils import * from pylot.core.pick.CharFuns import CharacteristicFunction + class AutoPicking(object): ''' - Superclass of different, automated picking algorithms applied on a CF determined - using AIC, HOS, or AR prediction. - ''' - def __init__(self, cf, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, Pick1=None): + Superclass of different, automated picking algorithms applied on a CF + determined using AIC, HOS, or AR prediction. + ''' + + def __init__(self, cf, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, + Pick1=None): ''' - :param: cf, characteristic function, on which the picking algorithm is applied - :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object + :param cf: characteristic function, on which the picking algorithm is + applied + :type cf: `~pylot.core.pick.CharFuns.CharacteristicFunction` object - :param: TSNR, length of time windows around pick used to determine SNR [s] - :type: tuple (T_noise, T_gap, T_signal) + :param TSNR: length of time windows for SNR determination - [s] + :type TSNR: tuple (T_noise, T_gap, T_signal) - :param: PickWindow, length of pick window [s] - :type: float + :param PickWindow: length of pick window - [s] + :type PickWindow: float - :param: iplot, no. of figure window for plotting interims results - :type: integer + :param iplot: no. of figure window for plotting interims results + :type iplot: integer - :param: aus ("artificial uplift of samples"), find local minimum at i if aic(i-1)*(1+aus) >= aic(i) - :type: float + :param aus: aus ("artificial uplift of samples"), find local minimum at + i if aic(i-1)*(1+aus) >= aic(i) + :type aus: float - :param: Tsmooth, length of moving smoothing window to calculate smoothed CF [s] - :type: float + :param Tsmooth: length of moving window to calculate smoothed CF - [s] + :type Tsmooth: float - :param: Pick1, initial (prelimenary) onset time, starting point for PragPicker and - EarlLatePicker - :type: float + :param Pick1: initial (prelimenary) onset time, starting point for + PragPicker and EarlLatePicker + :type Pick1: float ''' - assert isinstance(cf, CharacteristicFunction), "%s is not a CharacteristicFunction object" % str(cf) + assert isinstance(cf, + CharacteristicFunction), "%s is of wrong type" % str( + cf) self.cf = cf.getCF() self.Tcf = cf.getTimeArray() @@ -80,9 +87,8 @@ class AutoPicking(object): PickWindow=self.getPickWindow(), aus=self.getaus(), Tsmooth=self.getTsmooth(), - Pick1=self.getpick1()) + Pick1=self.getpick1()) - def getTSNR(self): return self.TSNR @@ -112,7 +118,7 @@ class AutoPicking(object): def getSNR(self): return self.SNR - + def getSlope(self): return self.slope @@ -136,144 +142,156 @@ class AICPicker(AutoPicking): ''' Method to derive the onset time of an arriving phase based on CF derived from AIC. In order to get an impression of the quality of this inital pick, - a quality assessment is applied based on SNR and slope determination derived from the CF, + a quality assessment is applied based on SNR and slope determination derived from the CF, from which the AIC has been calculated. ''' def calcPick(self): - + print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' self.Pick = None self.slope = None self.SNR = None - #find NaN's + # find NaN's nn = np.isnan(self.cf) if len(nn) > 1: - self.cf[nn] = 0 - #taper AIC-CF to get rid off side maxima + self.cf[nn] = 0 + # taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) - #smooth AIC-CF + # smooth AIC-CF ismooth = int(round(self.Tsmooth / self.dt)) aicsmooth = np.zeros(len(aic)) if len(aic) < ismooth: - print 'AICPicker: Tsmooth larger than CF!' - return + print 'AICPicker: Tsmooth larger than CF!' + return else: - for i in range(1, len(aic)): - if i > ismooth: - ii1 = i - ismooth - aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth - else: - aicsmooth[i] = np.mean(aic[1 : i]) - #remove offset + for i in range(1, len(aic)): + if i > ismooth: + ii1 = i - ismooth + aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ + ii1]) / ismooth + else: + aicsmooth[i] = np.mean(aic[1: i]) + # remove offset offset = abs(min(aic) - min(aicsmooth)) aicsmooth = aicsmooth - offset - #get maximum of 1st derivative of AIC-CF (more stable!) as starting point + # get maximum of 1st derivative of AIC-CF (more stable!) as starting + # point diffcf = np.diff(aicsmooth) - #find NaN's + # find NaN's nn = np.isnan(diffcf) if len(nn) > 1: - diffcf[nn] = 0 - #taper CF to get rid off side maxima + diffcf[nn] = 0 + # taper CF to get rid off side maxima tap = np.hanning(len(diffcf)) diffcf = tap * diffcf * max(abs(aicsmooth)) icfmax = np.argmax(diffcf) - - #find minimum in AIC-CF front of maximum - lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if aicsmooth[i - 1] >= aicsmooth[i]: - self.Pick = self.Tcf[i] - break - #if no minimum could be found: - #search in 1st derivative of AIC-CF - if self.Pick is None: - for i in range(icfmax -1, max([icfmax -lpickwindow, 2]), -1): - if diffcf[i -1] >= diffcf[i]: - self.Pick = self.Tcf[i] - break - - #quality assessment using SNR and slope from CF - if self.Pick is not None: - #get noise window - inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1]) - #check, if these are counts or m/s, important for slope estimation! - #this is quick and dirty, better solution? - if max(self.Data[0].data < 1e-3): - self.Data[0].data = self.Data[0].data * 1000000 - #get signal window - isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) - #calculate SNR from CF - self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / max(abs(aic[inoise] \ - - np.mean(aic[inoise]))) - #calculate slope from CF after initial pick - #get slope window - tslope = self.TSNR[3] #slope determination window - islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ - & (self.Tcf >= self.Pick)) - #find maximum within slope determination window - #'cause slope should be calculated up to first local minimum only! - imax = np.argmax(self.Data[0].data[islope]) - if imax == 0: - print 'AICPicker: Maximum for slope determination right at the beginning of the window!' - print 'Choose longer slope determination window!' - return - islope = islope[0][0 :imax] - dataslope = self.Data[0].data[islope] - #calculate slope as polynomal fit of order 1 - xslope = np.arange(0, len(dataslope), 1) - P = np.polyfit(xslope, dataslope, 1) - datafit = np.polyval(P, xslope) - if datafit[0] >= datafit[len(datafit) - 1]: - print 'AICPicker: Negative slope, bad onset skipped!' - return - self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] + # find minimum in AIC-CF front of maximum + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if aicsmooth[i - 1] >= aicsmooth[i]: + self.Pick = self.Tcf[i] + break + # if no minimum could be found: + # search in 1st derivative of AIC-CF + if self.Pick is None: + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if diffcf[i - 1] >= diffcf[i]: + self.Pick = self.Tcf[i] + break + + # quality assessment using SNR and slope from CF + if self.Pick is not None: + # get noise window + inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], + self.TSNR[1]) + # check, if these are counts or m/s, important for slope estimation! + # this is quick and dirty, better solution? + if max(self.Data[0].data < 1e-3): + self.Data[0].data *= 1000000 + # get signal window + isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) + # calculate SNR from CF + self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / \ + max(abs(aic[inoise] - np.mean(aic[inoise]))) + # calculate slope from CF after initial pick + # get slope window + tslope = self.TSNR[3] # slope determination window + islope = np.where( + (self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) + and (self.Tcf >= self.Pick)) + # find maximum within slope determination window + # 'cause slope should be calculated up to first local minimum only! + imax = np.argmax(self.Data[0].data[islope]) + if imax == 0: + print 'AICPicker: Maximum for slope determination right at ' \ + 'the beginning of the window!' + print 'Choose longer slope determination window!' + return + islope = islope[0][0:imax] + dataslope = self.Data[0].data[islope] + # calculate slope as polynomal fit of order 1 + xslope = np.arange(0, len(dataslope), 1) + P = np.polyfit(xslope, dataslope, 1) + datafit = np.polyval(P, xslope) + if datafit[0] >= datafit[len(datafit) - 1]: + print 'AICPicker: Negative slope, bad onset skipped!' + return + + self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] else: - self.SNR = None - self.slope = None + self.SNR = None + self.slope = None if self.iplot > 1: - p = plt.figure(self.iplot) - x = self.Data[0].data - p1, = plt.plot(self.Tcf, x / max(x), 'k') - p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') - if self.Pick is not None: - p3, = plt.plot([self.Pick, self.Pick], [-0.1 , 0.5], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) - else: - plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) + p = plt.figure(self.iplot) + x = self.Data[0].data + p1, = plt.plot(self.Tcf, x / max(x), 'k') + p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') + if self.Pick is not None: + p3, = plt.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', + linewidth=2) + plt.legend([p1, p2, p3], + ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) + else: + plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) - if self.Pick is not None: - plt.figure(self.iplot + 1) - p11, = plt.plot(self.Tcf, x, 'k') - p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) - p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') - p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') - p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) - plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], \ - loc='best') - plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ - self.SNR, self.slope)) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.ylabel('Counts') - ax = plt.gca() - plt.yticks([]) - ax.set_xlim([self.Tcf[inoise[0][0]] - 5, self.Tcf[isignal[0][len(isignal) - 1]] + 5]) + if self.Pick is not None: + plt.figure(self.iplot + 1) + p11, = plt.plot(self.Tcf, x, 'k') + p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) + p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], + 'r') + p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') + p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) + plt.legend([p11, p12, p13, p14, p15], + ['Data', 'Noise Window', 'Signal Window', + 'Slope Window', 'Slope'], + loc='best') + plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % ( + self.Data[0].stats.station, + self.SNR, self.slope)) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.ylabel('Counts') + ax = plt.gca() + plt.yticks([]) + ax.set_xlim([self.Tcf[inoise[0][0]] - 5, + self.Tcf[isignal[0][len(isignal) - 1]] + 5]) - plt.show() - raw_input() - plt.close(p) + plt.show() + raw_input() + plt.close(p) + + if self.Pick is None: + print 'AICPicker: Could not find minimum, picking window too short?' - if self.Pick == None: - print 'AICPicker: Could not find minimum, picking window too short?' - class PragPicker(AutoPicking): ''' @@ -283,90 +301,95 @@ class PragPicker(AutoPicking): def calcPick(self): if self.getpick1() is not None: - print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' + print 'PragPicker: Get most likely pick from HOS- or AR-CF using ' \ + 'pragmatic picking algorithm ...' - self.Pick = None - self.SNR = None - self.slope = None - #smooth CF - ismooth = int(round(self.Tsmooth / self.dt)) - cfsmooth = np.zeros(len(self.cf)) - if len(self.cf) < ismooth: - print 'PragPicker: Tsmooth larger than CF!' - return - else: - for i in range(1, len(self.cf)): - if i > ismooth: - ii1 = i - ismooth; - cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth - else: - cfsmooth[i] = np.mean(self.cf[1 : i]) + self.Pick = None + self.SNR = None + self.slope = None + # smooth CF + ismooth = int(round(self.Tsmooth / self.dt)) + cfsmooth = np.zeros(len(self.cf)) + if len(self.cf) < ismooth: + print 'PragPicker: Tsmooth larger than CF!' + return + else: + for i in range(1, len(self.cf)): + if i > ismooth: + ii1 = i - ismooth + cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ + ii1]) / ismooth + else: + cfsmooth[i] = np.mean(self.cf[1: i]) - #select picking window - #which is centered around tpick1 - ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ - & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) - cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) - Tcfpick = self.Tcf[ipick] - cfsmoothipick = cfsmooth[ipick]- np.mean(self.cf[ipick]) - ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) - cfpick1 = 2 * self.cf[ipick1] + # select picking window + # which is centered around tpick1 + ipick = np.where((self.Tcf >= + (self.getpick1() - self.PickWindow / 2)) and + (self.Tcf <= + (self.getpick1() + self.PickWindow / 2))) + cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) + Tcfpick = self.Tcf[ipick] + cfsmoothipick = cfsmooth[ipick] - np.mean(self.cf[ipick]) + ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) + cfpick1 = 2 * self.cf[ipick1] - #check trend of CF, i.e. differences of CF and adjust aus regarding this trend - #prominent trend: decrease aus - #flat: use given aus - cfdiff = np.diff(cfipick); - i0diff = np.where(cfdiff > 0) - cfdiff = cfdiff[i0diff] - minaus = min(cfdiff * (1 + self.aus)); - aus1 = max([minaus, self.aus]); + # check trend of CF, i.e. differences of CF and adjust aus regarding this trend + # prominent trend: decrease aus + # flat: use given aus + cfdiff = np.diff(cfipick) + i0diff = np.where(cfdiff > 0) + cfdiff = cfdiff[i0diff] + minaus = min(cfdiff * (1 + self.aus)) + aus1 = max([minaus, self.aus]) - #at first we look to the right until the end of the pick window is reached - flagpick_r = 0 - flagpick_l = 0 - flagpick = 0 - lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_r = self.Tcf[i] - self.Pick = pick_r - flagpick_l = 1 - cfpick_r = self.cf[i] - break + # at first we look to the right until the end of the pick window is reached + flagpick_r = 0 + flagpick_l = 0 + flagpick = 0 + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(max(np.insert(ipick, 0, 2)), + min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_r = self.Tcf[i] + self.Pick = pick_r + flagpick_l = 1 + cfpick_r = self.cf[i] + break - #now we look to the left - for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_l = self.Tcf[i] - self.Pick = pick_l - flagpick_r = 1 - cfpick_l = self.cf[i] - break + # now we look to the left + for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_l = self.Tcf[i] + self.Pick = pick_l + flagpick_r = 1 + cfpick_l = self.cf[i] + break - #now decide which pick: left or right? - if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: - self.Pick = pick_l - elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: - self.Pick = pick_r + # now decide which pick: left or right? + if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: + self.Pick = pick_l + elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + self.Pick = pick_r - if self.getiplot() > 1: - p = plt.figure(self.getiplot()) - p1, = plt.plot(Tcfpick,cfipick, 'k') - p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') - p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) - plt.show() - raw_input() - plt.close(p) + if self.getiplot() > 1: + p = plt.figure(self.getiplot()) + p1, = plt.plot(Tcfpick, cfipick, 'k') + p2, = plt.plot(Tcfpick, cfsmoothipick, 'r') + p3, = plt.plot([self.Pick, self.Pick], + [min(cfipick), max(cfipick)], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(p) - else: - self.Pick = None - print 'PragPicker: No initial onset time given! Check input!' - return + else: + self.Pick = None + print 'PragPicker: No initial onset time given! Check input!' diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index b8c9389b..eddff959 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -3,43 +3,38 @@ """ Function to run automated picking algorithms using AIC, -HOS and AR prediction. Uses object CharFuns and Picker and +HOS and AR prediction. Uses object CharFuns and Picker and function conglomerate utils. :author: MAGS2 EP3 working group / Ludger Kueperkoch """ -from obspy.core import read import matplotlib.pyplot as plt import numpy as np -from pylot.core.pick.CharFuns import * from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * -from pylot.core.pick import utils def run_autopicking(wfstream, pickparam): - - ''' + """ param: wfstream :type: `~obspy.core.stream.Stream` param: pickparam :type: container of picking parameters from input file, usually autoPyLoT.in - ''' + """ # declaring pickparam variables (only for convenience) # read your autoPyLoT.in for details! - #special parameters for P picking - algoP = pickparam.getParam('algoP') + # special parameters for P picking + algoP = pickparam.getParam('algoP') iplot = pickparam.getParam('iplot') pstart = pickparam.getParam('pstart') pstop = pickparam.getParam('pstop') thosmw = pickparam.getParam('tlta') - hosorder = pickparam.getParam('hosorder') - tsnrz = pickparam.getParam('tsnrz') + tsnrz = pickparam.getParam('tsnrz') hosorder = pickparam.getParam('hosorder') bpz1 = pickparam.getParam('bpz1') bpz2 = pickparam.getParam('bpz2') @@ -55,13 +50,13 @@ def run_autopicking(wfstream, pickparam): minAICPslope = pickparam.getParam('minAICPslope') minAICPSNR = pickparam.getParam('minAICPSNR') timeerrorsP = pickparam.getParam('timeerrorsP') - #special parameters for S picking - algoS = pickparam.getParam('algoS') + # special parameters for S picking + algoS = pickparam.getParam('algoS') sstart = pickparam.getParam('sstart') sstop = pickparam.getParam('sstop') bph1 = pickparam.getParam('bph1') bph2 = pickparam.getParam('bph2') - tsnrh = pickparam.getParam('tsnrh') + tsnrh = pickparam.getParam('tsnrh') pickwinS = pickparam.getParam('pickwinS') tpred1h = pickparam.getParam('tpred1h') tdet1h = pickparam.getParam('tdet1h') @@ -76,7 +71,7 @@ def run_autopicking(wfstream, pickparam): Srecalcwin = pickparam.getParam('Srecalcwin') nfacS = pickparam.getParam('nfacS') timeerrorsS = pickparam.getParam('timeerrorsS') - #parameters for first-motion determination + # parameters for first-motion determination minFMSNR = pickparam.getParam('minFMSNR') fmpickwin = pickparam.getParam('fmpickwin') minfmweight = pickparam.getParam('minfmweight') @@ -84,164 +79,192 @@ def run_autopicking(wfstream, pickparam): # split components zdat = wfstream.select(component="Z") edat = wfstream.select(component="E") - if len(edat) == 0: #check for other components + if len(edat) == 0: # check for other components edat = wfstream.select(component="2") ndat = wfstream.select(component="N") - if len(ndat) == 0: #check for other components + if len(ndat) == 0: # check for other components ndat = wfstream.select(component="1") if algoP == 'HOS' or algoP == 'ARZ' and zdat is not None: print '##########################################' - print 'run_autopicking: Working on P onset of station %s' % zdat[0].stats.station + print 'run_autopicking: Working on P onset of station %s' % zdat[ + 0].stats.station print 'Filtering vertical trace ...' print zdat z_copy = zdat.copy() - #filter and taper data + # filter and taper data tr_filt = zdat[0].copy() - tr_filt.filter('bandpass', freqmin=bpz1[0], freqmax=bpz1[1], zerophase=False) + tr_filt.filter('bandpass', freqmin=bpz1[0], freqmax=bpz1[1], + zerophase=False) tr_filt.taper(max_percentage=0.05, type='hann') z_copy[0].data = tr_filt.data ############################################################## - #check length of waveform and compare with cut times + # check length of waveform and compare with cut times Lc = pstop - pstart Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime Ldiff = Lwf - Lc if Ldiff < 0: - print 'run_autopicking: Cutting times are too large for actual waveform!' - print 'Use entire waveform instead!' - pstart = 0 - pstop = len(zdat[0].data) * zdat[0].stats.delta + print 'run_autopicking: Cutting times are too large for actual ' \ + 'waveform!' + print 'Use entire waveform instead!' + pstart = 0 + pstop = len(zdat[0].data) * zdat[0].stats.delta cuttimes = [pstart, pstop] if algoP == 'HOS': - #calculate HOS-CF using subclass HOScf of class CharacteristicFunction - cf1 = HOScf(z_copy, cuttimes, thosmw, hosorder) #instance of HOScf + # calculate HOS-CF using subclass HOScf of class + # CharacteristicFunction + cf1 = HOScf(z_copy, cuttimes, thosmw, hosorder) # instance of HOScf elif algoP == 'ARZ': - #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction - cf1 = ARZcf(z_copy, cuttimes, tpred1z, Parorder, tdet1z, addnoise) #instance of ARZcf + # calculate ARZ-CF using subclass ARZcf of class + # CharcteristicFunction + cf1 = ARZcf(z_copy, cuttimes, tpred1z, Parorder, tdet1z, + addnoise) # instance of ARZcf ############################################################## - #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it + # calculate AIC-HOS-CF using subclass AICcf of class + # CharacteristicFunction + # class needs stream object => build it tr_aic = tr_filt.copy() - tr_aic.data =cf1.getCF() + tr_aic.data = cf1.getCF() z_copy[0].data = tr_aic.data - aiccf = AICcf(z_copy, cuttimes) #instance of AICcf + aiccf = AICcf(z_copy, cuttimes) # instance of AICcf ############################################################## - #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking + # get prelimenary onset time from AIC-HOS-CF using subclass AICPicker + # of class AutoPicking aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, tsmoothP) ############################################################## - #go on with processing if AIC onset passes quality control - if aicpick.getSlope() >= minAICPslope and aicpick.getSNR() >= minAICPSNR: - aicPflag = 1 - print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ + # go on with processing if AIC onset passes quality control + if (aicpick.getSlope() >= minAICPslope and + aicpick.getSNR() >= minAICPSNR): + aicPflag = 1 + print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ (aicpick.getSlope(), aicpick.getSNR()) - print 'Go on with refined picking ...' - #re-filter waveform with larger bandpass - print 'run_autopicking: re-filtering vertical trace ...' - z_copy = zdat.copy() - tr_filt = zdat[0].copy() - tr_filt.filter('bandpass', freqmin=bpz2[0], freqmax=bpz2[1], zerophase=False) - tr_filt.taper(max_percentage=0.05, type='hann') - z_copy[0].data = tr_filt.data - ############################################################# - #re-calculate CF from re-filtered trace in vicinity of initial onset - cuttimes2 = [round(max([aicpick.getpick() - Precalcwin, 0])), \ - round(min([len(zdat[0].data) * zdat[0].stats.delta, \ - aicpick.getpick() + Precalcwin]))] - if algoP == 'HOS': - #calculate HOS-CF using subclass HOScf of class CharacteristicFunction - cf2 = HOScf(z_copy, cuttimes2, thosmw, hosorder) #instance of HOScf - elif algoP == 'ARZ': - #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction - cf2 = ARZcf(z_copy, cuttimes2, tpred1z, Parorder, tdet1z, addnoise) #instance of ARZcf - ############################################################## - #get refined onset time from CF2 using class Picker - refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, aicpick.getpick()) - ############################################################# - #quality assessment - #get earliest and latest possible pick and symmetrized uncertainty - [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, refPpick.getpick(), iplot) + print 'Go on with refined picking ...' + # re-filter waveform with larger bandpass + print 'run_autopicking: re-filtering vertical trace ...' + z_copy = zdat.copy() + tr_filt = zdat[0].copy() + tr_filt.filter('bandpass', freqmin=bpz2[0], freqmax=bpz2[1], + zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + z_copy[0].data = tr_filt.data + ############################################################# + # re-calculate CF from re-filtered trace in vicinity of initial + # onset + cuttimes2 = [round(max([aicpick.getpick() - Precalcwin, 0])), + round(min([len(zdat[0].data) * zdat[0].stats.delta, + aicpick.getpick() + Precalcwin]))] + if algoP == 'HOS': + # calculate HOS-CF using subclass HOScf of class + # CharacteristicFunction + cf2 = HOScf(z_copy, cuttimes2, thosmw, + hosorder) # instance of HOScf + elif algoP == 'ARZ': + # calculate ARZ-CF using subclass ARZcf of class + # CharcteristicFunction + cf2 = ARZcf(z_copy, cuttimes2, tpred1z, Parorder, tdet1z, + addnoise) # instance of ARZcf + ############################################################## + # get refined onset time from CF2 using class Picker + refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, + aicpick.getpick()) + ############################################################# + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, + refPpick.getpick(), iplot) - #get SNR - [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, refPpick.getpick()) + # get SNR + [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, + refPpick.getpick()) - #weight P-onset using symmetric error - if Perror <= timeerrorsP[0]: - Pweight = 0 - elif Perror > timeerrorsP[0] and Perror <= timeerrorsP[1]: - Pweight = 1 - elif Perror > timeerrorsP[1] and Perror <= timeerrorsP[2]: - Pweight = 2 - elif Perror > timeerrorsP[2] and Perror <= timeerrorsP[3]: - Pweight = 3 - elif Perror > timeerrorsP[3]: - Pweight = 4 + # weight P-onset using symmetric error + if Perror <= timeerrorsP[0]: + Pweight = 0 + elif timeerrorsP[0] < Perror <= timeerrorsP[1]: + Pweight = 1 + elif timeerrorsP[1] < Perror <= timeerrorsP[2]: + Pweight = 2 + elif timeerrorsP[2] < Perror <= timeerrorsP[3]: + Pweight = 3 + elif Perror > timeerrorsP[3]: + Pweight = 4 + + ############################################################## + # get first motion of P onset + # certain quality required + if Pweight <= minfmweight and SNRP >= minFMSNR: + FM = fmpicker(zdat, z_copy, fmpickwin, refPpick.getpick(), + iplot) + else: + FM = 'N' + + print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ + 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) - ############################################################## - #get first motion of P onset - #certain quality required - if Pweight <= minfmweight and SNRP >= minFMSNR: - FM = fmpicker(zdat, z_copy, fmpickwin, refPpick.getpick(), iplot) - else: - FM = 'N' - - print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) - else: - print 'Bad initial (AIC) P-pick, skip this onset!' - print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() - Pweight = 4 - Sweight = 4 - FM = 'N' - SNRP = None - SNRPdB = None - SNRS = None - SNRSdB = None - aicSflag = 0 - aicPflag = 0 + print 'Bad initial (AIC) P-pick, skip this onset!' + print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() + Pweight = 4 + Sweight = 4 + FM = 'N' + SNRP = None + SNRPdB = None + SNRS = None + SNRSdB = None + aicSflag = 0 + aicPflag = 0 else: - print 'run_autopicking: No vertical component data available, skipping station!' - return + print 'run_autopicking: No vertical component data available, ' \ + 'skipping station!' + return - if edat is not None and ndat is not None and len(edat) > 0 and len(ndat) > 0 and Pweight < 4: - print 'Go on picking S onset ...' + if edat is not None and ndat is not None and len(edat) > 0 and len( + ndat) > 0 and Pweight < 4: + print 'Go on picking S onset ...' print '##################################################' print 'Working on S onset of station %s' % edat[0].stats.station print 'Filtering horizontal traces ...' - #determine time window for calculating CF after P onset - #cuttimesh = [round(refPpick.getpick() + sstart), round(refPpick.getpick() + sstop)] - cuttimesh = [round(max([refPpick.getpick() + sstart, 0])), \ + # determine time window for calculating CF after P onset + # cuttimesh = [round(refPpick.getpick() + sstart), + # round(refPpick.getpick() + sstop)] + cuttimesh = [round(max([refPpick.getpick() + sstart, 0])), round(min([refPpick.getpick() + sstop, Lwf]))] if algoS == 'ARH': print edat, ndat - #re-create stream object including both horizontal components + # re-create stream object including both horizontal components hdat = edat.copy() hdat += ndat h_copy = hdat.copy() - #filter and taper data + # filter and taper data trH1_filt = hdat[0].copy() trH2_filt = hdat[1].copy() - trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) trH1_filt.taper(max_percentage=0.05, type='hann') trH2_filt.taper(max_percentage=0.05, type='hann') h_copy[0].data = trH1_filt.data h_copy[1].data = trH2_filt.data elif algoS == 'AR3': print zdat, edat, ndat - #re-create stream object including both horizontal components + # re-create stream object including both horizontal components hdat = zdat.copy() hdat += edat hdat += ndat h_copy = hdat.copy() - #filter and taper data + # filter and taper data trH1_filt = hdat[0].copy() trH2_filt = hdat[1].copy() trH3_filt = hdat[2].copy() - trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) - trH3_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) + trH3_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) trH1_filt.taper(max_percentage=0.05, type='hann') trH2_filt.taper(max_percentage=0.05, type='hann') trH3_filt.taper(max_percentage=0.05, type='hann') @@ -250,210 +273,289 @@ def run_autopicking(wfstream, pickparam): h_copy[2].data = trH3_filt.data ############################################################## if algoS == 'ARH': - #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction - arhcf1 = ARHcf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, addnoise) #instance of ARHcf + # calculate ARH-CF using subclass ARHcf of class + # CharcteristicFunction + arhcf1 = ARHcf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, + addnoise) # instance of ARHcf elif algoS == 'AR3': - #calculate ARH-CF using subclass AR3cf of class CharcteristicFunction - arhcf1 = AR3Ccf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, addnoise) #instance of ARHcf + # calculate ARH-CF using subclass AR3cf of class + # CharcteristicFunction + arhcf1 = AR3Ccf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, + addnoise) # instance of ARHcf ############################################################## - #calculate AIC-ARH-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it + # calculate AIC-ARH-CF using subclass AICcf of class + # CharacteristicFunction + # class needs stream object => build it tr_arhaic = trH1_filt.copy() tr_arhaic.data = arhcf1.getCF() h_copy[0].data = tr_arhaic.data - #calculate ARH-AIC-CF - haiccf = AICcf(h_copy, cuttimesh) #instance of AICcf + # calculate ARH-AIC-CF + haiccf = AICcf(h_copy, cuttimesh) # instance of AICcf ############################################################## - #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicarhpick = AICPicker(haiccf, tsnrh, pickwinS, iplot, None, aictsmoothS) + # get prelimenary onset time from AIC-HOS-CF using subclass AICPicker + # of class AutoPicking + aicarhpick = AICPicker(haiccf, tsnrh, pickwinS, iplot, None, + aictsmoothS) ############################################################### - #go on with processing if AIC onset passes quality control - if aicarhpick.getSlope() >= minAICSslope and aicarhpick.getSNR() >= minAICSSNR: - aicSflag = 1 - print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ - % (aicarhpick.getSlope(), aicarhpick.getSNR()) - print 'Go on with refined picking ...' - #re-calculate CF from re-filtered trace in vicinity of initial onset - cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), \ - round(aicarhpick.getpick() + Srecalcwin)] - #re-filter waveform with larger bandpass - print 'run_autopicking: re-filtering horizontal traces...' - h_copy = hdat.copy() - #filter and taper data - if algoS == 'ARH': - trH1_filt = hdat[0].copy() - trH2_filt = hdat[1].copy() - trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - h_copy[0].data = trH1_filt.data - h_copy[1].data = trH2_filt.data - ############################################################# - arhcf2 = ARHcf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, addnoise) #instance of ARHcf - elif algoS == 'AR3': - trH1_filt = hdat[0].copy() - trH2_filt = hdat[1].copy() - trH3_filt = hdat[2].copy() - trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH3_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - trH3_filt.taper(max_percentage=0.05, type='hann') - h_copy[0].data = trH1_filt.data - h_copy[1].data = trH2_filt.data - h_copy[2].data = trH3_filt.data - ############################################################# - arhcf2 = AR3Ccf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, addnoise) #instance of ARHcf + # go on with processing if AIC onset passes quality control + if (aicarhpick.getSlope() >= minAICSslope and + aicarhpick.getSNR() >= minAICSSNR): + aicSflag = 1 + print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ + % (aicarhpick.getSlope(), aicarhpick.getSNR()) + print 'Go on with refined picking ...' + # re-calculate CF from re-filtered trace in vicinity of initial + # onset + cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), + round(aicarhpick.getpick() + Srecalcwin)] + # re-filter waveform with larger bandpass + print 'run_autopicking: re-filtering horizontal traces...' + h_copy = hdat.copy() + # filter and taper data + if algoS == 'ARH': + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + ############################################################# + arhcf2 = ARHcf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, + addnoise) # instance of ARHcf + elif algoS == 'AR3': + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH3_filt = hdat[2].copy() + trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH3_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + trH3_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + h_copy[2].data = trH3_filt.data + ############################################################# + arhcf2 = AR3Ccf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, + addnoise) # instance of ARHcf - #get refined onset time from CF2 using class Picker - refSpick = PragPicker(arhcf2, tsnrh, pickwinS, iplot, ausS, tsmoothS, aicarhpick.getpick()) - ############################################################# - #quality assessment - #get earliest and latest possible pick and symmetrized uncertainty - h_copy[0].data = trH1_filt.data - [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) - h_copy[0].data = trH2_filt.data - [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) - if algoS == 'ARH': - #get earliest pick of both earliest possible picks - epick = [epickS1, epickS2] - lpick = [lpickS1, lpickS2] - pickerr = [Serror1, Serror2] - ipick =np.argmin([epickS1, epickS2]) - elif algoS == 'AR3': - [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) - #get earliest pick of all three picks - epick = [epickS1, epickS2, epickS3] - lpick = [lpickS1, lpickS2, lpickS3] - pickerr = [Serror1, Serror2, Serror3] - ipick =np.argmin([epickS1, epickS2, epickS3]) - epickS = epick[ipick] - lpickS = lpick[ipick] - Serror = pickerr[ipick] + # get refined onset time from CF2 using class Picker + refSpick = PragPicker(arhcf2, tsnrh, pickwinS, iplot, ausS, + tsmoothS, aicarhpick.getpick()) + ############################################################# + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + h_copy[0].data = trH1_filt.data + [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, + refSpick.getpick(), + iplot) + h_copy[0].data = trH2_filt.data + [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, + refSpick.getpick(), + iplot) + if algoS == 'ARH': + # get earliest pick of both earliest possible picks + epick = [epickS1, epickS2] + lpick = [lpickS1, lpickS2] + pickerr = [Serror1, Serror2] + ipick = np.argmin([epickS1, epickS2]) + elif algoS == 'AR3': + [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, + tsnrh, + refSpick.getpick(), + iplot) + # get earliest pick of all three picks + epick = [epickS1, epickS2, epickS3] + lpick = [lpickS1, lpickS2, lpickS3] + pickerr = [Serror1, Serror2, Serror3] + ipick = np.argmin([epickS1, epickS2, epickS3]) + epickS = epick[ipick] + lpickS = lpick[ipick] + Serror = pickerr[ipick] - #get SNR - [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, refSpick.getpick()) + # get SNR + [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, + refSpick.getpick()) - #weight S-onset using symmetric error - if Serror <= timeerrorsS[0]: - Sweight = 0 - elif Serror > timeerrorsS[0] and Serror <= timeerrorsS[1]: - Sweight = 1 - elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: - Sweight = 2 - elif Serror > timeerrorsS[2] and Serror <= timeerrorsS[3]: - Sweight = 3 - elif Serror > timeerrorsS[3]: - Sweight = 4 + # weight S-onset using symmetric error + if Serror <= timeerrorsS[0]: + Sweight = 0 + elif timeerrorsS[0] < Serror <= timeerrorsS[1]: + Sweight = 1 + elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: + Sweight = 2 + elif timeerrorsS[2] < Serror <= timeerrorsS[3]: + Sweight = 3 + elif Serror > timeerrorsS[3]: + Sweight = 4 - print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % (Sweight, SNRS, SNRSdB) + print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( + Sweight, SNRS, SNRSdB) else: - print 'Bad initial (AIC) S-pick, skip this onset!' - print 'AIC-SNR=', aicarhpick.getSNR(), 'AIC-Slope=', aicarhpick.getSlope() - Sweight = 4 - SNRS = None - SNRSdB = None - aicSflag = 0 - + print 'Bad initial (AIC) S-pick, skip this onset!' + print 'AIC-SNR=', aicarhpick.getSNR(), \ + 'AIC-Slope=', aicarhpick.getSlope() + Sweight = 4 + SNRS = None + SNRSdB = None + aicSflag = 0 + else: - print 'run_autopicking: No horizontal component data available or bad P onset, skipping S picking!' - return + print 'run_autopicking: No horizontal component data available or ' \ + 'bad P onset, skipping S picking!' + return ############################################################## if iplot > 0: - #plot vertical trace - plt.figure() - plt.subplot(3,1,1) - tdata = np.arange(0, zdat[0].stats.npts / tr_filt.stats.sampling_rate, tr_filt.stats.delta) - #check equal length of arrays, sometimes they are different!? - wfldiff = len(tr_filt.data) - len(tdata) - if wfldiff < 0: - tdata = tdata[0:len(tdata) - abs(wfldiff)] - p1, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - if Pweight < 4: - p2, = plt.plot(cf1.getTimeArray(), cf1.getCF() / max(cf1.getCF()), 'b') - if aicPflag == 1: - p3, = plt.plot(cf2.getTimeArray(), cf2.getCF() / max(cf2.getCF()), 'm') - p4, = plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'r') - plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'r') - plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'r') - p5, = plt.plot([refPpick.getpick(), refPpick.getpick()], [-1.3, 1.3], 'r', linewidth=2) - plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [1.3, 1.3], 'r', linewidth=2) - plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [-1.3, -1.3], 'r', linewidth=2) - plt.plot([lpickP, lpickP], [-1.1, 1.1], 'r--') - plt.plot([epickP, epickP], [-1.1, 1.1], 'r--') - plt.legend([p1, p2, p3, p4, p5], ['Data', 'CF1', 'CF2', 'Initial P Onset', 'Final P Pick']) - plt.title('%s, %s, P Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f Polarity: %s' % (tr_filt.stats.station, \ - tr_filt.stats.channel, Pweight, SNRP, SNRPdB, FM)) - else: - plt.legend([p1, p2], ['Data', 'CF1']) - plt.title('%s, P Weight=%d, SNR=None, SNRdB=None' % (tr_filt.stats.channel, Pweight)) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.ylabel('Normalized Counts') - plt.suptitle(tr_filt.stats.starttime) + # plot vertical trace + plt.figure() + plt.subplot(3, 1, 1) + tdata = np.arange(0, zdat[0].stats.npts / tr_filt.stats.sampling_rate, + tr_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(tr_filt.data) - len(tdata) + if wfldiff < 0: + tdata = tdata[0:len(tdata) - abs(wfldiff)] + p1, = plt.plot(tdata, tr_filt.data / max(tr_filt.data), 'k') + if Pweight < 4: + p2, = plt.plot(cf1.getTimeArray(), cf1.getCF() / max(cf1.getCF()), + 'b') + if aicPflag == 1: + p3, = plt.plot(cf2.getTimeArray(), + cf2.getCF() / max(cf2.getCF()), 'm') + p4, = plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], + 'r') + plt.plot([aicpick.getpick() - 0.5, aicpick.getpick() + 0.5], + [1, 1], 'r') + plt.plot([aicpick.getpick() - 0.5, aicpick.getpick() + 0.5], + [-1, -1], 'r') + p5, = plt.plot([refPpick.getpick(), refPpick.getpick()], + [-1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick() - 0.5, refPpick.getpick() + 0.5], + [1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick() - 0.5, refPpick.getpick() + 0.5], + [-1.3, -1.3], 'r', linewidth=2) + plt.plot([lpickP, lpickP], [-1.1, 1.1], 'r--') + plt.plot([epickP, epickP], [-1.1, 1.1], 'r--') + plt.legend([p1, p2, p3, p4, p5], + ['Data', 'CF1', 'CF2', 'Initial P Onset', + 'Final P Pick']) + plt.title('%s, %s, P Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f ' + 'Polarity: %s' % (tr_filt.stats.station, + tr_filt.stats.channel, + Pweight, + SNRP, + SNRPdB, + FM)) + else: + plt.legend([p1, p2], ['Data', 'CF1']) + plt.title('%s, P Weight=%d, SNR=None, ' + 'SNRdB=None' % (tr_filt.stats.channel, Pweight)) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.suptitle(tr_filt.stats.starttime) - #plot horizontal traces - plt.subplot(3,1,2) - th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) - #check equal length of arrays, sometimes they are different!? - wfldiff = len(trH1_filt.data) - len(th1data) - if wfldiff < 0: - th1data = th1data[0:len(th1data) - abs(wfldiff)] - p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - if Pweight < 4: - p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') - if aicSflag == 1: - p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') - p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') - plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) - plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % (trH1_filt.stats.channel, \ - Sweight, SNRS, SNRSdB)) - else: - plt.legend([p21, p22], ['Data', 'CF1']) - plt.title('%s, S Weight=%d, SNR=None, SNRdB=None' % (trH1_filt.stats.channel, Sweight)) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.ylabel('Normalized Counts') - plt.suptitle(trH1_filt.stats.starttime) + # plot horizontal traces + plt.subplot(3, 1, 2) + th1data = np.arange(0, + trH1_filt.stats.npts / + trH1_filt.stats.sampling_rate, + trH1_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(trH1_filt.data) - len(th1data) + if wfldiff < 0: + th1data = th1data[0:len(th1data) - abs(wfldiff)] + p21, = plt.plot(th1data, trH1_filt.data / max(trH1_filt.data), 'k') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), + arhcf1.getCF() / max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), + arhcf2.getCF() / max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], + [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], + ['Data', 'CF1', 'CF2', 'Initial S Onset', + 'Final S Pick']) + plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % ( + trH1_filt.stats.channel, + Sweight, SNRS, SNRSdB)) + else: + plt.legend([p21, p22], ['Data', 'CF1']) + plt.title('%s, S Weight=%d, SNR=None, SNRdB=None' % ( + trH1_filt.stats.channel, Sweight)) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.suptitle(trH1_filt.stats.starttime) - plt.subplot(3,1,3) - th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) - #check equal length of arrays, sometimes they are different!? - wfldiff = len(trH2_filt.data) - len(th2data) - if wfldiff < 0: - th2data = th2data[0:len(th2data) - abs(wfldiff)] - plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - if Pweight < 4: - p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') - if aicSflag == 1: - p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') - p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') - plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) - else: - plt.legend([p21, p22], ['Data', 'CF1']) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) - plt.ylabel('Normalized Counts') - plt.title(trH2_filt.stats.channel) - plt.show() - raw_input() - plt.close() + plt.subplot(3, 1, 3) + th2data = np.arange(0, + trH2_filt.stats.npts / + trH2_filt.stats.sampling_rate, + trH2_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(trH2_filt.data) - len(th2data) + if wfldiff < 0: + th2data = th2data[0:len(th2data) - abs(wfldiff)] + plt.plot(th2data, trH2_filt.data / max(trH2_filt.data), 'k') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), + arhcf1.getCF() / max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), + arhcf2.getCF() / max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], + [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], + ['Data', 'CF1', 'CF2', 'Initial S Onset', + 'Final S Pick']) + else: + plt.legend([p21, p22], ['Data', 'CF1']) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) + plt.ylabel('Normalized Counts') + plt.title(trH2_filt.stats.channel) + plt.show() + + +raw_input() +plt.close() From 883fdf6bf5f95358f21c979af362be799e6f4ec9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 12 Jun 2015 09:02:00 +0200 Subject: [PATCH 0361/1144] just cleaning up the code to meet coding conventions --- pylot/core/pick/Picker.py | 455 ++++++++++--------- pylot/core/pick/run_autopicking.py | 706 +++++++++++++++++------------ 2 files changed, 643 insertions(+), 518 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index c2e20304..2c1fc26f 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -3,58 +3,65 @@ Created Dec 2014 to Feb 2015 Implementation of the automated picking algorithms published and described in: -Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, 2010: -Automated determination of P-phase arrival times at regional and local distances -using higher order statistics, Geophys. J. Int., 181, 1159-1170 +Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, +2010: Automated determination of P-phase arrival times at regional and local +distances using higher order statistics, Geophys. J. Int., 181, 1159-1170 Kueperkoch, L., Meier, T., Bruestle, A., Lee, J., Friederich, W., & Egelados Working Group, 2012: Automated determination of S-phase arrival times using -autoregressive prediction: application ot local and regional distances, Geophys. J. Int., -188, 687-702. +autoregressive prediction: application ot local and regional distances, +Geophys. J. Int., 188, 687-702. -The picks with the above described algorithms are assumed to be the most likely picks. -For each most likely pick the corresponding earliest and latest possible picks are -calculated after Diehl & Kissling (2009). +The picks with the above described algorithms are assumed to be the most likely +picks. For each most likely pick the corresponding earliest and latest possible +picks are calculated after Diehl & Kissling (2009). -:author: MAGS2 EP3 working group / Ludger Kueperkoch +:author: MAGS2 EP3 working group / Ludger Kueperkoch """ import numpy as np import matplotlib.pyplot as plt from pylot.core.pick.utils import * from pylot.core.pick.CharFuns import CharacteristicFunction + class AutoPicking(object): ''' - Superclass of different, automated picking algorithms applied on a CF determined - using AIC, HOS, or AR prediction. - ''' - def __init__(self, cf, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, Pick1=None): + Superclass of different, automated picking algorithms applied on a CF + determined using AIC, HOS, or AR prediction. + ''' + + def __init__(self, cf, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, + Pick1=None): ''' - :param: cf, characteristic function, on which the picking algorithm is applied - :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object + :param cf: characteristic function, on which the picking algorithm is + applied + :type cf: `~pylot.core.pick.CharFuns.CharacteristicFunction` object - :param: TSNR, length of time windows around pick used to determine SNR [s] - :type: tuple (T_noise, T_gap, T_signal) + :param TSNR: length of time windows for SNR determination - [s] + :type TSNR: tuple (T_noise, T_gap, T_signal) - :param: PickWindow, length of pick window [s] - :type: float + :param PickWindow: length of pick window - [s] + :type PickWindow: float - :param: iplot, no. of figure window for plotting interims results - :type: integer + :param iplot: no. of figure window for plotting interims results + :type iplot: integer - :param: aus ("artificial uplift of samples"), find local minimum at i if aic(i-1)*(1+aus) >= aic(i) - :type: float + :param aus: aus ("artificial uplift of samples"), find local minimum at + i if aic(i-1)*(1+aus) >= aic(i) + :type aus: float - :param: Tsmooth, length of moving smoothing window to calculate smoothed CF [s] - :type: float + :param Tsmooth: length of moving window to calculate smoothed CF - [s] + :type Tsmooth: float - :param: Pick1, initial (prelimenary) onset time, starting point for PragPicker and - EarlLatePicker - :type: float + :param Pick1: initial (prelimenary) onset time, starting point for + PragPicker + :type Pick1: float ''' - assert isinstance(cf, CharacteristicFunction), "%s is not a CharacteristicFunction object" % str(cf) + assert isinstance(cf, + CharacteristicFunction), "%s is of wrong type" % str( + cf) self.cf = cf.getCF() self.Tcf = cf.getTimeArray() @@ -80,9 +87,8 @@ class AutoPicking(object): PickWindow=self.getPickWindow(), aus=self.getaus(), Tsmooth=self.getTsmooth(), - Pick1=self.getpick1()) + Pick1=self.getpick1()) - def getTSNR(self): return self.TSNR @@ -112,7 +118,7 @@ class AutoPicking(object): def getSNR(self): return self.SNR - + def getSlope(self): return self.slope @@ -136,144 +142,156 @@ class AICPicker(AutoPicking): ''' Method to derive the onset time of an arriving phase based on CF derived from AIC. In order to get an impression of the quality of this inital pick, - a quality assessment is applied based on SNR and slope determination derived from the CF, + a quality assessment is applied based on SNR and slope determination derived from the CF, from which the AIC has been calculated. ''' def calcPick(self): - + print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' self.Pick = None self.slope = None self.SNR = None - #find NaN's + # find NaN's nn = np.isnan(self.cf) if len(nn) > 1: - self.cf[nn] = 0 - #taper AIC-CF to get rid off side maxima + self.cf[nn] = 0 + # taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) - #smooth AIC-CF + # smooth AIC-CF ismooth = int(round(self.Tsmooth / self.dt)) aicsmooth = np.zeros(len(aic)) if len(aic) < ismooth: - print 'AICPicker: Tsmooth larger than CF!' - return + print 'AICPicker: Tsmooth larger than CF!' + return else: - for i in range(1, len(aic)): - if i > ismooth: - ii1 = i - ismooth - aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth - else: - aicsmooth[i] = np.mean(aic[1 : i]) - #remove offset + for i in range(1, len(aic)): + if i > ismooth: + ii1 = i - ismooth + aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ + ii1]) / ismooth + else: + aicsmooth[i] = np.mean(aic[1: i]) + # remove offset offset = abs(min(aic) - min(aicsmooth)) aicsmooth = aicsmooth - offset - #get maximum of 1st derivative of AIC-CF (more stable!) as starting point + # get maximum of 1st derivative of AIC-CF (more stable!) as starting + # point diffcf = np.diff(aicsmooth) - #find NaN's + # find NaN's nn = np.isnan(diffcf) if len(nn) > 1: - diffcf[nn] = 0 - #taper CF to get rid off side maxima + diffcf[nn] = 0 + # taper CF to get rid off side maxima tap = np.hanning(len(diffcf)) diffcf = tap * diffcf * max(abs(aicsmooth)) icfmax = np.argmax(diffcf) - - #find minimum in AIC-CF front of maximum - lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if aicsmooth[i - 1] >= aicsmooth[i]: - self.Pick = self.Tcf[i] - break - #if no minimum could be found: - #search in 1st derivative of AIC-CF - if self.Pick is None: - for i in range(icfmax -1, max([icfmax -lpickwindow, 2]), -1): - if diffcf[i -1] >= diffcf[i]: - self.Pick = self.Tcf[i] - break - - #quality assessment using SNR and slope from CF - if self.Pick is not None: - #get noise window - inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1]) - #check, if these are counts or m/s, important for slope estimation! - #this is quick and dirty, better solution? - if max(self.Data[0].data < 1e-3): - self.Data[0].data = self.Data[0].data * 1000000 - #get signal window - isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) - #calculate SNR from CF - self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / max(abs(aic[inoise] \ - - np.mean(aic[inoise]))) - #calculate slope from CF after initial pick - #get slope window - tslope = self.TSNR[3] #slope determination window - islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ - & (self.Tcf >= self.Pick)) - #find maximum within slope determination window - #'cause slope should be calculated up to first local minimum only! - imax = np.argmax(self.Data[0].data[islope]) - if imax == 0: - print 'AICPicker: Maximum for slope determination right at the beginning of the window!' - print 'Choose longer slope determination window!' - return - islope = islope[0][0 :imax] - dataslope = self.Data[0].data[islope] - #calculate slope as polynomal fit of order 1 - xslope = np.arange(0, len(dataslope), 1) - P = np.polyfit(xslope, dataslope, 1) - datafit = np.polyval(P, xslope) - if datafit[0] >= datafit[len(datafit) - 1]: - print 'AICPicker: Negative slope, bad onset skipped!' - return - self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] + # find minimum in AIC-CF front of maximum + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if aicsmooth[i - 1] >= aicsmooth[i]: + self.Pick = self.Tcf[i] + break + # if no minimum could be found: + # search in 1st derivative of AIC-CF + if self.Pick is None: + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if diffcf[i - 1] >= diffcf[i]: + self.Pick = self.Tcf[i] + break + + # quality assessment using SNR and slope from CF + if self.Pick is not None: + # get noise window + inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], + self.TSNR[1]) + # check, if these are counts or m/s, important for slope estimation! + # this is quick and dirty, better solution? + if max(self.Data[0].data < 1e-3): + self.Data[0].data *= 1000000 + # get signal window + isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) + # calculate SNR from CF + self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / \ + max(abs(aic[inoise] - np.mean(aic[inoise]))) + # calculate slope from CF after initial pick + # get slope window + tslope = self.TSNR[3] # slope determination window + islope = np.where( + (self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) + and (self.Tcf >= self.Pick)) + # find maximum within slope determination window + # 'cause slope should be calculated up to first local minimum only! + imax = np.argmax(self.Data[0].data[islope]) + if imax == 0: + print 'AICPicker: Maximum for slope determination right at ' \ + 'the beginning of the window!' + print 'Choose longer slope determination window!' + return + islope = islope[0][0:imax] + dataslope = self.Data[0].data[islope] + # calculate slope as polynomal fit of order 1 + xslope = np.arange(0, len(dataslope), 1) + P = np.polyfit(xslope, dataslope, 1) + datafit = np.polyval(P, xslope) + if datafit[0] >= datafit[len(datafit) - 1]: + print 'AICPicker: Negative slope, bad onset skipped!' + return + + self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] else: - self.SNR = None - self.slope = None + self.SNR = None + self.slope = None if self.iplot > 1: - p = plt.figure(self.iplot) - x = self.Data[0].data - p1, = plt.plot(self.Tcf, x / max(x), 'k') - p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') - if self.Pick is not None: - p3, = plt.plot([self.Pick, self.Pick], [-0.1 , 0.5], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) - else: - plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) + p = plt.figure(self.iplot) + x = self.Data[0].data + p1, = plt.plot(self.Tcf, x / max(x), 'k') + p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') + if self.Pick is not None: + p3, = plt.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', + linewidth=2) + plt.legend([p1, p2, p3], + ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) + else: + plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) - if self.Pick is not None: - plt.figure(self.iplot + 1) - p11, = plt.plot(self.Tcf, x, 'k') - p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) - p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') - p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') - p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) - plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], \ - loc='best') - plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ - self.SNR, self.slope)) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.ylabel('Counts') - ax = plt.gca() - plt.yticks([]) - ax.set_xlim([self.Tcf[inoise[0][0]] - 5, self.Tcf[isignal[0][len(isignal) - 1]] + 5]) + if self.Pick is not None: + plt.figure(self.iplot + 1) + p11, = plt.plot(self.Tcf, x, 'k') + p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) + p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], + 'r') + p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') + p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) + plt.legend([p11, p12, p13, p14, p15], + ['Data', 'Noise Window', 'Signal Window', + 'Slope Window', 'Slope'], + loc='best') + plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % ( + self.Data[0].stats.station, + self.SNR, self.slope)) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.ylabel('Counts') + ax = plt.gca() + plt.yticks([]) + ax.set_xlim([self.Tcf[inoise[0][0]] - 5, + self.Tcf[isignal[0][len(isignal) - 1]] + 5]) - plt.show() - raw_input() - plt.close(p) + plt.show() + raw_input() + plt.close(p) + + if self.Pick is None: + print 'AICPicker: Could not find minimum, picking window too short?' - if self.Pick == None: - print 'AICPicker: Could not find minimum, picking window too short?' - class PragPicker(AutoPicking): ''' @@ -283,90 +301,95 @@ class PragPicker(AutoPicking): def calcPick(self): if self.getpick1() is not None: - print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' + print 'PragPicker: Get most likely pick from HOS- or AR-CF using ' \ + 'pragmatic picking algorithm ...' - self.Pick = None - self.SNR = None - self.slope = None - #smooth CF - ismooth = int(round(self.Tsmooth / self.dt)) - cfsmooth = np.zeros(len(self.cf)) - if len(self.cf) < ismooth: - print 'PragPicker: Tsmooth larger than CF!' - return - else: - for i in range(1, len(self.cf)): - if i > ismooth: - ii1 = i - ismooth; - cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth - else: - cfsmooth[i] = np.mean(self.cf[1 : i]) + self.Pick = None + self.SNR = None + self.slope = None + # smooth CF + ismooth = int(round(self.Tsmooth / self.dt)) + cfsmooth = np.zeros(len(self.cf)) + if len(self.cf) < ismooth: + print 'PragPicker: Tsmooth larger than CF!' + return + else: + for i in range(1, len(self.cf)): + if i > ismooth: + ii1 = i - ismooth + cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ + ii1]) / ismooth + else: + cfsmooth[i] = np.mean(self.cf[1: i]) - #select picking window - #which is centered around tpick1 - ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ - & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) - cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) - Tcfpick = self.Tcf[ipick] - cfsmoothipick = cfsmooth[ipick]- np.mean(self.cf[ipick]) - ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) - cfpick1 = 2 * self.cf[ipick1] + # select picking window + # which is centered around tpick1 + ipick = np.where((self.Tcf >= + (self.getpick1() - self.PickWindow / 2)) and + (self.Tcf <= + (self.getpick1() + self.PickWindow / 2))) + cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) + Tcfpick = self.Tcf[ipick] + cfsmoothipick = cfsmooth[ipick] - np.mean(self.cf[ipick]) + ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) + cfpick1 = 2 * self.cf[ipick1] - #check trend of CF, i.e. differences of CF and adjust aus regarding this trend - #prominent trend: decrease aus - #flat: use given aus - cfdiff = np.diff(cfipick); - i0diff = np.where(cfdiff > 0) - cfdiff = cfdiff[i0diff] - minaus = min(cfdiff * (1 + self.aus)); - aus1 = max([minaus, self.aus]); + # check trend of CF, i.e. differences of CF and adjust aus regarding this trend + # prominent trend: decrease aus + # flat: use given aus + cfdiff = np.diff(cfipick) + i0diff = np.where(cfdiff > 0) + cfdiff = cfdiff[i0diff] + minaus = min(cfdiff * (1 + self.aus)) + aus1 = max([minaus, self.aus]) - #at first we look to the right until the end of the pick window is reached - flagpick_r = 0 - flagpick_l = 0 - flagpick = 0 - lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_r = self.Tcf[i] - self.Pick = pick_r - flagpick_l = 1 - cfpick_r = self.cf[i] - break + # at first we look to the right until the end of the pick window is reached + flagpick_r = 0 + flagpick_l = 0 + flagpick = 0 + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(max(np.insert(ipick, 0, 2)), + min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_r = self.Tcf[i] + self.Pick = pick_r + flagpick_l = 1 + cfpick_r = self.cf[i] + break - #now we look to the left - for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_l = self.Tcf[i] - self.Pick = pick_l - flagpick_r = 1 - cfpick_l = self.cf[i] - break + # now we look to the left + for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_l = self.Tcf[i] + self.Pick = pick_l + flagpick_r = 1 + cfpick_l = self.cf[i] + break - #now decide which pick: left or right? - if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: - self.Pick = pick_l - elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: - self.Pick = pick_r + # now decide which pick: left or right? + if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: + self.Pick = pick_l + elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + self.Pick = pick_r - if self.getiplot() > 1: - p = plt.figure(self.getiplot()) - p1, = plt.plot(Tcfpick,cfipick, 'k') - p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') - p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) - plt.show() - raw_input() - plt.close(p) + if self.getiplot() > 1: + p = plt.figure(self.getiplot()) + p1, = plt.plot(Tcfpick, cfipick, 'k') + p2, = plt.plot(Tcfpick, cfsmoothipick, 'r') + p3, = plt.plot([self.Pick, self.Pick], + [min(cfipick), max(cfipick)], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(p) - else: - self.Pick = None - print 'PragPicker: No initial onset time given! Check input!' - return + else: + self.Pick = None + print 'PragPicker: No initial onset time given! Check input!' diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index b8c9389b..eddff959 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -3,43 +3,38 @@ """ Function to run automated picking algorithms using AIC, -HOS and AR prediction. Uses object CharFuns and Picker and +HOS and AR prediction. Uses object CharFuns and Picker and function conglomerate utils. :author: MAGS2 EP3 working group / Ludger Kueperkoch """ -from obspy.core import read import matplotlib.pyplot as plt import numpy as np -from pylot.core.pick.CharFuns import * from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * -from pylot.core.pick import utils def run_autopicking(wfstream, pickparam): - - ''' + """ param: wfstream :type: `~obspy.core.stream.Stream` param: pickparam :type: container of picking parameters from input file, usually autoPyLoT.in - ''' + """ # declaring pickparam variables (only for convenience) # read your autoPyLoT.in for details! - #special parameters for P picking - algoP = pickparam.getParam('algoP') + # special parameters for P picking + algoP = pickparam.getParam('algoP') iplot = pickparam.getParam('iplot') pstart = pickparam.getParam('pstart') pstop = pickparam.getParam('pstop') thosmw = pickparam.getParam('tlta') - hosorder = pickparam.getParam('hosorder') - tsnrz = pickparam.getParam('tsnrz') + tsnrz = pickparam.getParam('tsnrz') hosorder = pickparam.getParam('hosorder') bpz1 = pickparam.getParam('bpz1') bpz2 = pickparam.getParam('bpz2') @@ -55,13 +50,13 @@ def run_autopicking(wfstream, pickparam): minAICPslope = pickparam.getParam('minAICPslope') minAICPSNR = pickparam.getParam('minAICPSNR') timeerrorsP = pickparam.getParam('timeerrorsP') - #special parameters for S picking - algoS = pickparam.getParam('algoS') + # special parameters for S picking + algoS = pickparam.getParam('algoS') sstart = pickparam.getParam('sstart') sstop = pickparam.getParam('sstop') bph1 = pickparam.getParam('bph1') bph2 = pickparam.getParam('bph2') - tsnrh = pickparam.getParam('tsnrh') + tsnrh = pickparam.getParam('tsnrh') pickwinS = pickparam.getParam('pickwinS') tpred1h = pickparam.getParam('tpred1h') tdet1h = pickparam.getParam('tdet1h') @@ -76,7 +71,7 @@ def run_autopicking(wfstream, pickparam): Srecalcwin = pickparam.getParam('Srecalcwin') nfacS = pickparam.getParam('nfacS') timeerrorsS = pickparam.getParam('timeerrorsS') - #parameters for first-motion determination + # parameters for first-motion determination minFMSNR = pickparam.getParam('minFMSNR') fmpickwin = pickparam.getParam('fmpickwin') minfmweight = pickparam.getParam('minfmweight') @@ -84,164 +79,192 @@ def run_autopicking(wfstream, pickparam): # split components zdat = wfstream.select(component="Z") edat = wfstream.select(component="E") - if len(edat) == 0: #check for other components + if len(edat) == 0: # check for other components edat = wfstream.select(component="2") ndat = wfstream.select(component="N") - if len(ndat) == 0: #check for other components + if len(ndat) == 0: # check for other components ndat = wfstream.select(component="1") if algoP == 'HOS' or algoP == 'ARZ' and zdat is not None: print '##########################################' - print 'run_autopicking: Working on P onset of station %s' % zdat[0].stats.station + print 'run_autopicking: Working on P onset of station %s' % zdat[ + 0].stats.station print 'Filtering vertical trace ...' print zdat z_copy = zdat.copy() - #filter and taper data + # filter and taper data tr_filt = zdat[0].copy() - tr_filt.filter('bandpass', freqmin=bpz1[0], freqmax=bpz1[1], zerophase=False) + tr_filt.filter('bandpass', freqmin=bpz1[0], freqmax=bpz1[1], + zerophase=False) tr_filt.taper(max_percentage=0.05, type='hann') z_copy[0].data = tr_filt.data ############################################################## - #check length of waveform and compare with cut times + # check length of waveform and compare with cut times Lc = pstop - pstart Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime Ldiff = Lwf - Lc if Ldiff < 0: - print 'run_autopicking: Cutting times are too large for actual waveform!' - print 'Use entire waveform instead!' - pstart = 0 - pstop = len(zdat[0].data) * zdat[0].stats.delta + print 'run_autopicking: Cutting times are too large for actual ' \ + 'waveform!' + print 'Use entire waveform instead!' + pstart = 0 + pstop = len(zdat[0].data) * zdat[0].stats.delta cuttimes = [pstart, pstop] if algoP == 'HOS': - #calculate HOS-CF using subclass HOScf of class CharacteristicFunction - cf1 = HOScf(z_copy, cuttimes, thosmw, hosorder) #instance of HOScf + # calculate HOS-CF using subclass HOScf of class + # CharacteristicFunction + cf1 = HOScf(z_copy, cuttimes, thosmw, hosorder) # instance of HOScf elif algoP == 'ARZ': - #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction - cf1 = ARZcf(z_copy, cuttimes, tpred1z, Parorder, tdet1z, addnoise) #instance of ARZcf + # calculate ARZ-CF using subclass ARZcf of class + # CharcteristicFunction + cf1 = ARZcf(z_copy, cuttimes, tpred1z, Parorder, tdet1z, + addnoise) # instance of ARZcf ############################################################## - #calculate AIC-HOS-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it + # calculate AIC-HOS-CF using subclass AICcf of class + # CharacteristicFunction + # class needs stream object => build it tr_aic = tr_filt.copy() - tr_aic.data =cf1.getCF() + tr_aic.data = cf1.getCF() z_copy[0].data = tr_aic.data - aiccf = AICcf(z_copy, cuttimes) #instance of AICcf + aiccf = AICcf(z_copy, cuttimes) # instance of AICcf ############################################################## - #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking + # get prelimenary onset time from AIC-HOS-CF using subclass AICPicker + # of class AutoPicking aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, tsmoothP) ############################################################## - #go on with processing if AIC onset passes quality control - if aicpick.getSlope() >= minAICPslope and aicpick.getSNR() >= minAICPSNR: - aicPflag = 1 - print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ + # go on with processing if AIC onset passes quality control + if (aicpick.getSlope() >= minAICPslope and + aicpick.getSNR() >= minAICPSNR): + aicPflag = 1 + print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ (aicpick.getSlope(), aicpick.getSNR()) - print 'Go on with refined picking ...' - #re-filter waveform with larger bandpass - print 'run_autopicking: re-filtering vertical trace ...' - z_copy = zdat.copy() - tr_filt = zdat[0].copy() - tr_filt.filter('bandpass', freqmin=bpz2[0], freqmax=bpz2[1], zerophase=False) - tr_filt.taper(max_percentage=0.05, type='hann') - z_copy[0].data = tr_filt.data - ############################################################# - #re-calculate CF from re-filtered trace in vicinity of initial onset - cuttimes2 = [round(max([aicpick.getpick() - Precalcwin, 0])), \ - round(min([len(zdat[0].data) * zdat[0].stats.delta, \ - aicpick.getpick() + Precalcwin]))] - if algoP == 'HOS': - #calculate HOS-CF using subclass HOScf of class CharacteristicFunction - cf2 = HOScf(z_copy, cuttimes2, thosmw, hosorder) #instance of HOScf - elif algoP == 'ARZ': - #calculate ARZ-CF using subclass ARZcf of class CharcteristicFunction - cf2 = ARZcf(z_copy, cuttimes2, tpred1z, Parorder, tdet1z, addnoise) #instance of ARZcf - ############################################################## - #get refined onset time from CF2 using class Picker - refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, aicpick.getpick()) - ############################################################# - #quality assessment - #get earliest and latest possible pick and symmetrized uncertainty - [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, refPpick.getpick(), iplot) + print 'Go on with refined picking ...' + # re-filter waveform with larger bandpass + print 'run_autopicking: re-filtering vertical trace ...' + z_copy = zdat.copy() + tr_filt = zdat[0].copy() + tr_filt.filter('bandpass', freqmin=bpz2[0], freqmax=bpz2[1], + zerophase=False) + tr_filt.taper(max_percentage=0.05, type='hann') + z_copy[0].data = tr_filt.data + ############################################################# + # re-calculate CF from re-filtered trace in vicinity of initial + # onset + cuttimes2 = [round(max([aicpick.getpick() - Precalcwin, 0])), + round(min([len(zdat[0].data) * zdat[0].stats.delta, + aicpick.getpick() + Precalcwin]))] + if algoP == 'HOS': + # calculate HOS-CF using subclass HOScf of class + # CharacteristicFunction + cf2 = HOScf(z_copy, cuttimes2, thosmw, + hosorder) # instance of HOScf + elif algoP == 'ARZ': + # calculate ARZ-CF using subclass ARZcf of class + # CharcteristicFunction + cf2 = ARZcf(z_copy, cuttimes2, tpred1z, Parorder, tdet1z, + addnoise) # instance of ARZcf + ############################################################## + # get refined onset time from CF2 using class Picker + refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, + aicpick.getpick()) + ############################################################# + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, + refPpick.getpick(), iplot) - #get SNR - [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, refPpick.getpick()) + # get SNR + [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, + refPpick.getpick()) - #weight P-onset using symmetric error - if Perror <= timeerrorsP[0]: - Pweight = 0 - elif Perror > timeerrorsP[0] and Perror <= timeerrorsP[1]: - Pweight = 1 - elif Perror > timeerrorsP[1] and Perror <= timeerrorsP[2]: - Pweight = 2 - elif Perror > timeerrorsP[2] and Perror <= timeerrorsP[3]: - Pweight = 3 - elif Perror > timeerrorsP[3]: - Pweight = 4 + # weight P-onset using symmetric error + if Perror <= timeerrorsP[0]: + Pweight = 0 + elif timeerrorsP[0] < Perror <= timeerrorsP[1]: + Pweight = 1 + elif timeerrorsP[1] < Perror <= timeerrorsP[2]: + Pweight = 2 + elif timeerrorsP[2] < Perror <= timeerrorsP[3]: + Pweight = 3 + elif Perror > timeerrorsP[3]: + Pweight = 4 + + ############################################################## + # get first motion of P onset + # certain quality required + if Pweight <= minfmweight and SNRP >= minFMSNR: + FM = fmpicker(zdat, z_copy, fmpickwin, refPpick.getpick(), + iplot) + else: + FM = 'N' + + print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ + 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) - ############################################################## - #get first motion of P onset - #certain quality required - if Pweight <= minfmweight and SNRP >= minFMSNR: - FM = fmpicker(zdat, z_copy, fmpickwin, refPpick.getpick(), iplot) - else: - FM = 'N' - - print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) - else: - print 'Bad initial (AIC) P-pick, skip this onset!' - print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() - Pweight = 4 - Sweight = 4 - FM = 'N' - SNRP = None - SNRPdB = None - SNRS = None - SNRSdB = None - aicSflag = 0 - aicPflag = 0 + print 'Bad initial (AIC) P-pick, skip this onset!' + print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() + Pweight = 4 + Sweight = 4 + FM = 'N' + SNRP = None + SNRPdB = None + SNRS = None + SNRSdB = None + aicSflag = 0 + aicPflag = 0 else: - print 'run_autopicking: No vertical component data available, skipping station!' - return + print 'run_autopicking: No vertical component data available, ' \ + 'skipping station!' + return - if edat is not None and ndat is not None and len(edat) > 0 and len(ndat) > 0 and Pweight < 4: - print 'Go on picking S onset ...' + if edat is not None and ndat is not None and len(edat) > 0 and len( + ndat) > 0 and Pweight < 4: + print 'Go on picking S onset ...' print '##################################################' print 'Working on S onset of station %s' % edat[0].stats.station print 'Filtering horizontal traces ...' - #determine time window for calculating CF after P onset - #cuttimesh = [round(refPpick.getpick() + sstart), round(refPpick.getpick() + sstop)] - cuttimesh = [round(max([refPpick.getpick() + sstart, 0])), \ + # determine time window for calculating CF after P onset + # cuttimesh = [round(refPpick.getpick() + sstart), + # round(refPpick.getpick() + sstop)] + cuttimesh = [round(max([refPpick.getpick() + sstart, 0])), round(min([refPpick.getpick() + sstop, Lwf]))] if algoS == 'ARH': print edat, ndat - #re-create stream object including both horizontal components + # re-create stream object including both horizontal components hdat = edat.copy() hdat += ndat h_copy = hdat.copy() - #filter and taper data + # filter and taper data trH1_filt = hdat[0].copy() trH2_filt = hdat[1].copy() - trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) trH1_filt.taper(max_percentage=0.05, type='hann') trH2_filt.taper(max_percentage=0.05, type='hann') h_copy[0].data = trH1_filt.data h_copy[1].data = trH2_filt.data elif algoS == 'AR3': print zdat, edat, ndat - #re-create stream object including both horizontal components + # re-create stream object including both horizontal components hdat = zdat.copy() hdat += edat hdat += ndat h_copy = hdat.copy() - #filter and taper data + # filter and taper data trH1_filt = hdat[0].copy() trH2_filt = hdat[1].copy() trH3_filt = hdat[2].copy() - trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) - trH3_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], zerophase=False) + trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) + trH3_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], + zerophase=False) trH1_filt.taper(max_percentage=0.05, type='hann') trH2_filt.taper(max_percentage=0.05, type='hann') trH3_filt.taper(max_percentage=0.05, type='hann') @@ -250,210 +273,289 @@ def run_autopicking(wfstream, pickparam): h_copy[2].data = trH3_filt.data ############################################################## if algoS == 'ARH': - #calculate ARH-CF using subclass ARHcf of class CharcteristicFunction - arhcf1 = ARHcf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, addnoise) #instance of ARHcf + # calculate ARH-CF using subclass ARHcf of class + # CharcteristicFunction + arhcf1 = ARHcf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, + addnoise) # instance of ARHcf elif algoS == 'AR3': - #calculate ARH-CF using subclass AR3cf of class CharcteristicFunction - arhcf1 = AR3Ccf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, addnoise) #instance of ARHcf + # calculate ARH-CF using subclass AR3cf of class + # CharcteristicFunction + arhcf1 = AR3Ccf(h_copy, cuttimesh, tpred1h, Sarorder, tdet1h, + addnoise) # instance of ARHcf ############################################################## - #calculate AIC-ARH-CF using subclass AICcf of class CharacteristicFunction - #class needs stream object => build it + # calculate AIC-ARH-CF using subclass AICcf of class + # CharacteristicFunction + # class needs stream object => build it tr_arhaic = trH1_filt.copy() tr_arhaic.data = arhcf1.getCF() h_copy[0].data = tr_arhaic.data - #calculate ARH-AIC-CF - haiccf = AICcf(h_copy, cuttimesh) #instance of AICcf + # calculate ARH-AIC-CF + haiccf = AICcf(h_copy, cuttimesh) # instance of AICcf ############################################################## - #get prelimenary onset time from AIC-HOS-CF using subclass AICPicker of class AutoPicking - aicarhpick = AICPicker(haiccf, tsnrh, pickwinS, iplot, None, aictsmoothS) + # get prelimenary onset time from AIC-HOS-CF using subclass AICPicker + # of class AutoPicking + aicarhpick = AICPicker(haiccf, tsnrh, pickwinS, iplot, None, + aictsmoothS) ############################################################### - #go on with processing if AIC onset passes quality control - if aicarhpick.getSlope() >= minAICSslope and aicarhpick.getSNR() >= minAICSSNR: - aicSflag = 1 - print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ - % (aicarhpick.getSlope(), aicarhpick.getSNR()) - print 'Go on with refined picking ...' - #re-calculate CF from re-filtered trace in vicinity of initial onset - cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), \ - round(aicarhpick.getpick() + Srecalcwin)] - #re-filter waveform with larger bandpass - print 'run_autopicking: re-filtering horizontal traces...' - h_copy = hdat.copy() - #filter and taper data - if algoS == 'ARH': - trH1_filt = hdat[0].copy() - trH2_filt = hdat[1].copy() - trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - h_copy[0].data = trH1_filt.data - h_copy[1].data = trH2_filt.data - ############################################################# - arhcf2 = ARHcf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, addnoise) #instance of ARHcf - elif algoS == 'AR3': - trH1_filt = hdat[0].copy() - trH2_filt = hdat[1].copy() - trH3_filt = hdat[2].copy() - trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH3_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - trH3_filt.taper(max_percentage=0.05, type='hann') - h_copy[0].data = trH1_filt.data - h_copy[1].data = trH2_filt.data - h_copy[2].data = trH3_filt.data - ############################################################# - arhcf2 = AR3Ccf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, addnoise) #instance of ARHcf + # go on with processing if AIC onset passes quality control + if (aicarhpick.getSlope() >= minAICSslope and + aicarhpick.getSNR() >= minAICSSNR): + aicSflag = 1 + print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ + % (aicarhpick.getSlope(), aicarhpick.getSNR()) + print 'Go on with refined picking ...' + # re-calculate CF from re-filtered trace in vicinity of initial + # onset + cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), + round(aicarhpick.getpick() + Srecalcwin)] + # re-filter waveform with larger bandpass + print 'run_autopicking: re-filtering horizontal traces...' + h_copy = hdat.copy() + # filter and taper data + if algoS == 'ARH': + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + ############################################################# + arhcf2 = ARHcf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, + addnoise) # instance of ARHcf + elif algoS == 'AR3': + trH1_filt = hdat[0].copy() + trH2_filt = hdat[1].copy() + trH3_filt = hdat[2].copy() + trH1_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH3_filt.filter('bandpass', freqmin=bph2[0], freqmax=bph2[1], + zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + trH3_filt.taper(max_percentage=0.05, type='hann') + h_copy[0].data = trH1_filt.data + h_copy[1].data = trH2_filt.data + h_copy[2].data = trH3_filt.data + ############################################################# + arhcf2 = AR3Ccf(h_copy, cuttimesh2, tpred2h, Sarorder, tdet2h, + addnoise) # instance of ARHcf - #get refined onset time from CF2 using class Picker - refSpick = PragPicker(arhcf2, tsnrh, pickwinS, iplot, ausS, tsmoothS, aicarhpick.getpick()) - ############################################################# - #quality assessment - #get earliest and latest possible pick and symmetrized uncertainty - h_copy[0].data = trH1_filt.data - [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) - h_copy[0].data = trH2_filt.data - [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) - if algoS == 'ARH': - #get earliest pick of both earliest possible picks - epick = [epickS1, epickS2] - lpick = [lpickS1, lpickS2] - pickerr = [Serror1, Serror2] - ipick =np.argmin([epickS1, epickS2]) - elif algoS == 'AR3': - [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, tsnrh, refSpick.getpick(), iplot) - #get earliest pick of all three picks - epick = [epickS1, epickS2, epickS3] - lpick = [lpickS1, lpickS2, lpickS3] - pickerr = [Serror1, Serror2, Serror3] - ipick =np.argmin([epickS1, epickS2, epickS3]) - epickS = epick[ipick] - lpickS = lpick[ipick] - Serror = pickerr[ipick] + # get refined onset time from CF2 using class Picker + refSpick = PragPicker(arhcf2, tsnrh, pickwinS, iplot, ausS, + tsmoothS, aicarhpick.getpick()) + ############################################################# + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + h_copy[0].data = trH1_filt.data + [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, + refSpick.getpick(), + iplot) + h_copy[0].data = trH2_filt.data + [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, + refSpick.getpick(), + iplot) + if algoS == 'ARH': + # get earliest pick of both earliest possible picks + epick = [epickS1, epickS2] + lpick = [lpickS1, lpickS2] + pickerr = [Serror1, Serror2] + ipick = np.argmin([epickS1, epickS2]) + elif algoS == 'AR3': + [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, + tsnrh, + refSpick.getpick(), + iplot) + # get earliest pick of all three picks + epick = [epickS1, epickS2, epickS3] + lpick = [lpickS1, lpickS2, lpickS3] + pickerr = [Serror1, Serror2, Serror3] + ipick = np.argmin([epickS1, epickS2, epickS3]) + epickS = epick[ipick] + lpickS = lpick[ipick] + Serror = pickerr[ipick] - #get SNR - [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, refSpick.getpick()) + # get SNR + [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, + refSpick.getpick()) - #weight S-onset using symmetric error - if Serror <= timeerrorsS[0]: - Sweight = 0 - elif Serror > timeerrorsS[0] and Serror <= timeerrorsS[1]: - Sweight = 1 - elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: - Sweight = 2 - elif Serror > timeerrorsS[2] and Serror <= timeerrorsS[3]: - Sweight = 3 - elif Serror > timeerrorsS[3]: - Sweight = 4 + # weight S-onset using symmetric error + if Serror <= timeerrorsS[0]: + Sweight = 0 + elif timeerrorsS[0] < Serror <= timeerrorsS[1]: + Sweight = 1 + elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: + Sweight = 2 + elif timeerrorsS[2] < Serror <= timeerrorsS[3]: + Sweight = 3 + elif Serror > timeerrorsS[3]: + Sweight = 4 - print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % (Sweight, SNRS, SNRSdB) + print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( + Sweight, SNRS, SNRSdB) else: - print 'Bad initial (AIC) S-pick, skip this onset!' - print 'AIC-SNR=', aicarhpick.getSNR(), 'AIC-Slope=', aicarhpick.getSlope() - Sweight = 4 - SNRS = None - SNRSdB = None - aicSflag = 0 - + print 'Bad initial (AIC) S-pick, skip this onset!' + print 'AIC-SNR=', aicarhpick.getSNR(), \ + 'AIC-Slope=', aicarhpick.getSlope() + Sweight = 4 + SNRS = None + SNRSdB = None + aicSflag = 0 + else: - print 'run_autopicking: No horizontal component data available or bad P onset, skipping S picking!' - return + print 'run_autopicking: No horizontal component data available or ' \ + 'bad P onset, skipping S picking!' + return ############################################################## if iplot > 0: - #plot vertical trace - plt.figure() - plt.subplot(3,1,1) - tdata = np.arange(0, zdat[0].stats.npts / tr_filt.stats.sampling_rate, tr_filt.stats.delta) - #check equal length of arrays, sometimes they are different!? - wfldiff = len(tr_filt.data) - len(tdata) - if wfldiff < 0: - tdata = tdata[0:len(tdata) - abs(wfldiff)] - p1, = plt.plot(tdata, tr_filt.data/max(tr_filt.data), 'k') - if Pweight < 4: - p2, = plt.plot(cf1.getTimeArray(), cf1.getCF() / max(cf1.getCF()), 'b') - if aicPflag == 1: - p3, = plt.plot(cf2.getTimeArray(), cf2.getCF() / max(cf2.getCF()), 'm') - p4, = plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], 'r') - plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [1, 1], 'r') - plt.plot([aicpick.getpick()-0.5, aicpick.getpick()+0.5], [-1, -1], 'r') - p5, = plt.plot([refPpick.getpick(), refPpick.getpick()], [-1.3, 1.3], 'r', linewidth=2) - plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [1.3, 1.3], 'r', linewidth=2) - plt.plot([refPpick.getpick()-0.5, refPpick.getpick()+0.5], [-1.3, -1.3], 'r', linewidth=2) - plt.plot([lpickP, lpickP], [-1.1, 1.1], 'r--') - plt.plot([epickP, epickP], [-1.1, 1.1], 'r--') - plt.legend([p1, p2, p3, p4, p5], ['Data', 'CF1', 'CF2', 'Initial P Onset', 'Final P Pick']) - plt.title('%s, %s, P Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f Polarity: %s' % (tr_filt.stats.station, \ - tr_filt.stats.channel, Pweight, SNRP, SNRPdB, FM)) - else: - plt.legend([p1, p2], ['Data', 'CF1']) - plt.title('%s, P Weight=%d, SNR=None, SNRdB=None' % (tr_filt.stats.channel, Pweight)) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.ylabel('Normalized Counts') - plt.suptitle(tr_filt.stats.starttime) + # plot vertical trace + plt.figure() + plt.subplot(3, 1, 1) + tdata = np.arange(0, zdat[0].stats.npts / tr_filt.stats.sampling_rate, + tr_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(tr_filt.data) - len(tdata) + if wfldiff < 0: + tdata = tdata[0:len(tdata) - abs(wfldiff)] + p1, = plt.plot(tdata, tr_filt.data / max(tr_filt.data), 'k') + if Pweight < 4: + p2, = plt.plot(cf1.getTimeArray(), cf1.getCF() / max(cf1.getCF()), + 'b') + if aicPflag == 1: + p3, = plt.plot(cf2.getTimeArray(), + cf2.getCF() / max(cf2.getCF()), 'm') + p4, = plt.plot([aicpick.getpick(), aicpick.getpick()], [-1, 1], + 'r') + plt.plot([aicpick.getpick() - 0.5, aicpick.getpick() + 0.5], + [1, 1], 'r') + plt.plot([aicpick.getpick() - 0.5, aicpick.getpick() + 0.5], + [-1, -1], 'r') + p5, = plt.plot([refPpick.getpick(), refPpick.getpick()], + [-1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick() - 0.5, refPpick.getpick() + 0.5], + [1.3, 1.3], 'r', linewidth=2) + plt.plot([refPpick.getpick() - 0.5, refPpick.getpick() + 0.5], + [-1.3, -1.3], 'r', linewidth=2) + plt.plot([lpickP, lpickP], [-1.1, 1.1], 'r--') + plt.plot([epickP, epickP], [-1.1, 1.1], 'r--') + plt.legend([p1, p2, p3, p4, p5], + ['Data', 'CF1', 'CF2', 'Initial P Onset', + 'Final P Pick']) + plt.title('%s, %s, P Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f ' + 'Polarity: %s' % (tr_filt.stats.station, + tr_filt.stats.channel, + Pweight, + SNRP, + SNRPdB, + FM)) + else: + plt.legend([p1, p2], ['Data', 'CF1']) + plt.title('%s, P Weight=%d, SNR=None, ' + 'SNRdB=None' % (tr_filt.stats.channel, Pweight)) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.suptitle(tr_filt.stats.starttime) - #plot horizontal traces - plt.subplot(3,1,2) - th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) - #check equal length of arrays, sometimes they are different!? - wfldiff = len(trH1_filt.data) - len(th1data) - if wfldiff < 0: - th1data = th1data[0:len(th1data) - abs(wfldiff)] - p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - if Pweight < 4: - p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') - if aicSflag == 1: - p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') - p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') - plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) - plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % (trH1_filt.stats.channel, \ - Sweight, SNRS, SNRSdB)) - else: - plt.legend([p21, p22], ['Data', 'CF1']) - plt.title('%s, S Weight=%d, SNR=None, SNRdB=None' % (trH1_filt.stats.channel, Sweight)) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.ylabel('Normalized Counts') - plt.suptitle(trH1_filt.stats.starttime) + # plot horizontal traces + plt.subplot(3, 1, 2) + th1data = np.arange(0, + trH1_filt.stats.npts / + trH1_filt.stats.sampling_rate, + trH1_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(trH1_filt.data) - len(th1data) + if wfldiff < 0: + th1data = th1data[0:len(th1data) - abs(wfldiff)] + p21, = plt.plot(th1data, trH1_filt.data / max(trH1_filt.data), 'k') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), + arhcf1.getCF() / max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), + arhcf2.getCF() / max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], + [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], + ['Data', 'CF1', 'CF2', 'Initial S Onset', + 'Final S Pick']) + plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % ( + trH1_filt.stats.channel, + Sweight, SNRS, SNRSdB)) + else: + plt.legend([p21, p22], ['Data', 'CF1']) + plt.title('%s, S Weight=%d, SNR=None, SNRdB=None' % ( + trH1_filt.stats.channel, Sweight)) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.suptitle(trH1_filt.stats.starttime) - plt.subplot(3,1,3) - th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) - #check equal length of arrays, sometimes they are different!? - wfldiff = len(trH2_filt.data) - len(th2data) - if wfldiff < 0: - th2data = th2data[0:len(th2data) - abs(wfldiff)] - plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - if Pweight < 4: - p22, = plt.plot(arhcf1.getTimeArray(), arhcf1.getCF()/max(arhcf1.getCF()), 'b') - if aicSflag == 1: - p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF()/max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g') - plt.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g') - p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') - plt.legend([p21, p22, p23, p24, p25], ['Data', 'CF1', 'CF2', 'Initial S Onset', 'Final S Pick']) - else: - plt.legend([p21, p22], ['Data', 'CF1']) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) - plt.ylabel('Normalized Counts') - plt.title(trH2_filt.stats.channel) - plt.show() - raw_input() - plt.close() + plt.subplot(3, 1, 3) + th2data = np.arange(0, + trH2_filt.stats.npts / + trH2_filt.stats.sampling_rate, + trH2_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(trH2_filt.data) - len(th2data) + if wfldiff < 0: + th2data = th2data[0:len(th2data) - abs(wfldiff)] + plt.plot(th2data, trH2_filt.data / max(trH2_filt.data), 'k') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), + arhcf1.getCF() / max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), + arhcf2.getCF() / max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], + [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], + ['Data', 'CF1', 'CF2', 'Initial S Onset', + 'Final S Pick']) + else: + plt.legend([p21, p22], ['Data', 'CF1']) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) + plt.ylabel('Normalized Counts') + plt.title(trH2_filt.stats.channel) + plt.show() + + +raw_input() +plt.close() From 57dfb868190f0bbef2eedc67c0bad0dcde42be8a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 18 Jun 2015 10:55:26 +0200 Subject: [PATCH 0362/1144] [bugfix] importing of run_autopicking.py was inhibited by badly indented call to raw_input() (fixed) --- pylot/core/pick/run_autopicking.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index eddff959..c06ebad5 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -555,7 +555,5 @@ def run_autopicking(wfstream, pickparam): plt.ylabel('Normalized Counts') plt.title(trH2_filt.stats.channel) plt.show() - - -raw_input() -plt.close() + raw_input() + plt.close() From fd796ba6e13e589a3fb5e33064b59fe4acfc30c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 19 Jun 2015 09:08:28 +0200 Subject: [PATCH 0363/1144] Implemented dictionary containing onset parameters. --- autoPyLoT.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 66024dd6..514f105c 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- @@ -13,15 +13,13 @@ from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE - __version__ = _getVersionString() -#METHOD = {'HOS':HOScf, 'AIC':AICcf} def autoPyLoT(inputfile): ''' Determine phase onsets automatically utilizing the automatic picking - algorithm by Kueperkoch et al. 2011. + algorithms by Kueperkoch et al. 2010/2012. :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. @@ -56,7 +54,7 @@ def autoPyLoT(inputfile): datastructure.modifyFields(**dsfields) datastructure.setExpandFields(exf) - # get streams + # multiple event processing # read each event in database datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): @@ -71,17 +69,23 @@ def autoPyLoT(inputfile): procstats = [] for i in range(len(wfdat)): stationID = wfdat[i].stats.station - #check if station has already been processed + # check if station has already been processed if stationID not in procstats: procstats.append(stationID) - #find corresponding streams + # find corresponding streams statdat = wfdat.select(station=stationID) - run_autopicking(statdat, parameter) + # get onset times and corresponding picking errors + picks = run_autopicking(statdat, parameter) + + # quality control + # check S-P times (Wadati) + # jackknife on P onset times + # jackknife on S onset times print '------------------------------------------' print '-----Finished event %s!-----' % event print '------------------------------------------' - #for single event processing + # single event processing else: data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) print 'Working on event ', parameter.getParam('eventID') @@ -91,6 +95,10 @@ def autoPyLoT(inputfile): ########################################################## # !automated picking starts here! procstats = [] + # initialize dictionary for onsets + picks = None + station = wfdat[0].stats.station + allonsets = {station: picks} for i in range(len(wfdat)): stationID = wfdat[i].stats.station #check if station has already been processed @@ -98,7 +106,15 @@ def autoPyLoT(inputfile): procstats.append(stationID) #find corresponding streams statdat = wfdat.select(station=stationID) - run_autopicking(statdat, parameter) + # get onset times and corresponding picking errors + picks = run_autopicking(statdat, parameter) + station = stationID + # add station and corresponding onsets to dictionary + allonsets[station] = picks + #quality control + #check S-P times (Wadati) + #jackknife on P onset times + #jackknife on S onset times print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' From 613bef1678c112e595b63cb92c7c596fc8078af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 19 Jun 2015 09:09:22 +0200 Subject: [PATCH 0364/1144] Implmented dictionary containing onset parameters. --- pylot/core/pick/run_autopicking.py | 81 ++++++++++++++++++------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index c06ebad5..686396e2 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -76,6 +76,26 @@ def run_autopicking(wfstream, pickparam): fmpickwin = pickparam.getParam('fmpickwin') minfmweight = pickparam.getParam('minfmweight') + # initialize output + Pweight = 4 # weight for P onset + Sweight = 4 # weight for S onset + FM = 'N' # first motion (polarity) + SNRP = None # signal-to-noise ratio of P onset + SNRPdB = None # signal-to-noise ratio of P onset [dB] + SNRS = None # signal-to-noise ratio of S onset + SNRSdB = None # signal-to-noise ratio of S onset [dB] + mpickP = None # most likely P onset + lpickP = None # latest possible P onset + epickP = None # earliest possible P onset + mpickS = None # most likely S onset + lpickS = None # latest possible S onset + epickS = None # earliest possible S onset + Perror = None # symmetrized picking error P onset + Serror = None # symmetrized picking error S onset + + aicSflag = 0 + aicPflag = 0 + # split components zdat = wfstream.select(component="Z") edat = wfstream.select(component="E") @@ -167,15 +187,14 @@ def run_autopicking(wfstream, pickparam): # get refined onset time from CF2 using class Picker refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, aicpick.getpick()) + mpickP = refPpick.getpick() ############################################################# # quality assessment # get earliest and latest possible pick and symmetrized uncertainty - [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, - refPpick.getpick(), iplot) + [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, mpickP, iplot) # get SNR - [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, - refPpick.getpick()) + [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, mpickP) # weight P-onset using symmetric error if Perror <= timeerrorsP[0]: @@ -193,8 +212,7 @@ def run_autopicking(wfstream, pickparam): # get first motion of P onset # certain quality required if Pweight <= minfmweight and SNRP >= minFMSNR: - FM = fmpicker(zdat, z_copy, fmpickwin, refPpick.getpick(), - iplot) + FM = fmpicker(zdat, z_copy, fmpickwin, mpickP, iplot) else: FM = 'N' @@ -204,19 +222,10 @@ def run_autopicking(wfstream, pickparam): else: print 'Bad initial (AIC) P-pick, skip this onset!' print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() - Pweight = 4 - Sweight = 4 - FM = 'N' - SNRP = None - SNRPdB = None - SNRS = None - SNRSdB = None - aicSflag = 0 - aicPflag = 0 + else: print 'run_autopicking: No vertical component data available, ' \ 'skipping station!' - return if edat is not None and ndat is not None and len(edat) > 0 and len( ndat) > 0 and Pweight < 4: @@ -226,10 +235,8 @@ def run_autopicking(wfstream, pickparam): print 'Filtering horizontal traces ...' # determine time window for calculating CF after P onset - # cuttimesh = [round(refPpick.getpick() + sstart), - # round(refPpick.getpick() + sstop)] - cuttimesh = [round(max([refPpick.getpick() + sstart, 0])), - round(min([refPpick.getpick() + sstop, Lwf]))] + cuttimesh = [round(max([mpickP + sstart, 0])), + round(min([mpickP + sstop, Lwf]))] if algoS == 'ARH': print edat, ndat @@ -349,17 +356,16 @@ def run_autopicking(wfstream, pickparam): # get refined onset time from CF2 using class Picker refSpick = PragPicker(arhcf2, tsnrh, pickwinS, iplot, ausS, tsmoothS, aicarhpick.getpick()) + mpickS = refSpick.getpick() ############################################################# # quality assessment # get earliest and latest possible pick and symmetrized uncertainty h_copy[0].data = trH1_filt.data [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, - refSpick.getpick(), - iplot) + mpickS, iplot) h_copy[0].data = trH2_filt.data [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, - refSpick.getpick(), - iplot) + mpickS, iplot) if algoS == 'ARH': # get earliest pick of both earliest possible picks epick = [epickS1, epickS2] @@ -369,8 +375,7 @@ def run_autopicking(wfstream, pickparam): elif algoS == 'AR3': [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, tsnrh, - refSpick.getpick(), - iplot) + mpickS, iplot) # get earliest pick of all three picks epick = [epickS1, epickS2, epickS3] lpick = [lpickS1, lpickS2, lpickS3] @@ -381,8 +386,7 @@ def run_autopicking(wfstream, pickparam): Serror = pickerr[ipick] # get SNR - [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, - refSpick.getpick()) + [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, mpickS) # weight S-onset using symmetric error if Serror <= timeerrorsS[0]: @@ -403,15 +407,10 @@ def run_autopicking(wfstream, pickparam): print 'Bad initial (AIC) S-pick, skip this onset!' print 'AIC-SNR=', aicarhpick.getSNR(), \ 'AIC-Slope=', aicarhpick.getSlope() - Sweight = 4 - SNRS = None - SNRSdB = None - aicSflag = 0 else: print 'run_autopicking: No horizontal component data available or ' \ 'bad P onset, skipping S picking!' - return ############################################################## if iplot > 0: @@ -557,3 +556,19 @@ def run_autopicking(wfstream, pickparam): plt.show() raw_input() plt.close() + ########################################################################## + # create dictionary + # for P phase + phase = 'P' + phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, \ + 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM} + picks = {phase: phasepick} + #stationID = tr_filt.stats.station + #onsets = {stationID: picks} + # add S phase + phase = 'S' + phasepick = {'lpp': lpickS, 'epp': epickS, 'mpp': mpickS, 'spe': Serror, \ + 'snr': SNRS, 'snrdb': SNRSdB, 'weight': Sweight, 'fm': None} + picks[phase] = phasepick + + return picks From b91f4c9619767e2b87045fe1cf182547715eb524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 19 Jun 2015 15:27:24 +0200 Subject: [PATCH 0365/1144] Implemented new function wadaticheck. --- autoPyLoT.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 514f105c..167fa8ae 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -12,7 +12,7 @@ from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE - +from pylot.core.pick.utils import wadaticheck __version__ = _getVersionString() @@ -35,6 +35,11 @@ def autoPyLoT(inputfile): parameter = AutoPickParameter(inputfile) + # get some parameters for quality control from + # parameter input file (usually autoPyLoT.in). + wdttolerance = parameter.getParam('wdttolerance') + iplot = parameter.getParam('iplot') + data = Data() # getting information on data structure @@ -67,6 +72,10 @@ def autoPyLoT(inputfile): ########################################################## # !automated picking starts here! procstats = [] + # initialize dictionary for onsets + picks = None + station = wfdat[0].stats.station + allonsets = {station: picks} for i in range(len(wfdat)): stationID = wfdat[i].stats.station # check if station has already been processed @@ -74,12 +83,18 @@ def autoPyLoT(inputfile): procstats.append(stationID) # find corresponding streams statdat = wfdat.select(station=stationID) + ###################################################### # get onset times and corresponding picking errors picks = run_autopicking(statdat, parameter) + ###################################################### + # add station and corresponding onsets to dictionary + station = stationID + allonsets[station] = picks # quality control - # check S-P times (Wadati) # jackknife on P onset times + # check S-P times (Wadati) + checkedonsets = wadaticheck(allonsets, wdttolerance, iplot) # jackknife on S onset times print '------------------------------------------' print '-----Finished event %s!-----' % event @@ -100,20 +115,25 @@ def autoPyLoT(inputfile): station = wfdat[0].stats.station allonsets = {station: picks} for i in range(len(wfdat)): + #for i in range(0,5): stationID = wfdat[i].stats.station #check if station has already been processed if stationID not in procstats: procstats.append(stationID) #find corresponding streams statdat = wfdat.select(station=stationID) - # get onset times and corresponding picking errors + ###################################################### + # get onset times and corresponding picking parameters picks = run_autopicking(statdat, parameter) - station = stationID + ###################################################### # add station and corresponding onsets to dictionary + station = stationID allonsets[station] = picks + #quality control - #check S-P times (Wadati) #jackknife on P onset times + #check S-P times (Wadati) + checkedonsets = wadaticheck(allonsets, wdttolerance, iplot) #jackknife on S onset times print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') From aa624c0358661d50459797a59ee04ff3409ee232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 19 Jun 2015 15:28:53 +0200 Subject: [PATCH 0366/1144] Added new function wadaticheck to test certainty of S-onsets using Wadati diagram. --- pylot/core/pick/utils.py | 123 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 1d620f47..5dc320ec 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -10,7 +10,7 @@ import numpy as np import matplotlib.pyplot as plt -from obspy.core import Stream +from obspy.core import Stream, UTCDateTime def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): @@ -66,9 +66,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): #get earliest possible pick #determine all zero crossings in signal window - # remove mean from signal window - signal = x[isignal] - x[isignal].mean() - zc = crossings_nonzero_all(signal) + zc = crossings_nonzero_all(x[isignal]) #calculate mean half period T0 of signal as the average of the T0 = np.mean(np.diff(zc)) * X[0].stats.delta #this is half wave length! #T0/4 is assumed as time difference between most likely and earliest possible pick! @@ -161,7 +159,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): index1 = [] i = 0 for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): - i += 1 + i = i + 1 if xraw[j - 1] <= 0 and xraw[j] >= 0: zc1.append(t[ipick][i]) index1.append(i) @@ -196,7 +194,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): index2 = [] i = 0 for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): - i += 1 + i = i + 1 if xfilt[j - 1] <= 0 and xfilt[j] >= 0: zc2.append(t[ipick][i]) index2.append(i) @@ -382,4 +380,117 @@ def getsignalwin(t, t1, tsignal): return isignal +def wadaticheck(pickdic, dttolerance, iplot): + ''' + Function to calculate Wadati-diagram from given P and S onsets in order + to detect S pick outliers. If a certain S-P time deviates from regression + of S-P time the S pick is marked and down graded. + : param: pickdic, dictionary containing picks and quality parameters + : type: dictionary + + : param: dttolerance, maximum adjusted deviation of S-P time from + S-P time regression + : type: float + + : param: iplot, if iplot > 1, Wadati diagram is shown + : type: int + ''' + + checkedonsets = pickdic + + # search for good quality picks and calculate S-P time + Ppicks = [] + Spicks = [] + SPtimes = [] + vpvs = [] + for key in pickdic: + if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: + # calculate S-P time + spt = UTCDateTime(pickdic[key]['S']['mpp']) - UTCDateTime(pickdic[key]['P']['mpp']) + # add S-P time to dictionary + pickdic[key]['SPt'] = spt + # add P onsets and corresponding S-P times to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCDateTime(1970,1,1,0,0,0) + UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - UTCDateTime(1970,1,1,0,0,0) + Ppicks.append(UTCPpick) + Spicks.append(UTCSpick) + SPtimes.append(spt) + vpvs.append(UTCPpick/UTCSpick) + + # calculate average vp/vs ratio before check + vpvsr = np.mean(vpvs) + print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr + + if len(SPtimes) >= 3: + # calculate slope + p1 = np.polyfit(Ppicks, SPtimes, 1) + wdfit = np.polyval(p1, Ppicks) + wfitflag = 0 + + checkedPpicks = [] + checkedSpicks = [] + checkedSPtimes = [] + checkedvpvs = [] + # calculate deviations from Wadati regression + for key in pickdic: + if pickdic[key].has_key('SPt'): + ii = 0 + wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) + ii += 1 + # check, if deviation is larger than adjusted + if wddiff >= dttolerance: + # mark onset and downgrade S-weight to 4 + # (not used anymore) + marker = 'badWadatiCheck' + pickdic[key]['S']['weight'] = 4 + else: + marker = 'goodWadatiCheck' + checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) - \ + UTCDateTime(1970,1,1,0,0,0) + checkedPpicks.append(checkedPpick) + checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) - \ + UTCDateTime(1970,1,1,0,0,0) + checkedSpicks.append(checkedSpick) + checkedSPtime = UTCDateTime(pickdic[key]['S']['mpp']) - \ + UTCDateTime(pickdic[key]['P']['mpp']) + checkedSPtimes.append(checkedSPtime) + checkedvpvs.append(checkedPpick/checkedSpick) + + pickdic[key]['S']['marked'] = marker + + # calculate average vp/vs ratio after check + cvpvsr = np.mean(checkedvpvs) + print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr + + # calculate new slope + p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) + wdfit2 = np.polyval(p2, checkedPpicks) + + checkedonsets = pickdic + + else: + print 'wadaticheck: Not enough S-P times available for reliable regression!' + print 'Skip wadati check!' + wfitflag = 1 + + # plot results + iplot = 2 + if iplot > 1: + f = plt.figure(iplot) + f1, = plt.plot(Ppicks, SPtimes, 'ro') + if wfitflag == 0: + f2, = plt.plot(Ppicks, wdfit, 'k') + f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') + f4, = plt.plot(checkedPpicks, wdfit2, 'g') + plt.ylabel('S-P Times [s]') + plt.xlabel('P Times [s]') + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(old)=%5.2f, Vp/Vs(checked)=%5.2f' \ + % (len(SPtimes), vpvsr, cvpvsr)) + plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', 'Reliable S-Picks', \ + 'Wadati 2'], loc='best') + plt.show() + raw_input() + plt.close(f) + + return checkedonsets From a95caa5efc4a164c9172edbf26fab11d402b62a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 19 Jun 2015 15:48:04 +0200 Subject: [PATCH 0367/1144] Weight 9 now for skiped S onsets, turned rank warning off. --- pylot/core/pick/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 5dc320ec..e252e255 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -11,7 +11,7 @@ import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime - +import warnings def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' @@ -133,6 +133,8 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): :type: int ''' + warnings.simplefilter('ignore', np.RankWarning) + assert isinstance(Xraw, Stream), "%s is not a stream object" % str(Xraw) assert isinstance(Xfilt, Stream), "%s is not a stream object" % str(Xfilt) @@ -440,10 +442,10 @@ def wadaticheck(pickdic, dttolerance, iplot): ii += 1 # check, if deviation is larger than adjusted if wddiff >= dttolerance: - # mark onset and downgrade S-weight to 4 + # mark onset and downgrade S-weight to 9 # (not used anymore) marker = 'badWadatiCheck' - pickdic[key]['S']['weight'] = 4 + pickdic[key]['S']['weight'] = 9 else: marker = 'goodWadatiCheck' checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) - \ @@ -475,7 +477,6 @@ def wadaticheck(pickdic, dttolerance, iplot): wfitflag = 1 # plot results - iplot = 2 if iplot > 1: f = plt.figure(iplot) f1, = plt.plot(Ppicks, SPtimes, 'ro') From 3330a3ae359f73f093ea56d5ee4c316d38fb7f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 09:32:33 +0200 Subject: [PATCH 0368/1144] Corrected calculation of Vp/Vs ratio in wadaticheck, now determined from slope of trend as it should be. --- pylot/core/pick/utils.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index e252e255..cc32ee6a 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -409,7 +409,7 @@ def wadaticheck(pickdic, dttolerance, iplot): for key in pickdic: if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: # calculate S-P time - spt = UTCDateTime(pickdic[key]['S']['mpp']) - UTCDateTime(pickdic[key]['P']['mpp']) + spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] # add S-P time to dictionary pickdic[key]['SPt'] = spt # add P onsets and corresponding S-P times to list @@ -420,15 +420,16 @@ def wadaticheck(pickdic, dttolerance, iplot): SPtimes.append(spt) vpvs.append(UTCPpick/UTCSpick) - # calculate average vp/vs ratio before check - vpvsr = np.mean(vpvs) - print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr if len(SPtimes) >= 3: # calculate slope p1 = np.polyfit(Ppicks, SPtimes, 1) wdfit = np.polyval(p1, Ppicks) wfitflag = 0 + + # calculate average vp/vs ratio before check + vpvsr = p1[0] + 1 + print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr checkedPpicks = [] checkedSpicks = [] @@ -454,21 +455,21 @@ def wadaticheck(pickdic, dttolerance, iplot): checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) - \ UTCDateTime(1970,1,1,0,0,0) checkedSpicks.append(checkedSpick) - checkedSPtime = UTCDateTime(pickdic[key]['S']['mpp']) - \ - UTCDateTime(pickdic[key]['P']['mpp']) + checkedSPtime = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] checkedSPtimes.append(checkedSPtime) checkedvpvs.append(checkedPpick/checkedSpick) pickdic[key]['S']['marked'] = marker - # calculate average vp/vs ratio after check - cvpvsr = np.mean(checkedvpvs) - print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr # calculate new slope p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) wdfit2 = np.polyval(p2, checkedPpicks) + # calculate average vp/vs ratio after check + cvpvsr = p2[0] + 1 + print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr + checkedonsets = pickdic else: @@ -478,7 +479,7 @@ def wadaticheck(pickdic, dttolerance, iplot): # plot results if iplot > 1: - f = plt.figure(iplot) + plt.figure(iplot) f1, = plt.plot(Ppicks, SPtimes, 'ro') if wfitflag == 0: f2, = plt.plot(Ppicks, wdfit, 'k') @@ -492,6 +493,6 @@ def wadaticheck(pickdic, dttolerance, iplot): 'Wadati 2'], loc='best') plt.show() raw_input() - plt.close(f) + plt.close(iplot) return checkedonsets From 864ef6c64da615a4a6c2be87f9d463c1b370bb4a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 22 Jun 2015 10:52:26 +0200 Subject: [PATCH 0369/1144] [bugfix] determination of zero crossings breaks down if data is not demeaned in the signal window (explicitly demeaning data in the signal window when determining the zero crossings) --- pylot/core/pick/utils.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index e252e255..84546534 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -65,8 +65,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): #get earliest possible pick - #determine all zero crossings in signal window - zc = crossings_nonzero_all(x[isignal]) + #determine all zero crossings in signal window (demeaned) + zc = crossings_nonzero_all(x[isignal] - x[isignal].mean()) #calculate mean half period T0 of signal as the average of the T0 = np.mean(np.diff(zc)) * X[0].stats.delta #this is half wave length! #T0/4 is assumed as time difference between most likely and earliest possible pick! @@ -390,8 +390,8 @@ def wadaticheck(pickdic, dttolerance, iplot): : param: pickdic, dictionary containing picks and quality parameters : type: dictionary - - : param: dttolerance, maximum adjusted deviation of S-P time from + + : param: dttolerance, maximum adjusted deviation of S-P time from S-P time regression : type: float @@ -425,11 +425,11 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr if len(SPtimes) >= 3: - # calculate slope + # calculate slope p1 = np.polyfit(Ppicks, SPtimes, 1) wdfit = np.polyval(p1, Ppicks) wfitflag = 0 - + checkedPpicks = [] checkedSpicks = [] checkedSPtimes = [] @@ -438,11 +438,11 @@ def wadaticheck(pickdic, dttolerance, iplot): for key in pickdic: if pickdic[key].has_key('SPt'): ii = 0 - wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) + wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) ii += 1 # check, if deviation is larger than adjusted if wddiff >= dttolerance: - # mark onset and downgrade S-weight to 9 + # mark onset and downgrade S-weight to 9 # (not used anymore) marker = 'badWadatiCheck' pickdic[key]['S']['weight'] = 9 @@ -465,12 +465,12 @@ def wadaticheck(pickdic, dttolerance, iplot): cvpvsr = np.mean(checkedvpvs) print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr - # calculate new slope + # calculate new slope p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) wdfit2 = np.polyval(p2, checkedPpicks) checkedonsets = pickdic - + else: print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' From ff52ec5410992eb0a7fb650714028a6f3b4f331f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 22 Jun 2015 10:56:16 +0200 Subject: [PATCH 0370/1144] started implementation of running of external programs (work in progress, pending until release of picking window) --- pylot/core/util/utils.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 61b3fafc..0c8a863e 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -3,6 +3,7 @@ # -*- coding: utf-8 -*- import os +import subprocess import pwd import re import hashlib @@ -10,6 +11,27 @@ import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope +def runProgram(cmd, parameter=None): + """ + run an external program specified by cmd with parameters input returning the + stdout output + + :param cmd: name of the command to run + :type cmd: str + :param parameter: filename of parameter file or parameter string + :type parameter: str + :return: stdout output + :rtype: str + """ + + if parameter: + cmd.strip() + cmd += ' %s 2>&1' % parameter + + output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), + shell = True) + + def fnConstructor(s): if type(s) is str: From 245a7455ffcc2434e4bccd0bf9d4a18001c4bf29 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 22 Jun 2015 10:59:14 +0200 Subject: [PATCH 0371/1144] FilterOptions class has new method parseFilterOptions which establishes a valid keyword arguments dictionary to be parsed to the obspy.core.stream.Stream 's filter method --- pylot/core/read/inputs.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 2dcfd359..a4cdc3c9 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -206,6 +206,19 @@ class FilterOptions(object): order=self.getOrder()) return hrs + def parseFilterOptions(self): + if self.getFilterType(): + robject = {'type':self.getFilterType()} + robject['order'] = self.getOrder() + if len(self.getFreq()) > 1: + robject['freqmin'] = self.getFreq()[0] + robject['freqmax'] = self.getFreq()[1] + else: + robject['freq'] = self.getFreq() if type(self.getFreq()) is \ + float else self.getFreq()[0] + return robject + return None + def getFreq(self): return self.__getattribute__('_freq') From 30bc8ccd824ee23be517a0bdbac6b50663aa419a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 22 Jun 2015 11:06:53 +0200 Subject: [PATCH 0372/1144] reformatting code to avoid indentation inconsistencies --- pylot/core/pick/utils.py | 141 +++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 64 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 6bc8a106..2647ac6c 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -13,6 +13,7 @@ import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings + def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) @@ -48,13 +49,13 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) # get latest possible pick - #get noise window + # get noise window inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1]) - #get signal window + # get signal window isignal = getsignalwin(t, Pick1, TSNR[2]) - #calculate noise level + # calculate noise level nlevel = np.sqrt(np.mean(np.square(x[inoise]))) * nfac - #get time where signal exceeds nlevel + # get time where signal exceeds nlevel ilup, = np.where(x[isignal] > nlevel) ildown, = np.where(x[isignal] < -nlevel) if not ilup.size and not ildown.size: @@ -63,17 +64,17 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): np.min(ildown) if ildown.size else float('inf')) LPick = t[isignal][il] - #get earliest possible pick + # get earliest possible pick - #determine all zero crossings in signal window (demeaned) + # determine all zero crossings in signal window (demeaned) zc = crossings_nonzero_all(x[isignal] - x[isignal].mean()) - #calculate mean half period T0 of signal as the average of the - T0 = np.mean(np.diff(zc)) * X[0].stats.delta #this is half wave length! - #T0/4 is assumed as time difference between most likely and earliest possible pick! + # calculate mean half period T0 of signal as the average of the + T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length! + # T0/4 is assumed as time difference between most likely and earliest possible pick! EPick = Pick1 - T0 / 2 - #get symmetric pick error as mean from earliest and latest possible pick - #by weighting latest possible pick two times earliest possible pick + # get symmetric pick error as mean from earliest and latest possible pick + # by weighting latest possible pick two times earliest possible pick diffti_tl = LPick - Pick1 diffti_te = Pick1 - EPick PickError = (diffti_te + 2 * diffti_tl) / 3 @@ -84,7 +85,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): p2, = plt.plot(t[inoise], x[inoise]) p3, = plt.plot(t[isignal], x[isignal], 'r') p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(t[isignal[0][zc]], np.zeros(len(zc)), '*g', markersize=14) + p5, = plt.plot(t[isignal[0][zc]], np.zeros(len(zc)), '*g', + markersize=14) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ @@ -149,13 +151,13 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): # get pick window ipick = np.where( (t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) - #remove mean + # remove mean xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) - #get next zero crossing after most likely pick - #initial onset is assumed to be the first zero crossing - #first from unfiltered trace + # get next zero crossing after most likely pick + # initial onset is assumed to be the first zero crossing + # first from unfiltered trace zc1 = [] zc1.append(Pick) index1 = [] @@ -171,9 +173,9 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): if len(zc1) == 3: break - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum + # if time difference betweeen 1st and 2cnd zero crossing + # is too short, get time difference between 1st and 3rd + # to derive maximum if zc1[1] - zc1[0] <= Xraw[0].stats.delta: li1 = index1[1] else: @@ -184,13 +186,13 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) - #calculate slope as polynomal fit of order 1 + # calculate slope as polynomal fit of order 1 xslope1 = np.arange(0, len(xraw[islope1]), 1) P1 = np.polyfit(xslope1, xraw[islope1], 1) datafit1 = np.polyval(P1, xslope1) - #now using filterd trace - #next zero crossing after most likely pick + # now using filterd trace + # next zero crossing after most likely pick zc2 = [] zc2.append(Pick) index2 = [] @@ -206,9 +208,9 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): if len(zc2) == 3: break - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum + # if time difference betweeen 1st and 2cnd zero crossing + # is too short, get time difference between 1st and 3rd + # to derive maximum if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: li2 = index2[1] else: @@ -219,12 +221,12 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) - #calculate slope as polynomal fit of order 1 + # calculate slope as polynomal fit of order 1 xslope2 = np.arange(0, len(xfilt[islope2]), 1) P2 = np.polyfit(xslope2, xfilt[islope2], 1) datafit2 = np.polyval(P2, xslope2) - #compare results + # compare results if P1 is not None and P2 is not None: if P1[0] < 0 and P2[0] < 0: FM = 'D' @@ -280,11 +282,13 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): return FM + def crossings_nonzero_all(data): pos = data > 0 npos = ~pos return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0] + def getSNR(X, TSNR, t1): ''' Function to calculate SNR of certain part of seismogram relative to @@ -311,7 +315,7 @@ def getSNR(X, TSNR, t1): # get noise window inoise = getnoisewin(t, t1, TSNR[0], TSNR[1]) - #get signal window + # get signal window isignal = getsignalwin(t, t1, TSNR[2]) if np.size(inoise) < 1: print 'getSNR: Empty array inoise, check noise window!' @@ -320,7 +324,7 @@ def getSNR(X, TSNR, t1): print 'getSNR: Empty array isignal, check signal window!' return - #calculate ratios + # calculate ratios noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) signallevel = np.sqrt(np.mean(np.square(x[isignal]))) SNR = signallevel / noiselevel @@ -382,6 +386,7 @@ def getsignalwin(t, t1, tsignal): return isignal + def wadaticheck(pickdic, dttolerance, iplot): ''' Function to calculate Wadati-diagram from given P and S onsets in order @@ -408,29 +413,34 @@ def wadaticheck(pickdic, dttolerance, iplot): vpvs = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: - # calculate S-P time - spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] - # add S-P time to dictionary - pickdic[key]['SPt'] = spt - # add P onsets and corresponding S-P times to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCDateTime(1970,1,1,0,0,0) - UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - UTCDateTime(1970,1,1,0,0,0) - Ppicks.append(UTCPpick) - Spicks.append(UTCSpick) - SPtimes.append(spt) - vpvs.append(UTCPpick/UTCSpick) - + # calculate S-P time + spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] + # add S-P time to dictionary + pickdic[key]['SPt'] = spt + # add P onsets and corresponding S-P times to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCDateTime(1970, + 1, 1, + 0, 0, + 0) + UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - UTCDateTime(1970, + 1, 1, + 0, 0, + 0) + Ppicks.append(UTCPpick) + Spicks.append(UTCSpick) + SPtimes.append(spt) + vpvs.append(UTCPpick / UTCSpick) if len(SPtimes) >= 3: - # calculate slope - p1 = np.polyfit(Ppicks, SPtimes, 1) - wdfit = np.polyval(p1, Ppicks) + # calculate slope + p1 = np.polyfit(Ppicks, SPtimes, 1) + wdfit = np.polyval(p1, Ppicks) wfitflag = 0 # calculate average vp/vs ratio before check vpvsr = p1[0] + 1 print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr - + checkedPpicks = [] checkedSpicks = [] checkedSPtimes = [] @@ -439,7 +449,7 @@ def wadaticheck(pickdic, dttolerance, iplot): for key in pickdic: if pickdic[key].has_key('SPt'): ii = 0 - wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) + wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) ii += 1 # check, if deviation is larger than adjusted if wddiff >= dttolerance: @@ -449,22 +459,23 @@ def wadaticheck(pickdic, dttolerance, iplot): pickdic[key]['S']['weight'] = 9 else: marker = 'goodWadatiCheck' - checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) - \ - UTCDateTime(1970,1,1,0,0,0) + checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) - \ + UTCDateTime(1970, 1, 1, 0, 0, 0) checkedPpicks.append(checkedPpick) checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) - \ - UTCDateTime(1970,1,1,0,0,0) + UTCDateTime(1970, 1, 1, 0, 0, 0) checkedSpicks.append(checkedSpick) - checkedSPtime = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] + checkedSPtime = pickdic[key]['S']['mpp'] - \ + pickdic[key]['P']['mpp'] checkedSPtimes.append(checkedSPtime) - checkedvpvs.append(checkedPpick/checkedSpick) + checkedvpvs.append(checkedPpick / checkedSpick) pickdic[key]['S']['marked'] = marker - # calculate new slope - p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) - wdfit2 = np.polyval(p2, checkedPpicks) + # calculate new slope + p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) + wdfit2 = np.polyval(p2, checkedPpicks) # calculate average vp/vs ratio after check cvpvsr = p2[0] + 1 @@ -473,24 +484,26 @@ def wadaticheck(pickdic, dttolerance, iplot): checkedonsets = pickdic else: - print 'wadaticheck: Not enough S-P times available for reliable regression!' + print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 # plot results if iplot > 1: - plt.figure(iplot) - f1, = plt.plot(Ppicks, SPtimes, 'ro') + plt.figure(iplot) + f1, = plt.plot(Ppicks, SPtimes, 'ro') if wfitflag == 0: - f2, = plt.plot(Ppicks, wdfit, 'k') - f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') - f4, = plt.plot(checkedPpicks, wdfit2, 'g') + f2, = plt.plot(Ppicks, wdfit, 'k') + f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') + f4, = plt.plot(checkedPpicks, wdfit2, 'g') plt.ylabel('S-P Times [s]') plt.xlabel('P Times [s]') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(old)=%5.2f, Vp/Vs(checked)=%5.2f' \ - % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', 'Reliable S-Picks', \ - 'Wadati 2'], loc='best') + plt.title( + 'Wadati-Diagram, %d S-P Times, Vp/Vs(old)=%5.2f, Vp/Vs(checked)=%5.2f' \ + % (len(SPtimes), vpvsr, cvpvsr)) + plt.legend([f1, f2, f3, f4], + ['Skipped S-Picks', 'Wadati 1', 'Reliable S-Picks', \ + 'Wadati 2'], loc='best') plt.show() raw_input() plt.close(iplot) From fd6e4cb02af8674fe99542b9e1fab0d9841d87ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 11:07:22 +0200 Subject: [PATCH 0373/1144] Uses now UTCDateTime.timestamp as this is more efficient and shorter. --- pylot/core/pick/utils.py | 53 ++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 6bc8a106..0909a895 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -13,6 +13,7 @@ import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings + def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) @@ -65,8 +66,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): #get earliest possible pick - #determine all zero crossings in signal window (demeaned) - zc = crossings_nonzero_all(x[isignal] - x[isignal].mean()) + #determine all zero crossings in signal window + zc = crossings_nonzero_all(x[isignal]) #calculate mean half period T0 of signal as the average of the T0 = np.mean(np.diff(zc)) * X[0].stats.delta #this is half wave length! #T0/4 is assumed as time difference between most likely and earliest possible pick! @@ -385,13 +386,13 @@ def getsignalwin(t, t1, tsignal): def wadaticheck(pickdic, dttolerance, iplot): ''' Function to calculate Wadati-diagram from given P and S onsets in order - to detect S pick outliers. If a certain S-P time deviates from regression - of S-P time the S pick is marked and down graded. + to detect S pick outliers. If a certain S-P time deviates by dttolerance + from regression of S-P time the S pick is marked and down graded. : param: pickdic, dictionary containing picks and quality parameters : type: dictionary - - : param: dttolerance, maximum adjusted deviation of S-P time from + + : param: dttolerance, maximum adjusted deviation of S-P time from S-P time regression : type: float @@ -405,7 +406,6 @@ def wadaticheck(pickdic, dttolerance, iplot): Ppicks = [] Spicks = [] SPtimes = [] - vpvs = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: # calculate S-P time @@ -413,65 +413,60 @@ def wadaticheck(pickdic, dttolerance, iplot): # add S-P time to dictionary pickdic[key]['SPt'] = spt # add P onsets and corresponding S-P times to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCDateTime(1970,1,1,0,0,0) - UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - UTCDateTime(1970,1,1,0,0,0) - Ppicks.append(UTCPpick) - Spicks.append(UTCSpick) + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) + Ppicks.append(UTCPpick.timestamp) + Spicks.append(UTCSpick.timestamp) SPtimes.append(spt) - vpvs.append(UTCPpick/UTCSpick) if len(SPtimes) >= 3: - # calculate slope + # calculate slope p1 = np.polyfit(Ppicks, SPtimes, 1) wdfit = np.polyval(p1, Ppicks) wfitflag = 0 - - # calculate average vp/vs ratio before check + + # calculate vp/vs ratio before check vpvsr = p1[0] + 1 print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr checkedPpicks = [] checkedSpicks = [] checkedSPtimes = [] - checkedvpvs = [] # calculate deviations from Wadati regression for key in pickdic: if pickdic[key].has_key('SPt'): ii = 0 - wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) + wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) ii += 1 # check, if deviation is larger than adjusted if wddiff >= dttolerance: - # mark onset and downgrade S-weight to 9 + # mark onset and downgrade S-weight to 9 # (not used anymore) marker = 'badWadatiCheck' pickdic[key]['S']['weight'] = 9 else: marker = 'goodWadatiCheck' - checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) - \ - UTCDateTime(1970,1,1,0,0,0) - checkedPpicks.append(checkedPpick) - checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) - \ - UTCDateTime(1970,1,1,0,0,0) - checkedSpicks.append(checkedSpick) + checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) + checkedPpicks.append(checkedPpick.timestamp) + checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) + checkedSpicks.append(checkedSpick.timestamp) checkedSPtime = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] checkedSPtimes.append(checkedSPtime) - checkedvpvs.append(checkedPpick/checkedSpick) pickdic[key]['S']['marked'] = marker - # calculate new slope + # calculate new slope p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) wdfit2 = np.polyval(p2, checkedPpicks) - # calculate average vp/vs ratio after check + # calculate vp/vs ratio after check cvpvsr = p2[0] + 1 print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr checkedonsets = pickdic - + else: print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' @@ -487,7 +482,7 @@ def wadaticheck(pickdic, dttolerance, iplot): f4, = plt.plot(checkedPpicks, wdfit2, 'g') plt.ylabel('S-P Times [s]') plt.xlabel('P Times [s]') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(old)=%5.2f, Vp/Vs(checked)=%5.2f' \ + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f, Vp/Vs(checked)=%5.2f' \ % (len(SPtimes), vpvsr, cvpvsr)) plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', 'Reliable S-Picks', \ 'Wadati 2'], loc='best') From aba3997b20e7cab43560a70848e20d052a4157d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 12:39:29 +0200 Subject: [PATCH 0374/1144] Modified earllatepicker: Mean is removed from trace calculated from noise + signal window. --- pylot/core/pick/utils.py | 81 ++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 25eef111..47d06b69 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -53,6 +53,9 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1]) # get signal window isignal = getsignalwin(t, Pick1, TSNR[2]) + # remove mean + meanwin = np.hstack((inoise, isignal)) + x = x - np.mean(x[meanwin]) # calculate noise level nlevel = np.sqrt(np.mean(np.square(x[inoise]))) * nfac # get time where signal exceeds nlevel @@ -412,34 +415,28 @@ def wadaticheck(pickdic, dttolerance, iplot): SPtimes = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: - # calculate S-P time - spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] - # add S-P time to dictionary - pickdic[key]['SPt'] = spt - # add P onsets and corresponding S-P times to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCDateTime(1970, - 1, 1, - 0, 0, - 0) - UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - UTCDateTime(1970, - 1, 1, - 0, 0, - 0) - Ppicks.append(UTCPpick) - Spicks.append(UTCSpick) - SPtimes.append(spt) - vpvs.append(UTCPpick / UTCSpick) + # calculate S-P time + spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] + # add S-P time to dictionary + pickdic[key]['SPt'] = spt + # add P onsets and corresponding S-P times to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) + Ppicks.append(UTCPpick.timestamp) + Spicks.append(UTCSpick.timestamp) + SPtimes.append(spt) + if len(SPtimes) >= 3: - # calculate slope - p1 = np.polyfit(Ppicks, SPtimes, 1) - wdfit = np.polyval(p1, Ppicks) + # calculate slope + p1 = np.polyfit(Ppicks, SPtimes, 1) + wdfit = np.polyval(p1, Ppicks) wfitflag = 0 # calculate vp/vs ratio before check vpvsr = p1[0] + 1 print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr - + checkedPpicks = [] checkedSpicks = [] checkedSPtimes = [] @@ -457,23 +454,19 @@ def wadaticheck(pickdic, dttolerance, iplot): pickdic[key]['S']['weight'] = 9 else: marker = 'goodWadatiCheck' - checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) - \ - UTCDateTime(1970, 1, 1, 0, 0, 0) - checkedPpicks.append(checkedPpick) - checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) - \ - UTCDateTime(1970, 1, 1, 0, 0, 0) - checkedSpicks.append(checkedSpick) - checkedSPtime = pickdic[key]['S']['mpp'] - \ - pickdic[key]['P']['mpp'] + checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) + checkedPpicks.append(checkedPpick.timestamp) + checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) + checkedSpicks.append(checkedSpick.timestamp) + checkedSPtime = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] checkedSPtimes.append(checkedSPtime) - checkedvpvs.append(checkedPpick / checkedSpick) pickdic[key]['S']['marked'] = marker - # calculate new slope - p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) - wdfit2 = np.polyval(p2, checkedPpicks) + # calculate new slope + p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) + wdfit2 = np.polyval(p2, checkedPpicks) # calculate vp/vs ratio after check cvpvsr = p2[0] + 1 @@ -482,26 +475,24 @@ def wadaticheck(pickdic, dttolerance, iplot): checkedonsets = pickdic else: - print 'wadaticheck: Not enough S-P times available for reliable regression!' + print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 # plot results if iplot > 1: - plt.figure(iplot) - f1, = plt.plot(Ppicks, SPtimes, 'ro') + plt.figure(iplot) + f1, = plt.plot(Ppicks, SPtimes, 'ro') if wfitflag == 0: - f2, = plt.plot(Ppicks, wdfit, 'k') - f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') - f4, = plt.plot(checkedPpicks, wdfit2, 'g') + f2, = plt.plot(Ppicks, wdfit, 'k') + f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') + f4, = plt.plot(checkedPpicks, wdfit2, 'g') plt.ylabel('S-P Times [s]') plt.xlabel('P Times [s]') - plt.title( - 'Wadati-Diagram, %d S-P Times, Vp/Vs(old)=%5.2f, Vp/Vs(checked)=%5.2f' \ - % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], - ['Skipped S-Picks', 'Wadati 1', 'Reliable S-Picks', \ - 'Wadati 2'], loc='best') + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f, Vp/Vs(checked)=%5.2f' \ + % (len(SPtimes), vpvsr, cvpvsr)) + plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', 'Reliable S-Picks', \ + 'Wadati 2'], loc='best') plt.show() raw_input() plt.close(iplot) From eb153679baf5b657b1acbc15b653f21390d42794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 14:37:47 +0200 Subject: [PATCH 0375/1144] Removed axis limits from plotting part. --- pylot/core/pick/utils.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 47d06b69..60082836 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -13,7 +13,6 @@ import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings - def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) @@ -258,8 +257,6 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): loc='best') plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) ax = plt.gca() - ax.set_xlim( - [t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) plt.yticks([]) plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[ 0].stats.station) @@ -275,8 +272,6 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) ax = plt.gca() - ax.set_xlim( - [t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) plt.yticks([]) plt.show() From 99a5a4499a164a1ee0e5c8dab19811a10c1a2a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 14:59:57 +0200 Subject: [PATCH 0376/1144] Debugging: If no P-Pick was determined, no plot of of horizontal data comes up. --- pylot/core/pick/run_autopicking.py | 208 +++++++++++++++-------------- 1 file changed, 110 insertions(+), 98 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 686396e2..8265d28e 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -14,13 +14,12 @@ import numpy as np from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * - def run_autopicking(wfstream, pickparam): """ - param: wfstream + :param: wfstream :type: `~obspy.core.stream.Stream` - param: pickparam + :param: pickparam :type: container of picking parameters from input file, usually autoPyLoT.in """ @@ -95,6 +94,7 @@ def run_autopicking(wfstream, pickparam): aicSflag = 0 aicPflag = 0 + Sflag = 0 # split components zdat = wfstream.select(component="Z") @@ -218,10 +218,12 @@ def run_autopicking(wfstream, pickparam): print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) + Sflag = 1 else: print 'Bad initial (AIC) P-pick, skip this onset!' print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() + Sflag = 0 else: print 'run_autopicking: No vertical component data available, ' \ @@ -295,7 +297,7 @@ def run_autopicking(wfstream, pickparam): # class needs stream object => build it tr_arhaic = trH1_filt.copy() tr_arhaic.data = arhcf1.getCF() - h_copy[0].data = tr_arhaic.data + h_copy[0].data = tr_arhaic.data # calculate ARH-AIC-CF haiccf = AICcf(h_copy, cuttimesh) # instance of AICcf ############################################################## @@ -463,108 +465,118 @@ def run_autopicking(wfstream, pickparam): plt.ylabel('Normalized Counts') plt.suptitle(tr_filt.stats.starttime) - # plot horizontal traces - plt.subplot(3, 1, 2) - th1data = np.arange(0, - trH1_filt.stats.npts / - trH1_filt.stats.sampling_rate, - trH1_filt.stats.delta) - # check equal length of arrays, sometimes they are different!? - wfldiff = len(trH1_filt.data) - len(th1data) - if wfldiff < 0: - th1data = th1data[0:len(th1data) - abs(wfldiff)] - p21, = plt.plot(th1data, trH1_filt.data / max(trH1_filt.data), 'k') - if Pweight < 4: - p22, = plt.plot(arhcf1.getTimeArray(), - arhcf1.getCF() / max(arhcf1.getCF()), 'b') - if aicSflag == 1: - p23, = plt.plot(arhcf2.getTimeArray(), - arhcf2.getCF() / max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], - [-1, 1], 'g') - plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], - [1, 1], 'g') - plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], - [-1, -1], 'g') - p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], - [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') - plt.legend([p21, p22, p23, p24, p25], - ['Data', 'CF1', 'CF2', 'Initial S Onset', - 'Final S Pick']) - plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % ( - trH1_filt.stats.channel, - Sweight, SNRS, SNRSdB)) - else: - plt.legend([p21, p22], ['Data', 'CF1']) - plt.title('%s, S Weight=%d, SNR=None, SNRdB=None' % ( - trH1_filt.stats.channel, Sweight)) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.ylabel('Normalized Counts') - plt.suptitle(trH1_filt.stats.starttime) + if len(edat) > 1 and len(ndat) > 1 and Sflag == 1: + # plot horizontal traces + plt.subplot(3, 1, 2) + th1data = np.arange(0, + trH1_filt.stats.npts / + trH1_filt.stats.sampling_rate, + trH1_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(trH1_filt.data) - len(th1data) + if wfldiff < 0: + th1data = th1data[0:len(th1data) - abs(wfldiff)] + p21, = plt.plot(th1data, trH1_filt.data / max(trH1_filt.data), 'k') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), + arhcf1.getCF() / max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), + arhcf2.getCF() / max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], + [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], + ['Data', 'CF1', 'CF2', 'Initial S Onset', + 'Final S Pick']) + plt.title('%s, S Weight=%d, SNR=%7.2f, SNR[dB]=%7.2f' % ( + trH1_filt.stats.channel, + Sweight, SNRS, SNRSdB)) + else: + plt.legend([p21, p22], ['Data', 'CF1']) + plt.title('%s, S Weight=%d, SNR=None, SNRdB=None' % ( + trH1_filt.stats.channel, Sweight)) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.ylabel('Normalized Counts') + plt.suptitle(trH1_filt.stats.starttime) - plt.subplot(3, 1, 3) - th2data = np.arange(0, - trH2_filt.stats.npts / - trH2_filt.stats.sampling_rate, - trH2_filt.stats.delta) - # check equal length of arrays, sometimes they are different!? - wfldiff = len(trH2_filt.data) - len(th2data) - if wfldiff < 0: - th2data = th2data[0:len(th2data) - abs(wfldiff)] - plt.plot(th2data, trH2_filt.data / max(trH2_filt.data), 'k') - if Pweight < 4: - p22, = plt.plot(arhcf1.getTimeArray(), - arhcf1.getCF() / max(arhcf1.getCF()), 'b') - if aicSflag == 1: - p23, = plt.plot(arhcf2.getTimeArray(), - arhcf2.getCF() / max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], - [-1, 1], 'g') - plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], - [1, 1], 'g') - plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], - [-1, -1], 'g') - p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], - [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [-1.3, -1.3], 'g', linewidth=2) - plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') - plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') - plt.legend([p21, p22, p23, p24, p25], - ['Data', 'CF1', 'CF2', 'Initial S Onset', - 'Final S Pick']) - else: - plt.legend([p21, p22], ['Data', 'CF1']) - plt.yticks([]) - plt.ylim([-1.5, 1.5]) - plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) - plt.ylabel('Normalized Counts') - plt.title(trH2_filt.stats.channel) - plt.show() - raw_input() - plt.close() + plt.subplot(3, 1, 3) + th2data = np.arange(0, + trH2_filt.stats.npts / + trH2_filt.stats.sampling_rate, + trH2_filt.stats.delta) + # check equal length of arrays, sometimes they are different!? + wfldiff = len(trH2_filt.data) - len(th2data) + if wfldiff < 0: + th2data = th2data[0:len(th2data) - abs(wfldiff)] + plt.plot(th2data, trH2_filt.data / max(trH2_filt.data), 'k') + if Pweight < 4: + p22, = plt.plot(arhcf1.getTimeArray(), + arhcf1.getCF() / max(arhcf1.getCF()), 'b') + if aicSflag == 1: + p23, = plt.plot(arhcf2.getTimeArray(), + arhcf2.getCF() / max(arhcf2.getCF()), 'm') + p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [1, 1], 'g') + plt.plot( + [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [-1, -1], 'g') + p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], + [-1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) + plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') + plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') + plt.legend([p21, p22, p23, p24, p25], + ['Data', 'CF1', 'CF2', 'Initial S Onset', + 'Final S Pick']) + else: + plt.legend([p21, p22], ['Data', 'CF1']) + plt.yticks([]) + plt.ylim([-1.5, 1.5]) + plt.xlabel('Time [s] after %s' % tr_filt.stats.starttime) + plt.ylabel('Normalized Counts') + plt.title(trH2_filt.stats.channel) + plt.show() + raw_input() + plt.close() ########################################################################## + # calculate "real" onset times + if mpickP is not None: + lpickP = zdat[0].stats.starttime + lpickP + epickP = zdat[0].stats.starttime + epickP + mpickP = zdat[0].stats.starttime + mpickP + + if mpickS is not None: + lpickS = edat[0].stats.starttime + lpickS + epickS = edat[0].stats.starttime + epickS + mpickS = edat[0].stats.starttime + mpickS + # create dictionary # for P phase phase = 'P' phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, \ 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM} picks = {phase: phasepick} - #stationID = tr_filt.stats.station - #onsets = {stationID: picks} # add S phase phase = 'S' phasepick = {'lpp': lpickS, 'epp': epickS, 'mpp': mpickS, 'spe': Serror, \ From ab53a72c574e581597096bc587d50c8acabedb23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 15:07:54 +0200 Subject: [PATCH 0377/1144] Debugging: If not enough S-P times are available, the plotting is modified. --- pylot/core/pick/utils.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 60082836..884a7d0d 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -482,12 +482,16 @@ def wadaticheck(pickdic, dttolerance, iplot): f2, = plt.plot(Ppicks, wdfit, 'k') f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') f4, = plt.plot(checkedPpicks, wdfit2, 'g') + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f, \ + Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) + plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ + 'Reliable S-Picks', 'Wadati 2'], loc='best') + else: + f1, = plt.plot(Ppicks, 'k') + plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) + plt.ylabel('S-P Times [s]') plt.xlabel('P Times [s]') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f, Vp/Vs(checked)=%5.2f' \ - % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', 'Reliable S-Picks', \ - 'Wadati 2'], loc='best') plt.show() raw_input() plt.close(iplot) From 4a911a4ac9475b74eca882ad47867ee45132b305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 15:20:03 +0200 Subject: [PATCH 0378/1144] Marginal changes only. --- pylot/core/pick/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 884a7d0d..5bd4c799 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -487,7 +487,6 @@ def wadaticheck(pickdic, dttolerance, iplot): plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ 'Reliable S-Picks', 'Wadati 2'], loc='best') else: - f1, = plt.plot(Ppicks, 'k') plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) plt.ylabel('S-P Times [s]') From f2510ff40086c73097f631cfb1d3454d50d0b55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 15:35:16 +0200 Subject: [PATCH 0379/1144] Switched off warnings. --- pylot/core/pick/Picker.py | 449 ++++++++++++++++++-------------------- 1 file changed, 215 insertions(+), 234 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 2c1fc26f..3ca67ef5 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -3,65 +3,62 @@ Created Dec 2014 to Feb 2015 Implementation of the automated picking algorithms published and described in: -Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, -2010: Automated determination of P-phase arrival times at regional and local -distances using higher order statistics, Geophys. J. Int., 181, 1159-1170 +Kueperkoch, L., Meier, T., Lee, J., Friederich, W., & Egelados Working Group, 2010: +Automated determination of P-phase arrival times at regional and local distances +using higher order statistics, Geophys. J. Int., 181, 1159-1170 Kueperkoch, L., Meier, T., Bruestle, A., Lee, J., Friederich, W., & Egelados Working Group, 2012: Automated determination of S-phase arrival times using -autoregressive prediction: application ot local and regional distances, -Geophys. J. Int., 188, 687-702. +autoregressive prediction: application ot local and regional distances, Geophys. J. Int., +188, 687-702. -The picks with the above described algorithms are assumed to be the most likely -picks. For each most likely pick the corresponding earliest and latest possible -picks are calculated after Diehl & Kissling (2009). +The picks with the above described algorithms are assumed to be the most likely picks. +For each most likely pick the corresponding earliest and latest possible picks are +calculated after Diehl & Kissling (2009). -:author: MAGS2 EP3 working group / Ludger Kueperkoch +:author: MAGS2 EP3 working group / Ludger Kueperkoch """ import numpy as np import matplotlib.pyplot as plt from pylot.core.pick.utils import * from pylot.core.pick.CharFuns import CharacteristicFunction - +import warnings class AutoPicking(object): ''' - Superclass of different, automated picking algorithms applied on a CF - determined using AIC, HOS, or AR prediction. - ''' + Superclass of different, automated picking algorithms applied on a CF determined + using AIC, HOS, or AR prediction. + ''' - def __init__(self, cf, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, - Pick1=None): + warnings.simplefilter('ignore') + + def __init__(self, cf, TSNR, PickWindow, iplot=None, aus=None, Tsmooth=None, Pick1=None): ''' - :param cf: characteristic function, on which the picking algorithm is - applied - :type cf: `~pylot.core.pick.CharFuns.CharacteristicFunction` object + :param: cf, characteristic function, on which the picking algorithm is applied + :type: `~pylot.core.pick.CharFuns.CharacteristicFunction` object - :param TSNR: length of time windows for SNR determination - [s] - :type TSNR: tuple (T_noise, T_gap, T_signal) + :param: TSNR, length of time windows around pick used to determine SNR [s] + :type: tuple (T_noise, T_gap, T_signal) - :param PickWindow: length of pick window - [s] - :type PickWindow: float + :param: PickWindow, length of pick window [s] + :type: float - :param iplot: no. of figure window for plotting interims results - :type iplot: integer + :param: iplot, no. of figure window for plotting interims results + :type: integer - :param aus: aus ("artificial uplift of samples"), find local minimum at - i if aic(i-1)*(1+aus) >= aic(i) - :type aus: float + :param: aus ("artificial uplift of samples"), find local minimum at i if aic(i-1)*(1+aus) >= aic(i) + :type: float - :param Tsmooth: length of moving window to calculate smoothed CF - [s] - :type Tsmooth: float + :param: Tsmooth, length of moving smoothing window to calculate smoothed CF [s] + :type: float - :param Pick1: initial (prelimenary) onset time, starting point for - PragPicker - :type Pick1: float + :param: Pick1, initial (prelimenary) onset time, starting point for PragPicker and + EarlLatePicker + :type: float ''' - assert isinstance(cf, - CharacteristicFunction), "%s is of wrong type" % str( - cf) + assert isinstance(cf, CharacteristicFunction), "%s is not a CharacteristicFunction object" % str(cf) self.cf = cf.getCF() self.Tcf = cf.getTimeArray() @@ -87,8 +84,9 @@ class AutoPicking(object): PickWindow=self.getPickWindow(), aus=self.getaus(), Tsmooth=self.getTsmooth(), - Pick1=self.getpick1()) + Pick1=self.getpick1()) + def getTSNR(self): return self.TSNR @@ -118,7 +116,7 @@ class AutoPicking(object): def getSNR(self): return self.SNR - + def getSlope(self): return self.slope @@ -142,156 +140,144 @@ class AICPicker(AutoPicking): ''' Method to derive the onset time of an arriving phase based on CF derived from AIC. In order to get an impression of the quality of this inital pick, - a quality assessment is applied based on SNR and slope determination derived from the CF, + a quality assessment is applied based on SNR and slope determination derived from the CF, from which the AIC has been calculated. ''' def calcPick(self): - + print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' self.Pick = None self.slope = None self.SNR = None - # find NaN's + #find NaN's nn = np.isnan(self.cf) if len(nn) > 1: - self.cf[nn] = 0 - # taper AIC-CF to get rid off side maxima + self.cf[nn] = 0 + #taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) - # smooth AIC-CF + #smooth AIC-CF ismooth = int(round(self.Tsmooth / self.dt)) aicsmooth = np.zeros(len(aic)) if len(aic) < ismooth: - print 'AICPicker: Tsmooth larger than CF!' - return + print 'AICPicker: Tsmooth larger than CF!' + return else: - for i in range(1, len(aic)): - if i > ismooth: - ii1 = i - ismooth - aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ - ii1]) / ismooth - else: - aicsmooth[i] = np.mean(aic[1: i]) - # remove offset + for i in range(1, len(aic)): + if i > ismooth: + ii1 = i - ismooth + aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth + else: + aicsmooth[i] = np.mean(aic[1 : i]) + #remove offset offset = abs(min(aic) - min(aicsmooth)) aicsmooth = aicsmooth - offset - # get maximum of 1st derivative of AIC-CF (more stable!) as starting - # point + #get maximum of 1st derivative of AIC-CF (more stable!) as starting point diffcf = np.diff(aicsmooth) - # find NaN's + #find NaN's nn = np.isnan(diffcf) if len(nn) > 1: - diffcf[nn] = 0 - # taper CF to get rid off side maxima + diffcf[nn] = 0 + #taper CF to get rid off side maxima tap = np.hanning(len(diffcf)) diffcf = tap * diffcf * max(abs(aicsmooth)) icfmax = np.argmax(diffcf) - - # find minimum in AIC-CF front of maximum + + #find minimum in AIC-CF front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if aicsmooth[i - 1] >= aicsmooth[i]: - self.Pick = self.Tcf[i] - break - # if no minimum could be found: - # search in 1st derivative of AIC-CF + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if aicsmooth[i - 1] >= aicsmooth[i]: + self.Pick = self.Tcf[i] + break + #if no minimum could be found: + #search in 1st derivative of AIC-CF if self.Pick is None: - for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if diffcf[i - 1] >= diffcf[i]: - self.Pick = self.Tcf[i] - break - + for i in range(icfmax -1, max([icfmax -lpickwindow, 2]), -1): + if diffcf[i -1] >= diffcf[i]: + self.Pick = self.Tcf[i] + break + # quality assessment using SNR and slope from CF if self.Pick is not None: - # get noise window - inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], - self.TSNR[1]) - # check, if these are counts or m/s, important for slope estimation! - # this is quick and dirty, better solution? - if max(self.Data[0].data < 1e-3): - self.Data[0].data *= 1000000 - # get signal window - isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) - # calculate SNR from CF - self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / \ - max(abs(aic[inoise] - np.mean(aic[inoise]))) - # calculate slope from CF after initial pick - # get slope window - tslope = self.TSNR[3] # slope determination window - islope = np.where( - (self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) - and (self.Tcf >= self.Pick)) - # find maximum within slope determination window - # 'cause slope should be calculated up to first local minimum only! - imax = np.argmax(self.Data[0].data[islope]) - if imax == 0: - print 'AICPicker: Maximum for slope determination right at ' \ - 'the beginning of the window!' - print 'Choose longer slope determination window!' - return - islope = islope[0][0:imax] - dataslope = self.Data[0].data[islope] - # calculate slope as polynomal fit of order 1 - xslope = np.arange(0, len(dataslope), 1) - P = np.polyfit(xslope, dataslope, 1) - datafit = np.polyval(P, xslope) - if datafit[0] >= datafit[len(datafit) - 1]: - print 'AICPicker: Negative slope, bad onset skipped!' - return + # get noise window + inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1]) + # check, if these are counts or m/s, important for slope estimation! + # this is quick and dirty, better solution? + if max(self.Data[0].data < 1e-3): + self.Data[0].data = self.Data[0].data * 1000000 + # get signal window + isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) + # calculate SNR from CF + self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / max(abs(aic[inoise] \ + - np.mean(aic[inoise]))) + # calculate slope from CF after initial pick + # get slope window + tslope = self.TSNR[3] #slope determination window + islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ + & (self.Tcf >= self.Pick)) + # find maximum within slope determination window + # 'cause slope should be calculated up to first local minimum only! + imax = np.argmax(self.Data[0].data[islope]) + if imax == 0: + print 'AICPicker: Maximum for slope determination right at the beginning of the window!' + print 'Choose longer slope determination window!' + return + islope = islope[0][0 :imax] + dataslope = self.Data[0].data[islope] + # calculate slope as polynomal fit of order 1 + xslope = np.arange(0, len(dataslope), 1) + P = np.polyfit(xslope, dataslope, 1) + datafit = np.polyval(P, xslope) + if datafit[0] >= datafit[len(datafit) - 1]: + print 'AICPicker: Negative slope, bad onset skipped!' + return - self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] + self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] else: - self.SNR = None - self.slope = None + self.SNR = None + self.slope = None if self.iplot > 1: - p = plt.figure(self.iplot) - x = self.Data[0].data - p1, = plt.plot(self.Tcf, x / max(x), 'k') - p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') - if self.Pick is not None: - p3, = plt.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', - linewidth=2) - plt.legend([p1, p2, p3], - ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) - else: - plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) + p = plt.figure(self.iplot) + x = self.Data[0].data + p1, = plt.plot(self.Tcf, x / max(x), 'k') + p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') + if self.Pick is not None: + p3, = plt.plot([self.Pick, self.Pick], [-0.1 , 0.5], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) + else: + plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) - if self.Pick is not None: - plt.figure(self.iplot + 1) - p11, = plt.plot(self.Tcf, x, 'k') - p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) - p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], - 'r') - p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') - p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) - plt.legend([p11, p12, p13, p14, p15], - ['Data', 'Noise Window', 'Signal Window', - 'Slope Window', 'Slope'], - loc='best') - plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % ( - self.Data[0].stats.station, - self.SNR, self.slope)) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.ylabel('Counts') - ax = plt.gca() - plt.yticks([]) - ax.set_xlim([self.Tcf[inoise[0][0]] - 5, - self.Tcf[isignal[0][len(isignal) - 1]] + 5]) + if self.Pick is not None: + plt.figure(self.iplot + 1) + p11, = plt.plot(self.Tcf, x, 'k') + p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) + p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') + p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') + p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) + plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], \ + loc='best') + plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ + self.SNR, self.slope)) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.ylabel('Counts') + ax = plt.gca() + plt.yticks([]) + ax.set_xlim([self.Tcf[inoise[0][0]] - 5, self.Tcf[isignal[0][len(isignal) - 1]] + 5]) - plt.show() - raw_input() - plt.close(p) - - if self.Pick is None: - print 'AICPicker: Could not find minimum, picking window too short?' + plt.show() + raw_input() + plt.close(p) + if self.Pick == None: + print 'AICPicker: Could not find minimum, picking window too short?' + class PragPicker(AutoPicking): ''' @@ -301,95 +287,90 @@ class PragPicker(AutoPicking): def calcPick(self): if self.getpick1() is not None: - print 'PragPicker: Get most likely pick from HOS- or AR-CF using ' \ - 'pragmatic picking algorithm ...' + print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' - self.Pick = None - self.SNR = None - self.slope = None - # smooth CF - ismooth = int(round(self.Tsmooth / self.dt)) - cfsmooth = np.zeros(len(self.cf)) - if len(self.cf) < ismooth: - print 'PragPicker: Tsmooth larger than CF!' - return - else: - for i in range(1, len(self.cf)): - if i > ismooth: - ii1 = i - ismooth - cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ - ii1]) / ismooth - else: - cfsmooth[i] = np.mean(self.cf[1: i]) + self.Pick = None + self.SNR = None + self.slope = None + #smooth CF + ismooth = int(round(self.Tsmooth / self.dt)) + cfsmooth = np.zeros(len(self.cf)) + if len(self.cf) < ismooth: + print 'PragPicker: Tsmooth larger than CF!' + return + else: + for i in range(1, len(self.cf)): + if i > ismooth: + ii1 = i - ismooth; + cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth + else: + cfsmooth[i] = np.mean(self.cf[1 : i]) - # select picking window - # which is centered around tpick1 - ipick = np.where((self.Tcf >= - (self.getpick1() - self.PickWindow / 2)) and - (self.Tcf <= - (self.getpick1() + self.PickWindow / 2))) - cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) - Tcfpick = self.Tcf[ipick] - cfsmoothipick = cfsmooth[ipick] - np.mean(self.cf[ipick]) - ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) - cfpick1 = 2 * self.cf[ipick1] + #select picking window + #which is centered around tpick1 + ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ + & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) + cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) + Tcfpick = self.Tcf[ipick] + cfsmoothipick = cfsmooth[ipick]- np.mean(self.cf[ipick]) + ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) + cfpick1 = 2 * self.cf[ipick1] - # check trend of CF, i.e. differences of CF and adjust aus regarding this trend - # prominent trend: decrease aus - # flat: use given aus - cfdiff = np.diff(cfipick) - i0diff = np.where(cfdiff > 0) - cfdiff = cfdiff[i0diff] - minaus = min(cfdiff * (1 + self.aus)) - aus1 = max([minaus, self.aus]) + #check trend of CF, i.e. differences of CF and adjust aus regarding this trend + #prominent trend: decrease aus + #flat: use given aus + cfdiff = np.diff(cfipick); + i0diff = np.where(cfdiff > 0) + cfdiff = cfdiff[i0diff] + minaus = min(cfdiff * (1 + self.aus)); + aus1 = max([minaus, self.aus]); - # at first we look to the right until the end of the pick window is reached - flagpick_r = 0 - flagpick_l = 0 - flagpick = 0 - lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(max(np.insert(ipick, 0, 2)), - min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_r = self.Tcf[i] - self.Pick = pick_r - flagpick_l = 1 - cfpick_r = self.cf[i] - break + #at first we look to the right until the end of the pick window is reached + flagpick_r = 0 + flagpick_l = 0 + flagpick = 0 + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_r = self.Tcf[i] + self.Pick = pick_r + flagpick_l = 1 + cfpick_r = self.cf[i] + break - # now we look to the left - for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_l = self.Tcf[i] - self.Pick = pick_l - flagpick_r = 1 - cfpick_l = self.cf[i] - break + #now we look to the left + for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_l = self.Tcf[i] + self.Pick = pick_l + flagpick_r = 1 + cfpick_l = self.cf[i] + break - # now decide which pick: left or right? - if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: - self.Pick = pick_l - elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: - self.Pick = pick_r + #now decide which pick: left or right? + if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: + self.Pick = pick_l + elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + self.Pick = pick_r - if self.getiplot() > 1: - p = plt.figure(self.getiplot()) - p1, = plt.plot(Tcfpick, cfipick, 'k') - p2, = plt.plot(Tcfpick, cfsmoothipick, 'r') - p3, = plt.plot([self.Pick, self.Pick], - [min(cfipick), max(cfipick)], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) - plt.show() - raw_input() - plt.close(p) + if self.getiplot() > 1: + p = plt.figure(self.getiplot()) + p1, = plt.plot(Tcfpick,cfipick, 'k') + p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') + p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(p) - else: - self.Pick = None - print 'PragPicker: No initial onset time given! Check input!' + else: + self.Pick = None + print 'PragPicker: No initial onset time given! Check input!' + return From 833b29a4885ce804663fa669eb2da7e5eb0a4d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 16:01:16 +0200 Subject: [PATCH 0380/1144] Debugging. --- pylot/core/pick/run_autopicking.py | 132 +++++++++++++++-------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 8265d28e..a0672645 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * - +import pdb def run_autopicking(wfstream, pickparam): """ :param: wfstream @@ -189,36 +189,37 @@ def run_autopicking(wfstream, pickparam): aicpick.getpick()) mpickP = refPpick.getpick() ############################################################# - # quality assessment - # get earliest and latest possible pick and symmetrized uncertainty - [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, mpickP, iplot) + if mpickP is not None: + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, mpickP, iplot) - # get SNR - [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, mpickP) + # get SNR + [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, mpickP) - # weight P-onset using symmetric error - if Perror <= timeerrorsP[0]: - Pweight = 0 - elif timeerrorsP[0] < Perror <= timeerrorsP[1]: - Pweight = 1 - elif timeerrorsP[1] < Perror <= timeerrorsP[2]: - Pweight = 2 - elif timeerrorsP[2] < Perror <= timeerrorsP[3]: - Pweight = 3 - elif Perror > timeerrorsP[3]: - Pweight = 4 + # weight P-onset using symmetric error + if Perror <= timeerrorsP[0]: + Pweight = 0 + elif timeerrorsP[0] < Perror <= timeerrorsP[1]: + Pweight = 1 + elif timeerrorsP[1] < Perror <= timeerrorsP[2]: + Pweight = 2 + elif timeerrorsP[2] < Perror <= timeerrorsP[3]: + Pweight = 3 + elif Perror > timeerrorsP[3]: + Pweight = 4 - ############################################################## - # get first motion of P onset - # certain quality required - if Pweight <= minfmweight and SNRP >= minFMSNR: - FM = fmpicker(zdat, z_copy, fmpickwin, mpickP, iplot) - else: - FM = 'N' + ############################################################## + # get first motion of P onset + # certain quality required + if Pweight <= minfmweight and SNRP >= minFMSNR: + FM = fmpicker(zdat, z_copy, fmpickwin, mpickP, iplot) + else: + FM = 'N' - print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ - 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) - Sflag = 1 + print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ + 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) + Sflag = 1 else: print 'Bad initial (AIC) P-pick, skip this onset!' @@ -360,50 +361,51 @@ def run_autopicking(wfstream, pickparam): tsmoothS, aicarhpick.getpick()) mpickS = refSpick.getpick() ############################################################# - # quality assessment - # get earliest and latest possible pick and symmetrized uncertainty - h_copy[0].data = trH1_filt.data - [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, + if mpickS is not None: + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + h_copy[0].data = trH1_filt.data + [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, + mpickS, iplot) + h_copy[0].data = trH2_filt.data + [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, iplot) - h_copy[0].data = trH2_filt.data - [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, - mpickS, iplot) - if algoS == 'ARH': - # get earliest pick of both earliest possible picks - epick = [epickS1, epickS2] - lpick = [lpickS1, lpickS2] - pickerr = [Serror1, Serror2] - ipick = np.argmin([epickS1, epickS2]) - elif algoS == 'AR3': - [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, + if algoS == 'ARH': + # get earliest pick of both earliest possible picks + epick = [epickS1, epickS2] + lpick = [lpickS1, lpickS2] + pickerr = [Serror1, Serror2] + ipick = np.argmin([epickS1, epickS2]) + elif algoS == 'AR3': + [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, iplot) - # get earliest pick of all three picks - epick = [epickS1, epickS2, epickS3] - lpick = [lpickS1, lpickS2, lpickS3] - pickerr = [Serror1, Serror2, Serror3] - ipick = np.argmin([epickS1, epickS2, epickS3]) - epickS = epick[ipick] - lpickS = lpick[ipick] - Serror = pickerr[ipick] + # get earliest pick of all three picks + epick = [epickS1, epickS2, epickS3] + lpick = [lpickS1, lpickS2, lpickS3] + pickerr = [Serror1, Serror2, Serror3] + ipick = np.argmin([epickS1, epickS2, epickS3]) + epickS = epick[ipick] + lpickS = lpick[ipick] + Serror = pickerr[ipick] - # get SNR - [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, mpickS) + # get SNR + [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, mpickS) - # weight S-onset using symmetric error - if Serror <= timeerrorsS[0]: - Sweight = 0 - elif timeerrorsS[0] < Serror <= timeerrorsS[1]: - Sweight = 1 - elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: - Sweight = 2 - elif timeerrorsS[2] < Serror <= timeerrorsS[3]: - Sweight = 3 - elif Serror > timeerrorsS[3]: - Sweight = 4 + # weight S-onset using symmetric error + if Serror <= timeerrorsS[0]: + Sweight = 0 + elif timeerrorsS[0] < Serror <= timeerrorsS[1]: + Sweight = 1 + elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: + Sweight = 2 + elif timeerrorsS[2] < Serror <= timeerrorsS[3]: + Sweight = 3 + elif Serror > timeerrorsS[3]: + Sweight = 4 - print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( - Sweight, SNRS, SNRSdB) + print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( + Sweight, SNRS, SNRSdB) else: print 'Bad initial (AIC) S-pick, skip this onset!' From d25e791bb8d21a8491b97206cf5f58ea090c7e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 16:01:25 +0200 Subject: [PATCH 0381/1144] Debugging. --- pylot/core/pick/Picker.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 3ca67ef5..d0c7a08f 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -292,6 +292,7 @@ class PragPicker(AutoPicking): self.Pick = None self.SNR = None self.slope = None + pickflag = 0 #smooth CF ismooth = int(round(self.Tsmooth / self.dt)) cfsmooth = np.zeros(len(self.cf)) @@ -354,15 +355,22 @@ class PragPicker(AutoPicking): #now decide which pick: left or right? if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: self.Pick = pick_l + pickflag = 1 elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: self.Pick = pick_r + pickflag = 1 + else: + print 'PragPicker: Could not find reliable onset!' + self.Pick = None + pickflag = 0 if self.getiplot() > 1: p = plt.figure(self.getiplot()) p1, = plt.plot(Tcfpick,cfipick, 'k') p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') - p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) + if pickflag > 0: + p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.yticks([]) plt.title(self.Data[0].stats.station) @@ -371,6 +379,6 @@ class PragPicker(AutoPicking): plt.close(p) else: - self.Pick = None print 'PragPicker: No initial onset time given! Check input!' + self.Pick = None return From cab58a10aa6d86fa4d9518fd11b4855fc2a73d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 16:28:21 +0200 Subject: [PATCH 0382/1144] Example of autoPyLoT.in for local data set. --- autoPyLoT_local.in | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 autoPyLoT_local.in diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in new file mode 100644 index 00000000..dd6d9815 --- /dev/null +++ b/autoPyLoT_local.in @@ -0,0 +1,100 @@ +%This is a parameter input file for autoPyLoT. +%All main and special settings regarding data handling +%and picking are to be set here! +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#main settings# +/DATA/Insheim #rootpath# %project path +EVENT_DATA/LOCAL #datapath# %data path +2013.02_Insheim #database# %name of data base +e0019.048.13 #eventID# %event ID for single event processing +PILOT #datastructure# %choose data structure +2 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything +AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file +AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file +AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities +HYPOSAT #locrt# %location routine used ("HYPOINVERSE" or "HYPOSAT") +6 #pmin# %minimum required P picks for location +4 #p0min# %minimum required P picks for location if at least + %3 excellent P picks are found +2 #smin# %minimum required S picks for location +/home/ludger/bin/run_HYPOSAT4autoPILOT.csh #cshellp# %path and name of c-shell script to run location routine +7.6 8.5 #blon# %longitude bounding for location map +49 49.4 #blat# %lattitude bounding for location map +#parameters for moment magnitude estimation# +5000 #vp# %average P-wave velocity +2800 #vs# %average S-wave velocity +2200 #rho# %rock density [kg/m^3] +300 #Qp# %quality factor for P waves +100 #Qs# %quality factor for S waves +#common settings picker# +15 #pstart# %start time [s] for calculating CF for P-picking +40 #pstop# %end time [s] for calculating CF for P-picking +-1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking +7 #sstop# %end time [s] after P-onset for calculating CF for S-picking +2 20 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] +2 30 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] +2 15 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] +2 20 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz] +#special settings for calculating CF# +%!!Be careful when editing the following!! +#Z-component# +HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3) +7 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s] +4 #hosorder# %for HOS-picker, order of Higher Order Statistics +2 #Parorder# %for AR-picker, order of AR process of Z-component +1.2 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick +0.4 #tpred1z# %for AR-picker, length of AR prediction window [s] for Z-component, 1st pick +0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick +0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick +0.001 #addnoise# %add noise to seismogram for stable AR prediction +3 0.1 0.5 0.1 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +3 #pickwinP# %for initial AIC pick, length of P-pick window [s] +8 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) +0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) +0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] +0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] +0.001 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P) +1.3 #nfacP# %for HOS/AR, noise factor for noise level determination (P) +#H-components# +ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3) +0.8 #tdet1h# %for HOS/AR, length of AR-determination window [s], H-components, 1st pick +0.4 #tpred1h# %for HOS/AR, length of AR-prediction window [s], H-components, 1st pick +0.6 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick +0.3 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick +4 #Sarorder# %for AR-picker, order of AR process of H-components +6 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) +3 #pickwinS# %for initial AIC pick, length of S-pick window [s] +2 0.2 1.5 0.5 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +0.05 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] +0.02 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) +0.2 #pepsS# %for AR-picker, artificial uplift of samples of CF (S) +0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) +1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) +%first-motion picker% +1 #minfmweight# %minimum required p weight for first-motion determination +2 #minFMSNR# %miniumum required SNR for first-motion determination +0.2 #fmpickwin# %pick window around P onset for calculating zero crossings +%quality assessment% +#inital AIC onset# +0.01 0.02 0.04 0.08 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P +0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S +80 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +1.2 #minAICPSNR# %below this SNR the initial P pick is rejected +50 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +1.5 #minAICSSNR# %below this SNR the initial S pick is rejected +#check duration of signal using envelope function# +1.5 #prepickwin# %pre-signal window length [s] for noise level estimation +0.7 #minsiglength# %minimum required length of signal [s] +0.2 #sgap# %safety gap between noise and signal window [s] +2 #noisefactor# %noiselevel*noisefactor=threshold +60 #minpercent# %per cent of samples required higher than threshold +#check for spuriously picked S-onsets# +3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude +#jackknife-processing for P-picks# +3 #thresholdweight#%minimum required weight of picks +3 #dttolerance# %maximum allowed deviation of P picks from median [s] +4 #minstats# %minimum number of stations with reliable P picks +#wadati check# +0.8 #wdttolerance# %maximum allowed deviation from Wadati-diagram + From 9ec7da7e4e3f84729abcd2ff506e9149e812dcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 22 Jun 2015 16:28:35 +0200 Subject: [PATCH 0383/1144] Example of autoPyLoT.in for regional data set. --- autoPyLoT_regional.in | 100 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 autoPyLoT_regional.in diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in new file mode 100644 index 00000000..3bc1bc9b --- /dev/null +++ b/autoPyLoT_regional.in @@ -0,0 +1,100 @@ +%This is a parameter input file for autoPyLoT. +%All main and special settings regarding data handling +%and picking are to be set here! +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#main settings# +/DATA/Egelados #rootpath# %project path +EVENT_DATA/LOCAL #datapath# %data path +2006.02_Nisyros #database# %name of data base +e0032.033.06 #eventID# %event ID for single event processing +PILOT #datastructure# %choose data structure +2 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything +AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file +AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file +AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities +HYPOSAT #locrt# %location routine used ("HYPOINVERSE" or "HYPOSAT") +6 #pmin# %minimum required P picks for location +4 #p0min# %minimum required P picks for location if at least + %3 excellent P picks are found +2 #smin# %minimum required S picks for location +/home/ludger/bin/run_HYPOSAT4autoPILOT.csh #cshellp# %path and name of c-shell script to run location routine +7.6 8.5 #blon# %longitude bounding for location map +49 49.4 #blat# %lattitude bounding for location map +#parameters for moment magnitude estimation# +5000 #vp# %average P-wave velocity +2800 #vs# %average S-wave velocity +2200 #rho# %rock density [kg/m^3] +300 #Qp# %quality factor for P waves +100 #Qs# %quality factor for S waves +#common settings picker# +20 #pstart# %start time [s] for calculating CF for P-picking +160 #pstop# %end time [s] for calculating CF for P-picking +1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking +50 #sstop# %end time [s] after P-onset for calculating CF for S-picking +3 10 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] +3 12 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] +3 8 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] +3 6 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz] +#special settings for calculating CF# +%!!Be careful when editing the following!! +#Z-component# +HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3) +7 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s] +4 #hosorder# %for HOS-picker, order of Higher Order Statistics +2 #Parorder# %for AR-picker, order of AR process of Z-component +1.2 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick +0.4 #tpred1z# %for AR-picker, length of AR prediction window [s] for Z-component, 1st pick +0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick +0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick +0.001 #addnoise# %add noise to seismogram for stable AR prediction +4 0.1 1.0 0.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +4 #pickwinP# %for initial AIC pick, length of P-pick window [s] +8 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) +0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) +0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] +0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] +0.001 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P) +1.3 #nfacP# %for HOS/AR, noise factor for noise level determination (P) +#H-components# +ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3) +0.8 #tdet1h# %for HOS/AR, length of AR-determination window [s], H-components, 1st pick +0.4 #tpred1h# %for HOS/AR, length of AR-prediction window [s], H-components, 1st pick +0.6 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick +0.3 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick +4 #Sarorder# %for AR-picker, order of AR process of H-components +20 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) +5 #pickwinS# %for initial AIC pick, length of S-pick window [s] +6 0.2 2.0 1.5 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +0.05 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] +0.02 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) +0.2 #pepsS# %for AR-picker, artificial uplift of samples of CF (S) +0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) +1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) +%first-motion picker% +1 #minfmweight# %minimum required p weight for first-motion determination +2 #minFMSNR# %miniumum required SNR for first-motion determination +5.0 #fmpickwin# %pick window around P onset for calculating zero crossings +%quality assessment% +#inital AIC onset# +0.04 0.08 0.16 0.32 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P +0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S +5 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +1.2 #minAICPSNR# %below this SNR the initial P pick is rejected +8 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +1.5 #minAICSSNR# %below this SNR the initial S pick is rejected +#check duration of signal using envelope function# +1.5 #prepickwin# %pre-signal window length [s] for noise level estimation +0.7 #minsiglength# %minimum required length of signal [s] +0.2 #sgap# %safety gap between noise and signal window [s] +2 #noisefactor# %noiselevel*noisefactor=threshold +60 #minpercent# %per cent of samples required higher than threshold +#check for spuriously picked S-onsets# +3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude +#jackknife-processing for P-picks# +3 #thresholdweight#%minimum required weight of picks +3 #dttolerance# %maximum allowed deviation of P picks from median [s] +4 #minstats# %minimum number of stations with reliable P picks +#wadati check# +1.5 #wdttolerance# %maximum allowed deviation from Wadati-diagram + From bb6ebc7ee167798bdd580ee9b9b2dcad62ebfedf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 08:22:59 +0200 Subject: [PATCH 0384/1144] reformatting code for reasons of coding conventions --- pylot/core/pick/utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 5bd4c799..97daa194 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -393,8 +393,8 @@ def wadaticheck(pickdic, dttolerance, iplot): : param: pickdic, dictionary containing picks and quality parameters : type: dictionary - - : param: dttolerance, maximum adjusted deviation of S-P time from + + : param: dttolerance, maximum adjusted deviation of S-P time from S-P time regression : type: float @@ -423,15 +423,15 @@ def wadaticheck(pickdic, dttolerance, iplot): if len(SPtimes) >= 3: - # calculate slope + # calculate slope p1 = np.polyfit(Ppicks, SPtimes, 1) wdfit = np.polyval(p1, Ppicks) wfitflag = 0 - + # calculate vp/vs ratio before check vpvsr = p1[0] + 1 print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr - + checkedPpicks = [] checkedSpicks = [] checkedSPtimes = [] @@ -443,7 +443,7 @@ def wadaticheck(pickdic, dttolerance, iplot): ii += 1 # check, if deviation is larger than adjusted if wddiff >= dttolerance: - # mark onset and downgrade S-weight to 9 + # mark onset and downgrade S-weight to 9 # (not used anymore) marker = 'badWadatiCheck' pickdic[key]['S']['weight'] = 9 @@ -459,7 +459,7 @@ def wadaticheck(pickdic, dttolerance, iplot): pickdic[key]['S']['marked'] = marker - # calculate new slope + # calculate new slope p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) wdfit2 = np.polyval(p2, checkedPpicks) @@ -468,7 +468,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr checkedonsets = pickdic - + else: print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' From dc281c6c0e9b3a9611e58324d0f237660bdc8a9f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 08:24:21 +0200 Subject: [PATCH 0385/1144] [bugfix] determine the SNR according to the demeaned waveform between the beginning of the noise window and the end of the signal window --- pylot/core/pick/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 97daa194..d74ab2a6 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -322,6 +322,9 @@ def getSNR(X, TSNR, t1): print 'getSNR: Empty array isignal, check signal window!' return + # demean over entire snr window + x -= x[inoise[0][0]:isignal[0][-1]].mean() + # calculate ratios noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) signallevel = np.sqrt(np.mean(np.square(x[isignal]))) From a2ca6fd66fc73eabe954b1f98de31338c801b700 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 08:48:14 +0200 Subject: [PATCH 0386/1144] [bugfix] return value of getnoisewin and getsignalwin should be of expected type numpy.ndarray (was tuple) --- pylot/core/pick/utils.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index d74ab2a6..ce1719a1 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -353,9 +353,8 @@ def getnoisewin(t, t1, tnoise, tgap): :type: float ''' - inoise = None # get noise window - inoise = np.where((t <= max([t1 - tgap, 0])) \ + inoise, = np.where((t <= max([t1 - tgap, 0])) \ & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: print 'getnoisewin: Empty array inoise, check noise window!' @@ -378,9 +377,8 @@ def getsignalwin(t, t1, tsignal): :type: float ''' - inoise = None # get signal window - isignal = np.where((t <= min([t1 + tsignal, len(t)])) \ + isignal, = np.where((t <= min([t1 + tsignal, len(t)])) \ & (t >= t1)) if np.size(isignal) < 1: print 'getsignalwin: Empty array isignal, check signal window!' From 8d4aaab92a15093e3698551c88dcddee4c8f0fec Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 08:48:14 +0200 Subject: [PATCH 0387/1144] [bugfix] return value of getnoisewin and getsignalwin should be of expected type numpy.ndarray (was tuple) --- pylot/core/pick/utils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index d74ab2a6..0d3ed952 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -323,7 +323,7 @@ def getSNR(X, TSNR, t1): return # demean over entire snr window - x -= x[inoise[0][0]:isignal[0][-1]].mean() + x -= x[inoise[0]:isignal[-1]].mean() # calculate ratios noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) @@ -353,9 +353,8 @@ def getnoisewin(t, t1, tnoise, tgap): :type: float ''' - inoise = None # get noise window - inoise = np.where((t <= max([t1 - tgap, 0])) \ + inoise, = np.where((t <= max([t1 - tgap, 0])) \ & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: print 'getnoisewin: Empty array inoise, check noise window!' @@ -378,9 +377,8 @@ def getsignalwin(t, t1, tsignal): :type: float ''' - inoise = None # get signal window - isignal = np.where((t <= min([t1 + tsignal, len(t)])) \ + isignal, = np.where((t <= min([t1 + tsignal, len(t)])) \ & (t >= t1)) if np.size(isignal) < 1: print 'getsignalwin: Empty array isignal, check signal window!' From 931853016abc53b115514fbe2af94a63e1613b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 12:01:36 +0200 Subject: [PATCH 0388/1144] Debugging. --- pylot/core/pick/run_autopicking.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index a0672645..c73868ac 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -375,7 +375,12 @@ def run_autopicking(wfstream, pickparam): epick = [epickS1, epickS2] lpick = [lpickS1, lpickS2] pickerr = [Serror1, Serror2] - ipick = np.argmin([epickS1, epickS2]) + if epickS1 == None and epickS2 is not None: + ipick = 1 + elif epickS1 is not None and epickS2 == None: + ipick = 0 + elif epickS1 is not None and epickS is not None: + ipick = np.argmin([epickS1, epickS2]) elif algoS == 'AR3': [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, tsnrh, @@ -384,7 +389,18 @@ def run_autopicking(wfstream, pickparam): epick = [epickS1, epickS2, epickS3] lpick = [lpickS1, lpickS2, lpickS3] pickerr = [Serror1, Serror2, Serror3] - ipick = np.argmin([epickS1, epickS2, epickS3]) + if epickS1 == None and epickS2 is not None \ + and epickS3 is not None: + ipick = np.argmin([epickS2, epickS3]) + elif epickS1 is not None and epickS2 == None \ + and epickS3 is not None: + ipick = np.argmin([epickS2, epickS3]) + elif epickS1 is not None and epickS2 is not None \ + and epickS3 == None: + ipick = np.argmin([epickS1, epickS2]) + elif epickS1 is not None and epickS2 is not None \ + and epickS3 is not None: + ipick = np.argmin([epickS1, epickS2, epickS3]) epickS = epick[ipick] lpickS = lpick[ipick] Serror = pickerr[ipick] From 44050564b8c78b3856b23a1d85971cdc76a63d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 12:02:04 +0200 Subject: [PATCH 0389/1144] Debugging. --- pylot/core/pick/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 5bd4c799..097863ae 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -61,7 +61,9 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ilup, = np.where(x[isignal] > nlevel) ildown, = np.where(x[isignal] < -nlevel) if not ilup.size and not ildown.size: - raise ValueError('earllatepicker: Signal lower than noise level') + print 'earllatepicker: Signal lower than noise level!' + print 'Skip this trace!' + return LPick, EPick, PickError il = min(np.min(ilup) if ilup.size else float('inf'), np.min(ildown) if ildown.size else float('inf')) LPick = t[isignal][il] From add4944c0504660ad9281a632d851120dd8856cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 12:18:45 +0200 Subject: [PATCH 0390/1144] Marginal changes. --- pylot/core/pick/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 166816ad..f16e8ffe 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -89,7 +89,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): p2, = plt.plot(t[inoise], x[inoise]) p3, = plt.plot(t[isignal], x[isignal], 'r') p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(t[isignal[0][zc]], np.zeros(len(zc)), '*g', + p5, = plt.plot(t[isignal[zc]], np.zeros(len(zc)), '*g', markersize=14) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', @@ -105,8 +105,6 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): [max(x) / 2, -max(x) / 2], 'r--') plt.xlabel('Time [s] since %s' % X[0].stats.starttime) plt.yticks([]) - ax = plt.gca() - ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) plt.title( 'Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) From c54ba04ae2cf1e589179dfd424f6fed0a916178f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 12:19:16 +0200 Subject: [PATCH 0391/1144] Marginal changes. --- pylot/core/pick/Picker.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index d0c7a08f..a9c8decb 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -23,7 +23,7 @@ import matplotlib.pyplot as plt from pylot.core.pick.utils import * from pylot.core.pick.CharFuns import CharacteristicFunction import warnings - +import pdb class AutoPicking(object): ''' Superclass of different, automated picking algorithms applied on a CF determined @@ -267,9 +267,7 @@ class AICPicker(AutoPicking): self.SNR, self.slope)) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.ylabel('Counts') - ax = plt.gca() plt.yticks([]) - ax.set_xlim([self.Tcf[inoise[0][0]] - 5, self.Tcf[isignal[0][len(isignal) - 1]] + 5]) plt.show() raw_input() From 1c749dd9a8afc8eb89ec412b6f3bb4bf61c1a5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 12:24:13 +0200 Subject: [PATCH 0392/1144] Debugging. --- pylot/core/pick/run_autopicking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index c73868ac..10ad21ae 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -379,7 +379,7 @@ def run_autopicking(wfstream, pickparam): ipick = 1 elif epickS1 is not None and epickS2 == None: ipick = 0 - elif epickS1 is not None and epickS is not None: + elif epickS1 is not None and epickS2 is not None: ipick = np.argmin([epickS1, epickS2]) elif algoS == 'AR3': [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, From 6d8a17b7e25644aaeedaebec9e22f3a99e3d81cc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 12:51:27 +0200 Subject: [PATCH 0393/1144] deleting obsolete class definition --- pylot/core/util/widgets.py | 92 -------------------------------------- 1 file changed, 92 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0161fc0e..4361f1e9 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -146,98 +146,6 @@ class MPLWidget(FigureCanvas): xycoords='axes fraction') axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) - -class multiComponentPlot(FigureCanvas): - def __init__(self, data, parent=None, components='ZNE'): - - self.data = data - self._parent = parent - self.components = components - - self.figure = Figure() - - self.noc = len(components) - FigureCanvas.__init__(self, self.figure) - self.multiCursor = None - self.resetPlot(components, data) - - def getData(self): - return self.data - - def setData(self, data): - self.data = data - - def getParent(self): - return self._parent - - def setParent(self, parent): - self._parent = parent - - def getComponents(self): - return self.components - - def setComponents(self, components): - self.components = components - - def getNoC(self): - return self.noc - - def setNoC(self, noc): - self.noc = noc - - def resetPlot(self, components=None, data=None): - - # clear figure - self.figure.clf() - - # delete multiCursor if existing - if self.multiCursor is not None: - self.multiCursor = None - - # set new attribute values - if data is not None: - self.setData(data) - if components is not None: - self.setComponents(components) - noc = len(self.getComponents()) - if self.getNoC() != noc: - self.setNoC(noc) - self.axesdict = dict() - - # prepare variables for plotting - stime = getGlobalTimes(self.getData())[0] - - xlabel = 'time since {0} [s]'.format(stime) - - # plot individual component traces in separate subplots - for n, comp in enumerate(components): - nsub = '{0}1{1}'.format(self.noc, n + 1) - if n >= 1: - subax = self.figure.add_subplot(nsub, sharex=self.axesdict[0]) - else: - subax = self.figure.add_subplot(nsub) - subax.autoscale(tight=True) - subset = data.copy().select(component=comp)[0] - time_ax = prepTimeAxis(subset.stats.starttime - stime, subset) - subax.plot(time_ax, subset.data) - self.axesdict[n] = subax - self.updateYLabel(n, comp) - if n == self.noc: - self.updateXLabel(self.noc, xlabel) - else: - self.updateXLabel(n, '') - - self.multiCursor = MultiCursor(self.figure.canvas, - tuple(self.axesdict.values()), - color='r', lw=1) - - def updateXLabel(self, pos, text): - self.axesdict[pos].set_xlabel(text) - - def updateYLabel(self, pos, text): - self.axesdict[pos].set_ylabel(text) - - class PickDlg(QDialog): def __init__(self, parent=None, data=None, station=None, rotate=False): super(PickDlg, self).__init__(parent) From c851fa69018e8009421dc0cc7937000b92e641e3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 12:57:36 +0200 Subject: [PATCH 0394/1144] avoid direct manipulation of attributes of an object; use get and set methods instead; new methods added to feature the desired behavior --- pylot/core/util/widgets.py | 128 ++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 36 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 4361f1e9..d88c3916 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -88,7 +88,7 @@ class MPLWidget(FigureCanvas): def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, noiselevel=None): - self.axes.cla() + self.getAxes().cla() self.clearPlotDict() wfstart = getGlobalTimes(wfdata)[0] for n, trace in enumerate(wfdata): @@ -98,41 +98,54 @@ class MPLWidget(FigureCanvas): print(msg) stime = trace.stats.starttime - wfstart time_ax = prepTimeAxis(stime, trace) - trace.detrend() - trace.detrend('demean') - trace.normalize(trace.data.max() * 2) - self.axes.plot(time_ax, trace.data + n, 'k') + trace.normalize(np.max(np.abs(trace.data)) * 2) + self.getAxes().plot(time_ax, trace.data + n, 'k') if noiselevel is not None: - self.axes.plot([time_ax[0], time_ax[-1]], - [noiselevel, noiselevel], '--k') - self.axes.plot([time_ax[0], time_ax[-1]], - [-noiselevel, -noiselevel], '--k') + self.getAxes().plot([time_ax[0], time_ax[-1]], + [noiselevel[0], noiselevel[0]], '--k') + self.getAxes().plot([time_ax[0], time_ax[-1]], + [noiselevel[1], noiselevel[1]], '--k') xlabel = 'seconds since {0}'.format(wfstart) ylabel = '' self.updateWidget(xlabel, ylabel, title) self.setPlotDict(n, (station, channel)) self.axes.autoscale(tight=True) - if zoomx: - self.axes.set_xlim(zoomx) - if zoomy: - self.axes.set_ylim(zoomy) + if zoomx is not None: + self.setXLims(zoomx) + if zoomy is not None: + self.setYLims(zoomy) self.draw() + def getAxes(self): + return self.axes + + def getXLims(self): + return self.getAxes().get_xlim() + + def getYLims(self): + return self.getAxes().get_ylim() + + def setXLims(self, lims): + self.getAxes().set_xlim(lims) + + def setYLims(self, lims): + self.getAxes().set_ylim(lims) + def setYTickLabels(self, pos, labels): - self.axes.set_yticks(pos) - self.axes.set_yticklabels(labels) + self.getAxes().set_yticks(pos) + self.getAxes().set_yticklabels(labels) self.draw() def updateXLabel(self, text): - self.axes.set_xlabel(text) + self.getAxes().set_xlabel(text) self.draw() def updateYLabel(self, text): - self.axes.set_ylabel(text) + self.getAxes().set_ylabel(text) self.draw() def updateTitle(self, text): - self.axes.set_title(text) + self.getAxes().set_title(text) self.draw() def updateWidget(self, xlabel, ylabel, title): @@ -141,8 +154,8 @@ class MPLWidget(FigureCanvas): self.updateTitle(title) def insertLabel(self, pos, text): - pos = pos / max(self.axes.ylim) - axann = self.axes.annotate(text, xy=(.03, pos), + pos = pos / max(self.getAxes().ylim) + axann = self.getAxes().annotate(text, xy=(.03, pos), xycoords='axes fraction') axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) @@ -160,6 +173,8 @@ class PickDlg(QDialog): self.press = None self.xpress = None self.ypress = None + self.cur_xlim = None + self.cur_ylim = None # set attribute holding data if data is None: @@ -182,9 +197,14 @@ class PickDlg(QDialog): # plot data self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) - self.limits = {'xlims': self.getPlotWidget().axes.get_xlim(), - 'ylims': self.getPlotWidget().axes.get_ylim()} - self.apd = self.getWFData() + + xlims = self.getPlotWidget().getXLims() + ylims = self.getPlotWidget().getYLims() + + self.limits = {'x': xlims, + 'y': ylims} + + self.updateCurrentLimits() # set plot labels @@ -293,6 +313,7 @@ class PickDlg(QDialog): def verifyPhaseSelection(self): phase = self.selectPhase.currentText() + self.updateCurrentLimits() if phase: self.disconnectReleaseEvent() self.disconnectScrollEvent() @@ -321,6 +342,25 @@ class PickDlg(QDialog): def getChannelID(self, key): return self.getPlotWidget().getPlotDict()[int(key)][1] + def getXLims(self): + return self.cur_xlim + + def getYLims(self): + return self.cur_ylim + + def setXLims(self, limits): + self.cur_xlim = limits + + def setYLims(self, limits): + self.cur_ylim = limits + + def getGlobalLimits(self, axis): + return self.limits[axis] + + def updateCurrentLimits(self): + self.setXLims(self.getPlotWidget().getXLims()) + self.setYLims(self.getPlotWidget().getYLims()) + def getWFData(self): return self.data @@ -394,11 +434,16 @@ class PickDlg(QDialog): zoomx = [ini_pick - x_res, ini_pick + x_res] zoomy = [-noiselevel * 1.5, noiselevel * 1.5] self.getPlotWidget().plotWFData(wfdata=wfdata, + self.setXLims([ini_pick - x_res, ini_pick + x_res]) + self.setYLims(np.array([-noiselevel * 2.5, noiselevel * 2.5]) + + trace_number) title=self.getStation() + ' picking mode', zoomx=zoomx, zoomy=zoomy, noiselevel=noiselevel) + zoomx=self.getXLims(), + zoomy=self.getYLims(), self.zoomAction.setEnabled(False) @@ -408,6 +453,10 @@ class PickDlg(QDialog): self.setPlotLabels() def setPick(self, gui_event): + + # get axes limits + self.updateCurrentLimits() + # setting pick pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global channel = self.getChannelID(round(gui_event.ydata)) @@ -452,12 +501,15 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.zoomAction.setEnabled(True) self.selectPhase.setCurrentIndex(-1) + self.getPlotWidget().setXLims(self.getXLims()) + self.getPlotWidget().setYLims(self.getYLims()) def drawPicks(self, phase=None): # plotting picks ax = self.getPlotWidget().axes ylims = ax.get_ylim() + ylims = self.getGlobalLimits('y') if self.getPicks(): if phase is not None: picks = self.getPicks()[phase] @@ -529,6 +581,8 @@ class PickDlg(QDialog): # set channel labels self.getPlotWidget().setYTickLabels(pos, labels) + self.getPlotWidget().setXLims(self.getXLims()) + self.getPlotWidget().setYLims(self.getYLims()) def zoom(self): if self.zoomAction.isChecked(): @@ -545,10 +599,7 @@ class PickDlg(QDialog): def scrollZoom(self, gui_event, factor=2.): - widget = self.getPlotWidget() - - curr_xlim = widget.axes.get_xlim() - curr_ylim = widget.axes.get_ylim() + self.updateCurrentLimits() if gui_event.button == 'up': scale_factor = 1 / factor @@ -561,20 +612,25 @@ class PickDlg(QDialog): print gui_event.button new_xlim = gui_event.xdata - \ - scale_factor * (gui_event.xdata - curr_xlim) + scale_factor * (gui_event.xdata - self.getXLims()) new_ylim = gui_event.ydata - \ - scale_factor * (gui_event.ydata - curr_ylim) + scale_factor * (gui_event.ydata - self.getYLims()) new_xlim.sort() - new_xlim[0] = max(new_xlim[0], self.limits['xlims'][0]) - new_xlim[1] = min(new_xlim[1], self.limits['xlims'][1]) + global_x = self.getGlobalLimits('x') + global_y = self.getGlobalLimits('y') + new_xlim[0] = max(new_xlim[0], global_x[0]) + new_xlim[1] = min(new_xlim[1], global_x[1]) new_ylim.sort() - new_ylim[0] = max(new_ylim[0], self.limits['ylims'][0]) - new_ylim[1] = min(new_ylim[1], self.limits['ylims'][1]) + new_ylim[0] = max(new_ylim[0], global_y[0]) + new_ylim[1] = min(new_ylim[1], global_y[1]) - widget.axes.set_xlim(new_xlim) - widget.axes.set_ylim(new_ylim) - widget.draw() + self.getPlotWidget().setXLims(new_xlim) + self.getPlotWidget().setYLims(new_ylim) + self.draw() + + def draw(self): + self.getPlotWidget().draw() def apply(self): picks = self.getPicks() From c94c569fcbf29e1eb97ce770c0502ca7855f9e94 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:07:24 +0200 Subject: [PATCH 0395/1144] closes ticket #151 : in the picking window always the entire waveform data is plotted with varying zoom window sizes --- pylot/core/util/widgets.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d88c3916..5d571c0d 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -384,11 +384,7 @@ class PickDlg(QDialog): def getPicks(self): return self.picks - def getAPD(self): - return self.apd - def updateAPD(self, wfdata): - self.apd = wfdata def setIniPick(self, gui_event): channel = self.getChannelID(round(gui_event.ydata)) @@ -447,8 +443,6 @@ class PickDlg(QDialog): self.zoomAction.setEnabled(False) - self.updateAPD(wfdata) - # reset labels self.setPlotLabels() @@ -461,7 +455,7 @@ class PickDlg(QDialog): pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global channel = self.getChannelID(round(gui_event.ydata)) - wfdata = self.getAPD().copy().select(channel=channel) + wfdata = self.getWFData().copy().select(channel=channel) # get earliest and latest possible pick and symmetric pick error [epp, lpp, spe] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) @@ -497,7 +491,11 @@ class PickDlg(QDialog): oepp=oepp, ompp=ompp, olpp=olpp) + + self.getPlotWidget().plotWFData(wfdata=self.getWFData(), + title=self.getStation()) self.drawPicks(phase) + self.setPlotLabels() self.disconnectPressEvent() self.zoomAction.setEnabled(True) self.selectPhase.setCurrentIndex(-1) @@ -563,14 +561,6 @@ class PickDlg(QDialog): ylims = ax.get_ylim() xlims = ax.get_xlim() if self.filterAction.isChecked(): - data = self.getAPD().copy() - data.filter(type='bandpass', freqmin=.5, freqmax=15.) - title = self.getStation() + ' (filtered)' - else: - data = self.getAPD().copy() - title = self.getStation() - self.getPlotWidget().plotWFData(wfdata=data, title=title, zoomx=xlims, - zoomy=ylims) self.setPlotLabels() def setPlotLabels(self): From c1818622deecc3b0c84b7f725f260144b4a51130 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:09:03 +0200 Subject: [PATCH 0396/1144] [bugfix] obsolete plotting command causing the plot to appear differently than desired --- pylot/core/util/widgets.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 5d571c0d..5af8cf35 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -326,9 +326,6 @@ class PickDlg(QDialog): self.cidmotion = self.connectMotionEvent(self.panMotion) self.cidrelease = self.connectReleaseEvent(self.panRelease) self.cidscroll = self.connectScrollEvent(self.scrollZoom) - self.getPlotWidget().plotWFData(wfdata=self.getWFData(), - title=self.getStation()) - self.drawPicks() def getComponents(self): return self.components From f1c7f00193fadaca4d26f3d14dccbf00813d4c58 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:12:20 +0200 Subject: [PATCH 0397/1144] new local variable reused in plotting command for noise level positioning while plotting --- pylot/core/util/widgets.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 5af8cf35..20866843 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -381,10 +381,11 @@ class PickDlg(QDialog): def getPicks(self): return self.picks - - def setIniPick(self, gui_event): - channel = self.getChannelID(round(gui_event.ydata)) + + trace_number = round(gui_event.ydata) + + channel = self.getChannelID(trace_number) wfdata = self.selectWFData(channel) self.disconnectScrollEvent() @@ -430,13 +431,13 @@ class PickDlg(QDialog): self.setXLims([ini_pick - x_res, ini_pick + x_res]) self.setYLims(np.array([-noiselevel * 2.5, noiselevel * 2.5]) + trace_number) + self.getPlotWidget().plotWFData(wfdata=data, title=self.getStation() + ' picking mode', - zoomx=zoomx, - zoomy=zoomy, - noiselevel=noiselevel) zoomx=self.getXLims(), zoomy=self.getYLims(), + noiselevel=(trace_number + noiselevel, + trace_number - noiselevel)) self.zoomAction.setEnabled(False) From 2393b88f0540dd83503562f579a4c1c41839373d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:15:39 +0200 Subject: [PATCH 0398/1144] rearranged filter waveform data method to match signature of the parseFilterOptions method of the FilterOptions object which will be used to hold filter information which can be defined by the project leader in advance --- pylot/core/util/widgets.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 20866843..2ef1f781 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -554,12 +554,25 @@ class PickDlg(QDialog): ax.figure.canvas.draw() - def filterWFData(self): - ax = self.getPlotWidget().axes - ylims = ax.get_ylim() - xlims = ax.get_xlim() + def filterWFData(self, phase): + self.updateCurrentLimits() + data = self.getWFData().copy() + old_title = self.getPlotWidget().getAxes().get_title() + title = None if self.filterAction.isChecked(): + filteroptions = self.getFilterOptions(phase).parseFilterOptions() + data.filter(**filteroptions) + if old_title.endswith(')'): + title = old_title[:-1] + ', filtered)' + else: + title = old_title + ' (filtered)' + if not title: + title = old_title + self.getPlotWidget().plotWFData(wfdata=data, title=title, + zoomx=self.getXLims(), + zoomy=self.getYLims()) self.setPlotLabels() + self.draw() def setPlotLabels(self): From b1990e6e1c4af474a351f59fd8ec1efda5152f1a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:22:01 +0200 Subject: [PATCH 0399/1144] filter waveform with default parameter for selected phase prior to initial phase identification --- pylot/core/util/widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 2ef1f781..cbda9d37 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -320,6 +320,7 @@ class PickDlg(QDialog): self.disconnectMotionEvent() self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.setIniPick) + self.filterWFData(phase) else: self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.panPress) From ca4c5edb1849b251c298aa5ec725b387d40238dc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:22:30 +0200 Subject: [PATCH 0400/1144] make the code more readable --- QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 47ba0d9a..14733925 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -64,8 +64,8 @@ class MainWindow(QMainWindow): settings.setValue("agency_id", agency) self.recentEvents = settings.value("data/recentEvents", []) self.fnames = None - self.dataStructure = DATASTRUCTURE[ - settings.value("data/Structure", "PILOT")]() + structure_setting = settings.value("data/Structure", "PILOT") + self.dataStructure = DATASTRUCTURE[structure_setting]() self.seismicPhase = str(settings.value("phase", "P")) self.dispComponent = str(settings.value("plotting/dispComponent", "Z")) if settings.value("data/dataRoot", None) is None: From dec3e733a2afa79f3554d3786f8d9e2ffa9800ca Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:23:31 +0200 Subject: [PATCH 0401/1144] use settings and defaults rather than hard coded parameters --- pylot/core/util/widgets.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index cbda9d37..0ff972f0 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -411,10 +411,17 @@ class PickDlg(QDialog): 'VLRW': 15. } - result = getSNR(wfdata, (5., .5, 2.), ini_pick) + settings = QSettings() + + nfac = settings.value('picking/nfac', 1.5) + noise_win = settings.value('picking/noise_win', 5.) + gap_win = settings.value('picking/gap_win', .5) + signal_win = settings.value('picking/signal_win', 1.5) + + result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick) snr = result[0] - noiselevel = result[2] * 1.5 + noiselevel = result[2] * nfac if snr < 1.5: x_res = res_wins['VLRW'] From 03219e69d6a05e91c5f02b5d014f241a6e2614e0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:24:47 +0200 Subject: [PATCH 0402/1144] substract the mean of the waveform data within the noise window prior to picking --- pylot/core/util/widgets.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0ff972f0..622090b9 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -433,9 +433,15 @@ class PickDlg(QDialog): x_res = res_wins['HRW'] x_res /= 2 - zoomx = [ini_pick - x_res, ini_pick + x_res] - zoomy = [-noiselevel * 1.5, noiselevel * 1.5] - self.getPlotWidget().plotWFData(wfdata=wfdata, + # demean data before plotting + data = self.getWFData().copy() + starttime = getGlobalTimes(data)[0] + for tr in data: + stime = tr.stats.starttime - starttime + t = prepTimeAxis(stime, tr) + inoise = getnoisewin(t, ini_pick, noise_win, gap_win) + tr.data -= tr.data[inoise].mean() + self.setXLims([ini_pick - x_res, ini_pick + x_res]) self.setYLims(np.array([-noiselevel * 2.5, noiselevel * 2.5]) + trace_number) From 37e50b12e606b2d7f3e3aa260972e86a983c86f7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:25:27 +0200 Subject: [PATCH 0403/1144] use more reliable hard coded sample phase names --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 622090b9..8076f70b 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -238,7 +238,7 @@ class PickDlg(QDialog): ' waveforms', checkable=True) self.selectPhase = QComboBox() - self.selectPhase.addItems([None, 'Pn', 'Pg', 'P1', 'P2']) + self.selectPhase.addItems([None, 'Pn', 'Pg', 'Sn', 'Sg']) self.zoomAction = createAction(parent=self, text='Zoom', slot=self.zoom, icon=zoom_icon, From 2c9ad97a4a00eac711a85246e8459441ccd50dfc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 23 Jun 2015 13:26:37 +0200 Subject: [PATCH 0404/1144] avoid weird behavior of the picking window --- pylot/core/util/widgets.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8076f70b..e69b2a92 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -25,7 +25,7 @@ from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime from obspy.core.event import Pick, WaveformStreamID from pylot.core.read import FilterOptions -from pylot.core.pick.utils import getSNR, earllatepicker +from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin from pylot.core.util.defaults import OUTPUTFORMATS from pylot.core.util import prepTimeAxis, getGlobalTimes @@ -457,6 +457,8 @@ class PickDlg(QDialog): # reset labels self.setPlotLabels() + self.draw() + def setPick(self, gui_event): @@ -507,7 +509,6 @@ class PickDlg(QDialog): self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) self.drawPicks(phase) - self.setPlotLabels() self.disconnectPressEvent() self.zoomAction.setEnabled(True) self.selectPhase.setCurrentIndex(-1) @@ -517,8 +518,6 @@ class PickDlg(QDialog): def drawPicks(self, phase=None): # plotting picks ax = self.getPlotWidget().axes - - ylims = ax.get_ylim() ylims = self.getGlobalLimits('y') if self.getPicks(): if phase is not None: @@ -540,8 +539,6 @@ class PickDlg(QDialog): [mpp, mpp], ylims, 'b-', [mpp + spe, mpp + spe], ylims, 'c--') - self.getPlotWidget().draw() - def panPress(self, gui_event): ax = self.getPlotWidget().axes if gui_event.inaxes != ax: return From 54fd3004b4c8856d51dbba809e8e61420adf40d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 16:22:21 +0200 Subject: [PATCH 0405/1144] Debugging. --- pylot/core/pick/Picker.py | 46 +++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index a9c8decb..f5e48a63 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -23,7 +23,7 @@ import matplotlib.pyplot as plt from pylot.core.pick.utils import * from pylot.core.pick.CharFuns import CharacteristicFunction import warnings -import pdb + class AutoPicking(object): ''' Superclass of different, automated picking algorithms applied on a CF determined @@ -327,40 +327,44 @@ class PragPicker(AutoPicking): #at first we look to the right until the end of the pick window is reached flagpick_r = 0 flagpick_l = 0 - flagpick = 0 + cfpick_r = 0 + cfpick_l = 0 lpickwindow = int(round(self.PickWindow / self.dt)) for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: if cfpick1 >= self.cf[i]: - pick_r = self.Tcf[i] - self.Pick = pick_r - flagpick_l = 1 - cfpick_r = self.cf[i] - break + pick_r = self.Tcf[i] + self.Pick = pick_r + flagpick_l = 1 + cfpick_r = self.cf[i] + break - #now we look to the left + # now we look to the left for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: if cfpick1 >= self.cf[i]: - pick_l = self.Tcf[i] - self.Pick = pick_l - flagpick_r = 1 - cfpick_l = self.cf[i] - break + pick_l = self.Tcf[i] + self.Pick = pick_l + flagpick_r = 1 + cfpick_l = self.cf[i] + break - #now decide which pick: left or right? + # now decide which pick: left or right? if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: - self.Pick = pick_l - pickflag = 1 + self.Pick = pick_l + pickflag = 1 elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: - self.Pick = pick_r - pickflag = 1 + self.Pick = pick_r + pickflag = 1 + elif flagpick_l == 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + self.Pick = pick_l + pickflag = 1 else: - print 'PragPicker: Could not find reliable onset!' - self.Pick = None - pickflag = 0 + print 'PragPicker: Could not find reliable onset!' + self.Pick = None + pickflag = 0 if self.getiplot() > 1: p = plt.figure(self.getiplot()) From 5410b4481af2575525aeff93fd324d3744166d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 16:23:18 +0200 Subject: [PATCH 0406/1144] Debugging. --- pylot/core/pick/utils.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index f16e8ffe..df023044 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -183,10 +183,17 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: li1 = index1[0] if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: - print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' + print 'fmpicker: Onset on unfiltered trace too emergent for first motion determination!' P1 = None else: imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) + if imax1 == 0: + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) + if imax1 == 0: + print 'fmpicker: Zero crossings too close!' + print 'Skip first motion determination!' + return FM + islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) # calculate slope as polynomal fit of order 1 xslope1 = np.arange(0, len(xraw[islope1]), 1) @@ -218,10 +225,17 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: li2 = index2[0] if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: - print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' + print 'fmpicker: Onset on filtered trace too emergent for first motion determination!' P2 = None else: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) + if imax2 == 0: + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) + if imax1 == 0: + print 'fmpicker: Zero crossings too close!' + print 'Skip first motion determination!' + return FM + islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) # calculate slope as polynomal fit of order 1 xslope2 = np.arange(0, len(xfilt[islope2]), 1) @@ -459,14 +473,17 @@ def wadaticheck(pickdic, dttolerance, iplot): pickdic[key]['S']['marked'] = marker + if len(checkedPpicks) >= 3: + # calculate new slope + p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) + wdfit2 = np.polyval(p2, checkedPpicks) - # calculate new slope - p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) - wdfit2 = np.polyval(p2, checkedPpicks) - - # calculate vp/vs ratio after check - cvpvsr = p2[0] + 1 - print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr + # calculate vp/vs ratio after check + cvpvsr = p2[0] + 1 + print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr + else: + print 'wadatacheck: Not enough checked S-P times available!' + print 'Skip Wadati check!' checkedonsets = pickdic @@ -476,6 +493,7 @@ def wadaticheck(pickdic, dttolerance, iplot): wfitflag = 1 # plot results + iplot=2 if iplot > 1: plt.figure(iplot) f1, = plt.plot(Ppicks, SPtimes, 'ro') From 8f0c1fb8ecc379a318dcada6417a6f65c4ca524a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 16:24:10 +0200 Subject: [PATCH 0407/1144] Debugging. --- pylot/core/pick/run_autopicking.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 10ad21ae..7bf63cef 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * -import pdb + def run_autopicking(wfstream, pickparam): """ :param: wfstream @@ -482,8 +482,8 @@ def run_autopicking(wfstream, pickparam): plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') plt.suptitle(tr_filt.stats.starttime) - - if len(edat) > 1 and len(ndat) > 1 and Sflag == 1: + + if len(edat[0]) > 1 and len(ndat[0]) > 1 and Sflag == 1: # plot horizontal traces plt.subplot(3, 1, 2) th1data = np.arange(0, From 123634924b6011abcb41381ecf3d3b3a1dc14ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 23 Jun 2015 16:27:28 +0200 Subject: [PATCH 0408/1144] Removed fix iplot flag. --- pylot/core/pick/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index df023044..58fd99a2 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -493,7 +493,6 @@ def wadaticheck(pickdic, dttolerance, iplot): wfitflag = 1 # plot results - iplot=2 if iplot > 1: plt.figure(iplot) f1, = plt.plot(Ppicks, SPtimes, 'ro') From ac99ec8a7614286e6b1da6ad5adec99faac37230 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 24 Jun 2015 09:24:21 +0200 Subject: [PATCH 0409/1144] added static method to the FilterOptionsDialog in order to be able to directly get an filter object without having to create an dialog object in advance explicitly --- pylot/core/util/widgets.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e69b2a92..a5e61da5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -978,6 +978,13 @@ class FilterOptionsDialog(QDialog): def getFilterOptions(self): return self.filterOptions + @staticmethod + def getFilterObject(): + dlg = FilterOptionsDialog() + if dlg.exec_(): + return dlg.getFilterOptions() + return None + def accept(self): self.updateUi() QDialog.accept(self) From 728cf15f19ac9cc70729a4dc0bd441127cd1c602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 09:29:59 +0200 Subject: [PATCH 0410/1144] Demean: Mean to be subtracted from time series is now calculated from noise part only. --- pylot/core/pick/utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 58fd99a2..971ab1fb 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -47,14 +47,11 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - # get latest possible pick - # get noise window inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1]) # get signal window isignal = getsignalwin(t, Pick1, TSNR[2]) # remove mean - meanwin = np.hstack((inoise, isignal)) - x = x - np.mean(x[meanwin]) + x = x - np.mean(x[inoise]) # calculate noise level nlevel = np.sqrt(np.mean(np.square(x[inoise]))) * nfac # get time where signal exceeds nlevel @@ -337,7 +334,7 @@ def getSNR(X, TSNR, t1): return # demean over entire snr window - x -= x[inoise[0]:isignal[-1]].mean() + x = x - np.mean(x[np.hstack([inoise, isignal])]) # calculate ratios noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) From 7ede5a7859f1f96a7b3d5b8888e5ae24e19a194d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 09:31:01 +0200 Subject: [PATCH 0411/1144] Modified parameters. --- autoPyLoT_local.in | 1 + 1 file changed, 1 insertion(+) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index dd6d9815..68ded8a6 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -1,6 +1,7 @@ %This is a parameter input file for autoPyLoT. %All main and special settings regarding data handling %and picking are to be set here! +%Parameters are optimized for local data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #main settings# From 61c745cd79a1ed35b6f5c62baf7934010b7b866f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 09:31:18 +0200 Subject: [PATCH 0412/1144] Modified parameters. --- autoPyLoT_regional.in | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in index 3bc1bc9b..7d66c537 100644 --- a/autoPyLoT_regional.in +++ b/autoPyLoT_regional.in @@ -1,13 +1,14 @@ %This is a parameter input file for autoPyLoT. %All main and special settings regarding data handling %and picking are to be set here! +%Parameters are optimized for regional data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #main settings# /DATA/Egelados #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2006.02_Nisyros #database# %name of data base -e0032.033.06 #eventID# %event ID for single event processing +2006.01_Nisyros #database# %name of data base +e1412.008.06 #eventID# %event ID for single event processing PILOT #datastructure# %choose data structure 2 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file @@ -30,8 +31,8 @@ HYPOSAT #locrt# %location routine used ("HYPO #common settings picker# 20 #pstart# %start time [s] for calculating CF for P-picking 160 #pstop# %end time [s] for calculating CF for P-picking -1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking -50 #sstop# %end time [s] after P-onset for calculating CF for S-picking +3.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking +100 #sstop# %end time [s] after P-onset for calculating CF for S-picking 3 10 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] 3 12 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] 3 8 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] @@ -48,13 +49,12 @@ HOS #algoP# %choose algorithm for P-onset 0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick 0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick 0.001 #addnoise# %add noise to seismogram for stable AR prediction -4 0.1 1.0 0.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] -4 #pickwinP# %for initial AIC pick, length of P-pick window [s] +4 0.2 2.0 1.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +4 #pickwinP# %for initial AIC and refined pick, length of P-pick window [s] 8 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) -0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) -0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] -0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] -0.001 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P) +3.0 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] +0.3 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] +0.3 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P) 1.3 #nfacP# %for HOS/AR, noise factor for noise level determination (P) #H-components# ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3) @@ -63,18 +63,17 @@ ARH #algoS# %choose algorithm for S-onset 0.6 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick 0.3 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick 4 #Sarorder# %for AR-picker, order of AR process of H-components -20 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) -5 #pickwinS# %for initial AIC pick, length of S-pick window [s] -6 0.2 2.0 1.5 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] -0.05 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] -0.02 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) -0.2 #pepsS# %for AR-picker, artificial uplift of samples of CF (S) +10 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) +6 #pickwinS# %for initial AIC and refined pick, length of S-pick window [s] +5 0.2 3.0 3.0 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +3.0 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] +1.0 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) 0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) 1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) %first-motion picker% 1 #minfmweight# %minimum required p weight for first-motion determination 2 #minFMSNR# %miniumum required SNR for first-motion determination -5.0 #fmpickwin# %pick window around P onset for calculating zero crossings +6.0 #fmpickwin# %pick window around P onset for calculating zero crossings %quality assessment% #inital AIC onset# 0.04 0.08 0.16 0.32 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P From b44fbe0b02060493b422b5d0708bc17281f03dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 10:23:24 +0200 Subject: [PATCH 0413/1144] Some Cosmetics. --- autoPyLoT.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 167fa8ae..c14514ee 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -13,6 +13,7 @@ from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE from pylot.core.pick.utils import wadaticheck +import pdb __version__ = _getVersionString() @@ -30,6 +31,18 @@ def autoPyLoT(inputfile): .. rubric:: Example ''' + print '************************************' + print '*********autoPyLoT starting*********' + print 'The Python picking and Location Tool' + print ' Version ', _getVersionString(), '2015' + print '**Authors:' + print '**S. Wehling-Benatelli' + print '** Ruhr-University Bochum' + print '**L. Kueperkoch' + print '** BESTEC GmbH' + print '**K. Olbert' + print '** Christian-Albrechts University Kiel' + print '************************************' # reading parameter file @@ -115,7 +128,6 @@ def autoPyLoT(inputfile): station = wfdat[0].stats.station allonsets = {station: picks} for i in range(len(wfdat)): - #for i in range(0,5): stationID = wfdat[i].stats.station #check if station has already been processed if stationID not in procstats: @@ -139,6 +151,10 @@ def autoPyLoT(inputfile): print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' + print '************************************' + print '*********autoPyLoT terminates*******' + print 'The Python picking and Location Tool' + print '************************************' if __name__ == "__main__": # parse arguments From 68bbea98545c66ad706eba350bd7180d3337ff5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 14:15:54 +0200 Subject: [PATCH 0414/1144] Implemented new function for quality control: checksignallength, checks signal length in order to detect spuriously picked noise peaks. --- pylot/core/pick/utils.py | 86 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 971ab1fb..c869ef69 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -9,6 +9,7 @@ """ import numpy as np +import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings @@ -511,3 +512,88 @@ def wadaticheck(pickdic, dttolerance, iplot): plt.close(iplot) return checkedonsets + + +def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): + ''' + Function to detect spuriously picked noise peaks. + Uses envelope to determine, how many samples [per cent] after + P onset are below certain threshold, calculated from noise + level times noise factor. + + : param: X, time series (seismogram) + : type: `~obspy.core.stream.Stream` + + : param: pick, initial (AIC) P onset time + : type: float + + : param: TSNR, length of time windows around initial pick [s] + : type: tuple (T_noise, T_gap, T_signal) + + : param: minsiglength, minium required signal length [s] to + declare pick as P onset + : type: float + + : param: nfac, noise factor (nfac * noise level = threshold) + : type: float + + : param: minpercent, minimum required percentage of samples + above calculated threshold + : type: float + + : param: iplot, if iplot > 1, results are shown in figure + : type: int + ''' + + assert isinstance(X, Stream), "%s is not a stream object" % str(X) + + print 'Checking signal length ...' + + x = X[0].data + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, + X[0].stats.delta) + + # generate envelope function from Hilbert transform + y = np.imag(sc.signal.hilbert(x)) + e = np.sqrt(np.power(x, 2) + np.power(y, 2)) + # get noise window + inoise = getnoisewin(t, pick, TSNR[0], TSNR[1]) + # get signal window + isignal = getsignalwin(t, pick, TSNR[2]) + # calculate minimum adjusted signal level + minsiglevel = max(e[inoise]) * nfac + # minimum adjusted number of samples over minimum signal level + minnum = len(isignal) * minpercent/100 + # get number of samples above minimum adjusted signal level + numoverthr = len(np.where(e[isignal] >= minsiglevel)[0]) + + if numoverthr >= minnum: + print 'checksignallength: Signal reached required length.' + returnflag = 1 + else: + print 'checksignallength: Signal shorter than required minimum signal length!' + print 'Presumably picked picked noise peak, pick is rejected!' + returnflag = 0 + + if iplot == 2: + plt.figure(iplot) + p1, = plt.plot(t,x, 'k') + p2, = plt.plot(t[inoise], e[inoise]) + p3, = plt.plot(t[isignal],e[isignal], 'r') + p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ + [minsiglevel, minsiglevel], 'g') + p5, = plt.plot([pick, pick], [min(x), max(x)], 'c') + plt.legend([p1, p2, p3, p4, p5], ['Data', 'Envelope Noise Window', \ + 'Envelope Signal Window', 'Minimum Signal Level', \ + 'Onset'], loc='best') + plt.xlabel('Time [s] since %s' % X[0].stats.starttime) + plt.ylabel('Counts') + plt.title('Check for Signal Length, Station %s' % X[0].stats.station) + plt.yticks([]) + plt.show() + raw_input() + plt.close(iplot) + + return returnflag + + From 017532272f6d13ba6f939c1e2cec342e8386a4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 14:17:43 +0200 Subject: [PATCH 0415/1144] Modified: new function checksignallength added. --- pylot/core/pick/run_autopicking.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 7bf63cef..65bf0514 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -74,6 +74,10 @@ def run_autopicking(wfstream, pickparam): minFMSNR = pickparam.getParam('minFMSNR') fmpickwin = pickparam.getParam('fmpickwin') minfmweight = pickparam.getParam('minfmweight') + # parameters for checking signal length + minsiglength = pickparam.getParam('minsiglength') + minpercent = pickparam.getParam('minpercent') + nfacsl = pickparam.getParam('noisefactor') # initialize output Pweight = 4 # weight for P onset @@ -94,6 +98,7 @@ def run_autopicking(wfstream, pickparam): aicSflag = 0 aicPflag = 0 + Pflag = 0 Sflag = 0 # split components @@ -152,9 +157,15 @@ def run_autopicking(wfstream, pickparam): # of class AutoPicking aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, tsmoothP) ############################################################## + # check signal length to detect spuriously picked noise peaks + z_copy[0].data = tr_filt.data + Pflag = checksignallength(z_copy, aicpick.getpick(), tsnrz, minsiglength, \ + nfacsl, minpercent, iplot) + ############################################################## # go on with processing if AIC onset passes quality control if (aicpick.getSlope() >= minAICPslope and - aicpick.getSNR() >= minAICPSNR): + aicpick.getSNR() >= minAICPSNR and + Pflag == 1): aicPflag = 1 print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ (aicpick.getSlope(), aicpick.getSNR()) @@ -190,7 +201,7 @@ def run_autopicking(wfstream, pickparam): mpickP = refPpick.getpick() ############################################################# if mpickP is not None: - # quality assessment + # quality assessment # get earliest and latest possible pick and symmetrized uncertainty [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, mpickP, iplot) @@ -227,8 +238,8 @@ def run_autopicking(wfstream, pickparam): Sflag = 0 else: - print 'run_autopicking: No vertical component data available, ' \ - 'skipping station!' + print 'run_autopicking: No vertical component data availabler!, ' \ + 'Skip station!' if edat is not None and ndat is not None and len(edat) > 0 and len( ndat) > 0 and Pweight < 4: From a6d19eae7d1917e94a4096a809e3b01dd2617a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 14:25:09 +0200 Subject: [PATCH 0416/1144] Added/modified parameters for new function checksignallength. --- autoPyLoT_local.in | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 68ded8a6..06498689 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -10,7 +10,7 @@ EVENT_DATA/LOCAL #datapath# %data path 2013.02_Insheim #database# %name of data base e0019.048.13 #eventID# %event ID for single event processing PILOT #datastructure# %choose data structure -2 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything +0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities @@ -85,11 +85,9 @@ ARH #algoS# %choose algorithm for S-onset 50 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# -1.5 #prepickwin# %pre-signal window length [s] for noise level estimation -0.7 #minsiglength# %minimum required length of signal [s] -0.2 #sgap# %safety gap between noise and signal window [s] -2 #noisefactor# %noiselevel*noisefactor=threshold -60 #minpercent# %per cent of samples required higher than threshold +2.5 #minsiglength# %minimum required length of signal [s] +3 #noisefactor# %noiselevel*noisefactor=threshold +70 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude #jackknife-processing for P-picks# From 0cc29802a4c44683085becdda34529dd7fbbafb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 14:25:18 +0200 Subject: [PATCH 0417/1144] Added/modified parameters for new function checksignallength. --- autoPyLoT_regional.in | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in index 7d66c537..6e050ff3 100644 --- a/autoPyLoT_regional.in +++ b/autoPyLoT_regional.in @@ -10,7 +10,7 @@ EVENT_DATA/LOCAL #datapath# %data path 2006.01_Nisyros #database# %name of data base e1412.008.06 #eventID# %event ID for single event processing PILOT #datastructure# %choose data structure -2 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything +0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities @@ -83,11 +83,9 @@ ARH #algoS# %choose algorithm for S-onset 8 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# -1.5 #prepickwin# %pre-signal window length [s] for noise level estimation -0.7 #minsiglength# %minimum required length of signal [s] -0.2 #sgap# %safety gap between noise and signal window [s] -2 #noisefactor# %noiselevel*noisefactor=threshold -60 #minpercent# %per cent of samples required higher than threshold +6 #minsiglength# %minimum required length of signal [s] +1.5 #noisefactor# %noiselevel*noisefactor=threshold +60 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude #jackknife-processing for P-picks# From 8dd100792e164b438027d9dd336668d4aebca76c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 24 Jun 2015 14:30:48 +0200 Subject: [PATCH 0418/1144] [bugfix] obspy filter function do not use order but corners as parameter name (keyword changed for argument parser) --- pylot/core/read/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index a4cdc3c9..a59ef071 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -209,7 +209,7 @@ class FilterOptions(object): def parseFilterOptions(self): if self.getFilterType(): robject = {'type':self.getFilterType()} - robject['order'] = self.getOrder() + robject['corners'] = self.getOrder() if len(self.getFreq()) > 1: robject['freqmin'] = self.getFreq()[0] robject['freqmax'] = self.getFreq()[1] From 0d3fb8be9381cf51f205b399db083646ff203c1b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 24 Jun 2015 14:31:43 +0200 Subject: [PATCH 0419/1144] [bugfix] false indentation corrected (broken program logic fixed) --- pylot/core/util/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 0c8a863e..3f2ba611 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -93,9 +93,9 @@ def getGlobalTimes(stream): for trace in stream: if trace.stats.starttime < min_start: min_start = trace.stats.starttime - if max_end is None or trace.stats.endtime > max_end: - max_end = trace.stats.endtime - return [min_start, max_end] + if max_end is None or trace.stats.endtime > max_end: + max_end = trace.stats.endtime + return min_start, max_end def createCreationInfo(agency_id=None, creation_time=None, author=None): From c840e07635c422167f41c25c98d0e2373d02039a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 24 Jun 2015 14:33:19 +0200 Subject: [PATCH 0420/1144] filtering of the waveform in the picking window implemented (work in progress, still bugs inside which have to be located and fixed) --- pylot/core/util/widgets.py | 41 ++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a5e61da5..9cb326a0 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -23,10 +23,9 @@ from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime -from obspy.core.event import Pick, WaveformStreamID from pylot.core.read import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin -from pylot.core.util.defaults import OUTPUTFORMATS +from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS from pylot.core.util import prepTimeAxis, getGlobalTimes @@ -90,7 +89,7 @@ class MPLWidget(FigureCanvas): noiselevel=None): self.getAxes().cla() self.clearPlotDict() - wfstart = getGlobalTimes(wfdata)[0] + wfstart, wfend = getGlobalTimes(wfdata) for n, trace in enumerate(wfdata): channel = trace.stats.channel station = trace.stats.station @@ -109,7 +108,8 @@ class MPLWidget(FigureCanvas): ylabel = '' self.updateWidget(xlabel, ylabel, title) self.setPlotDict(n, (station, channel)) - self.axes.autoscale(tight=True) + self.setXLims([0, wfend - wfstart]) + self.setYLims([-0.5, n + 0.5]) if zoomx is not None: self.setXLims(zoomx) if zoomy is not None: @@ -168,6 +168,7 @@ class PickDlg(QDialog): self.rotate = rotate self.components = 'ZNE' self.picks = {} + self.filteroptions = FILTERDEFAULTS # initialize panning attributes self.press = None @@ -238,7 +239,8 @@ class PickDlg(QDialog): ' waveforms', checkable=True) self.selectPhase = QComboBox() - self.selectPhase.addItems([None, 'Pn', 'Pg', 'Sn', 'Sg']) + phaseitems = [None] + FILTERDEFAULTS.keys() + self.selectPhase.addItems(phaseitems) self.zoomAction = createAction(parent=self, text='Zoom', slot=self.zoom, icon=zoom_icon, @@ -320,7 +322,7 @@ class PickDlg(QDialog): self.disconnectMotionEvent() self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.setIniPick) - self.filterWFData(phase) + self.filterWFData() else: self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.panPress) @@ -340,6 +342,10 @@ class PickDlg(QDialog): def getChannelID(self, key): return self.getPlotWidget().getPlotDict()[int(key)][1] + def getFilterOptions(self, phase): + options = self.filteroptions[phase] + return FilterOptions(**options) + def getXLims(self): return self.cur_xlim @@ -565,19 +571,28 @@ class PickDlg(QDialog): ax.figure.canvas.draw() - def filterWFData(self, phase): + def filterWFData(self): self.updateCurrentLimits() data = self.getWFData().copy() old_title = self.getPlotWidget().getAxes().get_title() title = None + phase = self.selectPhase.currentText() if self.filterAction.isChecked(): - filteroptions = self.getFilterOptions(phase).parseFilterOptions() - data.filter(**filteroptions) - if old_title.endswith(')'): - title = old_title[:-1] + ', filtered)' + if phase: + filtoptions = self.getFilterOptions(phase).parseFilterOptions() else: - title = old_title + ' (filtered)' - if not title: + filtoptions = FilterOptionsDialog.getFilterObject() + filtoptions = filtoptions.parseFilterOptions() + if filtoptions is not None: + data.filter(**filtoptions) + if old_title.endswith(')'): + title = old_title[:-1] + ', filtered)' + else: + title = old_title + ' (filtered)' + else: + if old_title.endswith(' (filtered)'): + title = old_title.replace(' (filtered)', '') + if title is None: title = old_title self.getPlotWidget().plotWFData(wfdata=data, title=title, zoomx=self.getXLims(), From c68597ce622a9f26b895756120162cc63c7d5765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 15:43:59 +0200 Subject: [PATCH 0421/1144] Debugging. --- pylot/core/pick/run_autopicking.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 65bf0514..cc690e8b 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * - +import pdb def run_autopicking(wfstream, pickparam): """ :param: wfstream @@ -157,10 +157,11 @@ def run_autopicking(wfstream, pickparam): # of class AutoPicking aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, tsmoothP) ############################################################## - # check signal length to detect spuriously picked noise peaks - z_copy[0].data = tr_filt.data - Pflag = checksignallength(z_copy, aicpick.getpick(), tsnrz, minsiglength, \ - nfacsl, minpercent, iplot) + if aicpick.getpick() is not None: + # check signal length to detect spuriously picked noise peaks + z_copy[0].data = tr_filt.data + Pflag = checksignallength(z_copy, aicpick.getpick(), tsnrz, minsiglength, \ + nfacsl, minpercent, iplot) ############################################################## # go on with processing if AIC onset passes quality control if (aicpick.getSlope() >= minAICPslope and @@ -320,7 +321,8 @@ def run_autopicking(wfstream, pickparam): ############################################################### # go on with processing if AIC onset passes quality control if (aicarhpick.getSlope() >= minAICSslope and - aicarhpick.getSNR() >= minAICSSNR): + aicarhpick.getSNR() >= minAICSSNR and + aicarhpick.getpick() is not None): aicSflag = 1 print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ % (aicarhpick.getSlope(), aicarhpick.getSNR()) From 7281220b5571df1edae5c006912463897b9b3675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 16:42:50 +0200 Subject: [PATCH 0422/1144] wadaticheck: bug fixed, initialization of running index at wrong place. --- pylot/core/pick/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index c869ef69..321348b9 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -449,11 +449,11 @@ def wadaticheck(pickdic, dttolerance, iplot): checkedSpicks = [] checkedSPtimes = [] # calculate deviations from Wadati regression + ii = 0 for key in pickdic: if pickdic[key].has_key('SPt'): - ii = 0 wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) - ii += 1 + ii += 1 # check, if deviation is larger than adjusted if wddiff >= dttolerance: # mark onset and downgrade S-weight to 9 @@ -489,7 +489,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - + iplot=2 # plot results if iplot > 1: plt.figure(iplot) @@ -498,8 +498,8 @@ def wadaticheck(pickdic, dttolerance, iplot): f2, = plt.plot(Ppicks, wdfit, 'k') f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') f4, = plt.plot(checkedPpicks, wdfit2, 'g') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f, \ - Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ + 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ 'Reliable S-Picks', 'Wadati 2'], loc='best') else: From 2cd7572ee446a512b71e3d8aa00eacad5e314855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 24 Jun 2015 16:44:15 +0200 Subject: [PATCH 0423/1144] Removed fixed plotting flag. --- pylot/core/pick/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 321348b9..8b818aa8 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -489,7 +489,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - iplot=2 + # plot results if iplot > 1: plt.figure(iplot) From c9f07b6540204f81a1918fa1d75fe895b9aa4c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 25 Jun 2015 10:07:35 +0200 Subject: [PATCH 0424/1144] getSNR: demean using only mean determined from noise window. --- pylot/core/pick/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 8b818aa8..1bcb7469 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -334,8 +334,8 @@ def getSNR(X, TSNR, t1): print 'getSNR: Empty array isignal, check signal window!' return - # demean over entire snr window - x = x - np.mean(x[np.hstack([inoise, isignal])]) + # demean over entire waveform + x = x - np.mean(x[inoise]) # calculate ratios noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) From 7ec28664b48870e8041b0b33e906074155491f8c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:21:52 +0200 Subject: [PATCH 0425/1144] new function getResolutionWindow and doc testing added --- pylot/core/pick/utils.py | 59 +++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 1bcb7469..c80ac572 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -398,6 +398,43 @@ def getsignalwin(t, t1, tsignal): return isignal +def getResolutionWindow(snr): + """ + Number -> Float + produce the half of the time resolution window width from given SNR + value + SNR >= 3 -> 2 sec HRW + 3 > SNR >= 2 -> 5 sec MRW + 2 > SNR >= 1.5 -> 10 sec LRW + 1.5 > SNR -> 15 sec VLRW + see also Diehl et al. 2009 + + >>> getResolutionWindow(0.5) + 7.5 + >>> getResolutionWindow(1.8) + 5.0 + >>> getResolutionWindow(2.3) + 2.5 + >>> getResolutionWindow(4) + 1.0 + >>> getResolutionWindow(2) + 2.5 + """ + + res_wins = {'HRW': 2., 'MRW': 5., 'LRW': 10., 'VLRW': 15.} + + if snr < 1.5: + time_resolution = res_wins['VLRW'] + elif snr < 2.: + time_resolution = res_wins['LRW'] + elif snr < 3.: + time_resolution = res_wins['MRW'] + else: + time_resolution = res_wins['HRW'] + + return time_resolution/2 + + def wadaticheck(pickdic, dttolerance, iplot): ''' Function to calculate Wadati-diagram from given P and S onsets in order @@ -453,7 +490,7 @@ def wadaticheck(pickdic, dttolerance, iplot): for key in pickdic: if pickdic[key].has_key('SPt'): wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) - ii += 1 + ii += 1 # check, if deviation is larger than adjusted if wddiff >= dttolerance: # mark onset and downgrade S-weight to 9 @@ -481,7 +518,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr else: print 'wadatacheck: Not enough checked S-P times available!' - print 'Skip Wadati check!' + print 'Skip Wadati check!' checkedonsets = pickdic @@ -489,7 +526,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - + # plot results if iplot > 1: plt.figure(iplot) @@ -518,14 +555,14 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): ''' Function to detect spuriously picked noise peaks. Uses envelope to determine, how many samples [per cent] after - P onset are below certain threshold, calculated from noise + P onset are below certain threshold, calculated from noise level times noise factor. : param: X, time series (seismogram) : type: `~obspy.core.stream.Stream` : param: pick, initial (AIC) P onset time - : type: float + : type: float : param: TSNR, length of time windows around initial pick [s] : type: tuple (T_noise, T_gap, T_signal) @@ -544,7 +581,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): : param: iplot, if iplot > 1, results are shown in figure : type: int ''' - + assert isinstance(X, Stream), "%s is not a stream object" % str(X) print 'Checking signal length ...' @@ -565,7 +602,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): # minimum adjusted number of samples over minimum signal level minnum = len(isignal) * minpercent/100 # get number of samples above minimum adjusted signal level - numoverthr = len(np.where(e[isignal] >= minsiglevel)[0]) + numoverthr = len(np.where(e[isignal] >= minsiglevel)[0]) if numoverthr >= minnum: print 'checksignallength: Signal reached required length.' @@ -574,12 +611,12 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): print 'checksignallength: Signal shorter than required minimum signal length!' print 'Presumably picked picked noise peak, pick is rejected!' returnflag = 0 - + if iplot == 2: plt.figure(iplot) p1, = plt.plot(t,x, 'k') p2, = plt.plot(t[inoise], e[inoise]) - p3, = plt.plot(t[isignal],e[isignal], 'r') + p3, = plt.plot(t[isignal],e[isignal], 'r') p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ [minsiglevel, minsiglevel], 'g') p5, = plt.plot([pick, pick], [min(x), max(x)], 'c') @@ -596,4 +633,6 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): return returnflag - +if __name__ == '__main__': + import doctest + doctest.testmod() From 7635f790fda6c454e33611f55ff848c2e63ec97b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:24:17 +0200 Subject: [PATCH 0426/1144] [bugfix] one filter parameter was type string and caused problems when parsing the parameters to the filter function of an obspy object --- pylot/core/util/defaults.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index b9815b45..df4fe5f7 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -10,7 +10,7 @@ FILTERDEFAULTS = {'P': {'filtertype': None, 'order': None, 'freq': None}, 'S': {'filtertype': 'bandpass', - 'order': '4', + 'order': 4, 'freq': [.5, 5]}} -OUTPUTFORMATS = {'QuakeML':'QUAKEML', 'VelEst':'VELEST'} \ No newline at end of file +OUTPUTFORMATS = {'QuakeML':'QUAKEML', 'VelEst':'VELEST'} From b42f88605bc9182cdff7f50bdf336ed36a6251f8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:25:08 +0200 Subject: [PATCH 0427/1144] the main application window now opens in fullscreen mode --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 14733925..a63153d5 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -549,7 +549,7 @@ def main(): pylot_form = MainWindow() # Show main window and run the app - pylot_form.show() + pylot_form.showMaximized() pylot_app.exec_() From bb52f8ac8311acf58f9c7d7b060b65eb35d0e8fd Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:27:00 +0200 Subject: [PATCH 0428/1144] moved the determination of the time resolution window to the utils module within the pick package --- pylot/core/util/widgets.py | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9cb326a0..cb4508bf 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -24,7 +24,8 @@ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime from pylot.core.read import FilterOptions -from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin +from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ + getResolutionWindow from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS from pylot.core.util import prepTimeAxis, getGlobalTimes @@ -403,20 +404,6 @@ class PickDlg(QDialog): ini_pick = gui_event.xdata - # calculate the resolution window width from SNR - # SNR >= 3 -> 2 sec HRW - # 3 > SNR >= 2 -> 5 sec MRW - # 2 > SNR >= 1.5 -> 10 sec LRW - # 1.5 > SNR -> 15 sec VLRW - # see also Diehl et al. 2009 - - res_wins = { - 'HRW': 2., - 'MRW': 5., - 'LRW': 10., - 'VLRW': 15. - } - settings = QSettings() nfac = settings.value('picking/nfac', 1.5) @@ -429,15 +416,7 @@ class PickDlg(QDialog): snr = result[0] noiselevel = result[2] * nfac - if snr < 1.5: - x_res = res_wins['VLRW'] - elif snr < 2.: - x_res = res_wins['LRW'] - elif snr < 3.: - x_res = res_wins['MRW'] - else: - x_res = res_wins['HRW'] - x_res /= 2 + x_res = getResolutionWindow(snr) # demean data before plotting data = self.getWFData().copy() From cb5bd7dc095caed672bbb0115f56c0bbcb7bd1d9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:30:59 +0200 Subject: [PATCH 0429/1144] differentiate between initial picks for p and s phases as they are picked from different components; zoom window determination for s phases not implemented yet --- pylot/core/util/widgets.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index cb4508bf..9eec8066 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -390,6 +390,12 @@ class PickDlg(QDialog): return self.picks def setIniPick(self, gui_event): + if self.selectPhase.currentText().upper().startswith('P'): + self.setIniPickP(gui_event) + elif self.selectPhase.currentText().upper().startswith('S'): + self.setIniPickS(gui_event) + + def setIniPickP(self, gui_event): trace_number = round(gui_event.ydata) @@ -444,6 +450,8 @@ class PickDlg(QDialog): self.setPlotLabels() self.draw() + def setIniPickS(self, gui_event): + pass def setPick(self, gui_event): From a383f8c769e04e99920fd67fb87ae75e6450f300 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:31:55 +0200 Subject: [PATCH 0430/1144] [bugfix] labels are now drawn correctly after picking an onset --- pylot/core/util/widgets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9eec8066..f7a460e3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -505,8 +505,7 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.zoomAction.setEnabled(True) self.selectPhase.setCurrentIndex(-1) - self.getPlotWidget().setXLims(self.getXLims()) - self.getPlotWidget().setYLims(self.getYLims()) + self.setPlotLabels() def drawPicks(self, phase=None): # plotting picks From ec3ecd04bb287683a98ab4b97a5800228b2e7d38 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:34:15 +0200 Subject: [PATCH 0431/1144] pick colors now depend on the type of phase picked; blueish colors for compressional wave (p) phases and reddish colors for shear wave (s) phases --- pylot/core/util/widgets.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index f7a460e3..b8b8b931 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -511,9 +511,12 @@ class PickDlg(QDialog): # plotting picks ax = self.getPlotWidget().axes ylims = self.getGlobalLimits('y') + phase_col = {'P': ('c', 'c--', 'b-'), + 'S': ('m', 'm--', 'r-')} if self.getPicks(): if phase is not None: picks = self.getPicks()[phase] + colors = phase_col[phase[0].upper()] else: for phase in self.getPicks(): self.drawPicks(phase) @@ -526,10 +529,11 @@ class PickDlg(QDialog): lpp = picks['lpp'] spe = picks['spe'] - ax.fill_between([epp, lpp], ylims[0], ylims[1], alpha=.5, color='c') - ax.plot([mpp - spe, mpp - spe], ylims, 'c--', - [mpp, mpp], ylims, 'b-', - [mpp + spe, mpp + spe], ylims, 'c--') + ax.fill_between([epp, lpp], ylims[0], ylims[1], + alpha=.5, color=colors[0]) + ax.plot([mpp - spe, mpp - spe], ylims, colors[1], + [mpp, mpp], ylims, colors[2], + [mpp + spe, mpp + spe], ylims, colors[1]) def panPress(self, gui_event): ax = self.getPlotWidget().axes From 4292197818e6cb8ab2038eb871fb07d1067a372a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:35:58 +0200 Subject: [PATCH 0432/1144] [bugfix] switching between zooming types now does not cause weird mouse event behavior anymore --- pylot/core/util/widgets.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b8b8b931..0c0ec11d 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -609,10 +609,11 @@ class PickDlg(QDialog): self.disconnectScrollEvent() self.figToolBar.zoom() else: - self.connectPressEvent(self.panPress) - self.connectMotionEvent(self.panMotion) - self.connectReleaseEvent(self.panRelease) - self.connectScrollEvent(self.scrollZoom) + self.figToolBar.zoom() + self.cidpress = self.connectPressEvent(self.panPress) + self.cidmotion = self.connectMotionEvent(self.panMotion) + self.cidrelease = self.connectReleaseEvent(self.panRelease) + self.cidscroll = self.connectScrollEvent(self.scrollZoom) def scrollZoom(self, gui_event, factor=2.): From e542aa70d9341893b874499586f7ee8cc5be18bc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 25 Jun 2015 10:36:45 +0200 Subject: [PATCH 0433/1144] doctesting added without having doctests inserted (pending) --- pylot/core/util/widgets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0c0ec11d..71c6cc5d 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1040,3 +1040,7 @@ class HelpForm(QDialog): def updatePageTitle(self): self.pageLabel.setText(self.webBrowser.documentTitle()) + +if __name__ == '__main__': + import doctest + doctest.testmod() From edd925af99911a2b2ee05fc6ed335d53704ee8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 25 Jun 2015 11:11:19 +0200 Subject: [PATCH 0434/1144] Some cosmetics. --- pylot/core/pick/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 1bcb7469..aad583a2 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -155,7 +155,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) - # get next zero crossing after most likely pick + # get zero crossings after most likely pick # initial onset is assumed to be the first zero crossing # first from unfiltered trace zc1 = [] @@ -199,7 +199,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): datafit1 = np.polyval(P1, xslope1) # now using filterd trace - # next zero crossing after most likely pick + # next zero crossings after most likely pick zc2 = [] zc2.append(Pick) index2 = [] @@ -578,11 +578,11 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): if iplot == 2: plt.figure(iplot) p1, = plt.plot(t,x, 'k') - p2, = plt.plot(t[inoise], e[inoise]) + p2, = plt.plot(t[inoise], e[inoise], 'c') p3, = plt.plot(t[isignal],e[isignal], 'r') p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ [minsiglevel, minsiglevel], 'g') - p5, = plt.plot([pick, pick], [min(x), max(x)], 'c') + p5, = plt.plot([pick, pick], [min(x), max(x)], linewidth=2) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Envelope Noise Window', \ 'Envelope Signal Window', 'Minimum Signal Level', \ 'Onset'], loc='best') From 0789f51d6985bb40c0570ce7156d25d8de71fa43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 26 Jun 2015 08:48:24 +0200 Subject: [PATCH 0435/1144] Implemented additional quality control function checkPonsets, using subfunction jackknife to skip misspicks. Yet not entirely finished. --- pylot/core/pick/utils.py | 118 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 5f1a8f0c..c504c2f1 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -492,7 +492,7 @@ def wadaticheck(pickdic, dttolerance, iplot): wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) ii += 1 # check, if deviation is larger than adjusted - if wddiff >= dttolerance: + if wddiff > dttolerance: # mark onset and downgrade S-weight to 9 # (not used anymore) marker = 'badWadatiCheck' @@ -526,7 +526,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - + iplot=2 # plot results if iplot > 1: plt.figure(iplot) @@ -615,16 +615,13 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): if iplot == 2: plt.figure(iplot) p1, = plt.plot(t,x, 'k') -<<<<<<< HEAD p2, = plt.plot(t[inoise], e[inoise], 'c') p3, = plt.plot(t[isignal],e[isignal], 'r') -======= p2, = plt.plot(t[inoise], e[inoise]) p3, = plt.plot(t[isignal],e[isignal], 'r') ->>>>>>> e542aa70d9341893b874499586f7ee8cc5be18bc p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ [minsiglevel, minsiglevel], 'g') - p5, = plt.plot([pick, pick], [min(x), max(x)], linewidth=2) + p5, = plt.plot([pick, pick], [min(x), max(x)], 'b', linewidth=2) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Envelope Noise Window', \ 'Envelope Signal Window', 'Minimum Signal Level', \ 'Onset'], loc='best') @@ -638,6 +635,115 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): return returnflag + +def checkPonsets(pickdic, dttolerance, iplot): + ''' + Function to check statistics of P-onset times: Control deviation from + median (maximum adjusted deviation = dttolerance) and apply pseudo- + bootstrapping jackknife. + + : param: pickdic, dictionary containing picks and quality parameters + : type: dictionary + + : param: dttolerance, maximum adjusted deviation of P-onset time from + median of all P onsets + : type: float + + : param: iplot, if iplot > 1, Wadati diagram is shown + : type: int + ''' + + checkedonsets = pickdic + + # search for good quality P picks + Ppicks = [] + for key in pickdic: + if pickdic[key]['P']['weight'] < 4: + # add P onsets to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + Ppicks.append(UTCPpick.timestamp) + + # apply jackknife bootstrapping on variance of P onsets + print 'checkPonsets: Apply jackknife bootstrapping on P-onset times ...' + [xjack,PHI_pseudo,PHI_sub] = jackknife(Ppicks, 'VAR', 1) + # get pseudo variances smaller than average variances + # these picks passed jackknife test + ij = np.where(PHI_pseudo <= xjack) + #ij = np.array(ij).tolist() + #jkpicks = Ppicks[ij] + + # calculate median from these picks + #pmedian = np.median(jkpicks) + # find picks that deviate more than dttolerance from median + #ibad = np.where(abs(jkpicks - pmedian) > dttolerance) + #pdb.set_trace() + + +def jackknife(X, phi, h): + ''' + Function to calculate the Jackknife Estimator for a given quantity, + special type of boot strapping. Returns the jackknife estimator PHI_jack + the pseudo values PHI_pseudo and the subgroup parameters PHI_sub. + + : param: X, given quantity + : type: list + + : param: phi, chosen estimator, choose between: + "MED" for median + "MEA" for arithmetic mean + "VAR" for variance + : type: string + + : param: h, size of subgroups, optinal, default = 1 + : type: integer + ''' + + PHI_jack = None + PHI_pseudo = None + PHI_sub = None + + # determine number of subgroups + g = len(X) / h + + if type(g) is not int: + print 'jackknife: Cannot divide quantity X in equal sized subgroups!' + print 'Choose another size for subgroups!' + return PHI_jack, PHI_pseudo, PHI_sub + else: + # estimator of undisturbed spot check + if phi == 'MEA': + phi_sc = np.mean(X) + elif phi == 'VAR': + phi_sc = np.var(X) + elif phi == 'MED': + phi_sc = np.median(X) + + # estimators of subgroups + PHI_pseudo = [] + PHI_sub = [] + for i in range(0, g - 1): + # subgroup i, remove i-th sample + xx = X[:] + del xx[i] + # calculate estimators of disturbed spot check + if phi == 'MEA': + phi_sub = np.mean(xx) + elif phi == 'VAR': + phi_sub = np.var(xx) + elif phi == 'MED': + phi_sub = np.median(xx) + + PHI_sub.append(phi_sub) + # pseudo values + phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) + PHI_pseudo.append(phi_pseudo) + # jackknife estimator + PHI_jack = np.mean(PHI_pseudo) + + return PHI_jack, PHI_pseudo, PHI_sub + + + if __name__ == '__main__': import doctest doctest.testmod() From 99adb5ce9c705697fdef0a74738eee1b3cec9797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 26 Jun 2015 15:59:50 +0200 Subject: [PATCH 0436/1144] Finialized new function checkPonset. --- pylot/core/pick/utils.py | 61 +++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index c504c2f1..29f29806 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -13,7 +13,7 @@ import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings - +import pdb def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) @@ -657,11 +657,13 @@ def checkPonsets(pickdic, dttolerance, iplot): # search for good quality P picks Ppicks = [] + stations = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4: # add P onsets to list UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) Ppicks.append(UTCPpick.timestamp) + stations.append(key) # apply jackknife bootstrapping on variance of P onsets print 'checkPonsets: Apply jackknife bootstrapping on P-onset times ...' @@ -669,15 +671,60 @@ def checkPonsets(pickdic, dttolerance, iplot): # get pseudo variances smaller than average variances # these picks passed jackknife test ij = np.where(PHI_pseudo <= xjack) - #ij = np.array(ij).tolist() - #jkpicks = Ppicks[ij] + # these picks did not pass jackknife test + badjk = np.where(PHI_pseudo > xjack) + badjkstations = np.array(stations)[badjk] # calculate median from these picks - #pmedian = np.median(jkpicks) - # find picks that deviate more than dttolerance from median - #ibad = np.where(abs(jkpicks - pmedian) > dttolerance) - #pdb.set_trace() + pmedian = np.median(np.array(Ppicks)[ij]) + # find picks that deviate less than dttolerance from median + ii = np.where(abs(np.array(Ppicks)[ij] - pmedian) <= dttolerance) + jj = np.where(abs(np.array(Ppicks)[ij] - pmedian) > dttolerance) + igood = ij[0][ii] + ibad = ij[0][jj] + goodstations = np.array(stations)[igood] + badstations = np.array(stations)[ibad] + print 'checkPonset: Skipped %d P onsets out of %d' % (len(badstations) \ + + len(badjkstations), len(stations)) + + goodmarker = 'goodPonsetcheck' + badmarker = 'badPonsetcheck' + badjkmarker = 'badjkcheck' + for i in range(0, len(goodstations)): + # mark P onset as checked and keep P weight + pickdic[goodstations[i]]['P']['marked'] = goodmarker + for i in range(0, len(badstations)): + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badstations[i]]['P']['marked'] = badmarker + pickdic[badstations[i]]['P']['weight'] = 9 + for i in range(0, len(badjkstations)): + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badjkstations[i]]['P']['marked'] = badjkmarker + pickdic[badjkstations[i]]['P']['weight'] = 9 + + checkedonsets = pickdic + + iplot = 2 + if iplot > 1: + p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) + p2, = plt.plot(igood, np.array(Ppicks)[igood], 'g*', markersize=14) + p3, = plt.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', \ + linewidth=2) + for i in range(0, len(Ppicks)): + plt.text(i, Ppicks[i] + 0.2, stations[i]) + + plt.xlabel('Number of P Picks') + plt.ylabel('Onset Time [s] from 1.1.1970') + plt.legend([p1, p2, p3], ['Skipped P Picks', 'Good P Picks', 'Median'], \ + loc='best') + plt.title('Check P Onsets') + plt.show() + raw_input() + + return checkedonsets def jackknife(X, phi, h): ''' From 27155ad816f5694aa4d2b938e8037baa994669fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 26 Jun 2015 16:00:20 +0200 Subject: [PATCH 0437/1144] Marginal changes. --- autoPyLoT.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index c14514ee..9bc28f38 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -12,7 +12,7 @@ from pylot.core.util import _getVersionString from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE -from pylot.core.pick.utils import wadaticheck +from pylot.core.pick.utils import wadaticheck, checkPonsets import pdb __version__ = _getVersionString() @@ -51,6 +51,7 @@ def autoPyLoT(inputfile): # get some parameters for quality control from # parameter input file (usually autoPyLoT.in). wdttolerance = parameter.getParam('wdttolerance') + mdttolerance = parameter.getParam('mdttolerance') iplot = parameter.getParam('iplot') data = Data() @@ -105,10 +106,11 @@ def autoPyLoT(inputfile): allonsets[station] = picks # quality control - # jackknife on P onset times + # median check and jackknife on P onset times + checkedonsetsjk = checkPonsets(allonsets, mdttolerance, iplot) # check S-P times (Wadati) - checkedonsets = wadaticheck(allonsets, wdttolerance, iplot) - # jackknife on S onset times + checkedonsetwd = wadaticheck(checkedonsetsjk, wdttolerance, iplot) + print '------------------------------------------' print '-----Finished event %s!-----' % event print '------------------------------------------' @@ -128,11 +130,12 @@ def autoPyLoT(inputfile): station = wfdat[0].stats.station allonsets = {station: picks} for i in range(len(wfdat)): + #for i in range(0,10): stationID = wfdat[i].stats.station #check if station has already been processed if stationID not in procstats: procstats.append(stationID) - #find corresponding streams + # find corresponding streams statdat = wfdat.select(station=stationID) ###################################################### # get onset times and corresponding picking parameters @@ -142,11 +145,12 @@ def autoPyLoT(inputfile): station = stationID allonsets[station] = picks - #quality control - #jackknife on P onset times - #check S-P times (Wadati) - checkedonsets = wadaticheck(allonsets, wdttolerance, iplot) - #jackknife on S onset times + # quality control + # median check and jackknife on P onset times + checkedonsetsjk = checkPonsets(allonsets, mdttolerance, iplot) + # check S-P times (Wadati) + checkedonsetswd = wadaticheck(checkedonsetsjk, wdttolerance, iplot) + print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' From 9e124436e3f04874ad3178b89ee4d5168a72da2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 26 Jun 2015 16:01:17 +0200 Subject: [PATCH 0438/1144] Modified parameters for checkPonset. --- autoPyLoT_local.in | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 06498689..0cb03f34 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -90,10 +90,8 @@ ARH #algoS# %choose algorithm for S-onset 70 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude -#jackknife-processing for P-picks# -3 #thresholdweight#%minimum required weight of picks -3 #dttolerance# %maximum allowed deviation of P picks from median [s] -4 #minstats# %minimum number of stations with reliable P picks +#check statistics of P onsets# +2.5 #mdttolerance# %maximum allowed deviation of P picks from median [s] #wadati check# -0.8 #wdttolerance# %maximum allowed deviation from Wadati-diagram +0.5 #wdttolerance# %maximum allowed deviation from Wadati-diagram From 90bf6cb99eaa6349efeec7b82a428f31a29e23c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 26 Jun 2015 16:01:43 +0200 Subject: [PATCH 0439/1144] Modified parameters for checkPonset. --- autoPyLoT_regional.in | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in index 6e050ff3..72afbd22 100644 --- a/autoPyLoT_regional.in +++ b/autoPyLoT_regional.in @@ -36,7 +36,7 @@ HYPOSAT #locrt# %location routine used ("HYPO 3 10 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] 3 12 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] 3 8 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] -3 6 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz] +3 6 #bph2# %lower/upper corner freq. of second band pass filter H-comp. [Hz] #special settings for calculating CF# %!!Be careful when editing the following!! #Z-component# @@ -49,10 +49,10 @@ HOS #algoP# %choose algorithm for P-onset 0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick 0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick 0.001 #addnoise# %add noise to seismogram for stable AR prediction -4 0.2 2.0 1.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] -4 #pickwinP# %for initial AIC and refined pick, length of P-pick window [s] +5 0.2 3.0 1.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +3 #pickwinP# %for initial AIC and refined pick, length of P-pick window [s] 8 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) -3.0 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] +1.0 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] 0.3 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] 0.3 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P) 1.3 #nfacP# %for HOS/AR, noise factor for noise level determination (P) @@ -88,10 +88,8 @@ ARH #algoS# %choose algorithm for S-onset 60 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude -#jackknife-processing for P-picks# -3 #thresholdweight#%minimum required weight of picks -3 #dttolerance# %maximum allowed deviation of P picks from median [s] -4 #minstats# %minimum number of stations with reliable P picks +#check statistics of P onsets# +35 #mdttolerance# %maximum allowed deviation of P picks from median [s] #wadati check# -1.5 #wdttolerance# %maximum allowed deviation from Wadati-diagram +2.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram From a854cf2762959fae33335e90d39b2c2140bdacbb Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sun, 28 Jun 2015 19:35:01 +0200 Subject: [PATCH 0440/1144] use default filter options for all expected phases set up by the project leader --- pylot/core/util/widgets.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 71c6cc5d..9d3718f3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -567,21 +567,22 @@ class PickDlg(QDialog): old_title = self.getPlotWidget().getAxes().get_title() title = None phase = self.selectPhase.currentText() + filtoptions = None + if phase: + filtoptions = self.getFilterOptions(phase).parseFilterOptions() if self.filterAction.isChecked(): - if phase: - filtoptions = self.getFilterOptions(phase).parseFilterOptions() - else: + if not phase: filtoptions = FilterOptionsDialog.getFilterObject() filtoptions = filtoptions.parseFilterOptions() - if filtoptions is not None: - data.filter(**filtoptions) - if old_title.endswith(')'): - title = old_title[:-1] + ', filtered)' - else: - title = old_title + ' (filtered)' + if filtoptions is not None: + data.filter(**filtoptions) + if old_title.endswith(')'): + title = old_title[:-1] + ', filtered)' else: - if old_title.endswith(' (filtered)'): - title = old_title.replace(' (filtered)', '') + title = old_title + ' (filtered)' + else: + if old_title.endswith(' (filtered)'): + title = old_title.replace(' (filtered)', '') if title is None: title = old_title self.getPlotWidget().plotWFData(wfdata=data, title=title, From 0fcd6fab9d0f96b9bd88e8eaf7c106553aa19f92 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 29 Jun 2015 16:14:11 +0200 Subject: [PATCH 0441/1144] clean-up to meet coding conventions --- pylot/core/pick/utils.py | 210 ++++++++++++++++++++------------------- 1 file changed, 108 insertions(+), 102 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 29f29806..71784565 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -14,6 +14,8 @@ import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings import pdb + + def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) @@ -59,7 +61,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ilup, = np.where(x[isignal] > nlevel) ildown, = np.where(x[isignal] < -nlevel) if not ilup.size and not ildown.size: - print 'earllatepicker: Signal lower than noise level!' + print 'earllatepicker: Signal lower than noise level!' print 'Skip this trace!' return LPick, EPick, PickError il = min(np.min(ilup) if ilup.size else float('inf'), @@ -186,11 +188,11 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) if imax1 == 0: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) if imax1 == 0: - print 'fmpicker: Zero crossings too close!' - print 'Skip first motion determination!' - return FM + print 'fmpicker: Zero crossings too close!' + print 'Skip first motion determination!' + return FM islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) # calculate slope as polynomal fit of order 1 @@ -228,11 +230,11 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) if imax2 == 0: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) if imax1 == 0: - print 'fmpicker: Zero crossings too close!' - print 'Skip first motion determination!' - return FM + print 'fmpicker: Zero crossings too close!' + print 'Skip first motion determination!' + return FM islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) # calculate slope as polynomal fit of order 1 @@ -367,7 +369,7 @@ def getnoisewin(t, t1, tnoise, tgap): # get noise window inoise, = np.where((t <= max([t1 - tgap, 0])) \ - & (t >= max([t1 - tnoise - tgap, 0]))) + & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: print 'getnoisewin: Empty array inoise, check noise window!' @@ -391,7 +393,7 @@ def getsignalwin(t, t1, tsignal): # get signal window isignal, = np.where((t <= min([t1 + tsignal, len(t)])) \ - & (t >= t1)) + & (t >= t1)) if np.size(isignal) < 1: print 'getsignalwin: Empty array isignal, check signal window!' @@ -432,7 +434,7 @@ def getResolutionWindow(snr): else: time_resolution = res_wins['HRW'] - return time_resolution/2 + return time_resolution / 2 def wadaticheck(pickdic, dttolerance, iplot): @@ -460,22 +462,21 @@ def wadaticheck(pickdic, dttolerance, iplot): SPtimes = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: - # calculate S-P time - spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] - # add S-P time to dictionary - pickdic[key]['SPt'] = spt - # add P onsets and corresponding S-P times to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - Ppicks.append(UTCPpick.timestamp) - Spicks.append(UTCSpick.timestamp) - SPtimes.append(spt) - + # calculate S-P time + spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] + # add S-P time to dictionary + pickdic[key]['SPt'] = spt + # add P onsets and corresponding S-P times to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) + Ppicks.append(UTCPpick.timestamp) + Spicks.append(UTCSpick.timestamp) + SPtimes.append(spt) if len(SPtimes) >= 3: - # calculate slope - p1 = np.polyfit(Ppicks, SPtimes, 1) - wdfit = np.polyval(p1, Ppicks) + # calculate slope + p1 = np.polyfit(Ppicks, SPtimes, 1) + wdfit = np.polyval(p1, Ppicks) wfitflag = 0 # calculate vp/vs ratio before check @@ -499,48 +500,50 @@ def wadaticheck(pickdic, dttolerance, iplot): pickdic[key]['S']['weight'] = 9 else: marker = 'goodWadatiCheck' - checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) + checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) checkedPpicks.append(checkedPpick.timestamp) checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) checkedSpicks.append(checkedSpick.timestamp) - checkedSPtime = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] + checkedSPtime = pickdic[key]['S']['mpp'] - \ + pickdic[key]['P']['mpp'] checkedSPtimes.append(checkedSPtime) pickdic[key]['S']['marked'] = marker if len(checkedPpicks) >= 3: - # calculate new slope - p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) - wdfit2 = np.polyval(p2, checkedPpicks) + # calculate new slope + p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) + wdfit2 = np.polyval(p2, checkedPpicks) - # calculate vp/vs ratio after check - cvpvsr = p2[0] + 1 - print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr + # calculate vp/vs ratio after check + cvpvsr = p2[0] + 1 + print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr else: - print 'wadatacheck: Not enough checked S-P times available!' - print 'Skip Wadati check!' + print 'wadatacheck: Not enough checked S-P times available!' + print 'Skip Wadati check!' checkedonsets = pickdic else: - print 'wadaticheck: Not enough S-P times available for reliable regression!' + print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - iplot=2 + iplot = 2 # plot results if iplot > 1: - plt.figure(iplot) - f1, = plt.plot(Ppicks, SPtimes, 'ro') + plt.figure(iplot) + f1, = plt.plot(Ppicks, SPtimes, 'ro') if wfitflag == 0: - f2, = plt.plot(Ppicks, wdfit, 'k') - f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') - f4, = plt.plot(checkedPpicks, wdfit2, 'g') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ - 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ - 'Reliable S-Picks', 'Wadati 2'], loc='best') + f2, = plt.plot(Ppicks, wdfit, 'k') + f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') + f4, = plt.plot(checkedPpicks, wdfit2, 'g') + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ + 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) + plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ + 'Reliable S-Picks', 'Wadati 2'], + loc='best') else: - plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) + plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) plt.ylabel('S-P Times [s]') plt.xlabel('P Times [s]') @@ -600,12 +603,12 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): # calculate minimum adjusted signal level minsiglevel = max(e[inoise]) * nfac # minimum adjusted number of samples over minimum signal level - minnum = len(isignal) * minpercent/100 + minnum = len(isignal) * minpercent / 100 # get number of samples above minimum adjusted signal level numoverthr = len(np.where(e[isignal] >= minsiglevel)[0]) if numoverthr >= minnum: - print 'checksignallength: Signal reached required length.' + print 'checksignallength: Signal reached required length.' returnflag = 1 else: print 'checksignallength: Signal shorter than required minimum signal length!' @@ -614,17 +617,18 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): if iplot == 2: plt.figure(iplot) - p1, = plt.plot(t,x, 'k') + p1, = plt.plot(t, x, 'k') p2, = plt.plot(t[inoise], e[inoise], 'c') - p3, = plt.plot(t[isignal],e[isignal], 'r') + p3, = plt.plot(t[isignal], e[isignal], 'r') p2, = plt.plot(t[inoise], e[inoise]) - p3, = plt.plot(t[isignal],e[isignal], 'r') - p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ - [minsiglevel, minsiglevel], 'g') + p3, = plt.plot(t[isignal], e[isignal], 'r') + p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal) - 1]]], \ + [minsiglevel, minsiglevel], 'g') p5, = plt.plot([pick, pick], [min(x), max(x)], 'b', linewidth=2) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Envelope Noise Window', \ - 'Envelope Signal Window', 'Minimum Signal Level', \ - 'Onset'], loc='best') + 'Envelope Signal Window', + 'Minimum Signal Level', \ + 'Onset'], loc='best') plt.xlabel('Time [s] since %s' % X[0].stats.starttime) plt.ylabel('Counts') plt.title('Check for Signal Length, Station %s' % X[0].stats.station) @@ -638,7 +642,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): def checkPonsets(pickdic, dttolerance, iplot): ''' - Function to check statistics of P-onset times: Control deviation from + Function to check statistics of P-onset times: Control deviation from median (maximum adjusted deviation = dttolerance) and apply pseudo- bootstrapping jackknife. @@ -660,14 +664,14 @@ def checkPonsets(pickdic, dttolerance, iplot): stations = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4: - # add P onsets to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - Ppicks.append(UTCPpick.timestamp) - stations.append(key) + # add P onsets to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + Ppicks.append(UTCPpick.timestamp) + stations.append(key) # apply jackknife bootstrapping on variance of P onsets print 'checkPonsets: Apply jackknife bootstrapping on P-onset times ...' - [xjack,PHI_pseudo,PHI_sub] = jackknife(Ppicks, 'VAR', 1) + [xjack, PHI_pseudo, PHI_sub] = jackknife(Ppicks, 'VAR', 1) # get pseudo variances smaller than average variances # these picks passed jackknife test ij = np.where(PHI_pseudo <= xjack) @@ -686,46 +690,48 @@ def checkPonsets(pickdic, dttolerance, iplot): badstations = np.array(stations)[ibad] print 'checkPonset: Skipped %d P onsets out of %d' % (len(badstations) \ - + len(badjkstations), len(stations)) + + len(badjkstations), + len(stations)) goodmarker = 'goodPonsetcheck' badmarker = 'badPonsetcheck' badjkmarker = 'badjkcheck' for i in range(0, len(goodstations)): # mark P onset as checked and keep P weight - pickdic[goodstations[i]]['P']['marked'] = goodmarker + pickdic[goodstations[i]]['P']['marked'] = goodmarker for i in range(0, len(badstations)): - # mark P onset and downgrade P weight to 9 - # (not used anymore) - pickdic[badstations[i]]['P']['marked'] = badmarker - pickdic[badstations[i]]['P']['weight'] = 9 + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badstations[i]]['P']['marked'] = badmarker + pickdic[badstations[i]]['P']['weight'] = 9 for i in range(0, len(badjkstations)): - # mark P onset and downgrade P weight to 9 - # (not used anymore) - pickdic[badjkstations[i]]['P']['marked'] = badjkmarker - pickdic[badjkstations[i]]['P']['weight'] = 9 + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badjkstations[i]]['P']['marked'] = badjkmarker + pickdic[badjkstations[i]]['P']['weight'] = 9 checkedonsets = pickdic iplot = 2 if iplot > 1: - p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) + p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) p2, = plt.plot(igood, np.array(Ppicks)[igood], 'g*', markersize=14) p3, = plt.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', \ - linewidth=2) + linewidth=2) for i in range(0, len(Ppicks)): - plt.text(i, Ppicks[i] + 0.2, stations[i]) + plt.text(i, Ppicks[i] + 0.2, stations[i]) - plt.xlabel('Number of P Picks') + plt.xlabel('Number of P Picks') plt.ylabel('Onset Time [s] from 1.1.1970') plt.legend([p1, p2, p3], ['Skipped P Picks', 'Good P Picks', 'Median'], \ - loc='best') + loc='best') plt.title('Check P Onsets') plt.show() raw_input() return checkedonsets + def jackknife(X, phi, h): ''' Function to calculate the Jackknife Estimator for a given quantity, @@ -744,7 +750,7 @@ def jackknife(X, phi, h): : param: h, size of subgroups, optinal, default = 1 : type: integer ''' - + PHI_jack = None PHI_pseudo = None PHI_sub = None @@ -753,44 +759,44 @@ def jackknife(X, phi, h): g = len(X) / h if type(g) is not int: - print 'jackknife: Cannot divide quantity X in equal sized subgroups!' + print 'jackknife: Cannot divide quantity X in equal sized subgroups!' print 'Choose another size for subgroups!' return PHI_jack, PHI_pseudo, PHI_sub else: - # estimator of undisturbed spot check - if phi == 'MEA': - phi_sc = np.mean(X) + # estimator of undisturbed spot check + if phi == 'MEA': + phi_sc = np.mean(X) elif phi == 'VAR': - phi_sc = np.var(X) + phi_sc = np.var(X) elif phi == 'MED': - phi_sc = np.median(X) + phi_sc = np.median(X) - # estimators of subgroups + # estimators of subgroups PHI_pseudo = [] PHI_sub = [] for i in range(0, g - 1): - # subgroup i, remove i-th sample - xx = X[:] - del xx[i] - # calculate estimators of disturbed spot check - if phi == 'MEA': - phi_sub = np.mean(xx) - elif phi == 'VAR': - phi_sub = np.var(xx) - elif phi == 'MED': - phi_sub = np.median(xx) - - PHI_sub.append(phi_sub) - # pseudo values - phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) - PHI_pseudo.append(phi_pseudo) + # subgroup i, remove i-th sample + xx = X[:] + del xx[i] + # calculate estimators of disturbed spot check + if phi == 'MEA': + phi_sub = np.mean(xx) + elif phi == 'VAR': + phi_sub = np.var(xx) + elif phi == 'MED': + phi_sub = np.median(xx) + + PHI_sub.append(phi_sub) + # pseudo values + phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) + PHI_pseudo.append(phi_pseudo) # jackknife estimator PHI_jack = np.mean(PHI_pseudo) return PHI_jack, PHI_pseudo, PHI_sub - if __name__ == '__main__': import doctest + doctest.testmod() From 9aa8a5bf13a0f9312e71856298b51166b244cc50 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 29 Jun 2015 16:16:59 +0200 Subject: [PATCH 0442/1144] function getSNR re-implemented in order to allow SNR calculation for stream object with more than one trace; the resulting SNR is the maximum SNR found over all traces in the stream object --- pylot/core/pick/utils.py | 87 +++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 71784565..9f2b4fb0 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -301,48 +301,71 @@ def crossings_nonzero_all(data): return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0] -def getSNR(X, TSNR, t1): - ''' - Function to calculate SNR of certain part of seismogram relative to - given time (onset) out of given noise and signal windows. A safety gap - between noise and signal part can be set. Returns SNR and SNR [dB] and - noiselevel. +def getSNR(st, TSNR, t0): + """ + returns the maximum signal to noise ratio SNR (also in dB) and the + corresponding noise level for a given data stream ST ,initial time T0 and + time window parameter tuple TSNR - :param: X, time series (seismogram) + :param: st, time series (seismogram) :type: `~obspy.core.stream.Stream` - - :param: TSNR, length of time windows [s] around t1 (onset) used to determine SNR + :param: TSNR, length of time windows [s] around t0 (onset) used to determine + SNR :type: tuple (T_noise, T_gap, T_signal) - - :param: t1, initial time (onset) from which noise and signal windows are calculated + :param: t0, initial time (onset) from which noise and signal windows are calculated :type: float - ''' + :return: SNR, SNRdB, noiselevel - assert isinstance(X, Stream), "%s is not a stream object" % str(X) + ..examples: - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, - X[0].stats.delta) + >>> from obspy.core import read + >>> st = read() + >>> result = getSNR(st, (6., .3, 3.), 4.67) + >>> print result + (5.1267717641040758, 7.0984398375666435, 132.89370192191919) + >>> result = getSNR(st, (8., .2, 5.), 4.67) + >>> print result + (4.645441835797703, 6.6702702677384131, 133.03562794665109) + """ - # get noise window - inoise = getnoisewin(t, t1, TSNR[0], TSNR[1]) + assert isinstance(st, Stream), "%s is not a stream object" % str(st) - # get signal window - isignal = getsignalwin(t, t1, TSNR[2]) - if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' - return - elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' - return + SNR = None + noiselevel = None - # demean over entire waveform - x = x - np.mean(x[inoise]) + for tr in st: + x = tr.data + t = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, + tr.stats.delta) + + # get noise window + inoise = getnoisewin(t, t0, TSNR[0], TSNR[1]) + + # get signal window + isignal = getsignalwin(t, t0, TSNR[2]) + if np.size(inoise) < 1: + print 'getSNR: Empty array inoise, check noise window!' + return + elif np.size(isignal) < 1: + print 'getSNR: Empty array isignal, check signal window!' + return + + # demean over entire waveform + x = x - np.mean(x[inoise]) + + # calculate ratios + new_noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) + signallevel = np.sqrt(np.mean(np.square(x[isignal]))) + newSNR = signallevel / new_noiselevel + if not SNR or newSNR > SNR: + SNR = newSNR + noiselevel = new_noiselevel + + if not SNR or not noiselevel: + raise ValueError('signal to noise ratio could not be calculated:\n' + 'noiselevel: {0}\n' + 'SNR: {1}'.format(noiselevel, SNR)) - # calculate ratios - noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) - signallevel = np.sqrt(np.mean(np.square(x[isignal]))) - SNR = signallevel / noiselevel SNRdB = 10 * np.log10(SNR) return SNR, SNRdB, noiselevel From 327a22818a9a992a9a2df2c7804eba77dc2d8b46 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 29 Jun 2015 16:19:17 +0200 Subject: [PATCH 0443/1144] added static method demeanWFData which simply returns the demeaned waveform stream object while the individual trace data are decreased by the average value of the waveform in a given window --- pylot/core/util/widgets.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9d3718f3..8069c293 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -386,6 +386,30 @@ class PickDlg(QDialog): wfdata = self.getWFData().select(component=component) return wfdata + @staticmethod + def demeanWFData(data, t0, win, gap): + """ + returns the DATA where each trace is demean by the average value within + a desired time window WIN before T0 and a GAP + :param data: waveform stream object + :type data: `~obspy.core.stream.Stream` + :param t0: time before which the noise + :type t0: float + :param win: time window for average calculation + :type win: tuple + :param gap: gap window to avoid polluting the average + :type gap: float + :return: data + :rtype: `~obspy.core.stream.Stream` + """ + starttime = getGlobalTimes(data)[0] + for tr in data: + stime = tr.stats.starttime - starttime + t = prepTimeAxis(stime, tr) + inoise = getnoisewin(t, t0, win, gap) + tr.data -= tr.data[inoise].mean() + return data + def getPicks(self): return self.picks @@ -424,14 +448,8 @@ class PickDlg(QDialog): x_res = getResolutionWindow(snr) - # demean data before plotting - data = self.getWFData().copy() - starttime = getGlobalTimes(data)[0] - for tr in data: - stime = tr.stats.starttime - starttime - t = prepTimeAxis(stime, tr) - inoise = getnoisewin(t, ini_pick, noise_win, gap_win) - tr.data -= tr.data[inoise].mean() + data = self.demeanWFData(data=self.getWFData().copy(), t0=ini_pick, + win=noise_win, gap=gap_win) self.setXLims([ini_pick - x_res, ini_pick + x_res]) self.setYLims(np.array([-noiselevel * 2.5, noiselevel * 2.5]) + From 1af88fbe85e12b9117554bb6df7a6ba02cae8d39 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 29 Jun 2015 16:23:46 +0200 Subject: [PATCH 0444/1144] default values changed to the defaults from autopylot --- pylot/core/util/widgets.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8069c293..2b718f8d 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -436,10 +436,10 @@ class PickDlg(QDialog): settings = QSettings() - nfac = settings.value('picking/nfac', 1.5) - noise_win = settings.value('picking/noise_win', 5.) - gap_win = settings.value('picking/gap_win', .5) - signal_win = settings.value('picking/signal_win', 1.5) + nfac = settings.value('picking/nfac_P', 1.3) + noise_win = settings.value('picking/noise_win_P', 5.) + gap_win = settings.value('picking/gap_win_P', .2) + signal_win = settings.value('picking/signal_win_P', 3.) result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick) @@ -469,6 +469,12 @@ class PickDlg(QDialog): self.draw() def setIniPickS(self, gui_event): + settings = QSettings() + + nfac = settings.value('picking/nfac_P', 1.5) + noise_win = settings.value('picking/noise_win_P', 5.) + gap_win = settings.value('picking/gap_win_P', .2) + signal_win = settings.value('picking/signal_win_P', 3.) pass def setPick(self, gui_event): From 69b345c96b31b17761ab974f16ad9260d1d9cd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 1 Jul 2015 15:23:21 +0200 Subject: [PATCH 0445/1144] New parameters for new quality control function checkZ4S. --- autoPyLoT_local.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 0cb03f34..22d90771 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -89,9 +89,9 @@ ARH #algoS# %choose algorithm for S-onset 3 #noisefactor# %noiselevel*noisefactor=threshold 70 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# -3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude +2.0 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude #check statistics of P onsets# 2.5 #mdttolerance# %maximum allowed deviation of P picks from median [s] #wadati check# -0.5 #wdttolerance# %maximum allowed deviation from Wadati-diagram +1.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram From 489199af10cde27c28bc3b3fcc81034821a5fd38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 1 Jul 2015 15:23:37 +0200 Subject: [PATCH 0446/1144] New parameters for new quality control function checkZ4S. --- autoPyLoT_regional.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in index 72afbd22..abcc0812 100644 --- a/autoPyLoT_regional.in +++ b/autoPyLoT_regional.in @@ -87,7 +87,7 @@ ARH #algoS# %choose algorithm for S-onset 1.5 #noisefactor# %noiselevel*noisefactor=threshold 60 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# -3.0 #zfac# %P-amplitude must exceed zfac times RMS-S amplitude +1.5 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude #check statistics of P onsets# 35 #mdttolerance# %maximum allowed deviation of P picks from median [s] #wadati check# From fb956f381a01c3c0e677717ba53df48c93873657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 1 Jul 2015 15:30:13 +0200 Subject: [PATCH 0447/1144] Implmented new quality control function checkZ4S. --- pylot/core/pick/run_autopicking.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index cc690e8b..061c5273 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -78,6 +78,8 @@ def run_autopicking(wfstream, pickparam): minsiglength = pickparam.getParam('minsiglength') minpercent = pickparam.getParam('minpercent') nfacsl = pickparam.getParam('noisefactor') + # parameter to check for spuriously picked S onset + zfac = pickparam.getParam('zfac') # initialize output Pweight = 4 # weight for P onset @@ -162,6 +164,27 @@ def run_autopicking(wfstream, pickparam): z_copy[0].data = tr_filt.data Pflag = checksignallength(z_copy, aicpick.getpick(), tsnrz, minsiglength, \ nfacsl, minpercent, iplot) + if Pflag == 1: + # check for spuriously picked S onset + # both horizontal traces needed + if len(ndat) == 0 or len(edat) == 0: + print 'One or more horizontal components missing!' + print 'Skip control function checkZ4S.' + else: + # filter and taper horizontal traces + trH1_filt = edat.copy() + trH2_filt = ndat.copy() + trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], \ + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], \ + zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + zne = z_copy + zne += trH1_filt + zne += trH2_filt + Pflag = checkZ4S(zne, aicpick.getpick(), zfac, \ + tsnrz[3], iplot) ############################################################## # go on with processing if AIC onset passes quality control if (aicpick.getSlope() >= minAICPslope and @@ -272,7 +295,7 @@ def run_autopicking(wfstream, pickparam): h_copy[1].data = trH2_filt.data elif algoS == 'AR3': print zdat, edat, ndat - # re-create stream object including both horizontal components + # re-create stream object including all components hdat = zdat.copy() hdat += edat hdat += ndat From 5bb616ffc5200d38de5fab28a8b4c1acaa8795de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 1 Jul 2015 15:31:02 +0200 Subject: [PATCH 0448/1144] Marginal changes. --- pylot/core/pick/run_autopicking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 061c5273..94ea1637 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * -import pdb + def run_autopicking(wfstream, pickparam): """ :param: wfstream From 3e81adfec644eee4febda70edf057b241ad902d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 1 Jul 2015 15:31:50 +0200 Subject: [PATCH 0449/1144] Marginal changes. --- pylot/core/pick/utils.py | 399 +++++++++++++++++++++++---------------- 1 file changed, 237 insertions(+), 162 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9f2b4fb0..4e1c9ca3 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -13,8 +13,6 @@ import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings -import pdb - def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' @@ -61,7 +59,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ilup, = np.where(x[isignal] > nlevel) ildown, = np.where(x[isignal] < -nlevel) if not ilup.size and not ildown.size: - print 'earllatepicker: Signal lower than noise level!' + print 'earllatepicker: Signal lower than noise level!' print 'Skip this trace!' return LPick, EPick, PickError il = min(np.min(ilup) if ilup.size else float('inf'), @@ -188,11 +186,11 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) if imax1 == 0: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) if imax1 == 0: - print 'fmpicker: Zero crossings too close!' - print 'Skip first motion determination!' - return FM + print 'fmpicker: Zero crossings too close!' + print 'Skip first motion determination!' + return FM islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) # calculate slope as polynomal fit of order 1 @@ -230,11 +228,11 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) if imax2 == 0: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) if imax1 == 0: - print 'fmpicker: Zero crossings too close!' - print 'Skip first motion determination!' - return FM + print 'fmpicker: Zero crossings too close!' + print 'Skip first motion determination!' + return FM islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) # calculate slope as polynomal fit of order 1 @@ -256,6 +254,8 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): FM = '+' elif P1[0] > 0 and P2[0] <= 0: FM = '+' + + print 'fmpicker: Found polarity %s' % FM if iplot > 1: plt.figure(iplot) @@ -301,71 +301,48 @@ def crossings_nonzero_all(data): return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0] -def getSNR(st, TSNR, t0): - """ - returns the maximum signal to noise ratio SNR (also in dB) and the - corresponding noise level for a given data stream ST ,initial time T0 and - time window parameter tuple TSNR +def getSNR(X, TSNR, t1): + ''' + Function to calculate SNR of certain part of seismogram relative to + given time (onset) out of given noise and signal windows. A safety gap + between noise and signal part can be set. Returns SNR and SNR [dB] and + noiselevel. - :param: st, time series (seismogram) + :param: X, time series (seismogram) :type: `~obspy.core.stream.Stream` - :param: TSNR, length of time windows [s] around t0 (onset) used to determine - SNR + + :param: TSNR, length of time windows [s] around t1 (onset) used to determine SNR :type: tuple (T_noise, T_gap, T_signal) - :param: t0, initial time (onset) from which noise and signal windows are calculated + + :param: t1, initial time (onset) from which noise and signal windows are calculated :type: float - :return: SNR, SNRdB, noiselevel + ''' - ..examples: + assert isinstance(X, Stream), "%s is not a stream object" % str(X) - >>> from obspy.core import read - >>> st = read() - >>> result = getSNR(st, (6., .3, 3.), 4.67) - >>> print result - (5.1267717641040758, 7.0984398375666435, 132.89370192191919) - >>> result = getSNR(st, (8., .2, 5.), 4.67) - >>> print result - (4.645441835797703, 6.6702702677384131, 133.03562794665109) - """ + x = X[0].data + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, + X[0].stats.delta) - assert isinstance(st, Stream), "%s is not a stream object" % str(st) + # get noise window + inoise = getnoisewin(t, t1, TSNR[0], TSNR[1]) - SNR = None - noiselevel = None + # get signal window + isignal = getsignalwin(t, t1, TSNR[2]) + if np.size(inoise) < 1: + print 'getSNR: Empty array inoise, check noise window!' + return + elif np.size(isignal) < 1: + print 'getSNR: Empty array isignal, check signal window!' + return - for tr in st: - x = tr.data - t = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, - tr.stats.delta) - - # get noise window - inoise = getnoisewin(t, t0, TSNR[0], TSNR[1]) - - # get signal window - isignal = getsignalwin(t, t0, TSNR[2]) - if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' - return - elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' - return - - # demean over entire waveform - x = x - np.mean(x[inoise]) - - # calculate ratios - new_noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) - signallevel = np.sqrt(np.mean(np.square(x[isignal]))) - newSNR = signallevel / new_noiselevel - if not SNR or newSNR > SNR: - SNR = newSNR - noiselevel = new_noiselevel - - if not SNR or not noiselevel: - raise ValueError('signal to noise ratio could not be calculated:\n' - 'noiselevel: {0}\n' - 'SNR: {1}'.format(noiselevel, SNR)) + # demean over entire waveform + x = x - np.mean(x[inoise]) + # calculate ratios + noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) + signallevel = np.sqrt(np.mean(np.square(x[isignal]))) + SNR = signallevel / noiselevel SNRdB = 10 * np.log10(SNR) return SNR, SNRdB, noiselevel @@ -392,7 +369,7 @@ def getnoisewin(t, t1, tnoise, tgap): # get noise window inoise, = np.where((t <= max([t1 - tgap, 0])) \ - & (t >= max([t1 - tnoise - tgap, 0]))) + & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: print 'getnoisewin: Empty array inoise, check noise window!' @@ -416,7 +393,7 @@ def getsignalwin(t, t1, tsignal): # get signal window isignal, = np.where((t <= min([t1 + tsignal, len(t)])) \ - & (t >= t1)) + & (t >= t1)) if np.size(isignal) < 1: print 'getsignalwin: Empty array isignal, check signal window!' @@ -457,7 +434,7 @@ def getResolutionWindow(snr): else: time_resolution = res_wins['HRW'] - return time_resolution / 2 + return time_resolution/2 def wadaticheck(pickdic, dttolerance, iplot): @@ -485,21 +462,22 @@ def wadaticheck(pickdic, dttolerance, iplot): SPtimes = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: - # calculate S-P time - spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] - # add S-P time to dictionary - pickdic[key]['SPt'] = spt - # add P onsets and corresponding S-P times to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - Ppicks.append(UTCPpick.timestamp) - Spicks.append(UTCSpick.timestamp) - SPtimes.append(spt) + # calculate S-P time + spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] + # add S-P time to dictionary + pickdic[key]['SPt'] = spt + # add P onsets and corresponding S-P times to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) + Ppicks.append(UTCPpick.timestamp) + Spicks.append(UTCSpick.timestamp) + SPtimes.append(spt) + if len(SPtimes) >= 3: - # calculate slope - p1 = np.polyfit(Ppicks, SPtimes, 1) - wdfit = np.polyval(p1, Ppicks) + # calculate slope + p1 = np.polyfit(Ppicks, SPtimes, 1) + wdfit = np.polyval(p1, Ppicks) wfitflag = 0 # calculate vp/vs ratio before check @@ -523,50 +501,48 @@ def wadaticheck(pickdic, dttolerance, iplot): pickdic[key]['S']['weight'] = 9 else: marker = 'goodWadatiCheck' - checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) + checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) checkedPpicks.append(checkedPpick.timestamp) checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) checkedSpicks.append(checkedSpick.timestamp) - checkedSPtime = pickdic[key]['S']['mpp'] - \ - pickdic[key]['P']['mpp'] + checkedSPtime = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] checkedSPtimes.append(checkedSPtime) pickdic[key]['S']['marked'] = marker if len(checkedPpicks) >= 3: - # calculate new slope - p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) - wdfit2 = np.polyval(p2, checkedPpicks) + # calculate new slope + p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) + wdfit2 = np.polyval(p2, checkedPpicks) - # calculate vp/vs ratio after check - cvpvsr = p2[0] + 1 - print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr + # calculate vp/vs ratio after check + cvpvsr = p2[0] + 1 + print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr else: - print 'wadatacheck: Not enough checked S-P times available!' - print 'Skip Wadati check!' + print 'wadatacheck: Not enough checked S-P times available!' + print 'Skip Wadati check!' checkedonsets = pickdic else: - print 'wadaticheck: Not enough S-P times available for reliable regression!' + print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - iplot = 2 + iplot=2 # plot results if iplot > 1: - plt.figure(iplot) - f1, = plt.plot(Ppicks, SPtimes, 'ro') + plt.figure(iplot) + f1, = plt.plot(Ppicks, SPtimes, 'ro') if wfitflag == 0: - f2, = plt.plot(Ppicks, wdfit, 'k') - f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') - f4, = plt.plot(checkedPpicks, wdfit2, 'g') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ - 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ - 'Reliable S-Picks', 'Wadati 2'], - loc='best') + f2, = plt.plot(Ppicks, wdfit, 'k') + f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') + f4, = plt.plot(checkedPpicks, wdfit2, 'g') + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ + 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) + plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ + 'Reliable S-Picks', 'Wadati 2'], loc='best') else: - plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) + plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) plt.ylabel('S-P Times [s]') plt.xlabel('P Times [s]') @@ -626,12 +602,12 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): # calculate minimum adjusted signal level minsiglevel = max(e[inoise]) * nfac # minimum adjusted number of samples over minimum signal level - minnum = len(isignal) * minpercent / 100 + minnum = len(isignal) * minpercent/100 # get number of samples above minimum adjusted signal level numoverthr = len(np.where(e[isignal] >= minsiglevel)[0]) if numoverthr >= minnum: - print 'checksignallength: Signal reached required length.' + print 'checksignallength: Signal reached required length.' returnflag = 1 else: print 'checksignallength: Signal shorter than required minimum signal length!' @@ -640,18 +616,17 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): if iplot == 2: plt.figure(iplot) - p1, = plt.plot(t, x, 'k') + p1, = plt.plot(t,x, 'k') p2, = plt.plot(t[inoise], e[inoise], 'c') - p3, = plt.plot(t[isignal], e[isignal], 'r') + p3, = plt.plot(t[isignal],e[isignal], 'r') p2, = plt.plot(t[inoise], e[inoise]) - p3, = plt.plot(t[isignal], e[isignal], 'r') - p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal) - 1]]], \ - [minsiglevel, minsiglevel], 'g') + p3, = plt.plot(t[isignal],e[isignal], 'r') + p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ + [minsiglevel, minsiglevel], 'g') p5, = plt.plot([pick, pick], [min(x), max(x)], 'b', linewidth=2) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Envelope Noise Window', \ - 'Envelope Signal Window', - 'Minimum Signal Level', \ - 'Onset'], loc='best') + 'Envelope Signal Window', 'Minimum Signal Level', \ + 'Onset'], loc='best') plt.xlabel('Time [s] since %s' % X[0].stats.starttime) plt.ylabel('Counts') plt.title('Check for Signal Length, Station %s' % X[0].stats.station) @@ -665,7 +640,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): def checkPonsets(pickdic, dttolerance, iplot): ''' - Function to check statistics of P-onset times: Control deviation from + Function to check statistics of P-onset times: Control deviation from median (maximum adjusted deviation = dttolerance) and apply pseudo- bootstrapping jackknife. @@ -687,14 +662,14 @@ def checkPonsets(pickdic, dttolerance, iplot): stations = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4: - # add P onsets to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - Ppicks.append(UTCPpick.timestamp) - stations.append(key) + # add P onsets to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + Ppicks.append(UTCPpick.timestamp) + stations.append(key) # apply jackknife bootstrapping on variance of P onsets print 'checkPonsets: Apply jackknife bootstrapping on P-onset times ...' - [xjack, PHI_pseudo, PHI_sub] = jackknife(Ppicks, 'VAR', 1) + [xjack,PHI_pseudo,PHI_sub] = jackknife(Ppicks, 'VAR', 1) # get pseudo variances smaller than average variances # these picks passed jackknife test ij = np.where(PHI_pseudo <= xjack) @@ -713,41 +688,40 @@ def checkPonsets(pickdic, dttolerance, iplot): badstations = np.array(stations)[ibad] print 'checkPonset: Skipped %d P onsets out of %d' % (len(badstations) \ - + len(badjkstations), - len(stations)) + + len(badjkstations), len(stations)) goodmarker = 'goodPonsetcheck' badmarker = 'badPonsetcheck' badjkmarker = 'badjkcheck' for i in range(0, len(goodstations)): # mark P onset as checked and keep P weight - pickdic[goodstations[i]]['P']['marked'] = goodmarker + pickdic[goodstations[i]]['P']['marked'] = goodmarker for i in range(0, len(badstations)): - # mark P onset and downgrade P weight to 9 - # (not used anymore) - pickdic[badstations[i]]['P']['marked'] = badmarker - pickdic[badstations[i]]['P']['weight'] = 9 + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badstations[i]]['P']['marked'] = badmarker + pickdic[badstations[i]]['P']['weight'] = 9 for i in range(0, len(badjkstations)): - # mark P onset and downgrade P weight to 9 - # (not used anymore) - pickdic[badjkstations[i]]['P']['marked'] = badjkmarker - pickdic[badjkstations[i]]['P']['weight'] = 9 + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badjkstations[i]]['P']['marked'] = badjkmarker + pickdic[badjkstations[i]]['P']['weight'] = 9 checkedonsets = pickdic iplot = 2 if iplot > 1: - p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) + p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) p2, = plt.plot(igood, np.array(Ppicks)[igood], 'g*', markersize=14) p3, = plt.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', \ - linewidth=2) + linewidth=2) for i in range(0, len(Ppicks)): - plt.text(i, Ppicks[i] + 0.2, stations[i]) + plt.text(i, Ppicks[i] + 0.2, stations[i]) - plt.xlabel('Number of P Picks') + plt.xlabel('Number of P Picks') plt.ylabel('Onset Time [s] from 1.1.1970') plt.legend([p1, p2, p3], ['Skipped P Picks', 'Good P Picks', 'Median'], \ - loc='best') + loc='best') plt.title('Check P Onsets') plt.show() raw_input() @@ -773,7 +747,7 @@ def jackknife(X, phi, h): : param: h, size of subgroups, optinal, default = 1 : type: integer ''' - + PHI_jack = None PHI_pseudo = None PHI_sub = None @@ -782,44 +756,145 @@ def jackknife(X, phi, h): g = len(X) / h if type(g) is not int: - print 'jackknife: Cannot divide quantity X in equal sized subgroups!' + print 'jackknife: Cannot divide quantity X in equal sized subgroups!' print 'Choose another size for subgroups!' return PHI_jack, PHI_pseudo, PHI_sub else: - # estimator of undisturbed spot check - if phi == 'MEA': - phi_sc = np.mean(X) + # estimator of undisturbed spot check + if phi == 'MEA': + phi_sc = np.mean(X) elif phi == 'VAR': - phi_sc = np.var(X) + phi_sc = np.var(X) elif phi == 'MED': - phi_sc = np.median(X) + phi_sc = np.median(X) - # estimators of subgroups + # estimators of subgroups PHI_pseudo = [] PHI_sub = [] for i in range(0, g - 1): - # subgroup i, remove i-th sample - xx = X[:] - del xx[i] - # calculate estimators of disturbed spot check - if phi == 'MEA': - phi_sub = np.mean(xx) - elif phi == 'VAR': - phi_sub = np.var(xx) - elif phi == 'MED': - phi_sub = np.median(xx) - - PHI_sub.append(phi_sub) - # pseudo values - phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) - PHI_pseudo.append(phi_pseudo) + # subgroup i, remove i-th sample + xx = X[:] + del xx[i] + # calculate estimators of disturbed spot check + if phi == 'MEA': + phi_sub = np.mean(xx) + elif phi == 'VAR': + phi_sub = np.var(xx) + elif phi == 'MED': + phi_sub = np.median(xx) + + PHI_sub.append(phi_sub) + # pseudo values + phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) + PHI_pseudo.append(phi_pseudo) # jackknife estimator PHI_jack = np.mean(PHI_pseudo) return PHI_jack, PHI_pseudo, PHI_sub +def checkZ4S(X, pick, zfac, checkwin, iplot): + ''' + Function to compare energy content of vertical trace with + energy content of horizontal traces to detect spuriously + picked S onsets instead of P onsets. Usually, P coda shows + larger longitudal energy on vertical trace than on horizontal + traces, where the transversal energy is larger within S coda. + Be careful: there are special circumstances, where this is not + the case! + + : param: X, fitered(!) time series, three traces + : type: `~obspy.core.stream.Stream` + + : param: pick, initial (AIC) P onset time + : type: float + + : param: zfac, factor for threshold determination, + vertical energy must exceed coda level times zfac + to declare a pick as P onset + : type: float + + : param: checkwin, window length [s] for calculating P-coda + energy content + : type: float + + : param: iplot, if iplot > 1, energy content and threshold + are shown + : type: int + ''' + + assert isinstance(X, Stream), "%s is not a stream object" % str(X) + + print 'Check for spuriously picked S onset instead of P onset ...' + + returnflag = 0 + + # split components + zdat = X.select(component="Z") + edat = X.select(component="E") + if len(edat) == 0: # check for other components + edat = X.select(component="2") + ndat = X.select(component="N") + if len(ndat) == 0: # check for other components + ndat = X.select(component="1") + + + z = zdat[0].data + tz = np.arange(0, zdat[0].stats.npts / zdat[0].stats.sampling_rate, + zdat[0].stats.delta) + + # calculate RMS trace from vertical component + absz = np.sqrt(np.power(z, 2)) + # calculate RMS trace from both horizontal traces + # make sure, both traces have equal lengths + lene = len(edat[0].data) + lenn = len(ndat[0].data) + minlen = min([lene, lenn]) + absen = np.sqrt(np.power(edat[0].data[0:minlen - 1], 2) \ + + np.power(ndat[0].data[0:minlen - 1], 2)) + + # get signal window + isignal = getsignalwin(tz, pick, checkwin) + + # calculate energy levels + zcodalevel = max(absz[isignal]) + encodalevel = max(absen[isignal]) + + # calculate threshold + minsiglevel = encodalevel * zfac + + # vertical P-coda level must exceed horizontal P-coda level + # zfac times encodalevel + if zcodalevel < minsiglevel: + print 'checkZ4S: Maybe S onset? Skip this P pick!' + else: + print 'checkZ4S: P onset passed checkZ4S test!' + returnflag = 1 + + if iplot > 1: + te = np.arange(0, edat[0].stats.npts / edat[0].stats.sampling_rate, + edat[0].stats.delta) + tn = np.arange(0, ndat[0].stats.npts / ndat[0].stats.sampling_rate, + ndat[0].stats.delta) + plt.plot(tz, z / max(z), 'k') + plt.plot(tz[isignal], z[isignal] / max(z), 'r') + plt.plot(te, edat[0].data / max(edat[0].data) + 1, 'k') + plt.plot(te[isignal], edat[0].data[isignal] / max(edat[0].data) + 1, 'r') + plt.plot(tn, ndat[0].data / max(ndat[0].data) + 2, 'k') + plt.plot(tn[isignal], ndat[0].data[isignal] / max(ndat[0].data) + 2, 'r') + plt.plot([tz[isignal[0]], tz[isignal[len(isignal) - 1]]], \ + [minsiglevel / max(z), minsiglevel / max(z)], 'g', \ + linewidth=2) + plt.xlabel('Time [s] since %s' % zdat[0].stats.starttime) + plt.ylabel('Normalized Counts') + plt.yticks([0, 1, 2], [zdat[0].stats.channel, edat[0].stats.channel, \ + ndat[0].stats.channel]) + plt.title('CheckZ4S, Station %s' % zdat[0].stats.station) + plt.show() + raw_input() + + return returnflag + if __name__ == '__main__': import doctest - doctest.testmod() From 82d9d45abf85a80b5dcd9f01b5d4da8ece4a7b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 1 Jul 2015 15:37:07 +0200 Subject: [PATCH 0450/1144] Marginal changes. --- pylot/core/pick/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 4e1c9ca3..3f3a91b7 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -528,7 +528,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - iplot=2 + # plot results if iplot > 1: plt.figure(iplot) @@ -709,7 +709,7 @@ def checkPonsets(pickdic, dttolerance, iplot): checkedonsets = pickdic - iplot = 2 + if iplot > 1: p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) p2, = plt.plot(igood, np.array(Ppicks)[igood], 'g*', markersize=14) From 8282a7aa12b5e8ffa081be665b2ac095f5794336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 2 Jul 2015 09:23:51 +0200 Subject: [PATCH 0451/1144] Marginal changes. --- pylot/core/pick/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 3f3a91b7..8868b462 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -611,9 +611,9 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): returnflag = 1 else: print 'checksignallength: Signal shorter than required minimum signal length!' - print 'Presumably picked picked noise peak, pick is rejected!' + print 'Presumably picked noise peak, pick is rejected!' returnflag = 0 - + if iplot == 2: plt.figure(iplot) p1, = plt.plot(t,x, 'k') @@ -709,7 +709,6 @@ def checkPonsets(pickdic, dttolerance, iplot): checkedonsets = pickdic - if iplot > 1: p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) p2, = plt.plot(igood, np.array(Ppicks)[igood], 'g*', markersize=14) From 0d8b6b444663ce8b7747dc62670e83bed14fe972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 2 Jul 2015 09:26:05 +0200 Subject: [PATCH 0452/1144] New key in pick dictionary: marker, indicating if pick has been rejected by checksignallength or checkZ4S. --- pylot/core/pick/run_autopicking.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 94ea1637..3295785d 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -102,6 +102,7 @@ def run_autopicking(wfstream, pickparam): aicPflag = 0 Pflag = 0 Sflag = 0 + Pmarker = [] # split components zdat = wfstream.select(component="Z") @@ -185,6 +186,12 @@ def run_autopicking(wfstream, pickparam): zne += trH2_filt Pflag = checkZ4S(zne, aicpick.getpick(), zfac, \ tsnrz[3], iplot) + if Pflag == 0: + Pmarker = 'SinsteadP' + Pweight = 9 + else: + Pmarker = 'shortsignallength' + Pweight = 9 ############################################################## # go on with processing if AIC onset passes quality control if (aicpick.getSlope() >= minAICPslope and @@ -631,10 +638,12 @@ def run_autopicking(wfstream, pickparam): phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, \ 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM} picks = {phase: phasepick} + # add P marker + picks[phase]['marked'] = Pmarker # add S phase phase = 'S' phasepick = {'lpp': lpickS, 'epp': epickS, 'mpp': mpickS, 'spe': Serror, \ 'snr': SNRS, 'snrdb': SNRSdB, 'weight': Sweight, 'fm': None} picks[phase] = phasepick - + return picks From a32f6fc0373d911a1a187afd9c13bf0a890ac1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 2 Jul 2015 09:27:11 +0200 Subject: [PATCH 0453/1144] Marginal changes. --- autoPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 9bc28f38..ca32f828 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -13,7 +13,7 @@ from pylot.core.read import Data, AutoPickParameter from pylot.core.pick.run_autopicking import run_autopicking from pylot.core.util.structure import DATASTRUCTURE from pylot.core.pick.utils import wadaticheck, checkPonsets -import pdb + __version__ = _getVersionString() From e99d2630a143e134737cbb685633eea132cc183d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Jul 2015 10:29:50 +0200 Subject: [PATCH 0454/1144] moved static methods as functions to the util package for flexibility reasons --- pylot/core/util/utils.py | 55 ++++++++++++++++++++++++++++++++++++++ pylot/core/util/widgets.py | 35 +++--------------------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 3f2ba611..7b409351 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,6 +10,7 @@ import hashlib import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope +from pylot.core.pick.utils import getnoisewin def runProgram(cmd, parameter=None): """ @@ -87,6 +88,60 @@ def prepTimeAxis(stime, trace): return time_ax +def scaleWFData(data, factor=None, components='all'): + """ + produce scaled waveforms from given waveform data and a scaling factor, + waveform may be selected by their components name + :param data: waveform data to be scaled + :type data: `~obspy.core.stream.Stream` object + :param factor: scaling factor + :type factor: float + :param components: components labels for the traces in data to be scaled by + the scaling factor (optional, default: 'all') + :type components: tuple + :return: scaled waveform data + :rtype: `~obspy.core.stream.Stream` object + """ + if components is not 'all': + for comp in components: + if factor is None: + max_val = np.max(np.abs(data.select(component=comp)[0].data)) + data.select(component=comp)[0].data /= 2 * max_val + else: + data.select(component=comp)[0].data /= 2 * factor + else: + for tr in data: + if factor is None: + max_val = float(np.max(np.abs(tr.data))) + tr.data /= 2 * max_val + else: + tr.data /= 2 * factor + + return data + +def demeanWFData(data, t0, win, gap): + """ + returns the DATA where each trace is demean by the average value within + a desired time window WIN before T0 and a GAP + :param data: waveform stream object + :type data: `~obspy.core.stream.Stream` + :param t0: time before which the noise + :type t0: float + :param win: time window for average calculation + :type win: tuple + :param gap: gap window to avoid polluting the average + :type gap: float + :return: data + :rtype: `~obspy.core.stream.Stream` + """ + starttime = getGlobalTimes(data)[0] + for tr in data: + stime = tr.stats.starttime - starttime + t = prepTimeAxis(stime, tr) + inoise = getnoisewin(t, t0, win, gap) + tr.data -= tr.data[inoise].mean() + return data + def getGlobalTimes(stream): min_start = UTCDateTime() max_end = None diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 2b718f8d..8bb13511 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -27,7 +27,8 @@ from pylot.core.read import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ getResolutionWindow from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS -from pylot.core.util import prepTimeAxis, getGlobalTimes +from pylot.core.util import prepTimeAxis, getGlobalTimes, scaleWFData, \ + demeanWFData def createAction(parent, text, slot=None, shortcut=None, icon=None, @@ -386,40 +387,10 @@ class PickDlg(QDialog): wfdata = self.getWFData().select(component=component) return wfdata - @staticmethod - def demeanWFData(data, t0, win, gap): - """ - returns the DATA where each trace is demean by the average value within - a desired time window WIN before T0 and a GAP - :param data: waveform stream object - :type data: `~obspy.core.stream.Stream` - :param t0: time before which the noise - :type t0: float - :param win: time window for average calculation - :type win: tuple - :param gap: gap window to avoid polluting the average - :type gap: float - :return: data - :rtype: `~obspy.core.stream.Stream` - """ - starttime = getGlobalTimes(data)[0] - for tr in data: - stime = tr.stats.starttime - starttime - t = prepTimeAxis(stime, tr) - inoise = getnoisewin(t, t0, win, gap) - tr.data -= tr.data[inoise].mean() - return data - def getPicks(self): return self.picks def setIniPick(self, gui_event): - if self.selectPhase.currentText().upper().startswith('P'): - self.setIniPickP(gui_event) - elif self.selectPhase.currentText().upper().startswith('S'): - self.setIniPickS(gui_event) - - def setIniPickP(self, gui_event): trace_number = round(gui_event.ydata) @@ -448,7 +419,7 @@ class PickDlg(QDialog): x_res = getResolutionWindow(snr) - data = self.demeanWFData(data=self.getWFData().copy(), t0=ini_pick, + data = demeanWFData(data=self.getWFData().copy(), t0=ini_pick, win=noise_win, gap=gap_win) self.setXLims([ini_pick - x_res, ini_pick + x_res]) From f77ba344c3a7b3245d46ff337e317c5465cc303a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Jul 2015 10:31:39 +0200 Subject: [PATCH 0455/1144] make the implementation of the picking dialog widget initialization method better readable --- pylot/core/util/widgets.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8bb13511..bda8e745 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -210,7 +210,6 @@ class PickDlg(QDialog): self.updateCurrentLimits() # set plot labels - self.setPlotLabels() # connect button press event to an action @@ -228,11 +227,9 @@ class PickDlg(QDialog): # create icons filter_icon = QIcon() filter_icon.addPixmap(QPixmap(':/icons/filter.png')) - zoom_icon = QIcon() zoom_icon.addPixmap(QPixmap(':/icons/zoom.png')) - # create actions self.filterAction = createAction(parent=self, text='Filter', slot=self.filterWFData, @@ -240,43 +237,47 @@ class PickDlg(QDialog): tip='Toggle filtered/original' ' waveforms', checkable=True) - self.selectPhase = QComboBox() - phaseitems = [None] + FILTERDEFAULTS.keys() - self.selectPhase.addItems(phaseitems) - self.zoomAction = createAction(parent=self, text='Zoom', slot=self.zoom, icon=zoom_icon, tip='Zoom into waveform', checkable=True) + # create other widget elements + self.selectPhase = QComboBox() + phaseitems = [None] + FILTERDEFAULTS.keys() + self.selectPhase.addItems(phaseitems) + # layout the outermost appearance of the Pick Dialog _outerlayout = QVBoxLayout() _dialtoolbar = QToolBar() # fill toolbar with content - _dialtoolbar.addAction(self.filterAction) _dialtoolbar.addWidget(self.selectPhase) _dialtoolbar.addAction(self.zoomAction) + # layout the innermost widget _innerlayout = QVBoxLayout() - _innerlayout.addWidget(self.multicompfig) + + # add button box to the dialog _buttonbox = QDialogButtonBox(QDialogButtonBox.Apply | QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + # merge widgets and layouts to establish the dialog _innerlayout.addWidget(_buttonbox) - _outerlayout.addWidget(_dialtoolbar) _outerlayout.addLayout(_innerlayout) + # connect widget element signals with slots (methods to the dialog + # object self.selectPhase.currentIndexChanged.connect(self.verifyPhaseSelection) - _buttonbox.accepted.connect(self.accept) _buttonbox.rejected.connect(self.reject) _buttonbox.button(QDialogButtonBox.Apply).clicked.connect(self.apply) + # finally layout the entire dialog self.setLayout(_outerlayout) def disconnectPressEvent(self): From a46b0db583500096f6960ba6319028b44800fb85 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Jul 2015 10:32:35 +0200 Subject: [PATCH 0456/1144] new method to return the traceID for given channel names --- pylot/core/util/widgets.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index bda8e745..8263f6ef 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -345,6 +345,16 @@ class PickDlg(QDialog): def getChannelID(self, key): return self.getPlotWidget().getPlotDict()[int(key)][1] + def getTraceID(self, channels): + plotDict = self.getPlotWidget().getPlotDict() + traceIDs = [] + for channel in channels: + channel = channel.upper() + for traceID, channelID in plotDict.iteritems(): + if channelID[1].upper().endswith(channel): + traceIDs.append(traceID) + return traceIDs + def getFilterOptions(self, phase): options = self.filteroptions[phase] return FilterOptions(**options) From d05e9320e512ae302114f03543c00770277fe50d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Jul 2015 10:35:36 +0200 Subject: [PATCH 0457/1144] make MPLWidget able to distinguish scaled and unscaled data; additionally make plotting noise levels more convenient --- pylot/core/util/widgets.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8263f6ef..53ee5cac 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -88,7 +88,7 @@ class MPLWidget(FigureCanvas): self._parent = parent def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, - noiselevel=None): + noiselevel=None, scaleddata=False): self.getAxes().cla() self.clearPlotDict() wfstart, wfend = getGlobalTimes(wfdata) @@ -99,13 +99,13 @@ class MPLWidget(FigureCanvas): print(msg) stime = trace.stats.starttime - wfstart time_ax = prepTimeAxis(stime, trace) - trace.normalize(np.max(np.abs(trace.data)) * 2) + if not scaleddata: + trace.normalize(np.max(np.abs(trace.data)) * 2) self.getAxes().plot(time_ax, trace.data + n, 'k') if noiselevel is not None: - self.getAxes().plot([time_ax[0], time_ax[-1]], - [noiselevel[0], noiselevel[0]], '--k') - self.getAxes().plot([time_ax[0], time_ax[-1]], - [noiselevel[1], noiselevel[1]], '--k') + for level in noiselevel: + self.getAxes().plot([time_ax[0], time_ax[-1]], + [level, level], '--k') xlabel = 'seconds since {0}'.format(wfstart) ylabel = '' self.updateWidget(xlabel, ylabel, title) From 5e6173b35196dcb78e86c0bb1f344b68b15f0ae7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Jul 2015 10:36:33 +0200 Subject: [PATCH 0458/1144] [bugfix] fixes an issue where the zoom action is not reset when setting the initial pick --- pylot/core/util/widgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 53ee5cac..95ca9613 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -320,6 +320,8 @@ class PickDlg(QDialog): phase = self.selectPhase.currentText() self.updateCurrentLimits() if phase: + if self.zoomAction.isChecked(): + self.zoomAction.toggle() self.disconnectReleaseEvent() self.disconnectScrollEvent() self.disconnectMotionEvent() From a5c1f68f03d3cbb8e47594aa8ff3d6c49c23e1cf Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Jul 2015 10:37:31 +0200 Subject: [PATCH 0459/1144] [bugfix] draw all picked phases not only the actually picked one --- pylot/core/util/widgets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 95ca9613..27d4de4c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -506,10 +506,9 @@ class PickDlg(QDialog): oepp=oepp, ompp=ompp, olpp=olpp) - self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) - self.drawPicks(phase) + self.drawPicks() self.disconnectPressEvent() self.zoomAction.setEnabled(True) self.selectPhase.setCurrentIndex(-1) From 97aaa0b0d39ac65bdd6cf1c6658627e546878996 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Jul 2015 10:40:01 +0200 Subject: [PATCH 0460/1144] convenience imports and implementation of initial pick set for S phases (including zooming to re-scaled horizontal data) --- pylot/core/util/__init__.py | 2 +- pylot/core/util/widgets.py | 56 +++++++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index d2cc70cb..07538df1 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -4,7 +4,7 @@ from pylot.core.util.errors import OptionsError, FormatError, DatastructureError from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ getHash, getLogin, createCreationInfo, createResourceID, prepTimeAxis, \ - getGlobalTimes + getGlobalTimes, scaleWFData, demeanWFData from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ PropertiesDlg, NewEventDlg, MPLWidget, createAction from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 27d4de4c..d0011911 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -416,6 +416,19 @@ class PickDlg(QDialog): self.disconnectMotionEvent() self.cidpress = self.connectPressEvent(self.setPick) + if self.selectPhase.currentText().upper().startswith('P'): + self.setIniPickP(gui_event, wfdata, trace_number) + elif self.selectPhase.currentText().upper().startswith('S'): + self.setIniPickS(gui_event, wfdata) + + self.zoomAction.setEnabled(False) + + # reset labels + self.setPlotLabels() + self.draw() + + def setIniPickP(self, gui_event, wfdata, trace_number): + ini_pick = gui_event.xdata settings = QSettings() @@ -446,20 +459,51 @@ class PickDlg(QDialog): noiselevel=(trace_number + noiselevel, trace_number - noiselevel)) - self.zoomAction.setEnabled(False) + def setIniPickS(self, gui_event, wfdata): - # reset labels - self.setPlotLabels() - self.draw() + ini_pick = gui_event.xdata - def setIniPickS(self, gui_event): settings = QSettings() nfac = settings.value('picking/nfac_P', 1.5) noise_win = settings.value('picking/noise_win_P', 5.) gap_win = settings.value('picking/gap_win_P', .2) signal_win = settings.value('picking/signal_win_P', 3.) - pass + + result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick) + + snr = result[0] + noiselevel = result[2] * nfac + + data = self.getWFData().copy() + + phase = self.selectPhase.currentText() + filteroptions = self.getFilterOptions(phase).parseFilterOptions() + data.filter(**filteroptions) + + data = demeanWFData(data=data, t0=ini_pick, win=noise_win, gap=gap_win) + + horiz_comp = ('n', 'e') + + data = scaleWFData(data, noiselevel * 2.5, horiz_comp) + + x_res = getResolutionWindow(snr) + + self.setXLims(tuple([ini_pick - x_res, ini_pick + x_res])) + traces = self.getTraceID(horiz_comp) + traces.sort() + self.setYLims(tuple(np.array([-0.5, +0.5]) + + np.array(traces))) + noiselevels = [trace + 1 / (2.5 * 2) for trace in traces] +\ + [trace - 1 / (2.5 * 2) for trace in traces] + + self.getPlotWidget().plotWFData(wfdata=data, + title=self.getStation() + + ' picking mode', + zoomx=self.getXLims(), + zoomy=self.getYLims(), + noiselevel=noiselevels, + scaleddata=True) def setPick(self, gui_event): From b73f9fab45487cf644fca6d89f811bc3a3515724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 2 Jul 2015 15:26:48 +0200 Subject: [PATCH 0461/1144] Cosmetics. --- autoPyLoT.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index ca32f828..2c816516 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -35,13 +35,14 @@ def autoPyLoT(inputfile): print '*********autoPyLoT starting*********' print 'The Python picking and Location Tool' print ' Version ', _getVersionString(), '2015' - print '**Authors:' - print '**S. Wehling-Benatelli' - print '** Ruhr-University Bochum' - print '**L. Kueperkoch' - print '** BESTEC GmbH' - print '**K. Olbert' - print '** Christian-Albrechts University Kiel' + print ' ' + print 'Authors:' + print 'S. Wehling-Benatelli' + print ' Ruhr-Universität Bochum' + print 'L. Küperkoch' + print ' BESTEC GmbH, Landau (Pfalz)' + print 'K. Olbert' + print ' Christian-Albrechts Universität Kiel' print '************************************' # reading parameter file @@ -106,7 +107,7 @@ def autoPyLoT(inputfile): allonsets[station] = picks # quality control - # median check and jackknife on P onset times + # median check and jackknife on P-onset times checkedonsetsjk = checkPonsets(allonsets, mdttolerance, iplot) # check S-P times (Wadati) checkedonsetwd = wadaticheck(checkedonsetsjk, wdttolerance, iplot) @@ -146,7 +147,7 @@ def autoPyLoT(inputfile): allonsets[station] = picks # quality control - # median check and jackknife on P onset times + # median check and jackknife on P-onset times checkedonsetsjk = checkPonsets(allonsets, mdttolerance, iplot) # check S-P times (Wadati) checkedonsetswd = wadaticheck(checkedonsetsjk, wdttolerance, iplot) @@ -155,6 +156,7 @@ def autoPyLoT(inputfile): print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' + print '####################################' print '************************************' print '*********autoPyLoT terminates*******' print 'The Python picking and Location Tool' From a08c9e91e0c431b97a841038f305ddaca7e5f49d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 6 Jul 2015 09:51:59 +0200 Subject: [PATCH 0462/1144] Ludger reported a Qt Problem that the MPLWidget was not recognized as an PySide.QtGui.QWidget; the problem was not reproducible on my system; maybe the problem was caused because getnoisewin is now imported from pick/utils and there matplotlib is imported for plotting reasons but to clarify the Qt backend used by matplotlib, it should not be imported before --- QtPyLoT.py | 4 ++++ pylot/core/util/widgets.py | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a63153d5..9fc6c8fd 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -24,6 +24,10 @@ https://www.iconfinder.com/iconsets/flavour """ import sys +import matplotlib + +matplotlib.use('Qt4Agg') +matplotlib.rcParams['backend.qt4'] = 'PySide' from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ QFileInfo, Qt diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d0011911..04911f54 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -7,10 +7,6 @@ Created on Wed Mar 19 11:27:35 2014 import datetime import numpy as np -import matplotlib - -matplotlib.use('Qt4Agg') -matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvas From 76f04bec6f86f8f8761c5454ef35cddce7c8fd51 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 6 Jul 2015 10:20:25 +0200 Subject: [PATCH 0463/1144] [task] starting method to make PyLoT capable of restituting waveform data with given inventory xml files --- pylot/core/read/data.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 95f3ee63..dd54f51b 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -4,7 +4,7 @@ import os from obspy.core import (read, Stream, UTCDateTime) -from obspy import readEvents +from obspy import readEvents, read_inventory from obspy.core.event import (Event, Catalog) from pylot.core.read import readPILOTEvent @@ -146,6 +146,13 @@ class Data(object): self.wfdata = self.getOriginalWFData().copy() self.dirty = False + def restituteWFData(self, fninventory): + st = self.getWFData() + inv = read_inventory(fninventory) + st.attach_response(inv) + pre_filt = (0.005, 0.006, 30.0, 35.0) # set in autoPyLoT.in + st.remove_response(output='VEL', pre_filt=pre_filt) + def getEvtData(self): return self.evtdata From 29de650b4e7528349f4b4f0a101f930abf79951c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 6 Jul 2015 10:57:34 +0200 Subject: [PATCH 0464/1144] reformatting code to avoid mixing up whitespace and tabulator characters --- pylot/core/pick/run_autopicking.py | 331 +++++++++++++++-------------- 1 file changed, 174 insertions(+), 157 deletions(-) diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/run_autopicking.py index 3295785d..27c7b411 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/run_autopicking.py @@ -14,6 +14,7 @@ import numpy as np from pylot.core.pick.Picker import * from pylot.core.pick.CharFuns import * + def run_autopicking(wfstream, pickparam): """ :param: wfstream @@ -81,22 +82,22 @@ def run_autopicking(wfstream, pickparam): # parameter to check for spuriously picked S onset zfac = pickparam.getParam('zfac') - # initialize output - Pweight = 4 # weight for P onset - Sweight = 4 # weight for S onset - FM = 'N' # first motion (polarity) - SNRP = None # signal-to-noise ratio of P onset - SNRPdB = None # signal-to-noise ratio of P onset [dB] - SNRS = None # signal-to-noise ratio of S onset - SNRSdB = None # signal-to-noise ratio of S onset [dB] - mpickP = None # most likely P onset - lpickP = None # latest possible P onset - epickP = None # earliest possible P onset - mpickS = None # most likely S onset - lpickS = None # latest possible S onset - epickS = None # earliest possible S onset - Perror = None # symmetrized picking error P onset - Serror = None # symmetrized picking error S onset + # initialize output + Pweight = 4 # weight for P onset + Sweight = 4 # weight for S onset + FM = 'N' # first motion (polarity) + SNRP = None # signal-to-noise ratio of P onset + SNRPdB = None # signal-to-noise ratio of P onset [dB] + SNRS = None # signal-to-noise ratio of S onset + SNRSdB = None # signal-to-noise ratio of S onset [dB] + mpickP = None # most likely P onset + lpickP = None # latest possible P onset + epickP = None # earliest possible P onset + mpickS = None # most likely S onset + lpickS = None # latest possible S onset + epickS = None # earliest possible S onset + Perror = None # symmetrized picking error P onset + Serror = None # symmetrized picking error S onset aicSflag = 0 aicPflag = 0 @@ -161,42 +162,45 @@ def run_autopicking(wfstream, pickparam): aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, tsmoothP) ############################################################## if aicpick.getpick() is not None: - # check signal length to detect spuriously picked noise peaks - z_copy[0].data = tr_filt.data - Pflag = checksignallength(z_copy, aicpick.getpick(), tsnrz, minsiglength, \ - nfacsl, minpercent, iplot) - if Pflag == 1: - # check for spuriously picked S onset - # both horizontal traces needed - if len(ndat) == 0 or len(edat) == 0: - print 'One or more horizontal components missing!' - print 'Skip control function checkZ4S.' - else: - # filter and taper horizontal traces - trH1_filt = edat.copy() - trH2_filt = ndat.copy() - trH1_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], \ - zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph1[0], freqmax=bph1[1], \ - zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - zne = z_copy - zne += trH1_filt - zne += trH2_filt - Pflag = checkZ4S(zne, aicpick.getpick(), zfac, \ - tsnrz[3], iplot) - if Pflag == 0: - Pmarker = 'SinsteadP' - Pweight = 9 + # check signal length to detect spuriously picked noise peaks + z_copy[0].data = tr_filt.data + Pflag = checksignallength(z_copy, aicpick.getpick(), tsnrz, + minsiglength, \ + nfacsl, minpercent, iplot) + if Pflag == 1: + # check for spuriously picked S onset + # both horizontal traces needed + if len(ndat) == 0 or len(edat) == 0: + print 'One or more horizontal components missing!' + print 'Skip control function checkZ4S.' else: - Pmarker = 'shortsignallength' + # filter and taper horizontal traces + trH1_filt = edat.copy() + trH2_filt = ndat.copy() + trH1_filt.filter('bandpass', freqmin=bph1[0], + freqmax=bph1[1], \ + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], + freqmax=bph1[1], \ + zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + zne = z_copy + zne += trH1_filt + zne += trH2_filt + Pflag = checkZ4S(zne, aicpick.getpick(), zfac, \ + tsnrz[3], iplot) + if Pflag == 0: + Pmarker = 'SinsteadP' Pweight = 9 + else: + Pmarker = 'shortsignallength' + Pweight = 9 ############################################################## # go on with processing if AIC onset passes quality control if (aicpick.getSlope() >= minAICPslope and - aicpick.getSNR() >= minAICPSNR and - Pflag == 1): + aicpick.getSNR() >= minAICPSNR and + Pflag == 1): aicPflag = 1 print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ (aicpick.getSlope(), aicpick.getSNR()) @@ -232,36 +236,37 @@ def run_autopicking(wfstream, pickparam): mpickP = refPpick.getpick() ############################################################# if mpickP is not None: - # quality assessment - # get earliest and latest possible pick and symmetrized uncertainty - [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, mpickP, iplot) + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, + mpickP, iplot) - # get SNR - [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, mpickP) + # get SNR + [SNRP, SNRPdB, Pnoiselevel] = getSNR(z_copy, tsnrz, mpickP) - # weight P-onset using symmetric error - if Perror <= timeerrorsP[0]: - Pweight = 0 - elif timeerrorsP[0] < Perror <= timeerrorsP[1]: - Pweight = 1 - elif timeerrorsP[1] < Perror <= timeerrorsP[2]: - Pweight = 2 - elif timeerrorsP[2] < Perror <= timeerrorsP[3]: - Pweight = 3 - elif Perror > timeerrorsP[3]: - Pweight = 4 + # weight P-onset using symmetric error + if Perror <= timeerrorsP[0]: + Pweight = 0 + elif timeerrorsP[0] < Perror <= timeerrorsP[1]: + Pweight = 1 + elif timeerrorsP[1] < Perror <= timeerrorsP[2]: + Pweight = 2 + elif timeerrorsP[2] < Perror <= timeerrorsP[3]: + Pweight = 3 + elif Perror > timeerrorsP[3]: + Pweight = 4 - ############################################################## - # get first motion of P onset - # certain quality required - if Pweight <= minfmweight and SNRP >= minFMSNR: - FM = fmpicker(zdat, z_copy, fmpickwin, mpickP, iplot) - else: - FM = 'N' + ############################################################## + # get first motion of P onset + # certain quality required + if Pweight <= minfmweight and SNRP >= minFMSNR: + FM = fmpicker(zdat, z_copy, fmpickwin, mpickP, iplot) + else: + FM = 'N' - print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ - 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) - Sflag = 1 + print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ + 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) + Sflag = 1 else: print 'Bad initial (AIC) P-pick, skip this onset!' @@ -340,7 +345,7 @@ def run_autopicking(wfstream, pickparam): # class needs stream object => build it tr_arhaic = trH1_filt.copy() tr_arhaic.data = arhcf1.getCF() - h_copy[0].data = tr_arhaic.data + h_copy[0].data = tr_arhaic.data # calculate ARH-AIC-CF haiccf = AICcf(h_copy, cuttimesh) # instance of AICcf ############################################################## @@ -351,8 +356,8 @@ def run_autopicking(wfstream, pickparam): ############################################################### # go on with processing if AIC onset passes quality control if (aicarhpick.getSlope() >= minAICSslope and - aicarhpick.getSNR() >= minAICSSNR and - aicarhpick.getpick() is not None): + aicarhpick.getSNR() >= minAICSSNR and + aicarhpick.getpick() is not None): aicSflag = 1 print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ % (aicarhpick.getSlope(), aicarhpick.getSNR()) @@ -405,71 +410,73 @@ def run_autopicking(wfstream, pickparam): mpickS = refSpick.getpick() ############################################################# if mpickS is not None: - # quality assessment - # get earliest and latest possible pick and symmetrized uncertainty - h_copy[0].data = trH1_filt.data - [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, - mpickS, iplot) - h_copy[0].data = trH2_filt.data - [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, - mpickS, iplot) - if algoS == 'ARH': - # get earliest pick of both earliest possible picks - epick = [epickS1, epickS2] - lpick = [lpickS1, lpickS2] - pickerr = [Serror1, Serror2] - if epickS1 == None and epickS2 is not None: - ipick = 1 - elif epickS1 is not None and epickS2 == None: - ipick = 0 - elif epickS1 is not None and epickS2 is not None: - ipick = np.argmin([epickS1, epickS2]) - elif algoS == 'AR3': - [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, + # quality assessment + # get earliest and latest possible pick and symmetrized uncertainty + h_copy[0].data = trH1_filt.data + [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, iplot) - # get earliest pick of all three picks - epick = [epickS1, epickS2, epickS3] - lpick = [lpickS1, lpickS2, lpickS3] - pickerr = [Serror1, Serror2, Serror3] - if epickS1 == None and epickS2 is not None \ - and epickS3 is not None: - ipick = np.argmin([epickS2, epickS3]) - elif epickS1 is not None and epickS2 == None \ - and epickS3 is not None: - ipick = np.argmin([epickS2, epickS3]) - elif epickS1 is not None and epickS2 is not None \ - and epickS3 == None: - ipick = np.argmin([epickS1, epickS2]) - elif epickS1 is not None and epickS2 is not None \ - and epickS3 is not None: - ipick = np.argmin([epickS1, epickS2, epickS3]) - epickS = epick[ipick] - lpickS = lpick[ipick] - Serror = pickerr[ipick] + h_copy[0].data = trH2_filt.data + [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, + tsnrh, + mpickS, iplot) + if algoS == 'ARH': + # get earliest pick of both earliest possible picks + epick = [epickS1, epickS2] + lpick = [lpickS1, lpickS2] + pickerr = [Serror1, Serror2] + if epickS1 == None and epickS2 is not None: + ipick = 1 + elif epickS1 is not None and epickS2 == None: + ipick = 0 + elif epickS1 is not None and epickS2 is not None: + ipick = np.argmin([epickS1, epickS2]) + elif algoS == 'AR3': + [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, + tsnrh, + mpickS, iplot) + # get earliest pick of all three picks + epick = [epickS1, epickS2, epickS3] + lpick = [lpickS1, lpickS2, lpickS3] + pickerr = [Serror1, Serror2, Serror3] + if epickS1 == None and epickS2 is not None \ + and epickS3 is not None: + ipick = np.argmin([epickS2, epickS3]) + elif epickS1 is not None and epickS2 == None \ + and epickS3 is not None: + ipick = np.argmin([epickS2, epickS3]) + elif epickS1 is not None and epickS2 is not None \ + and epickS3 == None: + ipick = np.argmin([epickS1, epickS2]) + elif epickS1 is not None and epickS2 is not None \ + and epickS3 is not None: + ipick = np.argmin([epickS1, epickS2, epickS3]) + epickS = epick[ipick] + lpickS = lpick[ipick] + Serror = pickerr[ipick] - # get SNR - [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, mpickS) + # get SNR + [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, mpickS) - # weight S-onset using symmetric error - if Serror <= timeerrorsS[0]: - Sweight = 0 - elif timeerrorsS[0] < Serror <= timeerrorsS[1]: - Sweight = 1 - elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: - Sweight = 2 - elif timeerrorsS[2] < Serror <= timeerrorsS[3]: - Sweight = 3 - elif Serror > timeerrorsS[3]: - Sweight = 4 + # weight S-onset using symmetric error + if Serror <= timeerrorsS[0]: + Sweight = 0 + elif timeerrorsS[0] < Serror <= timeerrorsS[1]: + Sweight = 1 + elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: + Sweight = 2 + elif timeerrorsS[2] < Serror <= timeerrorsS[3]: + Sweight = 3 + elif Serror > timeerrorsS[3]: + Sweight = 4 - print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( - Sweight, SNRS, SNRSdB) + print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( + Sweight, SNRS, SNRSdB) else: print 'Bad initial (AIC) S-pick, skip this onset!' print 'AIC-SNR=', aicarhpick.getSNR(), \ - 'AIC-Slope=', aicarhpick.getSlope() + 'AIC-Slope=', aicarhpick.getSlope() else: print 'run_autopicking: No horizontal component data available or ' \ @@ -525,7 +532,7 @@ def run_autopicking(wfstream, pickparam): plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') plt.suptitle(tr_filt.stats.starttime) - + if len(edat[0]) > 1 and len(ndat[0]) > 1 and Sflag == 1: # plot horizontal traces plt.subplot(3, 1, 2) @@ -544,20 +551,25 @@ def run_autopicking(wfstream, pickparam): if aicSflag == 1: p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF() / max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], - [-1, 1], 'g') + p24, = plt.plot( + [aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [aicarhpick.getpick() - 0.5, + aicarhpick.getpick() + 0.5], [1, 1], 'g') plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [aicarhpick.getpick() - 0.5, + aicarhpick.getpick() + 0.5], [-1, -1], 'g') p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [-1.3, -1.3], 'g', linewidth=2) + plt.plot( + [refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot( + [refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') plt.legend([p21, p22, p23, p24, p25], @@ -591,20 +603,25 @@ def run_autopicking(wfstream, pickparam): if aicSflag == 1: p23, = plt.plot(arhcf2.getTimeArray(), arhcf2.getCF() / max(arhcf2.getCF()), 'm') - p24, = plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], - [-1, 1], 'g') + p24, = plt.plot( + [aicarhpick.getpick(), aicarhpick.getpick()], + [-1, 1], 'g') plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [aicarhpick.getpick() - 0.5, + aicarhpick.getpick() + 0.5], [1, 1], 'g') plt.plot( - [aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], + [aicarhpick.getpick() - 0.5, + aicarhpick.getpick() + 0.5], [-1, -1], 'g') p25, = plt.plot([refSpick.getpick(), refSpick.getpick()], - [-1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [1.3, 1.3], 'g', linewidth=2) - plt.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], - [-1.3, -1.3], 'g', linewidth=2) + [-1.3, 1.3], 'g', linewidth=2) + plt.plot( + [refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [1.3, 1.3], 'g', linewidth=2) + plt.plot( + [refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], + [-1.3, -1.3], 'g', linewidth=2) plt.plot([lpickS, lpickS], [-1.1, 1.1], 'g--') plt.plot([epickS, epickS], [-1.1, 1.1], 'g--') plt.legend([p21, p22, p23, p24, p25], @@ -623,15 +640,15 @@ def run_autopicking(wfstream, pickparam): ########################################################################## # calculate "real" onset times if mpickP is not None: - lpickP = zdat[0].stats.starttime + lpickP - epickP = zdat[0].stats.starttime + epickP - mpickP = zdat[0].stats.starttime + mpickP + lpickP = zdat[0].stats.starttime + lpickP + epickP = zdat[0].stats.starttime + epickP + mpickP = zdat[0].stats.starttime + mpickP if mpickS is not None: - lpickS = edat[0].stats.starttime + lpickS - epickS = edat[0].stats.starttime + epickS - mpickS = edat[0].stats.starttime + mpickS - + lpickS = edat[0].stats.starttime + lpickS + epickS = edat[0].stats.starttime + epickS + mpickS = edat[0].stats.starttime + mpickS + # create dictionary # for P phase phase = 'P' From 8463a8750718adae65ee7cbdcec56239a196b8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 6 Jul 2015 15:52:25 +0200 Subject: [PATCH 0465/1144] Introduced saftey factor for jackknife test to be less conservative. --- pylot/core/pick/utils.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 8868b462..f442b077 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -13,7 +13,7 @@ import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings - +import pdb def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) @@ -482,6 +482,7 @@ def wadaticheck(pickdic, dttolerance, iplot): # calculate vp/vs ratio before check vpvsr = p1[0] + 1 + print '###############################################' print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr checkedPpicks = [] @@ -519,6 +520,7 @@ def wadaticheck(pickdic, dttolerance, iplot): cvpvsr = p2[0] + 1 print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr else: + print '###############################################' print 'wadatacheck: Not enough checked S-P times available!' print 'Skip Wadati check!' @@ -528,7 +530,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - + iplot=2 # plot results if iplot > 1: plt.figure(iplot) @@ -668,14 +670,16 @@ def checkPonsets(pickdic, dttolerance, iplot): stations.append(key) # apply jackknife bootstrapping on variance of P onsets + print '###############################################' print 'checkPonsets: Apply jackknife bootstrapping on P-onset times ...' [xjack,PHI_pseudo,PHI_sub] = jackknife(Ppicks, 'VAR', 1) # get pseudo variances smaller than average variances - # these picks passed jackknife test - ij = np.where(PHI_pseudo <= xjack) + # (times safety factor), these picks passed jackknife test + ij = np.where(PHI_pseudo <= 2 * xjack) # these picks did not pass jackknife test - badjk = np.where(PHI_pseudo > xjack) + badjk = np.where(PHI_pseudo > 2 * xjack) badjkstations = np.array(stations)[badjk] + print 'checkPonsets: %d picks did not pass jackknife test!' % len(badjkstations) # calculate median from these picks pmedian = np.median(np.array(Ppicks)[ij]) @@ -687,7 +691,8 @@ def checkPonsets(pickdic, dttolerance, iplot): goodstations = np.array(stations)[igood] badstations = np.array(stations)[ibad] - print 'checkPonset: Skipped %d P onsets out of %d' % (len(badstations) \ + print 'checkPonsets: %d picks deviate too much from median!' % len(ibad) + print 'checkPonsets: Skipped %d P onsets out of %d' % (len(badstations) \ + len(badjkstations), len(stations)) goodmarker = 'goodPonsetcheck' @@ -867,7 +872,7 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): if zcodalevel < minsiglevel: print 'checkZ4S: Maybe S onset? Skip this P pick!' else: - print 'checkZ4S: P onset passed checkZ4S test!' + print 'checkZ4S: P onset passes checkZ4S test!' returnflag = 1 if iplot > 1: From b5e279a318e483c38cba1d36b9958cdc21ddbe7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 6 Jul 2015 15:53:56 +0200 Subject: [PATCH 0466/1144] removed fixed iplot flag. --- pylot/core/pick/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index f442b077..a97057d4 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -530,7 +530,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - iplot=2 + # plot results if iplot > 1: plt.figure(iplot) From f99f5dbc8fdaebab6ec22572dd82707ca70f1c5b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:31:39 +0200 Subject: [PATCH 0467/1144] unified icon set used for PyLoT in order to match the license restriction --- QtPyLoT.py | 10 ++++++---- icons.qrc | 16 +++++++++++++--- icons/key_E.png | Bin 0 -> 3958 bytes icons/key_N.png | Bin 0 -> 4054 bytes icons/key_P.png | Bin 0 -> 4004 bytes icons/key_Q.png | Bin 0 -> 4183 bytes icons/key_R.png | Bin 0 -> 4066 bytes icons/key_S.png | Bin 0 -> 4062 bytes icons/key_T.png | Bin 0 -> 3866 bytes icons/key_U.png | Bin 0 -> 3999 bytes icons/key_V.png | Bin 0 -> 4077 bytes icons/key_W.png | Bin 0 -> 4241 bytes icons/key_Z.png | Bin 0 -> 4031 bytes icons/picon.png | Bin 674 -> 0 bytes icons/printer.png | Bin 937 -> 6683 bytes icons/sicon.png | Bin 726 -> 0 bytes icons/zoom.png | Bin 7635 -> 0 bytes icons/zoom_in.png | Bin 0 -> 7132 bytes icons/zoom_minus.png | Bin 7037 -> 0 bytes icons/zoom_out.png | Bin 0 -> 7083 bytes icons_rc.py | 8 ++++---- pylot/core/util/widgets.py | 2 +- 22 files changed, 24 insertions(+), 12 deletions(-) create mode 100755 icons/key_E.png create mode 100755 icons/key_N.png create mode 100755 icons/key_P.png create mode 100755 icons/key_Q.png create mode 100755 icons/key_R.png create mode 100755 icons/key_S.png create mode 100755 icons/key_T.png create mode 100755 icons/key_U.png create mode 100755 icons/key_V.png create mode 100755 icons/key_W.png create mode 100755 icons/key_Z.png delete mode 100644 icons/picon.png delete mode 100644 icons/sicon.png delete mode 100755 icons/zoom.png create mode 100755 icons/zoom_in.png delete mode 100755 icons/zoom_minus.png create mode 100755 icons/zoom_out.png diff --git a/QtPyLoT.py b/QtPyLoT.py index 9fc6c8fd..c621e664 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -133,11 +133,13 @@ class MainWindow(QMainWindow): # create resource icons p_icon = QIcon() - p_icon.addPixmap(QPixmap(':/icons/picon.png')) + p_icon.addPixmap(QPixmap(':/icons/key_P.png')) s_icon = QIcon() - s_icon.addPixmap(QPixmap(':/icons/sicon.png')) + s_icon.addPixmap(QPixmap(':/icons/key_S.png')) print_icon = QIcon() print_icon.addPixmap(QPixmap(':/icons/printer.png')) + filter_icon = QIcon() + filter_icon.addPixmap(QPixmap(':/icons/filter.png')) newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, @@ -166,7 +168,7 @@ class MainWindow(QMainWindow): "Close event and quit PyLoT") self.filterAction = self.createAction(self, "&Filter ...", self.filterWaveformData, - "Ctrl+F", QIcon(":/filter.png"), + "Ctrl+F", filter_icon, """Toggle un-/filtered waveforms to be displayed, according to the desired seismic phase.""", True) @@ -259,7 +261,7 @@ class MainWindow(QMainWindow): if recentEvents: for i, eventID in enumerate(recentEvents): fname = fnConstructor(eventID) - action = QAction(QIcon(":/icon.png"), + action = QAction(self.windowIcon(), "&{0} {1}".format(i + 1, QFileInfo(fname).fileName()), self) diff --git a/icons.qrc b/icons.qrc index 4f9c34a9..5f25e229 100644 --- a/icons.qrc +++ b/icons.qrc @@ -2,11 +2,21 @@ icons/pylot.ico icons/printer.png - icons/picon.png - icons/sicon.png + icons/key_E.png + icons/key_N.png + icons/key_P.png + icons/key_Q.png + icons/key_R.png + icons/key_S.png + icons/key_T.png + icons/key_U.png + icons/key_V.png + icons/key_W.png + icons/key_Z.png icons/pick.png icons/filter.png - icons/zoom.png + icons/zoom_in.png + icons/zoom_out.png help/index.html diff --git a/icons/key_E.png b/icons/key_E.png new file mode 100755 index 0000000000000000000000000000000000000000..eb51a67d5634f40ffe747d03ef541c0d4c6eae37 GIT binary patch literal 3958 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA#=yX+z;$dN2@jVKAuPb(=;EJ|f?Ovz75 zRq)JBOiv9;O-!jQJeg|4z`*q|GbExU!q>+tIX_n~5u`@1BDVmjn}NZ`zM>#8IXksP zAt^OIGtXA({qFrr3YjUkO5vuy2EGN(sTr9bRYj@6RemAKRoTgwDN6QsTs9R}Km&49 zOA-|-a&z*EttxDlz~)*3*&tzkB?YjOl5ATgh@&EW0~DO|i&7O#^bB>A4UEkcOw9C* z%+1V=40IHH^YawkQj-)6EftIm49pdbjI0cetxSv+pg_rvOTnfnCCw_x#SLmBkY}ru zQBqQ1rLSLJUanVete0Puu5V~*X{m2uq;F)T3sj+7T$xvrSfQI&tPC;&Vunj!;?V=BDPA6zd!68R{deuShHi z%1qBFDTZsyD~1L%Fv#_R(m*W+`bA(h5S?%>R=$Zv*{Q$)KeQ6&T_CNdT;XU93Jgny zq)G+8(5=(PRl!Ri4B4h>lf{P2$ZcWVtww=m> zty0fbCJz`GShPJ|978H@mG~I)F&J_2mVE8JyoCn~5k7#UYMeq?@p@=RfQ z2geUZrMv~JES~GE=1MDL-nOe+Jfw(O%50_1#d@ZWi8kT2|Ns59KanKBAbI%Y$&)8o zimx8;mp{0UapOa`#y5>SdD7ImeXLJ3@2~&Q=H0~Lz+iVmf;B;)ZIR$E&Rsj$ZJgij zujVXg|H#zzsnsJl)1JlX$@8brKd-vKc)^^{1?P0PeKNJyYDwU8T3Z^Cz1{KunzkS= z!_7BW8#Ej$$nFjDYL8rbU6{A>y{F5H@B1`g#4nuq*zfXpN5f-N6S*8LMQ=0kG;dhN zU328W@bL}lzT83+rHw?EbTF<-*l;^~*ZQ_UyY% zdt`V{TKX^UT^bbR)u6gzdt}?y$b%{jlW*_8uxN_WzNJ%Acqi;<`7!O-{38SlSY1iMu;F9osLW zAF34DDfDD>^3QYscD!Kz!5n+oW2;(b&JM+mj{I*!Y8e_nODKC@6k$-mlf-tzQuOby zzTGpep6i|~6pJ~c#&B}S3wg$ZhP~zDPCM=&t_$$8e6^4%<6^_2#JdxWjJ`6e|B3l{ zRx)6Z{jnYIEz_q56^W^Ro0Ke*xHeQoM$|#Lz-zgwg8x?g^0Suw7muf%YOuNy6};1f z^~V3bZp9_J#;!*4IN(A5Pu)B)q^j z`*fl}TcZ8rX66FzcmEGd{&>hTd7@Hiq}1#U8+{jD+L9Tf`c8S{-TwU#-e>>geZcnN z@!N$ZS;4}q|E_vHL6Y@eqz?0j0|z%R&)ij#wlMVVUrB*oOdN7eH?;!trz!87IdQez z^NsmF631?ATf2|R_SQVc6B#Ot|Jt5^bwx7ap!+06VTX&Gr|sGDS2XVL3by;zZPpCY zW$#b&9Nyq9=Jt3Kr)I8V>g^l(26}f|&V2Sc@8BZazs_ML-m zwN5`eL+1G3;!4Hju-qxJKFf^x)}1;o#1Jog+h^iS?aBYnM1(RJ28cL_?2$M5b$+?~ zvh|`4%hz;rw48sOu|uOpEq9fGt_p(cIIzaFOfU7q3_2lCQwh< M)78&qol`;+0Na4`*#H0l literal 0 HcmV?d00001 diff --git a/icons/key_N.png b/icons/key_N.png new file mode 100755 index 0000000000000000000000000000000000000000..4a8e81eab6e1d50d8008d612bb01802e08197cd1 GIT binary patch literal 4054 zcmeH~YcyQv8prn-Q4vI%U}(xVqSY`L<2utBcf%;o6j@Xv>0I`hiMhzkW`+=#o+O&M zrs9^Qh}(%&4uTb76(wnAohqTy6++~w5Q?M@a_EV(2i579W1SEEX0Nr^`@H-8KmX_1 zzjuFl_vg$YUp*aD9RL9I7=GTt$VgP(+FAgB0(U9*ArlkWCmaru@L)Mh3IQHm2^#_# zB32X>46(TJr<4#C0Cb)SLc`(kzyJzIBEqv&8oWXjgJc5$)lCt@;zUC*$cCZ>Vj6b7 zwH*rzxHN2-V<0gw#tY&L{1T*4NJ3C3Cn1_c=3?Dkb*KsoVjzNG7N`&j#WIS5hF$li zAhGH(0Sm56;Ak3lqp9#fCg>%RLZAcQ4#y$dJAn>Pc#^Y|Gl^&m21>*r9b$uaWROH8 zI)fw<#m=7MU=MCySXUj8D&_Jh!QS-EipY$H<->3cg+P$Y<#@S0ULuVm*pbO(0+B=@ zk#L9tCyN)uECo(1vs77Zcz8oHj#Ll>3y`8JPZnDe3)8SzWF6cz=W@2ZV`8Pkb!RSz z00|)xB!*=KJG>o1U9Vup1Ph}0uxu+$EZYQ)ASWQpND7f4UDs@6ZlzEHS<+|-0shU6 z-(D9P-?LPBe5is!2HEl8Uxb1h_DWO2cgCb;!qi9$KjtlWvu|gQ@ z`u}uFC8!t+1)EjYXTO!968_M{hE#2PkerQY$hIRN9bxOM+iX&e*PjutbNMrj)jfBL zsMFXs-odpkB6YzIE_E8)#yhySMWim+!KF@P+js}pwusaP{|lGS@8?!XjGUe1$f=Z3 z<1hvQnim<~9-)dMIKWOHu+#ROnO;kt`pNi*J#n+S;GqyecR^F*$hx|YCRcaD zor0F?Z>#qP0Nok8I>0uj>m=atfhQX6SDQyd=l*^1;xP?Ag?NwylP4$2itlE(Df^*4 zRe8s12Ny@5PPcv{d&^I}$9wLq!4nMUg^%0Gqku?|*sy$tSJZc?J+lOeh^~G@tXQru ze`WP5wnXlG&kI<(i8=3q(ZY7`3BQzFXE8C7^CLHfEaB7c`1d?MWz)3ZtHRIaYx~c8 zmuh{SJNEum9C3KA0@ov`Of#Nf+6)>4QZvt#w=;ppZ$xzPn&r~3kv#i`E7b3y1Ew6~ zlqQEIqYl1F-MDCYH05ON;kwl#|M9E0TQ4N#y_Kkl{IM+Wx)bUI`el1n{R1XN|wMntJwO#?$3U(aTwFeu7hSYO|;| z+_m@io%6R6Ryk+CnxTXoI6GU%Us@^lw~b}L)*B+G1PpMkq*@6z>nD~+P(hW)S-1B`i}W73riPmYHHug{|y!n~lbP}fIt`gxDO%dO;r zxEvcWNG3U&ApLgE> z^M9WCz4PInv*BR}HeqeC003+XI>_RnBUyW!7y*DDXsEVCCpJhxG!h{dAPPtZ1HODI z7Y2ipAtB6xA^x#cHOv42>_0`3(MU8ql+KeT6CtgJs7y{lvjKqNtxSP^XH0wof~3$%B!xmjRJakPG9k&8Mx&9) z6cU9(Kotb}F$n@G2@<)z)?&qj1J@xN6Slt$C-U$;8Xf zd>#oF!^yA&k&|4Bt|VQ(5=!BSgb9dzElna{1&tynq049rnIv1*tYogG(AkhI2}Xf; z)c*Rq=;&at^!3!jpn=?D;32Un9hQNS=qB@}3VD)fx%h7uQ7|_Gfsp0y)>`0-r9!j` zzKDmwd_>BV3Is4rbf z>;LJLLDDi7i&m>F&wee#ApKVpD^i{9L337~q1%prbfmSfZna4rUcX1U%;onm*7e*d zqDy1lcmvnEh;#)TxO8c(8*kuR7m==D1D7t1b>j_O>mt$>{4ZSC-_EVD1U)+`&{Jtp z9d!%<3=Rjed?S^AM?zgK09O;g=_yUtdlbRE0#%IkW1v z-N8yWEp+XkeH?M#k#MQ+;YjOVuS<9tr6#ev!Ro21v1k~^7!h*gj;5O1fbEWrgQdw) zlO%`Jd47}XqbeW5qT}J8HX8v&4wAk~)xBqJ2HWB{x0&I3qJZ%_(C{cOcedez-Fq61 z9Vw3S2wQAD^^H--Dc|83x9_O;1|O$8y|Wk_u&VwU*Rk6rMP>H-Waf3Z^3LteKzN;T zJ2(Dz3EWA!=DXyWI)DL2}&MW#1S z!SR8;uiCRehznb^R0tj7v+j>Bz8%UL$~p5Ho#I4d+kWxG%LfrVT+ToEyo+nxlQK3_ zKP@SAX?AWMm34j#6c|I*7F7XY literal 0 HcmV?d00001 diff --git a/icons/key_Q.png b/icons/key_Q.png new file mode 100755 index 0000000000000000000000000000000000000000..6334af3dc45523e85c236608459090ab8bcd75cf GIT binary patch literal 4183 zcmeH~X;f2Z8pm%4U<5@$3qdJf18QX;y9hC1i6BdjBKsmjNG=e`Msh;}mQ_&-h!~(# z8kD8NlsHmwL6MQo1sPOIS!Bdk1Q(zJLhXT(R_3ylGGB%>ANq}Z&OPt*-uM6fpXdJG z`{BKxdmM0CtFuW50D!e_u1=oFNSwVf+5mt8wOaI%i81W#4||EiVL4j@0ghZz7zDZr z*b$H?#OB6Ew?K3N(0R`D_J{ph`)M4J0MDM);1vQfk_`ZKdxeyoVDcXd^<;;MWH1AVL)O6sb1rAmTP&0C=bX75 z0>p;|kPwy>^SU|1}8kk5;OB%n94$y||K8pWF{{)dGh6y^o9;koV>TL|NeB9JDy zJPr(TVG&0Z9u7fxE;L61q;om6`F>`>ETD1Z!}GIwQheSl4;r5>j9}mtR$M5YE#t#D zyZ@(CI$@SEpSMtDZuWZ_I^n;Xn3pcu9wcY}8M5ujM@Lxv>K2+@!t2in=eYbC#!Ed{ ziY%qEY`lVNSwu?(E4Y@@STjC52EiEngHKkn{Mg(lk z=+o8Vw@+*Q>vm&fBfvQh^h|skQvcypxn@nKN~O{Vfsjt2h684=40Z7xYI4h6<2P33 z=uZ@rCycJ%(7iy4t)Chie!NlLebduDNcDH_z6@8G=RFpGOR3j)Zgu1(EuYf|Qnje4 zNQ(4r-uv8p=Ux@CGxgnfe)vdHYe{M@KWY5h_Vu~v`biS9FA94i`I`~k4V$T%XUcUF z1E;6_gI}$`W;NKaq|~)Sy`>%e^Md3DTkrK;Ev0?AvoxNw5{)3UY_o;^3e61r|p&0g*@|rL90va7RcC4Kt%aL0D99oM0B6Wsd`=THy znEBbRe;CpOh9|cHQlD(}Rt(y9$|$kq*L*paV48WyNIcOwk_}UQ=?dc66KOU?=HEJ+ z1LK0-3(W?9)ud*9I*A%j32Pk)-TLHzE_$ zrk);Reva?a9od%2uAjx26pozAFiqO>-L61&kDFJ!&6*Sw_i`FKY2s!@K>8~n($FZ@ zAy3-fJVRC0$b3GR45xCV^^>+Mf7ya^P3oZLm+?HZ*YtPFf(_3E0jv2YE*qFO_16V^ z(me{~=4a9;0M&F|z2Zdy{qhTh_i8#WFo$x7D=`fpiSJ*w zG{jPzUI}^J^=3MOR|k%{4ViGd zDwtoR4ztD>QU4y%=YfF(7qw`$&f*+SYtO3oN!Y4i+Us*iUuEX-@HnhF6V*FmP{g?C zm%m;+MN)7suL!hG$ty~0drp~NZyneZ7&N8Xtci2Htnz7_u4_uVQ&=$i=9M)?)oU-7 z9|P!sDWCy_A1%SYvm4o*4GEyHb=aJBmy}*o7-tyhvm-$-EoXhB0uz2qV4z=Dq&j>!$1c(n~+lQPL82 zX&5{#Yb)N|H(o8c6pF&QJeFB7zuRbiT=XoX_zAsDW%puJ$?>Yhz=q;n>@|;Qbz|u; z9#t7%K*shPcS%$hq;F5L-+oWsmORoHTy(_Xp!9s#1&p zsnuc6Wabv4A-L-0aJ(s@)zf&@5S^9t-s`wt-=>oh0cCN1)H6=(!DF(Nqu4uubO#d6(ZP{MA%gxAF}Mwm2C2>@#%R(T|Qi|JBVkxq#Q75zcV=GmIB{E)`iw zW6^jC*P@6P3YKs!q_Jqcglkbm3k6HK7SdQWUc$8~qJ@J0g$wulxfK?pXD0=ED)qfY z?*)Km$9=un!OAXV2Ne%cu^z)iQz?Tl%%12bjvfVVQfc-|_OF`#>b7`ACU(M_AU=1= zX@Gbc$4}I(s@Z22EYq}fC?P&d(a1c?3`?H+Jn7aNZ3tL2snzP%W%7UD+Be=%in;oH z+kue-H}&|ooo~wa)_?xGWSOr_F1OcMW*89mJYfYE82$XPrA4R8ivL9$WN2z+#LbA% z*O2toO&VM?F2$HWpy-qL)owt;17yd&$ylSDQ;Q(f-A(q(Dc;YTx0X8 z4{vqEx|xUjYzo|RMCn>}1vI1q+M9tUP;a%%2k*(LAh+f`aE}YEyeM)LlIEE1 z|2I~^FLn6g@OJ;pU-k!^@38-*%FI00M0C$?b#W24vZGoxdaK{%q%m`A4SuS+7f80Bylkd@5)tm-gu}5893?_BS>(-_~if zh`Sr_wc3YHrGCX3ygoT{;pfngs~byGGFuAeg;&pA-w%?nnVo)TBa1R_C^5z4jk$j~ zrQ2@Pps^t*Om(G0Rc2xz;jqu9T{fon#11Gn!PSQk7UEA}{)$_^-LEA3rCyxzxtmFv zFD&n`;XRW*i0r;f9Sb^y6ve~78x$wT_dB(1PV49rIHta1@@aca*ACpQ`2vsWEQ_|n za?K0UmOsu&yEel1%+KGI&MxSk$|3%I|EaqEUGR#XIqt45jRUn`XRk?hYu{p%zfo`t z@f-`?-HCR3qYcZVe?dcvC^LzAJjR`A5nlz8MXTflVEViq^^7G-j|rWkVOY!yA|;%`$zu}YtvIk=fc>cMoGRM8Pip%TB*$i zPk)zvitq}b1y0~f>#Fl=)AR7I8G{^Bj$RtxrHfCKoqG*7VijgU>gN63_6hc}`F-@m^g`Mc`%2zT#EfbwG1^DBS`Z0iW$I zQkjGw6_u7pjp?_07h7kj6YpYnB>6yY9hEiLbe#H=*K39VANE)Nkj*-q8{LPMZma!K z^wS=xrk?lCB5Q2m3+Vdmr?5^@Idy!vW+*Nsr0lQ({y)iOkLe)^k$I1)bdBOSK$S;$ zQSa`5h$6N;tSrr{9`8A$Q;Mgk%(WvE*emMJv~C=!8P}%0EYOOTp4^~yowMuM#^kTc z^UOW!Ml$1Xu*@99xi&Rb_uuYLh%El5Kd|3Pb=m8#^@B|v7(b1PBA}z6l;vS}@B8=3 zwmrIsv#Pf|@Bi|63%B5#v^c%?*niP2IfkoACfnDI<+%HB)}JzJq1MQx0C?d1WE*Rh z)2&0vOuG~0!Y1dp4yL#Sn>%Xz9$vO8g0tQ7Xf9+!-A%agy7UhD@3tvekIM7yLpP@X Ox_!3>c$IoYrTrIv_at%v literal 0 HcmV?d00001 diff --git a/icons/key_S.png b/icons/key_S.png new file mode 100755 index 0000000000000000000000000000000000000000..63de7ec77df81d71e13c56e1fa3132d28d39a659 GIT binary patch literal 4062 zcmeH~X;c&E8pnsQqbML{b72}p1QN0kSz^K>5kV7#04-GMB_xwYvOqGB1hoXzg2+-) zK-7bZUJw+nRnQ|SveX4d(Q1oI*%S!Yasi<#73B^~Y3`THxgYwCIcLuMyz~B_|MSf6 zoe%Hq4-VR7Y-D2u0Dy5|0F8-^_^I1K4*;}4-DXo{VhwMMfI}r*Sk96{fGMGw{9 zK!XAf75(Kp2A&b;2k`{~Nm3{@DJYDc6wCJHpuN3}C<-!SAc9~Ps1OOoGO~h-p7tdp z@zi4+8l0BEu~hU-QxS|{&`%*<3aB{iaMegb%k@9c^Pft%Ao`@q7 zv4{dIQ;K1h0xOo;O$IGDu{yxNYN=zR*WP8rlQfvIyh_2Vb6KTB}j$S&Kx!l z5<((K49jo?7XnUGuVBS71w1}1n@bbRW-(ATBFG2&29J zpH3;bDaJyIMLvLxD8kFvWA2PFMmE3^u&}buxLN*Ye4t#KD8$Y8BB{;X9)JvbiN*o^J5|bF}zm zxrZ8a&WM|+eYs|*Wtddk#;pwVC>a$;)^h;rL6dX;T-uTB-S#joI0B!Xn|ty0-HsE+)}8)bH~X-knBkr>FS8>JpZR!1?Q07GfvOMQwzjrU#YaBk5BWyW zXnRd>etSjI^-0~||81;lpDVrTUIEU!x3AB-$3Mkfg{L=N7PSYHnuL(=6`Na$a=CsQ z&|+b-yy-ER47Tg#?rC;*bWHgOn5bk<0a{jiEXaF!vUPNz`9NFHqWmi7<(1%dD|!s3 z7OsBbtX;x4eKlaD7aD%TrsX`(6;(`eJ}oc&F3Z$UhgOt**i5zM?)q`_L4sk%kc)lS zTJ(;VYZx(c9BU)lslQ_5rd=TuONObLlNPE%C;t>Hom7i5jA*w?a$&gRt=l(^?oZ#R zu=~OkH}E=X2#y zt&{bLsqz;4e3?@cABYbbwTuMwhpS)wzCnv-adAoRhd(fn5<2tV z-BuIPed&qU((ZIuS578GZ&YMtuiZZSp}pI0h}w%^*QkF?S#>ksG(S8t zzc}|E>y~?L@liPN9Df2Go@(ef< z4;)_C(Dy4T=X6htut@Er{oUq}=I>9Xv4X=Z$2uH=cn&6yZk$$_5OtvFOu^=0Z|aIW zoj+_ZdYkz!`}_K9m_;jfr8=AkAL${sp*wO+Upe|0?caHJtZ(zghMmBgW6nLhizHKj P@Ic_kAX?>y=+yrJOR_6g literal 0 HcmV?d00001 diff --git a/icons/key_T.png b/icons/key_T.png new file mode 100755 index 0000000000000000000000000000000000000000..7b725f4fe0d09fdcf266338f0df9c56001eb7208 GIT binary patch literal 3866 zcmeH~X;c$e6vrn-5mV40h+s<{M?_GPNkT*u5)C120vckF)K+AYnLs2YX)>CG=G1_d zLyuje7L+cEdutV!iq^JhEs7wZ)+&o4!40aSXw_mZOD6$o`lXzH=r`t^Iq$xC|NFmp zes4a!d0)IR&Wkdc0sw$lLcB;qj10%^;SK;KaCD6~F$ut9WcXsO5;r1x42VLtatut! zK{7E3hM?xRYcL)FP;RKCGF+CN#8qf>=!io@H|6MvYyjZRH|Y>XHim<8EK{Z7LtWpW zgg_O_htg&zGm>@DSe7b2SC1{uT_{!LW-B--G=Cn2XW|kDIT((BrX01#z%}t9r!SX? z9gkrMbV~4SJ~YsjELjXjYxNk&rZZ^@MtB6sj-a#VM9g6^!oXy$28_kzAd>^K7>qd} zi^XMzbJ^kG;04X2fIK~_gnC7q;@DO1JgE*sA9-lw^FZmI-?30Q)4-p z1~WW@(^oHFml%Vm znW7>cFgT#x3@%cuR$+QjN^CN!H5#&2&fs4aJnmN;MA6`!AjHU|0d z6&jk?3s9{%OGff{M}BRp>F-Zol>Wo2)avR^%EpO1CrwFa=PbI*`*e?2%kI>bkECZ!ZJ;*8Zd*sw5U}ygs%Dz)>5_ygJ2=mj;SwhANv%aGEZ2F6q1t-a!Jp$hA z!f*W(%i9z0q&}El*7$rM*{5`2IpA^Xy_@_9fluiaP!Jo6)m4O?-FhxRbk`Z>F`zh_ z#OYu*Eo9p%!{~S*_cQzBgTLK$dtN65w@geQ$8H_1OZaBpPGR|;3b!K-$_Xhm z8#WnDOD**sjr(s>t-UL1N&cgdvyXuEpZ?scM#jGXHLY#KgWBnRoyc9Q-gJO z!-^tRZ6E<6J zo#^WE79PD$ZfmZt{(Z-mE0+Qb^W(N`X109yS*!h7ZdKYQ^`jlf8z%E&C!aQNDf{rr zgbiF_-J$h+8eT-qz-%9+Q$F;(9CANA(sqgT$y9v&<{(CM>w?aox26S}u0%_(&_WNT z#?||#++8DGr7{j9$pM7u<4=6HZY0P8xT|8*Tdf$HS z^7ThR)AhvTDX%tp+7@j%HNhHY3$T@Rt&OQTdO68n8K2{8*rWx|a7zW_@g%xnMv literal 0 HcmV?d00001 diff --git a/icons/key_U.png b/icons/key_U.png new file mode 100755 index 0000000000000000000000000000000000000000..87bac4712a356a5022ab60ba2f12f8b754c4007f GIT binary patch literal 3999 zcmeH~Yg7|w8pnqO1C78zK*30L60i#bxpT=F?vaZ%XhSHlUBO(2FeD@)Gmr=_jY10^ zL5gb;#nM{EBXk9^1+iVO+G`6XHKuj=U?a9835(aHVS0%U2EyeU zDGbJDN))gNmdN*KnP5HukiSR7Nm!C_7bMeU(j{gM-H@rpvjKn~YS2n#N*DvBumVvB z)1H3OP6H8nFfGwv$P{WL;dCS>R|m)E#))OQN*Pa13k@Oj4G?aS31bq_kf~CmkRh11 z;0xig`89(EE=VwCFm17^B;g(~Qlo=GE}i8kV{!sOZUCJf7!b&2`hr4@8jOOaAd3gG znan_t&4ySUh|2+&E?Ni~VkVp`?V7)&;U z&Gy3;e&~KRCNcP_Q4h1lqK5!RWjaKQA$U={-oB|0YIwgz)@2dTE zy7==o4@3BNGYlRm-4E_oAvv%P6yu95*XU6tvQYdti$qu&k4dnF?v`7Ssx%6`2{|Ie zU^%9dY0}bQm@bDGNPv8~3|j2R3}y*UxC&bwEs|0pW*(4AqE-ac41RJrO_Hs`Xd(Yk zr+kK)u?ktLvM~C!44?5|O)N?+mIu#Se1|VPzUdgtyKbpT3tqoRxWMK2Ft+quEn-Py z#dsChiij))tGFy_tQfE2S`m?@U=^1ojTPfnTq`276#Oq-G4x(Y#sML z09cD-1>s`DAhwI;0I+N$CLYhfJvQQccWusOIp}F*a}43ni9eXSv-HKbGgO-Tqq?9` z-%rSCImG7A^IXL`A|;^Kx94p_aXCNn&+~J$p*N+Dz{YDPlj){4`dw?@3%A>+dBn79 zrG$a05tB0sK@d{q2m8Km&!2s{<-F4fTdh_{8G!@ko%R6L*1E4jf3l$MZ&PuizdWL$K5(-EXrqt*J7 zb+^uutU_Z5)^;MXe^c*B6L-1=xzISA7fLS!XuRWWi$e2% zDIPD<+-sXH@{Z~Sy4>B}_ceJ1bhsQkM!ihhqr3jtvywCtbLgS-V*}+;N@aBUm(MbQ zQ2}@N4|f5S^9Ci%1Zi#{ey#6S4^i(jB^4p#jQ&70m0cPp-Y0uY(Rv!4<<&pJun zcV-HTp;2N&M!S-$Dj8u!N|aWQ_!A~^~70KwH4n?+p0C34`u z!+fH9%br42J)g^>uTFPeHmo%shJ%TQ#x;>y-VccvdNT+NSw}~IpmONw)=iW@AnVIA zfxWNw{M-Qit1)uTSGfTNCHXGpBUPN{Ceo1*0`%9($_CUH<0NihD z*uHhLWM=I2@p@K8&id_KV@vwg9d?EOZN7J|HrN(Z`;X%OxCozZTb*`#hYrO) zd6CjKcd)(Tr-6e{I!r@*QD>nrL@<8dCpPU7kz!5 z@j2MtO!(>J?(i`C+gWGJjNiXH#lNwexy;Kb~`&@o-AbEr87>Vb}%l=ky>WJ@b z!vu6z_SUefcKyq?r?=?vP;<%~dv#uJJO5_4zkjE@=%(X%mb-oQjp`ulTovTHgBmqF z?N*DN+BJA_;^issJ6zwBA@LOtYY+;0=Mj1KG;#XM&%0bGH4KNWkqdHdF|qMzVbI2@ skn+3nF|TeDoOi1~_)YTrL6La``I#Rx_t|_hVE$!}-4Q3KkJxwUKdR69zW@LL literal 0 HcmV?d00001 diff --git a/icons/key_V.png b/icons/key_V.png new file mode 100755 index 0000000000000000000000000000000000000000..d51827b87ac44a4e7b32897c8c7a7ca95312c8ed GIT binary patch literal 4077 zcmeH~Yg7|w8pnrwhy@Xl6O`=^p;S;vLK4uV2@(i55#)ZgXp$K)CL|#lNMOArg+*`? z1eHsRhyr#6VG&n>#)?!9Y}JC8qId%Z+oiOWa#=x9*$Gfezbt1z^c!=|ocDR>{XhTb zncq7f-pL6L@-@M3zybhZ;_t@}MMskM#ux&C4ruW8I&@-JjUNrGkr0AssC!r=*E1mwbrd=V2r zrf$W9e29sUat$N}O1$8Ge7|%l9F`sw&Px~YXb^t8JC>oKqXtPZ!T}XYLXnKFVB+U} z>1eEdOvHop5=6knFEkY$7z}!er7%b(xH$7j6gQCSMj&r>+e#)mfq`NX=mT>>7aB+= zk+yL9?JGvtccE-_p%aO6xtt)U5X90%q6>{iBa+BO zGT9kbILp#R2uI;8lG$l37ChLnj3?zw5I$N|>&fAYQxGN|kFJA@<`8emTaqFb&O1Xq zA}oZHU=boCx)5B5%k>J5B$S`H50NdUiDZkQQRGB)8BHM(rSqDF%%v21AV(^IQQ%)) z`1N(s@k2WW>mw};8purpcM18auoMhOHyIMkWdiU~U+~LFT(#YJn>hC!$S2 zd>#Trh?pl%NPuAi1kaNI84!=Y(2o|(A{v$uS(q)567sb?=t7Ptk%?D0LvR8oMTp?t z|DR47L@i?>f3eE^?AJ03;_sSRkS^OEG-u%%y6xykM_l^q7Mong>yHTMx%?5v%RN_% zET^$zyozf@M9T%MxR%pcFxxBObwRkcnD&w3y}q7 zW11VLud8afxjoQ_cYX@Gl+vYAH=Qt@X=u5>f72PDEDyCIVQyB80CNL3U2Syq%p;B~ zD#{x_aG8toW)I=^`b50Cc&fBOy~}X2pwz@WIN}5Rfu`%`B>i2$b7XzIujc5$oI0vWE@LVSL)d&r?l}T zQ2sOH68rKOG3N1`r;LmSUU*PrUthWVQZNuZrOODeKU;n4G}7YW*11NCDIUQBC-aB= ziuPfZcA}n)P#S#j-JS%B{WL!$KD{mGi9YDum2O;7tlv18@-fjP@uMQbNnMd?nA1;X zYC!n-ew!KIQPic^C#)Ow#B5h`aDBI1O^)A`3`|ck^6U!otjafgw4}upO_^Z!3~}6! zRvqgoL3U0uqO6^C{uBF}H<<5|s-Z?e-6^<>MrF7AXV+X9FudXTk5f7|R<1e{Fmz~3 z+Q;5Kw2cK;gR;)CXsgD!(ajUtf$Kd+ukRqt1U#NtU%?avsLZld4Z}x_%-D?F>1p3L zx4}}G+Y(+ zQt)YSwfqL9zxGMpxy;%hE_~DJJ2K*1KQ2wFcq@Ku|uux*0A4`ltOmmOJTuflaEPYZE| z>5&n=)09gc?RS6vA}^YP+nvth9MDjw?##Xo(6NDiol_?_h5}dDbTwCu4OtxAkaHPG zt2mnd`4!UVf89~m^VMFVJ*2rl=H-*FY@ARUcy=Tox{?&|wr_Hq@8!n&UU`}UbS_7? zL!oRak?AWLHvg*espcdqUl?W#+jxugkLuF&$cgU7-%b|NS1L)w4wb39mU-YQ}0 Z+`w%=^r#{0Z`vPlfA1i6z31Lf{tLNBI}iW> literal 0 HcmV?d00001 diff --git a/icons/key_W.png b/icons/key_W.png new file mode 100755 index 0000000000000000000000000000000000000000..ee218f85ab0ef860dde2af14e9001556e0396997 GIT binary patch literal 4241 zcmeI0dpuNmAIE20XNNMQ3%WU-+EJN1k}*UsMaBwY6&Z654RbSdWKhf`U95zsdvqaf zHJLK2mWr^5waQZ1Y^bSCDqU7Zp7M;#TF*a^*Yn5z=e%C$b-urIKHtyx`+NU>fBar& zpO5$28Q6JP6bd!NW1Xw7=1Cp7F?uKz8qlrBYX)YBTM*(Wj6mYpVi@HN3BzH)gU^nH zePK2fA5#xAQ7G&qu73~`#PVWrgnTl4L_?PHMVf3Bin&@UVsoNl1PF&CxdI39^^IE~ zz=a&ZKpPg7C31nca@Qq@VZQ`#e@;R)#|{ElJ7SqqhQ@#oBWys*=LsYXsRKCb%h1Fl zuPGofDnX(hz_F%+SU!M@Pz(dsWEzP>wOR>SuO!oLSK894M1Um}0Pb)&K(hnrRH`jN zr!#0)3~MW3`~n@Z089T+!kLsu|0iNMW$p;%Ha{yz0VV z)73l)OQg>BBQWfM@Oa=G9(Nlo2K+UP3<={T(cIDEe_I5?;eH4k8SQSOg>arQQqu&) z{fvMaN8`*x#zteLc-#>l3?5q$=>SSe5FEjdz@cmx%?BxlRc-4 zOr|kqJdJBgM3V*6xF*w>GM>gYC8EiKXtQ_dRl`GE~fobZTC!%6q>)j&>UU8?xdnTdH9Ptv>YQ# zfAr)D1j$RWPmGVcbi2z}nUFV!wO^05+^oeLY`eT=I0dQPT-Vsxh{4H>=mBU`J^5Cx z+}z)Fhlfw7><9GM1Z!JxPdnhW{@DKgsS8Oi1+}LC+8Z#_tL6zYqA%6EbZC1fT#?JB z{y+fBFoTO#w|IB@C9q88Pj@{7ua0`#h{IkRQXrfHrQw%o7vqwHD*C-(c}=sd@G9Ze3*EWl zh6QMK2^^-q&2yi-!D6pJt16!6_|=*kmZ@5m_m~#+;#Qy*qYkW0G%Aa`o|uLVpNUVk z|LHJVZTaZd?zcurrxrD^W*IKUNu^{IJ>U4GMUuxEWCvU)7CPtOq z07W%bwXPQS_V!;c72Gc9k3;Pz`>0#2%f9-qq|alQ9j3Q~$1x`Fii*TJjCnef1 zeO=9)h8mHZ+*PR|90i#V*|tndOx)Y<7Bic z`UiqGstXTPEL}La3-qXnnTd-Rr}wyEz6aH^TE)BDRsPR&EE5-46GH9;Y_#@CrJYk` zm&!8EoJlW=3jEbYdx1%e+4=oPwn+1DVUyd1J(gSAD(^3Avj1*RK2CdWZtl|h%YDS+ z75Ygg_{+MeMLUmKW%7@(-k)pKB4;(;YNuW*A~wXBs1Cg%Ybih73;fWL5MZ=J+wl#d zzT4R8Tw>JQJjd57YFsze>Ccm#jPx}iEG*{FA1vw|RNJvyj=1Mk+r-U!Q`)#HqmpXa zRdI%M-^M$XZs6*Wuvp$svJY=*IiX zhe@7bidTv%9GxbwlUr4)h$+mw&x7|90R1c-(w`Q`f{c=V>TtdbqGHxy-jwLa=*k;Z zcu4T${BQ5f^-NBeEw?`&ipgx>Q1bIrap{YYFRP2mvMrYb6ZE?I!Rv2*$oiD|vba#k zXN!oD+jAPDtUi9MU`|GxifC}>nN=36G>I9g4VO@?+*NL#-gOnedM`=9!wStxfRD_)XrRhZc?$56s=bK@0H6cv_q>fy%+S4IXrLk%RST5}z>z3K z2iE=bTsHniAO7>^HnD3l23K%v=*srHVbgCm9RjiMozA4$Gac-~r3-Q)fh?sYmMQSuw^R|Iv7vYrO=iNdTCJw2?I{Xn986;{7%-I% z)9H4&!cLVgM}-b!6uheUV$pUFyJgQnwldG0M=t6hPEr?`_IJ^mo zRE#1LR3TQx#v%wsf-H~#SrRdGu^%m%B{Uovx;R@TC6j7-Fl9n{92?TuNsw4!stko( z{+~`+u$Hk*x>RLh_FEYi{9jEhN>^+Tp0oH2-*)_?gO|U$r6yPK`Xj;xE`NmaO3$?- zD`~76ui;u1(MrJ@u9Y-ajn{CkifE-^4cAHU{AB*n*uyP%)}{SY2JXtH+iN(_bszNfrl<>uGk=EU zry89{ZO@Wo({x)}Gq(gOb;*tmwl@zE3d&faS@UymT)RcafLUixPY+SOsoRp8-%(6R zkq@^ST{PG0s59~Kt!-k{`FYi>rGK=izx-`M!lt9{k1c6g zbzOn2pLMEzzdIAiz3=x8*?zQbiqCzl_dWs2qc&F%a++%`J4yz3cwyzD;}tbOh6$Q3 z{CfRUk6I9lDBs;2?U~5?^4avHhHx;C@B+@c_{-nVTXwX!r2EfIQD4{qDnzDBBYFfB zoecvDi_FI2t80#iZ#}GEexk2T+#4nEB$;sYMA6OjP8TmsUaE{Xoaxq&ABxB{+59MC z0~da@Bjs|}MvHnt&}e`eNAu_Uww)rK+UN}YiCwJw-A(cFirzVbgh?zFko&){N$DGz zbNk_zRC-c%voVSV_G6)?HOS|h$c{-P_ei^Cr@yT=TSzR}M z->AK0C+wq;h!vgW2nut}n&wu$bhL?4qf$0ZMC{jmbAUQ=H=g z(x&iH3*Bqt4+Vlg#?9wLgo&5|vUPPivzJhx({#jGzK64Sf`q0F(p(s$gRdeI$L+7# z3T?B`eI0S*)lWp~(OJrR{uf?)7-%zcSviRoVdwdqOa8L^z?B3=!Aw(Qe5h=<&a>Gg ziC6YZZoHR0_35sB&Q^md{y|_rW_B{&c_U_I&QLuP@OFhZY=e!YK9*20hnVw`^MmTg zU9J<;?0GK6#U4I*mTrC2sxUx#>0G4Yx7Jvnh(-#)uzOK=vr>`TV56A}p1s@MCX9RU z)y9^%<#qc!Go9`=zH%UR16>IB|8bg?CUBb$5`}@89Wvx+GZL@wa>D zRc`iojITMDDDs0z?Fkmv+ubMywe91%A+zB-D@=3I;`Hz;tZ_r9aZK2cSXJY@mB4rj zO%Q)8uI5aPz7G^p;qi{eYd)a?d+GCdN6m&O+Z|mU9Kvsf=>BRI6yzRZV%}8-OghIl zT7r*-qpnehfp>$IYH+@DCKIYAMrZHq{B%sNUzQ?x;Nh7J&&T#-BXbW9MYn gPd?(F6taN9SDWIV9y`{o{iXKa>*x6o_n5=~0cq|Q=Kufz literal 0 HcmV?d00001 diff --git a/icons/picon.png b/icons/picon.png deleted file mode 100644 index d41c0db8a77a35b65af1488c5c5b85101893fee9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 674 zcmV;T0$u%yP)+NPcN z=FPNeniWMs|9Q(l=sOWe1QLNnAQ4Ce5`nLfLEG)N;C_~6+=_i}-K{7Jx0U*~9SjB; z!p3tU2)i|?C8$y$#z|-j#VAlt z3Pq%u02Yk%O{oBu7K6k4z>h)!Y_hZG^GT=Ei4KPYZ8jS^pU>u)peO{bR={-#Be&o0 zX}Mg|ZnrbwEs6PjPK(8Y-tRY7((jj6#KGqqjRuX!V>X&jr}TQg3^5RKy#mr|;!z;)UW z8a)wHuqNL*^7hGexm;*6nJ9tl6<`2j6dCk-y*6gSce`Dh&1Pn5j_##U0E0F;3(xao zbTk^Fud!0nGY6oJfrvS+eBDpuf2~Na%y&ePE%0lsd<8)3ffXa}?_(wPHNYV87_gKg z^s#T90$2?{(ydmD`u#p1XOkix5`A?Fu>}=hv)QCxugBm-A~=Qg3ZQ%ixh~!-MuE~( zD7J{%z*qnt7IFN`jQdUj1E-1t#|r43vLVoHYVcSAtc0t8Vw#|s3xNezp2xe+M9}!1 zfXN(>NA_LX@_P8{oj&r;@;W8}NklI}>eEL5g9BUoN0tbD1NQ@;K_W|=f&c&j07*qo IM6N<$f+C+Z!TsMi$)ux&_*adw3d!B z25o@R*Fk)JkQO2cGX{~2aoxP-YdD@}jtm34wMB?!jLjq_fkB}I6j~@^5fkrE3j@rNNZvZ)D>#wx9h)A;pe`X3 z2`Cbk6haCGm?#}>9n?yD9uuyVfIxux-7S>)m1Q2^s0cir=YmEtmTdmvNe`p}G-e=; z{_mdOmzVrkhP0;x1btrIMMA&(@^(d7N|p%_Q7bK$+?VRK9Q}L37*CSF8-NFvHufFI zpGphh1!4xypV)>|)uThq?Z(+<(|4d>rCjx_n2WX5HNx6~s{4*(w|5K1}Zc9N{ zqAo@OC;*kTBF49jzINXaHjet?CjQ1V`@3p8n7s4tPGW>G|Gy&r5Zi2JA7N~kIkchi z&J_W$4<*x&T>rH1+ex(k6uxreH;^B;^m@J$cl3aA)1sed`rH>3r&~ z0t=`=N85sC$rf8SPv2v1-;fY=Zt$k1^WL-$1z}b6@WGC}WHDyDv^8j0DOl^qol3%~ zqk%T3rK83I%o3U69o}0v=-jbNl`IDNuYI#IBgIPI4|$CDfCo1sI{Cv#JI?tuWglp4 zh~>(ARC>3v8Iv6!ioLzPVFgMBFa&e|{B(Px`g+5-+}s0~K7LM9tn}^nh0%I$kUDSf zGiv!%Cz=l7mq@JW;Q_({=BxD$FpW7L54hA8DY0^!v-Czm}=oN(0gi5 za&BEh_C;M+*k4wTsaDe}LR`L<*=J*~M8phs!04|G!arLi9f2Ja^l81t&l3e^q@_*5 z>jly4FH{i%-{&=oUBTM9InIyN1jEFp!eFmr=W)|hlSVFmGy#v2Eqe_O6VLbTO00;j z(&@e#en#x}V~ry!-1~eYcI8s=5{7e;#lnz^JX_qKmv(z?3)IE>?a;mBTx^rbwlG9g zdw~geB8U~Kz;A_pH!|{PHNCI-D*K}*TPXwS~;-P#ol5J3l5F|KJVAEFd zvI`~Ge*4Hd#zZwL?7A5>#T7(1v-W}sx$x`Sox7)}-u$%iHicNL$iz5Ej%KH^vAnmB zPt#!;85!06Z$?J=>@w2R#f?P|#LEt@%6Q>ih=GRt@7xJjGov`<>Yna+^jD>DQM#4W z?3Yyz`8oM5P%vN8;N{I5aZMV++fopyCO@IWMuGYZvr{SfmP9d_Wh`etKP&6NW2I<| z&QG&b!Mih{)e(o_;wiB?r(awNnG;!00`}l3OEfN#S_blodv*r5h`#1}e$K&E8^(tDnF+QbUi}_O206l$dL##!E zPviaj_s1s&><&UhIjZY6$^B&!GjK;tx~;kdBq%4PcH{B_cg)g5g#|0BqjydI@tADS zotu4aY4&;F^E`2|8^3XQi9)-OrdSR#Vm3a3~ynJ2rM#gfZiYE96fR1dhi~e{9_N`02f2c3QAePuw>1k_)@J3mgyF(YbAt zrF;lhU?^A$k_K+K4!V6M28UNI6ze6P7YLIXTDv3})=)-=2rCDCa;s zJYlM;syC(9Ijld)=KD0Ou#OxYEP8&J1;3P4fOyXDP~?kNX@o&*`f>JB9vbn{kdTKn znS&4VBMxvss{I1(|0jRQxpf4hpEX)RU?q@UAW5^ zo2c6OJ2RIwQfSYsbKi1KYHXbXmum81Gx{?6XHhLD&)lyZZqEx_;1;XcWhe`wHsWel ziB`Ed><)Mr4^p9#T`Ity12Vp<3jwTWdrL5oCz?;u`mwWbhX`<(3cr8=njNfo_+>2+ z+E;V$c#gJjEr=Epr+97rl*oG#^t=tfDm$AC8<&0ka?|iBN0!z@=5SRYD@d;vdP4OC z%&gQ^Zh!PbGr!|AFU8yJHV*-pVu+UbXm?#p^}fldnxtER8{&&}cl?mzRZ-`$cNzT0 zbfTl9y@K@tQG|g4J3bcXGxmmc`{q&90Yzf=>#|MQ8Hv!ij0~J1JB8ZVv1wZlL@hfR zUgOALrH#e%2kAoguwFYJeKOoPvNnFTVB?mGb?blMQp{%rMPs6^u{32_paIsMEs_;UJav) zTc82$1+54*wYHM-N+E}wYIee9O@z6c0DUn2mR0r|)4i#ELn(6fg)xg5Y)x<1q?}5e zvRW+;heOoLoT)mqN*sxM9N%YI4i;z^_WESaAcrr8!JDTdIJ;9>qiwP*(9@lM)EDm37JK^V zv)7%fcrLTrz~-Io6G0q)hlKSAB$cAM3LWMyJ@M!!D+08`c3(eK_|`|h)vIac)`3C~ zn+`tVv$mmlaeaFlAprKo;}WqeU3spkc&>0P zI{dL^b;#xNr&~5;MPKFGu5BJwHVw;*uDCok7YYDCgb$KcK%>(i+%I{U%FMYb7m&Kv z9&Kt`p}R3i!uH;X@laxG2)^D-ZJL2|Gt&Z>7IIaxSiujyFm zP7!!+A$*Q(E%QoWx8>TkYlzIPooHl&HQ2+$gT-Jyd-Gu+Ul}}j_3j&1f!MiAO+xis z;K_*Cv@aBXgtQ3wwma}zXP3;&i$dc-sz&aeI(&f~IrGIMXq;%Uk|%2RlvYw8aB{rRhis*NJZnSIb)7?dB<|ET?(6BiUY*xiE)%+yFj2_8E}oO% zE@2LN;;Hi4b}n4-`B*U1*zf5TL)1ANr)^0e(o918Z8*tiIS#o1f zJ829?pFf^<=tHZ9fJN@?$4R;HsYE}|^O}uSa@vjO{4a=KJ6u%jX#CeOrpGqhDJaUv zW*f!7;H+=IRzg_Jr-Wy+vW)|i_zP0)4uzwfce7Jj&gL<1QgSC7ZhY){F>p&ap(qC5 zxn`tmrfb4zcWlqV-W0FTnb(E#4$TBW+dyha0|%m_l!rmMRHHuBsC%z(H(0 z>Sjx(A0DMW&@Eg%)zoj|>6f4S;P<=6^bW0l3@qjFGs*atn1;6$?*xo%U(W2Bk*T7I z+H@}k7xs4R{b6a9*ZsnGuqB#vXPb9V{)X%ENNR?cmcNvEY&1G}`RP-$%v14GYum|} zGW^FcM#Gf2E>M43Q$$+_@!-Imgp8})_8nU#^%XUpx5T7vfNcU440)gHd^ z3UT)Ets8&JLUNoEg>Y9XX9c%7e7FXs2NG%2=o{dd<9hP!8h_zOMvprLPPtKL#%`;R z&vYXsl&3%KiXaZE8;1q0E`50D?V6|NzJnw~l$anp4SFnF{;X+Q# z{UQr5Z(nyUsb>6kb@{t-nfSoVC*TLYrLtISO1NWPU65TzoH>-&^Vhn)@G*i0JUOt0lW!uVl(9{*ipFKDg}5Ba?CmXJ@XC vq2cF?cg*NDu2(uI>KXSdR`Ycays*>&Y+p+59IRXXyT;zuY4dq&T>Sq4cWtwq delta 913 zcmV;C18)4AG^qz6iBL{Q4GJ0x0000DNk~Le0000W0000W2nGNE0CReJ^pPPae*;TN zL_t(|+SFIgYZE~ff0LcfS98e0=2BWgY6G@N+hCj8*pGk;dTm<#3%o0c|AX{UL_zQ$ zkm^OTph7Q38$YT&sG#;@p;%OE4kc;QWXJcW*|?ePmaJ|ozGaxqZsz@d^X9!bZ`k*r z-(cYQAXr7yG|+WjQ169Qt5w+9f7xNwtHSLjAUH&$Q3wWuo)FmH-iCZW56%Qc<4d2< z2f;uP!r`zd1V}&WRB;?FKz|hU2;mXFum!IZ5Xx5*+%p0~fcdnv1(LAG1SE#|Uo1f0 z`e5Nccqv1PVP^}VhjwUOo7H?;iZRwf@jsrL033Uh2aGD{To=BOkF2qdf8%VmQoU># z#yacl?2Kc$<@^DE)bIDpaAElDhtL)vD3r@(D3{9lLZOi4?XmVbWEBmCLNWuQ1zqsK#>=tZIQM)j(}-%SmI0#^s1YvX2QUU>A-Q>=4mf6wn8NLC!g=5~&U&HlbxhG_BudM78;zEOL!_}CTj z>GYZ1+@k1GakyRKQ9li9J*{9gnUbb#MQH2Da%4qSmx|-K1=gB?f7-KL)|CY&$I`#e z?oeOIEs!j2K3Ho4Y9m7@ZHr1fp)lM$nG4fDZd|{4$a)GkO~fa8V#Ku%>_vcn6E>g9OkLghx^esN{5@zm7Ze}Jy;^c* zfw9qXM<1YmdL{#ef6eG-gfhGKW$n`P@*7zFyejv4{?0tZRR}Cp3tmMrm^*tlK#Gdi=l8xrcVOSE_X1i-jUvdH42K?b8ajMa29_dL@CLBXMbdv&s0xXMK!BB6R|^5;qng%b5hW zwze4g50!eVZ*gnlu~w~IbHKv2<#WEFv=iV+-u_~u+zliF&j0K_P+RGqS2Nqm nn5SR7&UsP`kdHaZF98MsZ%>DZeSWjR00000NkvXXu0mjfHygOR diff --git a/icons/sicon.png b/icons/sicon.png deleted file mode 100644 index 746acde59060fed57d3e7588e428710a7945acb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 726 zcmV;{0xA88P)uwx#|XQ52~_Xt&$ZdcBUz<#NIMO2gg(O%O+Jxm?n4 zIHc3*L<}ZBnM~xKj+`_~-_R`Lxc_`U({{V1e!ow-T#f|y`<>G1v^=nQd|V@72aJHD z#8IzQDneesPjgMiJmU5O44)ey4egO~O}tnv(&2DmCkzCCS%BoS2&mO+G@H$6u~_g1 z2EY4)07R(OYEie_jW_OSG@|Kr%56m4y#O3JY3qJ=f<+7l17eKFV-|ttaw`DajgO0b z>+Ezol+WjBwOXlh7VAO*LIOri*ZOybLV+Df8V?+?Z~!=h1WGYDjk$FI)QT+MbrkXE z^GW;tp33F2atO=|5!&XUjFJ2Ld_E^ewOS>{>-A#t@sqh;ubYq*p)NNu{G#7|IMraUyc?nbXdtVraEC9}hQ^?!_Y$J9+%D?6^alm;BflaKg zrGT-RJWe5upHk+11DoWq@%C|KQb@i50Pw@b&&aNqW}N^07*qo IM6N<$f@cg-MF0Q* diff --git a/icons/zoom.png b/icons/zoom.png deleted file mode 100755 index d8faba4a0f4ac9cdda160815df8a20d55bf11aaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7635 zcmeHMcQ~7G_peRt5n>lbsM@1ek)rk*rz=bZC7=REg)@?5!-%}nml1GoTJu3Vwl*VDGRa)p%e zmGlZV89`vGt_>3e862W-MNLipdChc{pwa~C*}<<|pF604nV4Bv*?{aEAWklB9$r5Fs{(>T*Mvnx!D8aqC2mMc zNz2H}$tx%--BealRa3vEp{WJAt*xVbM^E3t(8$=t)Xdz%(#qP#7HVhj;OOM+;tF$f zzw6<7&&%7#*Uvv7Feo?#9(q44JmNuQ)WhhQM~`FU;t>gnPm+>TQq!KM|C5pVEGs+b zd2U{QL19sGNog6fyrL3SRbBI|gTPEF6u&V8K!wD5UxX?bOJZT-vFZ@BLpn?JUGZtv{w?f*K!|2{nW^Y{4V z^z3|#`jY}-bg1ZSYgvIfxBFM_Er{7mO4K-&V+u*NXgbh(6NTR)v@5cTMiWb zHg#V=*-gSohgJOOTB>mFVoIe*h`ug|pg=|H&XGk_(@{7M{yra`p6}F9a&*@8`+Vi- zZwv`4+rmKXzy3%|vVQRqpQrded?)5$dqn0#m%R7sFpMlRY&+;s|BgbVC8v0^PjTq% zlGMYVkh-z%VZXn@L+w2r3QbplTnZ)|A>ZMp0bkFDPxszq+8l$tn6t;lTX^xy%^f?-Xi&)6*|41*zq8*lT^$Ebdg2E~_e~@PKCB3774?^|qdA zw1|>*6cn`LMCzeuesQC@SM|y5{Gd6v1=vw(9pvx!>PYju+C~|$hBfF_yiGn_BKy%o z^qtRlht4TS`Hfd9pUxdgAbvIeQhz&P;nE!Yt9E+D`{j2o%?JC1AHCX)i_5+)oN8}9 zZ6Fu6l&(w@K%*(Lwa+-|o@_564_6-eg1EA3wYE#ksT>oBr@oXOz7r^wb_`nrSp-S!2pYh4U z>+?MTVoH>3OEQq+^r{7gLY zIOlyQNNl5a${sp zw()h-8qR+(3&q$cItpbI$`~Gd8TiACR0rOWA7ppy8dn}|2DrB!&!79YEC?@0|L3KmV0EhDMK@f;sBZp?!*QTJ9rLss)tSxV zsmyRfTzctLc$<*D_>IaBtry>zzGA%tr|)@G3s?+Ixu_<7gqZLuB%IsZaWGFMpBFro z4%z7v#?RtOwRJ0u@zb{V1o=}~9X6|xNr3O}$6oMK!LRxleY`o2PFwrgd2s2ACu%Al zx6TgW>e~IEB%|eakA|E2ow+v(k7$`me%nk-IYhEu-Ssgp3rrL`b=#eiDpkM3)1Qmwau|)&LNO`r=xhg!rg!bvI)$TmNfX#(Bi{4XMfdF7gIWH&OW>Cn$U>C{}4dDhvR(${Gsbz z{`i`DAf$@EyTAcBck_%RQeG+1OsvTCpXW1)Iw~F;lPKyOXS|n(5ELb@35$nIGI8F`%Bk zMOpW>Vo`xQwS186gh{ckHOsAZ$aTIi_lQ?!^V3P-CX^$wSf_&>JwM9^cfi!(Fg>dp z9=K}^apqE%e%B-p83s!b`t&8kd$utT$|&i4nzJl?Q(+BnJhB(8sBi-TpWi=veTyM} zrxJP(TBkG^kf@w9E%9*!0j*X9U=MY2C#? zeBm<|dn~hS;d4_$A<06jH*Y?5F;$~44~Js2ccxaoQ9g0F(w|4NSeFXJQX4o?U+2|- zft0Q~wAq;rACyKNivWz{a&}M?b6nCmfgOyoG|m~|4@M-oje+G^hEBscj$r=4Jd0fp zz!?d(G3_h*v{-!)hJ|sCN<5A(QpgT==*?5%+?l4N z8tO7a#=927!FVpL_Xmk)gp8=gMkMsRIiQ#v9w|%v9aChN{eXAw?=#ugu!$f~q$;-K zv8<|izif-+s3Ube-#vCpOwqjHXc6OBXc@K~L}wsFs}GvVk!8flGFnto8C~jDU73Po zuK6FtUUiCGB6;|m9_f#*Ywv-7QKk`1@5$5XaVtbu|crYy%Ed3K*LaHG^ zWr2mT;FfffvtS7Y*?B2EdPK@_WnmQzpg~NAOF4s>(r$zSJ{H(msU7%Llh~FnGO!Gb z4M;Z`os2>{+i_Hb^-Ms55w>zZy z)dGNxqYyD)^&WmW+!X~9bRx8r9b_6ilA$lR8}Nh>;13-;NI%v)<3ZAckMx3PVDuGs zkZ!DU2Ip{>DwSZkg)6g!^kY)mt8kVBe_Ms5w6b0vXqhNQ&l96QAKa(U8Ju@wm0V*R z*`TyY(Y$-=SLKpPKlGFGg3+vyYU#JFb8%QUKw2Wj`ziz(QYp{&wgCPX#ZDZBN%;T? zlaKk3+HnUt1TGdNClMqs%z&8D!rz5ftBo92<2IefwcqPnoS+)gi z+-VN(`Uq6|SghZLmH~v1Z#PZ~;szb81?CD^Zn(zg8OHAKmHpVl#%CsEoCp!_Kc$11 zG?_EoVo#28YdLKfVF9L~`%KyX&@9hzcBd*g!!(5>o|X49Kw%;jiw1i&u-zkreAdq* zE_U>M;}yLC=$ecH56!Mm(f&J*T+}27 zKg`_#^%Es5WSPW8PI6#^95_SwGiOKe5O)QObd##^8w>#G1i(TVD)SP^v7MzXq8J7Z z3s<-Rap%$G9oZV3QikQ3B)h|$3HzqEJ*5+jW_TWfU}@_m?!?Ez)#mGD&~+PwF46L_h!i5ghU#G`^c!dtpI}Z*Dr>ASzt^4um@SPX_fkl7}!*p zTJufvfrHZD`8<)V*uMzbl|B_3z##1XQ*8f;G)^Ay6mg#cIk65(+w3L$8p;G()+qdx zdji>p8Ik3lG|N(2rdLfIq0OWK(d6)3xLZ#XWU|27h=d>6<{NCFtkqk^w3ZBF6=sI> zUg8PM2$`pb9gKA;uu%{on*REI)J&$dr`i#c%IHORJS4o&z&hN*3y|iGj*t4?LT1T_ zw1Urz8NWDi#X2q49l#96=n~j_Epp9#^kt01B2uGdc!nNozR*HjB^YOOQM=bhkEll~ zP+B%tLRsg3&=~mpQ@e9TkE`c7lUmj`R?av=x9C!gFT|nrBkKCrg-J?Fw0SO>chwkw*y+(1TyQdZ|9wlaaz-0MOt>%*pP158JddR{YT^c z%WNVo)r-RDPl74_H58ciH?TjffySIsce3@s5_-Lu&ztP-~Og}&i{n3$L0Vr_M|{K6U4K@=y#8-3Gg=cQu8 z6W;vJfJI~w+fSu*zItEkPV0+UzOGcJoAS3AJ%Rl`Qd6mLW=g5aR8`pPt&PEQQ2%N~ zWTrCkP{oa^dm}w4qSU$GhumS-&Oj=r@o~`2+!Bcw^r|@zbxkRSByiJNriQK+<=*(a z)-?U=29a_N)Q8FHhJlSbUN^@!#s@(4aE)#|Xwu$>(LcJqift1Wa3&GC4f99y-(+)_ zjdW^w%nbq;8_^@2og2oVc9|p@rz)lnT>p7qxH%btF#0`<_xikv))_0^oSciKE)2l? z*xs8*ZhJ;5fRL;wdCne@eU!Y9khWvjg&wAlO}y@8eyA>o|0#}`jEFpn5Fib>*?ppx zC|%nUyWDLelPZitU?JSKgMNjulSioXPsq%g1T(go$!vivJ^>}IEcuoFH0@??(s`AW3D zOMA7z;?NX-C)EJcRrRn5ej(N;)zKm}o$?z}Ougsz)uy2O&6v1ubnyGXwV?-Z`Ea@38MmSX6U-(uQtU4``mJ zWY8X8_|DN#H7V({*IdDf62Ik@7jYGV1zm}MiJ=%L+l>PmPNSI*ZP$XMtNmP;D|xkV zOAI>B?=SC3iq2?;G2A>)K?VlGVhHKo-VeoL-~1y&*KykZV?OIggCw^s?7clDp;mdT zvm)hI&+{kK&XLDbN6RsmUDFh~&P!1f-HfIY9>=0C4X`p|?4{kB(X22gdShG596|@U z3eNPbOPb@oyxNm$)x4=D&AyP;{vsNp#H_yQt0qx?7rWH8pAj!J2iF-|rxWXYRanbI z)OQqXLkQ;DTlcNL=@H7rJgOAGLukoNiLP~eX#6vfAktMYu~sdAe2nAmLY*?CXw$3S=Cq1>?deJbX$Wc`%dpdNY2bIHM~1z(PaH{K*6}{}bq_??V?NRSV_HCB zQgEvfN&~|!ewXDj*bUlaBm&_~knR58bn#LSYj2gkkNx}|5DR^eP`?*5f%~QegX`Og zW~qZ8Cj-VzFc)7cH|3PlgX(^>`5#&-oTg+|eOTO~?y&4BZ?JJmd-RQPxlI+)=rzG$ zzlmD-6Hf4lcMARFdTV@gx*eK5raygeM}fNAJr}%8%Qg zGYUtDaAc2gsq1Lg#*I+UUl%nl?f58o>c-14LAl#9&~wxK7~9Rj%(T`1SgWm__th%V zK}B1u{tGGPrv+}_yMqLd07h*H5tuz*o6WpI457|M|1zBxq-_{blXgb*X`>PpoC_w2 z_r&SQ{N%r65fXRf{Vt~&W~#=1@`0G?@1%4MweI3R^nMSSz&eqLQd{pgH%{iFXUmCH zjOe6v)gnFmF6+Ta@htKRA2GS@WUm*C_Z1I30^Y{2W@c5bL<;&aJaVOn2lkbT?8Op* z0PU}A_a8pE4&U7BEWn7G6vT8gGh02x)d*vs?q=Os6B0Fk`x_$98##t|lK&!XcpK z#(BIeQuC;G>StPGJYW8=zdpGqX01D)y&0D#T5pB;%F(xT{%`^EPvL&W96jZc+Znm% z67l!p1D%X@v=&jETeL_&rf~0} zOf#FLPWg2nu!LHL2Pjn^g5 zX30jygL8X)s62Ap4K-TgUKuT-9vRf#{v@(9r6h;^$r`?y8)L?v(uvoX19J~L9j`lp zRS9A(25dO!6xr4wncc81sm{&d(bR7E&zyDqE9fFhsSRRBXk#i0SoV{h@R;!}+^jXV zGkdf-{*}ujYC^L{I;oTG0lPTQx+F!F&0|1v``DRci=dU)fYVO(3m_75Z71ZdQ3ExQ zklG2%N2ewuxf8oElVXNs4bMjr4Qjq9dVb3Xm?1;)vO5lT{59xWg{+fTYkoC*A?NEM zP3-asfEM@VuO&B9IUos3BXV{kc2@nZ{5=lTQp-2;?iy#_6MCphH z1SK>zH0ecp5k!%$BK5;^-sAV@yzBfp|K6E3YxXm9?|bjLo>{Y=wI;^mi~$F$AS(a> z;4m`Owc0$jo=7*06$Xh8@S$N; z0RYxXtc^Xv-po`Ph4++3{?JI1JiYg`0RUAEk~b3NfgwO#Fm6~cHRw`J9Tb8^t3l7o zo59VzbujK&!$4n*b>JBrRGjnortHluAUb$o3`9;^MhXQ-C_v;Cq-7Nq6lLL(5Hq|NL?7b?vK=s6;0KeDmVy1LCn3G9ux{=IzrWJF{CgXe^3=K@;#OysIk)BaO!VBmq%Hqm+O3^8?IpG+H>quhB13IP4D| z$~dH#n;MiPg~qrdFXISM_5Y?*RoD;4IPC8#KS%#6qYC?fP5hGnF+KY^zpm|1`~FJ@ z`|GXy-Q*v<{ukj-F8>STKRy2`@+XbIjsL;*w}}1}{DbRH8h;!AgX?b*{VDhd*Pk^0 zHvR|K-y-@`@Skw8{(Ieu@!DTI5%*W6Mul<{0038#k*<~v>7C2=-kt`70u>1DFLT-p zSxp#!H77mF9?=>~Sg76}2OA#dm)ykH&b$eEg?jsJ*Nx6-tcqa2ICX5;AYhT5-FWJ{ zi{NZ5*HOs!!f1#;S9WUZx-brAN-q#DN=F~fT~^dxP^YL)%C0BPcjreTdD^+(4sYF4V-5WnkUcCrA783pjV3>Zy>%iFn+JlkDy2rk|eK)5+g&ra@9TzFF7ZhpyJY212NaQ8HkJlTEA zI>hPaRe`*+vb%NeUAd5At%U33oC(%gdPtCfSb}%cH7>3qKt)Mdm^LVpkKq91woepW zITgHPJ^O%dtzvXzZ4T5}V15_O%E}6^zygNhsM%tjrPC#wbrmO7yRB!Cohc~iDqmdt zt;9qw{bGe>w%E$6b9N!Z!(7P@bK8frzW38oPAj&bSnHI z4?|;25gxo9xQE!Nc0g?JY~2(9gI1%2aa(mu-Tix-3T*b_s%v^EPXQ*TyDbOVnHOW> z8(>*k@F@Vt=1vxJD=iK5ayR7da@;qDgoK zYSkX&b%#raYyD5M532c>{kWV`IHXqLm*% zj%f3`*erPDNbUFO*Efj7rLpXA9uU8fZ22VS_*J@wn~_Q)Ag;bi1ol85h}y2gcXANx)An|I_e!YL7)+D> z&L}60Js^vSUXy`GGw7s%GCCrw1i9+Y*D>uyI*|JyjKuuc+La z&_b|iXvWd#arzYx%Jg2nlrs{EwCGV#IwekJ_m?`(5cGW|3c1o>#LfoVcDCr3bF;9N zL3T36t}(>(yzkQ3+T39*uczj2_K^!dRV7~BU1C0_sFJnM({Qx=Ed6W;X&!!jOoSM1rvOv z^ema$fX0P59$Fk9Os%7t=C!g>=G6(}{9-__$)HCfGcj-go5LZU@ga3}-lv0BNpJHc z%)xD@rordWo%0zQ9#(v_QJJ-uBA3z>jQLINBvjXg#9J{To9Q1Vc z1jcHE8<*FIdi*qzeiXfs2QGBUddTi>t5dqVx&q_1!5oi244F{Gcx);Gv|LHbjr;UL z>%NO4)xFOEhXDsPuRM9xWT`jVD7k=geO9hgvrwcmY+np|5d9?8#j3Z{MlV|YsYbMM zMNBC6ph@1wgn{iztb5aa_wG)xd<9EalS^}SJ_|_8K`d6^l;uPlr4`jEUB1vDSra$c z*7-`mJ1R%9y1KIR*4n7w;*0Tbqr;ru7nOq+%3<$^^y`pj2ne6lXy;gi;NzN@WXmDm zie!wy%!lr|s6x=<_i?~dw!6O)X%U}71LkZzzuy&b*5QKNUB|bKq{gYcdU1X!TP*Bv zBWr4FM~EhX`1ttqS?%JVOcM|#H!D9axE|%oQeSPRE>aZhEsN zD1X)L3b!1R`>4n6{PqUJW$ovDM{0#|4`~xx@sC76x0KT2a4N!OpFYp~BCcy}FD;d2 zX$3WI2^C10ZHn0?XL%tH_V@BGFAJ96vPsUHXezD$4zBgS2hye;&WPQTzHpnf8y z5yhZJ_Q$4Ez?JwYMe&7uj`lp6@^w(zP2SMAFAtM%yxXBv%r@?5`$C15!tDu@qQSfD zUQF-e-Hk$DpTT5NW1`CF-R0Vj8M-7+_MUul|CaTH%?QQ1(!{$M15{?FPplLJ-Nt_0 zYQoW5C=-*N3d_ngQns3Ap*n#)O?-5(HeI0`yN*EaLjcw@X63p07Ydl*NZ}(KT3=S! zxGHCK#-5r@Xi6DkY+0T3-NFE^eSLjpUc-Jw?z|h?sgh(q>P|UXk6TbTk)YoWsZP=C zpANI)V%_a{9irJ6b&$#ux!kE%yn45X-Co}YTy!aN<@)W`mKJ4EiAbbYg68M3ycWaJ z#rCDso-PKf)js)L!7E$rbvy#Rz3-pcZ3fvc%YU8@Y+#40! z(b1t$BY%ge*X|gwD5ck)j?zpmv6dZl_*xsGQ(H5)1u_PG3k46e5Bn*EshDwmP$(LW z%>i~X-$ zJAYC!J$&J0T7tw-h5Lntg~fS!)6paKPVAA|iPqwPcw}8IeEsvx z317Ye3q75lf-$*lRpW0bvT0*zf7ho&*`-)dTr9{mfwvvVuyulZl;{=qY+Q{MR%b(p zuiZU=E+k~Hx}l*#@uKuVy7y^SgsSQ`jY_5RgH^r?Kl0cRlxyo;fn7dtCNnQXp4>bh z7pvO)Zg{w+rm9N!>2mr#>-KhtF;ur9iQ$2>*|U4h8tio8ahVYc&D42hj8T{eses~a zV8a!`yLvHMl&+(eN9#=K!wKGnnpeP(PO@#oJHZ$e^{r_5eB zk|$3;J#_hdmplW7FF|)z%yK);ODJrDF=4vn*qam&rckkSnu+w5f?IL1@t^1=U*EZi z2tAajR9puaH%>i3QX6Zt6bS`}^@T3`TJBbJL_a02tLothI_Ybz z_30PY1uP@}%<3n1&l^`n#o|;|Ql?L{88OHQGUBK z15PZvc%ZR~NeV8Eq|J*9w@8|FWYHkKYrYr-;~0DGbJdh;?5=aL#5ECD>}qG{nQNrE zf=k5~7q!{FD?RgJ!ChFdX}Rtr%4as|p|zvOM@xE4kzHA;F0xeAhzrqrNaGp%$r_&d z>GzVrj4rw?vY|WGtgwx{e21?Kc;?_Gf~kjxN9Ef73W{EgH&eGJH{uuuR=3_TA|z72 zy>{pjXU#f08IP#u+ny4xFsK=pq(^J%PH>VLYit>!BRNGK4?7KVNxF4urX=0eQ+Bbi zT?&6c{ArSgG)^$VIT+?yO9mfGkuVQ_>cArDV5e%393e|DJeSgXNkw=F%`fT9-*%FY z-c0kTg6De~#=+C=(d^uiofsq?v-!nH#23fY7m0@ociF>luL9Q12n{_CqYAvcX2UFw z19K8rKQ0X6y*@vcL@OZt46_n7IffR8)hu`{DVa7CqBhOa&NLDbhkPO*>brTiw#Rnv z{AAqDmx-9up~QRjYDS>OJk;at+>0#maDSC!i}bNE_L7^y^T8Xwi`J9`gwa-B?48W? zbcGWlBFJ*dR?dbcf zjKG2#uKP8x`O?N)PX?1PLs1(KEn zpn=+aGPeN5i#}a9U+w*(y%tbH+8orJe)*wmb#*n&p`YwI{IsOrb(-S~*0SkBMOmrS zxtfj8o0*zXLLhx6sN%ff$g6WtO38xpZ?l8Eb84`O8WZV0Qq1Y>612DzJ~ zK*H)G2!vnyXJm(CK~ zRBO#(dhkLCwUg5`;&yZVPGnj}yg;>6XMK^LsYLxlU`0)ddfHLer|s*3FLn;;(cRSm z)PRme-e)Y_~%*| zPFBwfzwRZkXIq9C%=^IYs>yWE@%FNOk8di)No*z}e7DFI_1&e3e6sJf8l&&YL{P>> z)pYq{baON4LAjCwYN$=Qx%ooGa{E_&&i${F_NFpb-M!a6O%lb6mn=gHJqSjMj!{}i zky&1>@k4tlw|BpaW=QWnk}p9lw8k5#h(CIu&t1w!mdJTRsb2B8nK&WcHaY%&ZcdT# zLR~`QTlLEH>$2iPC;x~%8|#%xc4qY>9#xML?7i1D99N`yfWxXdZR5mB^P~RnES@f3noFp1l|6x9~0PV zriXw&;Rr3Jp+!w-t#Q-RH}dOgopxT_y+HjN=tNV>z|&MiG?ZuP1ED1devKNX4O+UYe_z|zEta=r3+?M{w|28ANGrFbP&SC!tS4dCr literal 0 HcmV?d00001 diff --git a/icons/zoom_minus.png b/icons/zoom_minus.png deleted file mode 100755 index 5207ae7758e76f2a76eeba58fbdd6aff7081098a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7037 zcmeHMc{r3``^RK3#%}BpC1Z~nB1;&=*v8oRA^VaE5!v^pk__3(zGNLCglt0^Ymr?c zWQnp1@s98N&+q;3{r~qq*Y!M~=f1Dc{WS!`D z3eqbD88v_e-6hxhNC5Cv(>A82rk+_am?xPuSS<@*GBWDfi<>-m)lQM*(g*f}`4xOpz~@?GHiqhz5&V*ZDed>YG!U>X=QC=YlpFS zxa;WT?BeR??&0Zm&l`K+$Jg(Hf55}Qphv+Wp<&?>kx|hxv2pPUiAj&2JWYO<@;nun zmi{6mGb=mi<*VGh{DQ)w;*!#`*X0$JRn;~4+BbFe4R0Hpn%}jww!Lrf=Z9NA7)xs4Pf+ zj>+H09=`(5x_=!Uozkq$ZLH|6hFOgIpoXA|RsHd9I{v@0MeU)xwKEd(vJ6CL$TWRm z&?i%3TIHXOZkr8+McGcwVVjZe)%Te}cUhoG#wUXa9UZr^660T~K6wSd`i8zI2FE&l z@BkR><`sz6{4E;htZwtBR|JV9`V75ldXhOW`P1UEh=Zka{i{r)or!N|v5AkrYX%SL z&YYhv|3NXK8qcW)o?cGQO$DTxe7SdOm1VIvH#wU3X62}qL3+c~Hk&Gqu03?n0lwP8MW{lR*YjhT!x_dKXNx^vpuY%G3FSu(ookj%b9-%M(n73z9);xoFw79}vO0|K;7CxR78pH+P7a3cSbC#Mz&$6$N zJou{pW1yV=fvbpaQPOxW&j$%5tA`AR4PY zDE7balze(%We|5PMd`cxKIDZ;T2(K)S5^CF*&DcdcSaLn{oJEArFI)jv2IsC)58%c z!MC34C*fQonUjWVXdEWIulB zfluMLQdmCzekFCL{Jdg@_8@%bh+XU_ zf57xSwRg*&%R-#$Xhg ztHhc4Z1g_OlNOe3v^^!=%;Bn3Uq;=xJo{potERo9m46G%uNLa(XxF{3h~ZdMud`L; z;tcrY&Lwk`s@5<}Nbh*h+7WTK`N;J%mJoBOOgH^SlEDHUaElI^8IiQpU2#tDC}-9~FdM@qIUaf?^J@7=nQ>j1P#a+^)>PaeNOe*ZnL z=gNFTEm0+p2k)rEEj1Cc6A|A-E!1Fd8}9fmB^UF1cY58aEas%$;73c$Az|-2++eQi zLBynCUQQ{YVVSSqu^Ww+mtoR&@hT!emviBYva|%GH@QjzM7(G| z)Z3$UM7y~*Loq5kELh~053v3nQ0%ktV0b8P>7Ikiw=Z!fLDG54G;&49{=m9jb@P>b zz%g?A%nl5F4$X!x<_4e8{Npf%J{6>7T!@dY!ws_z&byogba=z&Kj5XpOFV{Nk^oMS zg2~^E2y6N~;Ja78V=AWYq~aMCVnNxQgaID&iZxr{ea;*#ZPd|QF0t_^s$x;JKJ5-< z#a@_(HE@9v4;B)|0c*+sg|`g@;xCP7-;qBT zn3UWG0^}g+9zA5d(X{Ttcbd2HvtXk5$0(K|Z6nVBLG#T?c#pqK_|>gq>zn3Y6wB3d zqC2LBKW!!}iR6jm+8vxf468ZC{4e*Mj$`|DSh)d7ZQ8X$3(t?Kpbdh2mt;tK^$4RHw7PqZ$808#5H% zy@FnmG5^6r6pQX3DjYc5!RtQAm44CX)(`cS5*v+>7G=Rs|AOvvi0ujy_rhmd2U#s> z`-8PoqQ1;GeSrEZR-$F}QY^4G+(dAsiA4Uu7p%=9ccNIh32$ECdnEQNpYRTX*K>zg z<5kRwe!ecPc@dgMLjnN~)=$~O_cgOly-Mb2$`(lO6u2rZO>=)FTHsf=04?-0eeG{4 z!Gj-NT+8&R#TFhy7bX!NCo_#&k|H2j60K=Hz$IEj1j2QsB87);iIzBl07kabdbA=E zOyZN2ag*5qE6)PLC4vTRB0Ng49JS<5&={geS_5?Ii?nPAy<*76F@n_v1orr3Rh&7Q z&a6g)5EEaik;7H7ZLnaqM)1;RVR$Z;LaRZ-aePWMPL9ieQZvGvNhrW*(G0I9gB;Mz zvDQc!!mD{86(i-`G!jDbT{5`UCvx(U%%nM#f@>nHeG@|=*#R0x7m!DaetYxxN%x&N zch|!ttB2J0m?+sgL42q@Id*c$*;!B9K>TGR1;~}WQeiy3>7+`833UmE!0SBn_H3E4 z4+X+4bpTt0Z?HsIpDV#Uy@W1pDWDVg0sit%Jbr5-VkY})2@oGT@*ae+ER-{n8N zbq@Dr;!W}i5WI>wvOdPV)5rN^fe+)1dW0=r$X}kz=9%PLdFYTDatI#6HBFD6x z4R33;AtFxfBV=)9m-02KGBr-z5w5b7ypfa=1uD15^I7ZMnWLRi^M>YmFiL+!nM_?p zJ*VS!BaEWhv!adwMy_EoZo14n!oRPHjl@ypBCgj)SR4pDZWVSIo6qZm#G-UXo#$#9 zIn^TAF%c)iH~6Fh84o59MGTa-v6Q`r7&gZAjxI%+6&gvYiN-KF0%03LDogsH%4kZUVIH-#%Z-gJRYy2!JSdcee?uI7 zWTpDYRaT@x1Cf;P&=}h2SD19AOPRu~4}ol}DiHw2Td7majo|~LtPd}-$>lQhMmtMY zl&GzGfoADwb5s+qUKy*zS?qOxVzg9?u(FRxg&H!T?`CKOsJcTa-*r*Y34h{2f3+IO zdcpi`8X0kLF+SGxwOwBZo+pc_mOo=|kE9eabhxs#UgKygRz+7k3jG9T(>h|!h$j$kIzv!ywUByr@`1QY{)Bnluq$YAR{Z86&6K*n$H-aTig+9 zZm&#v3ZgE%_p4DSgQ=&^yF4bG%?Jaiypg~>$Yi}B-_8g34=(Ek|NLAHik#7pfZ)5X zApKNwj0*|6<_kVZux3tu34sq2^IdpbZgGhV=@$nfp0JvS%W-Na*mcrxK=GpNQajfL zL&+5eiV4E;$*paylOUbbOy)*@W&Kiu0$$w?+4}g1LpMPPlb|D9D;P0(Bq|8(Vtyj* zqqry#z%D2eCAfnWz7Jb05Y%`YCH_8-P#2#Rij!OIWb;Nj02UWbkd=`#y!l!P0>VK- z&^A`4x=`yTp*w-KnQz=xCB=n_Aplhb_*wgk`H?6;n<=WRhW?RmX`e}a6btkXGh1_l zKn*|rR$-rHhwc*oV z+V{|W*^#&xw?SGQ)9}b4{Y0^-jaNmwUX#lYi164M;$~5wySX!?(^lL}b^gFL^A$hh z&oXc+#Bhbn{JJ-v6R^XH-KdP$6ZS%yduIVpiTlGm-uzNcyJ^D@^d#cJ)BKV0OF8?} zF_zuRNl-s|>o0`T0W&;G64XXzrlg(!36B~?gI?0y{32e0qMdY=jj@f(<}Dg=!K2NP zGI2>yC-w4M?2Cz1P&kVO7vgmZ-Y5~x-l;T*gc@Jcu~B{fte0V>&+bRVPnL_^#LWyNxcPO}8Id&(`*tL9S6 ze&OtvCRTNy4%fnvUeOxm)4#J2Zh;f6rkk1TC+qDBZIlWx#k5&cadZ^p=Y)H}^Xik{ z6o5%fc7IXxL9(xrfTwz}aLQ)7XDmQ{Zm?t zYnP*3<&Zwtwwj5$UXM21!3)w<$ufBKPNrZv+Q2nBOm9ltln$Bwp_@C%AByx)QghS>4Gkvu$L&hlYj#N2vf@j&^_ zW~BgE-6Vp?_)p|YYcTlD>#ucim$18p?$ z{LB0jv7_~iAGoi=xK96&A)fCrvdHhcS|1mWQ-Aw7oF=U(%pi520E)$&X;4qddrMoB zY{P)d-S|^?2K$d2d*ld`DvRD*X;!NbxoP2n@-BLly}DX~@d=}cQr9;4Zb6(Ig0V_4 z!DasI62kVb##^4>#>{E_$mkRIw$qG zwJCGz8HvUK^HZ?nJMCBGsh%}D8J-=g)DDb~Blr$P|MgB@_p#)sA7rYk<~{4Z=WIqq zC@u{<^?HVQ6Y&-LRf&8?4a;fWC1TTETBNMvh&+s=oSKkC0YJ$UyO zu@&Y??Du64dzYC)ssTg8`yvq?k`#Pe-6S0s_t3TVX}Op%!!{~F(8L1GyO{s`a`~Gg zMFJuw)7XiW43)R^h2Oye{tpz?7?{N3;xbl!o|S_yD!5=f~Mj7#< zXEH0OG=7uPD-q^>oarSZIzbc1CN+ERpdSr$TKnH+b=NGJ?kV{2eleI*Y$DU|zq~vu z#qBosHs55vp7!mC;K`#O#jfK|zhW)tV*EZV_J+SGus0IzaQX^Ro>N=^YIGytb-E=inNE9cP95EDWu@Cq#aiuu zte;vEITEr>jQumW&;+|(TqXM#cuv&$bfaB?=fzFO+g0#F;Mr|b_~{nbChDeZZt`@# z?gf3KJMg9Khy-_ae`oShm_zM9oDYDxL*DEN{t|fBRmv@2w8Lc8g7F-L&BY|33;$_n)nr6QDCbNpXn9V%LmsfZ7X3*|coA z2z?q!GL(UUD7ed1=(;}$xzBN-@#xBR=F3SOGP{yG^S?>GUilW?q~&KlL@h73&&s2G zk#{yDUwf!KU!na6!2XpB*)W(x(fVM2Mu^>kL^$pu)4}*OqqEl3sU^wPXHjJT=nB}1 z_}lb(THKn4URo-4wKO2;rQW{hyaJ(fqXx9H>a|eTrcP`ZD-9G4Z`}hS{_#lR!nP*A z3_PyUYz$YmO)S}0Q@s3WX4WD0XTMaHea4Fdw>CzzGzG=l6nhJ$wX7iOhzl2!T|#<* zxJ0pAMX#L&xBWI6UUk!r(eEq@tjIex`l8 zTz=J$5?;eF_H8-W=dT^xuX+2K=A*S4N$=-z16oCHwtQt_Wn)J<)rd32-EKYQFwM1b z8DHM_&0b!P;}Ws+re$GiVY=BBK*bx*v(ZAifoH`N&h2f#|2k>piUoyCHyQqYZk7}B z`I8BAU*bNvy9$+V-h-M2I>niBJe|p_97*C|qjicZ0qfX7xjixF$Ep?>5Mq=;(%Z&Mbk6ZW6%CtC*;rRH<1sp?44|5Zb=qa{s7hJ<#vqC=GO_73_{)_t z*9!dOd@eA=>yDRw84#giHNzac?qBA*u>v!0B$=OG%C^E2-xdv{=Exyl+PV2$6#p0A zv4~7RS4KP#GOia4iFJqm2XOq``Zuggl)360gTL6IKBez*mM{DVg#Kfdd?ualiLb$Q zXSmfaTFx+KRrv>PqZ6BohHuzVK<;bj@*hLV|G{-I#gqrk2 zRGkuCgXLA399d+L;*vM} zY)|+`3lHwR*t(V>g&a<4=Y>w85L?>pJ>&@8CdGC#EvFD-{sQ&At bqV_qt9w1`}BjVb1@zXKkA|!+mLQ4!mLMNc~qXGf~(o{f^UIYOt z2I(Cv6ai5|I#>uI$i?zM*ZXqMxDWU3>@oJ(bM5`jZ_fQ&>tXG&KYpN5DW8)YR0V@-Qe2 zCVQxm^$+qRB1y7-{-QrDetGDl{ZRxgo`^jx`ok0Hh6^BSg29K+LBGx2QGa{m0|-7p zo!wDTv=7=B?ML*7D#$57|J0L^cpEInlj#3{L(BB*Tv-p2oIH28ZiAdtl z?*4AU%?F1$Y{DIjBBI@iI26vq1C5q*NB<-N(r`z?fA#YN%x^ThKEz+6U!;7nKX|}> zkbW3VFiF-O?STyNA%eC3FP&;Ye=zpJ{;u+K^q(>s(EriIFXtUtIr)=ug4Fxc;Q^kMX~_{t?lif`4)SN#h^m ze{uaIqCW-y8!qPm-nXLt4);!hhr3df5~Vo+fE{6?uWLt|blW>P(56#qqX)mJa6O3l zcx|?GQSLSHN?#;#_3_>;pRpjX%r36^U3Ww$YN-A@hQ?&3f#|qCFFIuuv`C zQC`tO$%hWLqaJ$MUZ*-dNQCa-^IklDiLBevm7p8%Y#dSMQ4FP z@$wer=F0xi+@jyWCUwKr1EJ9^9A3G0&|-SwfNnwtaqMG1?_YIjKE6;XOL?IN>8Eq3 z?ruaWK34~vLx87nC|kr?6)eR()O{j)UBm4wS%h-pJ8=rNrPG~mk{$~mU%L+&0$0G zO#$5Ac>LzVbz$w9*H1zMSm zI$Vu|weAjIZzbnkO(w@CUNtw1+^=Y+Uu-SWxfTHwqXn`o^ydUxTSdBF&-{zJleiI; zxE|r4CJpKC1q~1fc4ilkUYczQuim3>%of(xDut!Ud|1vwh{=isZ}({L=VwH`7iRiY z1v6%@QZ#0ir&XYfW@H;a(CMeNw^e@XR(>3jCtM!#WjPHQ8oI}DhrlMR+BG4(dNy;9 zHtV_)eI|d$S3bTDazW2gv&A#hK%j8zcaQG%rtZzTyX?_fB`-w%+i{sm5xF@>)i?yi zb& zza{^H_SsjmVf@Drd13qCX+|=y!)jKSyMI3 zT9j+46?|gnWFk)$Y3Z?7Y8|K9HNGV{FKuP@TB9ax{&3p9M^(=R4f^`<4#MPp{4`kW z+v;ah-V6wOxZX5&sUfXQc*>&0=LwQHNl*6M`?i0TvXt5t za#AzsDZ<0;<5+KRLf_0xi*9FL**oDbsKqTOA8~FAS?30MfH!O{uhBSTi`rzmy1EJk z0hzVf0cn0_W@cH^4rR=f4M;t23kx|)Vg{5iS2JvvQ(}On|5_^(#MTB-x%)9CJ!Mh~ zEY-WY@I^4DcqliQjyao7c2|#^<%SG%YI^#45iq!SXW@%?zQK?St!#3B7K1SlnkGz% z`@`J~|HZ+=7KT><9l+#6m7+2!VC%GL>$>mJwKdh1wWujK`dI&KBHH%Kb~%hA9cx`~ zXYlb1uQ|F>o-Kv*p*bjExs|-D&+hkd9OvwUB>G>pkvC9{PB2Wm0u0vG((99uogU?x ztzYX_>CpnJ_n7!u5M~!OH-d&fX)KcIrjsr5B5-C~UVXJ5=7Bmgje(SBDi(LA&2J|4 zrt?TUrR!dU)(Lx^Zdi<<3k|J)?#I0oL4=>sPg!ZEeA*to*9)u3Wn;>E)+)uj2%&QF z%_K%mPEJ;RqxHmMt+UJ^PK7nOYYq0Jo+A$<`JQ`OMPJNA+TDrM4sXO0!?ixzkhC6@ zPt`lLV4zVDnWFj6+O8)fnyr1Qq zn!2ag_YS1ud(&1(iRr)*Ids2mq#mOEUf&HN2Jy?shQ#OUP@3#LuiHqEoFBtEa}{4a zX~8X&t6RYyq;{yX>SkBp2u7Mnc#BBGycpUtH)7|;635yE`rargsHT~1>wsJE%n=a` z+2qFKvP_JpANX+IA`uxP(V4}`9f-T;ndQo83-YH*-Qsd+`_?0oP3l#|$|G3id&~>l zm$s3kOOV#_@p0m#&sUA4l^2AJWoVN=_jls=kJB?1(MX&=HWHUkxCn`4#T?-21uUG& zR|9>QLu?7LKrKjhllZ`uH0B@{s5tQccz;F221Q|u7YrS1x(HJq@^ZYAm)z%z(dVNGAIc{EF;S~QnnNO#cfTx&kPJ0VP zzVu?Nl)2zLm(pLiMOr3Jfj(Q}t!zG9H99Y`j(PwjNG*^a;9Qta*yhgucZ);?r^`)Q zvf_~v7wQOXlT%ZiQ6qbOZxjhnDwmvI&38-iJ;+kaRtw;w**+o{ss@MKeAps6)=#a; zmRh$4g-~U#6W*2?wM;fXHMnhGk)50MDtz;C$B6jIqU{rwO7ZS;_?C~*j`^v*Z`Acq zZ}6UPIj2lBcXxLM@`eJz9qpFPF;b-N?|Ak`&{|>F%QxK<@#Kis>bGEa=mwONC@Mnu2BQ;iv6h^W8b#4kKAE)o-iU?MlH1lxO1vgOd<2X7M`x^< z&&UXPPNBOU~C9q$#F2)h>%ve3Uuw zD^WdEC}?Z$lUnFiHsHtE+11&!6#_&Voow*^>JYh5s-cnqOK8o>%$dl^d_7@vwqBOw zorEl(sKj>>PtLd2=hbQ-sLEQ!A1{b79@pnIUq0T>m1d8UPau%+h4ha{m8?V)j$-=G z`bsj7ujx*NV4Bv}cV~pov(G2LAgV%V-7y#{^;-*dh8nUVxUJ2%&P&A!lL7`}6snc& zJI-^Q0`@s)oQ1$d(um?I4=kp`8!xfJGmM=cT`2vSi;JBkXc-iS{VMnrE*kAaW>6!7a_JRow@M`9(z#irm@QQzKe5 z-2CNfJJA)ElL4E19bp!EVebmKDQD)&>SrJL!{2?S*Ydg4O4J~Y zoeX>AMBg2Cr7Z`{!kW#>t_r`w@FWarRM~v)sJWpF&xYk0+YrFa|ElsO+&)@ z;)bu!(=swB~%6Q~UTkUpSQZ1?Wz#9N|k{kNQ^4CO6Ody6^6Yg#O*-w|(iFbwS|F|$ zsVXAeLUooiVNYZVcI!ObOc+d~+ftYqU-d4d7~g+3xxG~M$mLaS>xs}y;*5hrd^wj` zrnO96CC{g^DkRWe+oYWoF;;o6j0o+Yk+SRE}gzr}SO-WnkF^r?r zc0!aIC+vQtV49UbW-wQ?sid$FE$rr;P~RxG0Z?4UHYXds#FnbQ&e1CGqBoV(t1%DX zH>CnrHK&3a?jdFM81#s)U4feMyW{DW6-tAFuVAggXCzBGwW z64nk6c4pSqC{jhaIydZ=?6^+t+zuB}&>0%8$zZr-$u1MJqB0(T=k#@2HN~-gn>=OA z(C(Agr$Ma%vN0{mh_yjcv9{!jeXNZ*j=m;6K8edKN;Q5MgVC7f3J8MQVbe^+s%xsN z2{b1vZr;2(-pDfHl$3&JzlKHOnx)9LycWyZn8;%R>!w1JEn(k2_SplxBki1=CiY}6 zJZi2eSx!B1g16-9)*ydaPgUJ5gS}LtU71l}HhL-^OXWx~lt@*ehS|4yr9Hd(RoXou$4N zBJ4w$DvRH6?1Wi79N?c~qno~eQP+rOy~Ag;fBz1R%Tx!DquG%C;f0~9r=DWX;~P}h zSWa3pg7NZ8RUMagcTz(weQ-!{=;_3Y6Q}WWlov1T3-j~y!P3$ztR6O}19cL5O$sbo zFh!PDkK_0(9`$I4JejEw5ERtzxUOD)e*5Cr^H2O*$9S}x1X8xdcBRI(}goblgk$94J{zY#tWBR|VG6ew==p{+j-a zzdj@Ydt`VFmP8%0XcH*$`t*Mp%ku&9ppjXUqb7A+Cwf4c!*Q~b$=w=a)idvH43R)G zzi2yvgNfc|#Q0u3D{Hd;KwNoza`)>yV&w)yz2BmZ^A^ruBu8$PcXn1}8HHM37n2Yl zO4eWEK4soxf4etFP}T4A-b22f*s$~n0)isY`x5S{G|+4m0TIv?82RuLuaa#Z_slK4 xdAr~uENMGxc=qzC7|(!|n315n@Nwxc>O2G??M~yZ+#kQsObjga%g-R9{tJBx7TEv* literal 0 HcmV?d00001 diff --git a/icons_rc.py b/icons_rc.py index fdac89a5..e6ee0972 100644 --- a/icons_rc.py +++ b/icons_rc.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Di. Apr. 7 09:18:41 2015 +# Created: Di. Juli 7 10:05:17 2015 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x02\xa2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\x00\x00\x02iIDATx^\xedYM\x8b\xc20\x10\x9d\x96\xbd(\xa8\xa8\x08\xa27\xaf\xfe\x01\xf5\xa8\xff\xdb\xbb\x07\xaf\x1e\x14\xc4\x83\x17\x11\x05\x11?@\xa1K\x02sI\x98}\xc9NYYHaH\xda\xa6\x9d\xf7\xe6\xcd\xb4i\x9a\x15EA\xffy\xcb?\xe8;\x11H\x04\x12\x81D \x11H\x04\x12\x81\xaf\x90A\xdb\xed\xb6\xe0~\x96e\xdc\x8a}n\xdd\xad(\x0a\xb7\x95\xfa\xb6\x1d\x0c\x06\x19\xc2\xc6s!\x08\xbeV\xab10\xdc\xca\x1b\x03\x84\xed\xf5z\x0d\x22\xf1\x15\x0a\xbe\xd3\xe9\xd0\x1fo\x86\x84\xc5 \x90\xc0\x04\x18|\xb5Z\xa5\xdb\xed\xc6\xd1\x95\x22\x1e\xa7\x80\xac\x881\x0e\x18+\xa1.b\x00\xde\xf6\xfd}6\xff\x9c2\x18\x98\x00p&\x163\x9b|/\x7f\xbc\x8aD\x1e\x10u\x09\xbcD\x0c\x19R\xd4%\xa8R \xc6Ih\x0a\xc5(PN\x0aE\xa4\x99\x00\x16\x8c\xf3M\xa9\x00\x96\x16\x83\xc3\xfb\xc0\x8fB\x01l\xb2\xb3\xe7\xf3I\xa7\xd3\x89\x0e\x87\x03m6\x1b:\x9f\xcf\xe6\x98\xa0(\x04\xadV\xc0u\x08#\xb7\xdf\xefi\xb9\x5c\xd2n\xb73\xe0-\x89\xf9|N\x8b\xc5\x82\xde\xef7V\xd2\xef\x97V\xc4\xc1\xe7\x1b\x8d\x06\x8d\xc7c6\x9aN\xa7\xf4z\xbd\x0c1\x10q\xbd\x02\xd8\xe2\xa3d\xdf\xe4\xc3\xe1\xd0\xa6\xd6\xe3\xf1\xd0\xdcKO \xf6\xbd\xc1\xd6n\xb7\xed\xb1\xfb\xfd\x0e\x80~\x80\x00\x00\xc4\xc0Y\x0d\x97\xf0\xe7\x09\x84,O\xaeV+\xaa\xd7\xebT\xa9T\xc0u\xda\x0f\x1a=\x11S\xb0&\xdf9\xf2\xf6\xc9t\xb9\x5ch2\x99(\x81\xeb\x15`\x00b\x14\x19\xf4z\xbd6f\xc1w\xbb]\x9a\xcdffj\x8e\xee\xa5P\x00\x8369\x0b\xcf\xf3ct4\x1a\xa1\xaf\xb1R\xd23\x07\xa0\x8d\x81\x889\xad|\xddO\xe3\x7f\xadH\xae\xccwD\x90-\xf0k\xac|\x05@\xeb\x81\x15#\xee\xefc%\xf55\xc0 \xf1\x18\xb0\x94\x22\xf4\xb1o\x9d\x02X\x0d?\xd2\xadV\x8b\xfa\xfd>\x1fg\x93\x22\x1e\x12}u\x0a\xb1\x05\x15_\xb3\xd9\xa4^\xaf\xc7\xe0D\x228\x85\xf4\x0a\xa0|\x05\xb9.\xde+F\x81\xd2S(\xb6\x88\xd9\xc0X\x00\x1e\x16q\xfc\xcb\x8c\xfbN\x01\x03\xa7\x8a\x02\xc7\x0a\xe8\x9d\xb2!\xd0lj\xf0X\x01\xac\x84\xab\x80b\x9a\xa0\x98\x0b\x81\x05V\x9e\xc7\xbb\xceD\xd0\xf8\x9d\x80\xc9\x1c\x8fG\xf6]\xda\xf2z\xf8\xea\x9d>\xf2\xce\xf2:&\x00ID/A\xea\xd3F\xff\x83\x03[\xfaG\x96\x08|\x03\xf7\x03\x9fA\x22K\x9b\x82\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1d\xd3\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x00\x00\x00\x02\x00\x08\x03\x00\x00\x00\xc3\xa6$\xc8\x00\x00\x00\x03sBIT\x08\x08\x08\xdb\xe1O\xe0\x00\x00\x00\x09pHYs\x00\x00\x08\xdc\x00\x00\x08\xdc\x01\x9f\xe8_<\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x03\x00PLTE\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b#\xb7\xe1\x00\x00\x00\xfftRNS\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\x5c]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xeb\x08\xd95\x00\x00\x19:IDAT\x18\x19\xed\xc1\x0b\xbc\x95s\xba\x07\xf0\xdf\xda\xfb\xdd\xbb\xdb\xae\xd4t\xcf\x90\x8a\x94A\x84\x0c\x13r\xbfER\x1dd\xa2\x15\x22\xe5\xd2uI\x89b,LE\x13*\xf7$\x8b%\x97\xdc\x8a\xa4&rIF\x17\x22!\xa3\x8b\xee\xf7R{\xaf\xf7w\xe6s\xce\x99s\x8e\x99]\xad\x9e\xf7\xfd\xaf\xf5\xfe\xdf\xf7\xf9~\x01\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x8c\x88U\xa9\xd7\xf4\x986\xe7w\xee~\xf3\xed\xc91\xcf\xbc4i\xfc\xc8a\x03zv\xedp\xf6IG5\xaeS\x19*\xb4j\x9dt\xd5\xdd/|\xber\xab\xcb\xbd\xc8l\xf9q\xc6\xb8\xbe\x175\xaf\x00\x15\x1a5N\xe8r\xe7s\x9fn\xe4\xfe\xc8\xfc\xf0\xce\xc3\xb7\x5cph\x11\x94\xcd*\xb5\x1d\xf4\xcc\x9cu\x94+\xfd\xf6\xad\x87\xba6\x82\xb2P\xd5s\xff\xf4\xc1.\xfa\xe2\xef\x13\xaf;\x1c\xca\x225/\x1e1\xb7\x8c\xbeZ\x9d\xee}t\x01T\xf0\xd5\xeb~F\x01T^\xd5\xbc\xee\xfd\x0c\xf3h\xe5\xa8\x13\xa0\xf2\xe6\x88gv3\xef\x16]U\x04\x95\x0f\x7fx\xdde \xfc\xd4\xa7\x04*\xc7b\x17}\xc8\xe0\xd8pw\x1d\xa8\x1c*\xba\xfa+\x06\xcb\xceG\x9b@\xe5HI\x9f\x9f\x18<\x99\x17ZA\xe5@\x9d\xbb70\xa0\xa6\x9f\x05eX\x93Gw2\xc0>\xbf\xac\x10\xca\x9c\x06\x13\xca\x18p\xdf]\x02eHQ\xff-\xb4\xc0;\x87C\x99p\xd6b\xdaa\xf7\x9f\xabB\xf9\xed\xe0\xc9\xb4\xc7\xaa\xae1(?U\x18\xb2\x83V\x99s,\x94\x7f\xda}G\xdbd\xc6\xfe\x06\xca\x1fM\xdf\xa4\x8d\xd6\xf7,\x84\xf2\xae\xf2=\xbf\xd0R_\xb4\x81\xf2\xaa\xd3\xdfi\xb1\xe7\x1a@y\xf1\x9b\xd7h\xb7M\x9d\xa0\xe4\xda\xfcD\xeb\x8d\xad\x04%S0\xa4\x8c!\xb0\xb0\x05\x94D\xfd\x19\x0c\x87\xed\xdd\xa1\xf6\xdfyk\x18\x1a\x93\xaaA\xed\x9f\xa2\x07\x5c\x86\xc8\xd2\xe3\xa0\xf6\xc7!\x9f0\x5cv\xdd\x0a\x95\xbd\xce\x9b\x18:o\xd4\x82\xcaN\xa5\xf1\x0c\xa3\xe5\xa7Be\xe3\x88E\x0c\xa7\xb2\xa1\x05P\xfb\xd4e\x07Ck\xc6\x01P\xfb\xd0\xdfe\x88-l\x08\xb57\xb1\x91\x0c\xb7\x1f\x0f\x87\xda\xb3\xa2\xe7\x18v\xebN\x84\xda\x93\x92i\x0c\xbf\xed\x17@\x95\xaf\xf6\x5cFAi7\xa8\xf24\xfe\x96\x11q\x1b\xd4\xbfk\xb9\x8a\x91\xf1P\x0c\xea_\x9c\xbe\x99\x11\x92*\x86\xfa\x95\xce\xbb\x18)\xd3\xabB\xfd?\xbd3\x8c\x98\xcf\xebB\xfd\xaf{\x18=\xdf5\x85\xfa\x1fc\x19E\xab\x9bA\xfd\x97{\x18M?6\x84\xfa\x87\xde\xcc\x8b\xed\xab\x97~1\xfb\xed\xf4\xab\xd3?^\xf8\xc3\xda\x9d\xcc\x87E5\xa0\xd09\xc3\x1cZ1s|\xff\x8b[\x1d\xd6\xa0Z\x01~\xa5\xb0Z\x83\xc3Z_1t\xe2\xc7\xeb\x99C\x1fTB\xe4\x9d\xbe\x8b9\xb1\xf1\xa3g\x06wnY\x82}\xaa\xd9\xba\xcb\x9d\xcf}\xba\x9391\xc5A\xc4\xb5\xdcL\xf3V\xbdp\xe3\x911\xec\x97\x0a\x7f\x184u+\xcd{\x0a\xd1\xd6x\x15\x0d[6\xe1\x9a\xc3 Sx|\xdf\xd7\xd6\xd3\xb0$\xa2\xac\xf6\xb74i\xcdcW\x1e\x04obG\xde\xf8\xda.\x9at+\xa2\xabd.\xcd\xd9:\xe1\x5c\x07\xbe\xa8\xd1\xfd\xbd\x0c\x8dq\xbb \xaa\x8a\xa6\xd1\x94]\xafu\xae\x04\x1f\xd5\xbf\xe5\x13\x9a\xb2\xfb\x5cDS\xec9\x9a\x91y\xff\xda\x1a\xf0]\x93\xc1_\xd2\x8cm\xad\x11I#i\xc4w\xfd\x1a\xc2\x90\xa3Gm\xa5\x09\xeb\x0eG\x04\xf5\xa7\x09\xf3\xfe\xa3\x10\x06\xd5\x18\xf43\x0d\xf8\xb16\x22\xa7\x8bK\xff\xbds&L\xab\xd8\xe3[\xfaoZ\x0c\x11\xd3b\x07\xfdV\xf6\xfc1\xc8\x85\x82\x8e\x9f\xd2w\xb7#Z*-\xa4\xcf\xb6\xff\xe5\x10\xe4\xccio\xd3ge\xa7!R\xc6\xd2_;\x86\xd7BN\x1d5\x85\xfeZY\x17\x11\xd2\x89\xfe\x9a|0r\xee\xbc%\xf4\xd5\xf4\x02DF\xa3M\xf4\xd3Wg\x22\x1f\x8a\x13[\xe9\xa7\xa1\x88\x0a\xe7c\xfahs\x9f\x22\xe4I\xc3I\xf4Q\xe6\x0cDD\x92\xfeq\x9f\xa9\x87<:e>\xfd\xf3s=D\xc2\xd9.}3\xef\xf7\xc8\xaf\xc2^\x1b\xe8\x9b\xf7\x0b\x11\x01\xf5V\xd3/[z\x14 \xefjM\xa0o\x86#\xfcb\xef\xd2/\x9f5A t\xd9B\x9fd\xceF\xe8\xddF\xbf\x8c*F@4\x9dG\x9f\xaci\x80\x90\xfb})\xfd\xb1\xbe\x1d\x82\xa3x\x14}\xf2.\xc2\xed\x80e\xf4\xc7\xec\x03\x11(\x17\xae\xa3?\xba \xd4^\xa2/2\xc3\x0b\x110\x0dg\xd1\x17\xabk \xc4\xda\xd3\x17\xabNG\xf0\x14\xde\x99\xa1\x1f\xc6!\xbc*\xfe@?L\xab\x83@:u\x15}\xe0\x9e\x84\xd0\x1aJ?\x8c/D@5ZB\x1f,p\x10R\x8dv\xd0\x07\xc3\x11\x5c\xb5?\xa5\x0f\x06 \xa4^\xa6w\x99\x9e\x08\xb2\x92i\xf4n{#\x84\xd2\xd9\xf4\xee\x97\x8e\x08\xb6\xa2\x89\xf4\xee\x0d\x84Q\xd1\xd7\xf4ls[\x04]l\x04\xbd\xbb\x14!\xd4\x9f\x9e\xadj\x09\x0b\xf4w\xe9\xd5\xf2\xaa\x08\x9d\x06[\xe9\xd5\x92C`\x85\xae\xa5\xf4\xea!\x84\xces\xf4jnmX\xe2\xbcm\xf4\xa8\xac\x15B\xa6\x0d\xbd\x9a[\x15\xd68\xfd\x17z47\x86P)\x9cO\x8f\x96\xd4\x86E:e\xe8\xd1\xa5\x08\x95^\xf4he#X\xa5'=\x9a\x1fC\x88\xd4\xda@o6\x1d\x05\xcb\xdcE\x8f: D\xc6\xd1\x9b\x9d\xa7\xc0:\xe3\xe8\xcd\x171\x84\xc6ow\xd3\x93\xb2\xf6\xb0O\xc1\xcb\xf4\xe6\x12\x84\xc6(zs-lTa&=\xf9[\x0c!Qs\x1b=\x19\x0c;U\xff\x82\x9e\xb4GH\x0c\xa1'c`\xab\xfa\xdf\xd3\x8b\xcf\x11\x0e\x95\xd6\xd0\x8b)\x05\xb0V\xf3m\xf4\xe2\x22\x84\xc2\x8d\xf4\xe2\xa7\x9a\xb0X7z1\x0faP\xf8==(k\x03\xabM\xa2\x17\xed\x10\x02\x97\xd3\x8b!\xb0[\xb5\xa5\xf4\xe03\x84\xc0\xdf\xe8\xc1\x8c\x02X\xee\xf8\xdd\xf4\xe0BX\xef\x1cz\xb0\xa6\x01\xac\xd7\x97\x1e|\x0a\xeb\xcd\xa0\x9c{\x1e\xec\x17{\x9b\x1e\x1c\x07\xcb\x1dO\x0f\xfe\x8c0\xa8\xbd\x92rca\xb9\x97(\xf7i\x11B\xe1\x8c\x0c\xc5\xb6T\x81\xd5\x0e\xcdPlsc\x84\xc4=\x94\xeb\x06\xab\x8d\xa3\xdc5\x08\x8b\xa2\xc5\x14\xfb\x106\xab\xb6\x93b\x9f\xc6`\xd8\xc1\x97\xf7\x1d1\xe9\xd9\xfbo\xe9X\x17\x86\x9dE\xb9\x16\xb0\xd8\xd5\x14s[\xc3\xa8\xe6\xc3\xbe\xe0?e\xe6$\x0e\x82Q/Sl$,\xf6\x0e\xc5\x9e\x84I\x0d\x9e\xc8\xf0W~y\xa0\x06\x0cj\xb4\x93Rk\x8ba\xad\xbae\x94\xdaT\x07\xe6\xc4n\xdb\xce\x7f\xb3>\x0e\x83\xee\xa4XgX\xab7\xc5n\x869\x95\xd3,\xd7\xe8B\x18Si\x19\xa5\xde\x85\xb5>\xa2\xd4\x22\x07\xc6\xd4\xff\x9c{\xf0NU\x18\xd3\x81R\xee!\xb0\xd4!\x14k\x0bc*\xcf\xe3\x1eM)\x801\xefRj8,5\x88R/\xc0\x98\xd8\x8b\xdc\x8b?\xc1\x98\xe6\xa5\x14Z^\x08;-\xa2\xd0\xf6\xdf\xc2\x98\x01\xdc\xab\x8ba\xcc\x08J]\x08+\x1dI\xa9\xa10\xa6\xf6V\xee\xd5w\xc50\xa5\xfa&\x0aM\x82\x95\xee\xa5\xd0\xd6\x1a0\xe6!\xee\xc3M0\xe6^\x0am(\x84\x85b\xcb(4\x12\xc6\xfcv\x17\xf7am\x15\x98R\xef\x17\x0a\xfd\x01\x16:\x89B\xbb\x0f\x841}\xb9O\x97\xc2\x98\xc7(t/,4\x86BO\xc1\x9c\xd9\xdc\xa7ga\xcca\x19\xca,\x80}\x9c5\x94q[\xc0\x98:\x19\xee\xd3\x06\x07\xc6\xbcL\xa1\x83`\x9ds(\xf4\x1a\xcci\xcf,\xb4\x841'R\xe8zX\xe7Q\x0a\x9d\x04sz2\x0b\xe7\xc3\x9cY\x94y\x1d\xd6\xf9\x922\xb3a\xd0pf\xa1;\xcc\xb9\x802;*\xc22\xb5\x5c\xca\x5c\x08\x83\x1eg\x16\x06\xc3\x9c\xd8\x22\xca\x9c\x0f\xcbt\xa0\xcc\xa2\x18\x0cJ1\x0bI\x18\xd4\x952\x0f\xc32\x0fR\xa6\x07LJ1\x0bI\x18T\xbc\x91\x22\xcb`\x99\xcf)\xb2\xbb&LJ1\x0bI\x98\xf48e\x8e\x80U\xaag(\xf2\x06\x8cJ1\x0bI\x98t\x06e\x06\xc0*\x17P\xe6\x0a\x18\x95b\x16\x920\xa9`%Ef\xc1*\xf7Qd{\x15\x18\x95b\x16\x920\xeaA\x8a\xfcR\x04\x9b|D\x91\xe7aV\x8aYH\xc2\xa8\x13(\xd3\x12\x16\xa9RJ\x91\x8b`V\x8aYH\xc2\xac\xa5\x14\xe9\x0e\x8b\x9cI\x91\x0d\xc50+\xc5,$a\xd6\xdd\x14y\x18\x16\x19F\x91\xc7`X\x8aYH\xc2\xac\x16\x14\xf9\x18\x16\x99E\x91\xd3aX\x8aYH\xc2\xb0\xf9\x94\xd8Q\x08kT\xd8I\x89\x95\x050,\xc5,$a\xd8@\x8a\x1c\x09k\xb4\xa1\xc8\xc30-\xc5,$a\xd8\xe1\x14\xb9\x0a\xd6\x18H\x91N0-\xc5,$a\xdajJ\x8c\x865\x9e\xa2H]\x98\x96b\x16\x920\xed%J|\x08k|@\x89\xc50.\xc5,$a\xdaM\x94\xd8V\x00[\xac\xa6\xc48\x18\x97b\x16\x920\xad%EZ\xc0\x12\xd5(r\x05\x8cK1\x0bI\x98V\xb0\x91\x12W\xc2\x12\xad(\xd2\x10\xc6\xa5\x98\x85$\x8c{\x83\x12\xa3`\x89\xcb)\xb1\x14\xe6\xa5\x98\x85$\x8c\x1b@\x89Y\xb0\xc4\x1d\x94x\x12\xe6\xa5\x98\x85$\x8c;\x91\x12\xab`\x89g)q\x15\xccK1\x0bI\x18W\xb4\x9d\x02n\x11\xec\xf01%\x1a\xc1\xbc\x14\xb3\x90\x84y\xd3)q0\xec\xb0\x9e\x02?!\x07R\xccB\x12\xe6\xddE\x89\x93a\x85\x9a\x94\x98\x8a\x1cH1\x0bI\x98w9%:\xc3\x0a\xad)1\x1a9\x90b\x16\x920\xefxJ\xf4\x81\x15\xae\xa4Do\xe4@\x8aYH\xc2\xbc\x03(1\x12V\x18F\x89s\x90\x03)f!\x89\x1cXK\x81\x17a\x85\xe7)\xd1\x18\xdeUl\xd5\xee\xba\xa1\xc9\xbd\xf8\x8aY\xf8(\xb9\x17\x83\xbb\x9f\xdf\xd2\x81ws(0\x07V\xf8\x8c\x02\xbb\x0a\xe1Q\x8d?N\xde\xc6\x9c\xd88\xb1S\x09\x05\xee\x82\x05N\xa2\xc4\x01\x10jQ\xc6<\xfa\xb9\x04Bs)p3,p4%\x0e\x84\xd4H\xe6QWH-\xa6@WX\xa0\x09%\x8e\x84\x94\xf3.\xf3f\x14\xc4\xd6S\xa0\x1d,P\x97\x12\xed!Vc\x09\xf3dj!\xa4jR\xe2dX\xa0\x0a%\xfaC\xae\xd1\x22\xe6\xc5[U!\xd6\x9a\x12-`\x81\x98K\x81\xf1\xf0\xa0\xea\xeb\xcc\x83\x91\x85\x90\xfb#%\xea\xc3\x06[)0\x03^\x14\xdc\xbe\x959\xb6\xa2\x0b\xbc\x18F\x89\x8a\xb0\xc1O\x14\xf8\x09\xde\xd4\x19\xbd\x8b9\xb4a@%x\x92\xa2\xc0\x0eXa&\x05\xdcJ\xf0\xa8a\xef\xf7J\x99\x13\xdb&_Y\x0d\x1e\xcd\xa3\xc0\x0aX\xe11J\x1c\x09\xefj\x9c\xf1\xc7\x01\xa3\xc6\xee\xc5Rf\xe1\xe3\xb1{\xf1\xc0\xad\x97\xb5\xa9\x04\xef\xb6P`!\xac0\x80\x12\x1d\x90\x03)f!\x01\xf3\xeaQb\x0a\xac\xd0\x9e\x12\x03\x91\x03)f!\x01\xf3N\xa1\xc4\xfd\xb0\xc2\x11\x94x\x1c9\x90b\x16\x120\xaf;%\xba\xc3\x0a\x15]\x0a\xccB\x0e\xa4\x98\x85\x04\xcc\xbb\x9f\x12'\xc3\x0e?R`s!\xccK1\x0b\x09\x98\xf7WJ\xfc\x06v\x98N\x89ca^\x8aYH\xc0\xb8\x8a\xbb(\xb0\x0e\x96x\x84\x12\xb7\xc0\xbc\x14\xb3\x90\x80q\xa7Q\xe2\x03X\xe2VJ\xbc\x02\xf3R\xccB\x02\xc6\x0d\xa5\xc4\x13\xb0\xc4\x85\x94X\x1b\x83q)f!\x01\xe3fP\xa2?,\xd1\x8c\x22G\xc0\xb8\x14\xb3\x90\x80i\xc5;(q\x11,QTF\x89\x1b`\x5c\x8aYH\xc0\xb4\x93)r\x18l\xb1\x94\x12\xcf\xc3\xb8\x14\xb3\x90\x80i\x83(\xb1\xdb\x81-\xde\xa6\xc4\x0a\x18\x97b\x16\x120m\x1a%\x16\xc3\x1a\xa3)\xd2\x04\xa6\xa5\x98\x85\x04\x0cs\xb6R\xe2\x15X\xe3j\x8at\x83i)f!\x01\xc3N\xa0\xc8\xbd\xb0Fc\x8a<\x0f\xd3R\xccB\x02\x86\xddE\x91\x0e\xb0\xc7rJl\xab\x02\xc3R\xccB\x02\x86-\xa1\x84[\x0b\xf6\x98D\x91\xcbaX\x8aYH\xc0\xac\xe3(\xb2\x08\x16\xb9\x9e\x22\xaf\xc3\xb0\x14\xb3\x90\x80Y#)2\x06\x16iA\x91\xdd5aV\x8aYH\xc0\xa8\x82\x15\x14\xe9\x04\x9b\xac\xa1\xc8u0+\xc5,$`T[\xca\xd4\x85M&Sd&\xccJ1\x0b\x09\x185\x9e\x22\x8ba\x95\x9b)\xe2\x1e\x08\xa3R\xccB\x02&\x15\xad\xa7\xc8XX\xe5\x18\xca\xf4\x85Q)f!\x01\x93\xdaQ\xe62X\xa5`\x13E\xe6\xc1\xa8\x14\xb3\x90\x80I\x93(S\x1fvy\x932\xcd`R\x8aYH\xc0\xa0*\xdb(\xb2\x04\x96\x19@\x99{aR\x8aYH\xc0\xa0k(\xf3\x18,s\x22e6V\x85A)f!\x01s\x0a\xbe\xa1\xcc\x95\xb0L\xd1v\xca\xf4\x83AO2\x0b7\xc1\x9c\x0e\x14:\x10\xb6\x99N\x99\x15\xc50\xe7\x1ef\xa1\x13\xcc\xf9\x842\xdf\xc1:\x09\x0a\xc5a\xce\x8d\xcc\xc2\xc90\xe64\x0a\x8d\x84u\x0e\xa1\xd0\xe2\x18\x8c\xe9\xc0,4\x861oS\xa85\xec3\x87B\xedaL\x03\x97\xfb\xb42\x06S\x8e\xa6\xd0\xf7\xb0P/\x0a}\x04s>\xe6>\x8d\x851\x93(\x94\x84\x85\xea\x94Q\xe8\x14\x18\x93\xe0>\x9d\x0bS\x0e)\xa3PK\xd8h*\x85\xde\x841\x8dK\xb9\x0f\xab\x8aa\xca\x18\x0a}\x0d+u\xa5\xd4\x910f<\xf7\xa1\x07L\xa9\xbd\x83Bw\xc1JUwRh\x0a\x8ci\xb0\x83{\xf5\xb5\x03S\x1e\xa1Ts\xd8)M\xa9\xf3`\xcc]\xdc\xab\xf3aJ\xcb\x0c\x85\x16\xc0R\x97PjI1L)x\x8b{1\x1c\xc6|@\xa9A\xb0T\x85M\x94\x1a\x08c\xaa}\xc9=z5\x06S\xae\xa4X\x13\xd8\xea\x09Jmm\x08c\x1a}\xcd=\x98^\x02S\xaa\xae\xa4\xd4\x5cX\xeb\x0c\x8aM\x829\x07Lc\xb9\x1ev`\xcc\xfd\x14\xeb\x07k\x15\xac\xa4\xd8)0\xa7\xf0\xbe\xdd\xfc7\x9b\xaf\x839\xcdvS\xca=\x08\xf6z\x90b\xf3\x0ba\xd0\xa1\x93\xf9k\xbbG\xd7\x82A\xd3(6\x0b\x16;\x81r\xbd`\xd4\x09\xa3\x97\xf1\x7f\xcd\x1f\xde\x14&\xb5\xa7\xdc%\xb0\xd9\x22\x8am\xa8\x05\xc3\x8e\xbaa\xf8\x13oM\x19{G\xbc\x11\xcc\xaa\xf8\x03\xc5\x96\x16\xc0fWQn\x02\xc2\xe2\x01\xca\xf5\x82\xd5\x8a~\xa2\x5c\x17\x84\xc39.\xc56T\x81\xdd\xfaPnKS\x84A\xbd\xd5\x94\xfb\x13,Wu#\xe5\xe6\x15\xc3~\xb1w)\xb7\xab>lw\x0f=\x18\x05\xfb%\xe8\xc1\xd3\xb0^\xdd\x9d\xf4\xe0B\xd8\xee\xc4Rzp\x14\xec7\x8e\x1e\xack\x08\xbbU\xff\x81\x1e\xbc\x8b\x1084C\x0ff\x15\xc2j/\xd2\x8bs\x11\x06/\xd1\x8b;a\xb3\xeb\xe8\xc5\xa2\x18\xc2\xe0xz\x919\x15\xf6:b\x07\xbd\xe8\x8epx\x9f^\xacj\x04[\xd5YJ/~\xae\x80p8\x97\x9e,\xa9\x0d;\x95|FO\x06#,\xe6\xd3\x93OK`\xa3\xa2i\xf4dU\x09\xc2\xe2Jz3\xad\x08\xf6\x89=Ko\xaeEh8\xcb\xe8\xcd\xc4\x18\xacs?\xbdYX\x88\xf0\xe8L\x8fF\xc06\xb7\xd0\xa3s\x10&3\xe8Q\x7f\xd8\xe52\x97\xdeLE\xa8\x1cQJo\xdc\xae\xb0\xc9\x19\xbb\xe8M\xd9\xef\x10.\x0f\xd2\xa3\xd2\xf3`\x8f\x96\x9b\xe9\xd1x\x84L\xf5\xd5\xf4h\xdb\xe9\xb0E\xcb\x9f\xe9\xd1\xd6z\x08\x9bn\xf4jWg\xd8\xa1\xedfz5\x18\xa1\x13\xfb\x98^ez\xc1\x06\x1d\x7f\xa1W\xcb+#|\x8e\xcb\xd0\xb3\xbb\x11|=3\xf4\xec*\x84\xd1xz\xf7x!\x02n8\xbd\xfb<\x860\xaa\xb5\x81\xde\xbdV\x09AV8\x9e>h\x8bp\xba\x91>\xf8\xa0\x06\x82\xab\xe2\xab\xf4\xc1K\x08\xa9\xc2\xf9\xf4\xc1\xa2\x03\x11T\x07\xcc\xa6\x0fV\xd7FX\xb5\xa1\x1f\xfe~$\x82\xa9\xf1B\xfa\xe1b\x84\xd7s\xf4\xc3\x8e\xeb\x10D\x97m\xa6\x1f\x9eB\x885\xd8D_\xbcX\x1dAS\xf91\xfabY5\x84Y'\xfa\xe3\x87\xd6\x08\x96\xdf}I_\xb8\xa7!\xdc\xc6\xd2\x1f\xbb\x07\xc4\x10 =v\xd0\x1f#\x11r\x95\x16\xd2'S\xeb (\xaa\xa7\xe9\x93/+\x22\xecZl\xa7OV\x9d\x89`8\xf1\x07\xfa\xa4\xb4\x15\xc2\xaf;\xfd\x92\xf9S\x11\xf2\xaf0QJ\xbf\xdc\x81(\x98D\xdf,>\x1b\xf9v\xea\x02\xfa\xe6\x13\x07QPm)\xfd\xf3\xca!\xc8\xa7\x03\x9f\xa7\x7fv4C4\x1c\xb7\x8b\xfe\xd99\xac2\xf2\xa5\xf8\xb6m\xf4QoD\xc5\xad\xf4\xd3\xdf;!?\xce\xff\x96~ZU\x84\xc8x\x83\xbe\x9a\xf1;\xe4^\x93\xd7\xe9\xb3\xb4\x83\xa8\xa8\xb5\x9c\xbe*\x1b]\x03\xb9Ur\xcf/\xf4]\xdaAT\x9cZF\x7fm\xbe\xbf\x01r\xa7\xce\xdd\x1bhB\xdaAT\x0c\xa5\xdfv=\xde\x0c\xb9\xd1\xf8\x91\x9d4$\xed \x22\x0af\xd0w\x99\x97[\xc3\xbccRe4'\xed \x22\x0eXH\x03f\x9e\x07\xb3\xcex\x87f\xa5\x1dDD\xc3\x1fi\xc2\xfc+\x1c\x98R\xd4\xe93\x1a\x97v\x10\x11\x87\xaf\xa3\x11\xeb\xc6\x9e\x1a\x83\xff\x0aN\x1d\xb7\x9e\xb9\x90v\x10\x11'n\xa7!\xcbG\x1c\x07\x7f\xb5\xfa\xf3r\xe6J\xdaAD\x5cPJc\xbe\x1d\xd6\x1c~iv\xe77\xcc\xa5\xb4\x83\x88\xe8F\x93\xbe\x18\xd8\x1c\x9e\xc5Z\xf4\x9b\xc7\x5cK;\x88\x88\xdbh\xd6\x9a\xc97\x1fS\x00\xa9\x82\xa3o\x9a\xbc\x86\xf9\x90v\x10\x11\x0f\xd1\xb8Mo\x0e\xfc}\x11\xf6W\xe1q}\xa7l`\xde\xa4\x1dDC,\xc5\x5c\xd8\xf1\xde\xf0.'\xd4@v\x0e8\xfe\x8a\xa1ooa~\xa5\x1dDC\xf1t\xe6\xcc\xba\x8f&\x0c\xb9\xfc\xb8\xea\xd8\x93\xea\xad.\x1b2a\xceZ\x06B\xdaA4T\xfd\x9c9\xb6f\xfe\x87\xd3^z\xfa/\xf7\xde~S\xbc\xd3\x05\x97^\xdd{\xd0\xbd\x0fOxe\xfa'_\xada\xa0\xa4\x1dDC\xdd\xef\xa8\xca\x93v\x10\x0dMWS\x95'\xed \x1a\x9a\xfdHU\x9e\xb4\x83hh\xb8\x88\xaa\x9bX\x09a\xe6\xdc\xe72\x1cJ\xefp\xf0\x0fN\x9a>\x9bw\x10B\xed\xc2\xf5\x0c\x83/[\xe1\xbf9i\xfalm[\x84\xda\xc1\x7f\xa5\xf5\xdc\x91\x15\xf1ON\x9a>+\xed\x86P\x8b\xc5\xd7\xd1n\xcbN\xc3\xff\xe3\xa4\xe937\x8ep\xab\xf5\x14m\xf6T5\xfc\x8a\x93\xa6\xcf\xdc\xee\x08\xb9S\xbe\xa2\xadV_\x8c\x7f\xe5\xa4\xe93\xb7;B\xaeh\xd0\x0eZ\xe9\xa5\xda\xf8wN\x9a>s\xbb#\xec\x1a\xbfM\xfb\xfc\xedt\x94\xcbI\xd3g\xee5\x08\xbdN+h\x97\x15W\x17`\x0f\x9c4}\xe6^\x83\xd0\xab6:C{l\x1bZ\x19{\xe6\xa4\xe93\xf7Z\x84_\xab\xb9\xb4D\xe6\xc9\x06\xd8+'M\x9f\xb9\xd7\x22\xfcb\x1d\x17\xd0\x06\xef\xb5\xc4\xbe8i\xfa\xcc\xbd\x0e\x11\x10\xeb\xb8\x80A\xb7\xb8\x1d\xb2\xe0\xa4\xe93\xf7:DA\xac\xe3\x02\x06\xd9\xda\x1b\x1dd\xc5I\xd3gn\x0fDB\xac\xe3\x02\x06\xd5\xfa\xe1\xd5\x91-'M\x9f\xb9=\x10\x0d\xb1\x8e\x0b\x18DKn\xa8\x8c\xfd\xe0\xa4\xe93\xb7\x07\x22\x22\xd6q\x01\x83f\xe6E1\xec\x1f'M\x9f\xb9\xd7#*b\x1d\xe73@vO<\x16\xfb\xcfI\xd3g\xee\xf5\x88\x8e?<\xbd\x9d\xc1\xb0\xf1\xbe\x03!\xe2\xa4\xe93\xf7\x06DH\xf5\x1b\xe61\xff\xbe\xeb]\x02)'M\x9f\xb97 R\x8e}d\x13\xf3\xc9\x9d\xd5\xa1\x00\x1e8i\xfa\xcc\xed\x89h\xa9\xdcu6\xf3en\xbf\x83\xe0\x91\x93\xa6\xdfz\x22j\x0e\x7f`\x0dso\xc1\xa0&\xf0\x81\x93\xa6\xdfnD\xe4\x14u|y+s\xe9\xeb\xbb\x9a\xc3'N\x9a~\xbb\x11\x11T|\xe6\xa8%\xcc\x8d\xef\x93-\xe1#'M\xbf\xf5B4\x1dz\xcb\xf4]4\xec\xbb\x91\xad\xe13'M\xbf\xf5BTU\xed\xf0\xc4*\x9a\xb2\xe4\xb1+\x0f\x84\x01N\x9a~\xeb\x8d\xe8\x8a\xb5\xba\xe3\x932\xfam\xf1\xd8\xcb\xea\xc3\x14'M\xbf\xf5F\xa4U:\xf1\xc6'\xbe(\xa5?\xdcEc:\xd5\x85Q\x0d\xe8\xbb\x9b\x10y\x15\x8e\xef1~\xde.zQ\xfa\xf5\xab\xc9\x0e\xb5`\x5c\x09\xfdw\x13\xd4?\x14\x1f{\xcd\xa3\x9fl\xe4~\xdb\xf4\xc93\x89\xf6\x87\x17!7b\x19\xfa\xef&\xa8\x7f\xaa\xd2\xec\x8c\xae\x83\x1e\x99\xf2\xf9j\x97\xfbP\xb6\xee\xdbi\x0f\xdd\xd0\xb6>rk3\x0d\xb8\x19\xea_\x15\x1f\xd2\xe6\xf2~\xf7\x8d\x1c3\xee\xc9g_x\xf9\xf5\xa9\xef\xcd\xfex\xde\x82/\xe7\xbc\xf9\xdc\x98\xe1}\xe2\x97\x9cv\xf4\xc1\xd5b\xc8\x8b\xe54\xe1f(K,\xa6\x11\xb7@\xd9\xe1U\x9aq\x0b\x94\x15z\xd1\x90[\xa1l\xd0\x9c\xa6\xdc\x0ae\x83\x154\xa5\x0f\x94\x05&\xd0\x98>P\xc1\xd7\x95\xe6\xf4\x85\x0a\xbc\xcakhN_\xa8\xc0\x1bH\x83\xfaA\x05]\xc9z\x1a\xd4\x0f*\xe8\x06\xd3\xa4\xfeP\x01Wm#M\xea\x0f\x15p\xbdi\xd4\x00\xa8\x80\x9bH\xa3\x06@\x05[\xa5y4j T\xb0\x1d\xb4\x96F\x0d\x84\x0a\xb6\xb6\xa54*\x01\x15l\xdd\x5c\x1a\x95\x80\x0a\xb6\xb8K\xa3n\x83\x0a\xb6\xb8K\xa3n\x83\x0a\xb6\xb8K\xa3\x06A\x05[\xdc\xa5Q\x83\xa0\x82-\xee\xd2\xa8\xdb\xa1\x82-\xee\xd2\xa8\xdb\xa1\x82-\xee\xd2\xa8\xc1P\xc1\x16wi\xd4`\xa8`\x8b\xbb4j\x08T\xb0\xc5]\x1a5\x04*\xd8\xe2.\x8d\xba\x03*\xd8\xe2.\x8d\xba\x03*\xd8\xe2.\x8d\x1a\x0a\x15lq\x97F\x0d\x85\x0a\xb6\xb8K\xa3\xfa@\x05[\xdc\xa5IegA\x05[\xdc\xa5I\x1b\x9a@\x05[\xdc\xa5I\x0bK\xa0\x82-\xee\xd2\xa4\xc9P\x01\x17wi\xd2\x05P\x01\x17wi\xd0\xdfbP\x01\x17wiPg\xa8\xa0\x8b\xbb4\xe7\xebB\xa8\xa0\x8b\xbb4\xa7\x1bT\xe0\xc5]\x1a3\x0b*\xf8\xe2.M)\xab\x01\x15|q\x97\xa6\x5c\x0ee\x81\xb8KC\x9e\x83\xb2A\xdc\xa5\x19\xeb\x0b\xa1l\x10wi\xc6\x91PV\x88\xbb4\xe2\x1c(;\xc4]\x9ap5\x94%\xe2.\x0dH@\xd9\x22\xee\xd2\x7f\x0fAY#\xee\xd2w)({\xc4]\xfa\xedq(\x8b\xc4]\xfal\x18\x94M\xe2.\xfd\xd5\x03\xca*q\x97\xbej\x07e\x97\xb8K?\xb5\x82\xb2L\xdc\xa5\x7fJ\xabC\xd9&\xee\xd273\xa1\xec\x13w\xe9\x97~P\x16\x8a\xbb\xf4I3(\x1b\xc5]\xfa\xe2[(;\xc5]\xfa\xe1~(K\xc5]z\xb7\xad.\x94\xad\xe2.=\x1b\x0ee\xaf\xb8K\x8f\xd6U\x83\xb2X\xdc\xa57\xb7BY-\xee\xd2\x8b\xa5\x15\xa0\xec\x16w)\xb7\xe5wP\xb6\x8b\xbb\x94\xca\xb4\x83\xb2_\xdc\xa5\xd0@\xa80\x88\xbb\x14y\x16*\x1c\xe2.\x05\xa6V\x80\x0a\x89\xb8\xcb\xfd6\xb5\x22Th\xc4]\xee\xa7\xa9\x15\xa1B$\xeer\xbfL\xad\x08\x15*q\x97\xfbajE\xa8\x90\x89\xbb\xcc\xda\xd4\x8aP\xa1\x13w\x99\xa5\xa9\x15\xa1B\xa8\xe36f\xe5\x99\x0aP\xa1t\xf42\xee[Y\x1f\xa8\xb0\xaa5\x93\xfb\xb2\xe1l\xa8\xf0r\xfe\xe2r\xaf\x164\x85\x0a\xb5c\xde\xe4\x9e-\x8b\x17B\x85\xdd\xc93Y\xbe\x9f{\x17CE\xc1Y\x13\xd7\xf3_\x95\xcd\xba\xb92TT\x14\xb6\xb9\xefK\xfe\x9f\x0d\x93.\xaf\x01\x151\xd5\x9b\xb7\xbd\xa2Orp\xf7\xf3\x8f\xad_\x00\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\xa5\x94RJ)\x15J\xff\x09/\x9b\x0e\xdc\xb0\x5c\x1aa\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x03\xa9\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x03KIDATx\xda\xd4W\xcdk\x13A\x14\x7f\x93\x9d\xcdWs\xc8\xc1\xe6RZAj\x03\xb6H\xdb`\x9b\xda\xd8\x8f\x80\x0azmZ\xfc\x0b\xbc+\x88\xff\x83\xf4PDA\xf0\x1f\x90\xeaE\xb1\xa0B/F\x1b?\xaa=\xa8\xa0\xf6b\xa1XTj\x0e%i\xd2d\xc7\xf7\xa6\xd9\xb8\x99\xec\x96\xacn+\xbee\x98\xccn\xe6\xfd~\xf3\xe6\xbd7o\xd8\xf7\x9f\xdf`\xe0\xf8 XE\xd34\xd0u]P\xef\x85T\xabU\xd8\xd9\xd9a\xd4\xab\xc2\xed& 8D\xa3Q\x08\x06\x83\x9e\x10\xd8\xde\xde\x86|>\x0f\xce\x04D\xe3K\x9f\xcf\x07\xc1@\x10\xc2\xe1\xb0'\x04H?\xe9Tq\x1c-@\x7f\x14\xf4\x08\xe1\x11\xbe\xb0\x05\xaf\x13\x10\xcaW\x13\xdc3\x02B\x80\xf9\xb4\xb4\x05\x92\xb0\xc7\x04$\x86\xf8_,@\xde\xfa`\xe1>x)C\x89ag\x0b\xa0\x87vh\x5c\x9b\xd5|Z\x8a1\xd6A\xf1?\x9e\x9a\x00\x1c{\x93\x07\x8c*\xe8\x5c\x17\xbe\x90\x8f\xac\xb1\x8e\xe3l\xb5R\xbdl\x18\xc6:\xeb\xec\xec\x8cc\xb8\xe5\xfc\x01\x7f\xd4\xef\xf7\xcbpa\xf8\xec\x87\xd0\x16 (\x94\xcbe(\x97\xca\xf9B\xa1\x90\xe4\xed\xb1\xf69d\x15\x0d\x85B2\x03\xa2\x05`?\x85\xfc!\x10\x08@\x91\x17\xa3m\x91\xb69\x8e\x80is\xe5^:\xde^BXD\x02\xd3s\x9a\xa3\xc3\xe948\x08`\x95\x04as\xd3\xe4\x07M\x80\x84\xb0\xb9\x99t\xfe\x89 ,7\x84!=\xd3*4^x\xf4\xd0S\xacsg\xcf\xef\x1eHV\x1c\xc4\xe6v\x8e\x87\xce\x01\xa3\xc3)\x86D\x84:\xc9\xad\xd0bP\x07\x0b\xb7\x85\x059\xbb\x1a\x11\xb6\x04(\x13R\xf2\xe0\x9c\x83:\xc9\xadP\xccW*\x15\xa9S\xc5q$@cr\x10*L(B\xfe6\xeeMp{\x02\x86h\xf2\x01z''\xd4\xbeQ{\xb2\xf8\xd8\x15\xf0\xe9\xf4\x99\xdd\xdc\xa2\xe8Rq\xb8]\xe1Q?\x0dk=\xad`4\x99\x92\xa6lEh\xeb\xc8rdEU\x97\x8a\xe3\xb8\x05\xd6\x9a\x80\xda\xb3\x5c\xd6\x95\x05&\xc7\xd2\xbf\xcd\xeeP_\xc8-\x90,m>Xk\x02j#C'm\x8bJ;\xa13\xc5\xcc\xae\xaa\xae\xa6D\x14\x8b\xc5`ss\xb3\x99\x80\xa5\xd1\x0a\x96^>we\x81\x89\xd4d\x93\x1e\x95\x00a\xf3\x99\x0b\xd3?n\x5c\xbfy\xc8z\x0a6M\xc4'yb\xc4\xb5\x0f\xecE\x80~\x136\x9f\xcaL]\xdd\xf8\xbaq\xfb\xee\xfc=h8\x17\x14\x1f\xc8\xbdZre\x81\xb1\xd1qG\x1f\xa0~z&\x03\x84\xcd\xe8f\x842\xbb\xf6e\xed\xd2\xf2\xf2\x1bX\xfd\xbc*\xf7z\xfe\xce<\xc3U\x08,T\xe4\xb8T*\xb9\xf6\x01\xea\xb1\xf0\xa0\xb9,3\x93\x917\xad\xee\xa3\xdd\x90H\x0cB\xd7\xe1\xaek\xbd\xf1\xbe+\x92\xc0\xb1\x9e^\xf8\xf0\xe9}\x1c\xe7\xa6\xb1\xf5\x93\x92\xfe\xbe\x81\x8b\xa1pH\xa6O\x02\xce.=ue\x81\xd4\xc8\xa9]\x02\x98\x09\x8b\x85\x22[y\xf7\xf6V\xed\xd3\x0a\xb6E\xc4\xfcHz%\x81\x9e#qi~\xb3\xc9\xf8\xc4}\x8cD\x22u\x02\xb4\x127\x16 \xcb\x99\x04\xb6\xb6\xb6\x18\xf9\x0f\x95z\xaaoqk\xe2\xb1V\xad\x5c\xe3\x8d\xe1\xa2s\xc0\xc2\xb5\xe5s\xbe\xa1\xb4\x13\xe0H\xde\xfeb\xa2\xdc\x0d$\x01\xce\xff\xec>P[\xe9\x9eW3\xdb\xc9\x98\xa7_\xbc\xceyR\x0b\x90\x8f9\xc9/\x01\x06\x00oO\x87\x87}~\xb3\xc0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\x00\x00\x02\x9dIDATx^\xedY\xc1\xebqA\x14\xbdOoC\xa1H\x14\xa5\xec\xec\xac\x84\x85\x85\x7f\x5c$v\xb2S\x14\x16\x16J,\x08%\xf5\xbe\xee\xab[\xfa\xee\xef}\xc74\xd3\xa7_\xcd\xa9i\xe65c\xe6\x9c{\xee\xbc\xf7\xe6\x09\xa2(\xa2\xdf\x8c\x14}\x0f^\x80\x17\xe0\x05x\x01^\x80\x17\xe0\x05\x84\x84A\x9b\xcd&\x92v\x10\x04R'\xb6\xa5\xfe\x1bQ\x14\xa9:\xa9\xcdh4\x1a\x01\x01\x042\x18\x91\xcff\xb3B\x0c\xd6\x00L\x10\xd6\xd7\xeb\xf5#\x11\xe1\xa7\xe4K\xa5\x12\xfdo\xb0\x08\xe6 \x22\x8c\x05\x08\xf9L&C\xb7\xdbM\xa2\x9b\x14q3\x07\x92\x1dap\xc0D\x84\x93M\x8c\xc8s[_K\xd1}N\x82\x81\x05\xe0\xc5\xa4_\x91N\x9eK\x8f\xc7\x22\xcc\x05$M\xa6k\xdd\x87\x0arT\x09t\x98B\xd8\x01\x9cBd\xe2\x80\x9b\x14\x02PD\x14Y4N\xc3\xd2\x01l-&\x87\xaf\xc1:\x16\x0e`\xe0M\xf7x\xd3v\xbb\xa5~\xbfO\xb9\x5c\x8e\x04\xf7\xfb\x9d\xd2\xe9\xb4<\xb0x|\x5c#`\x07\x8c\x80\xa3\xc4Q\xafT*B^\xc0OsM\xc6<\xe2\xf6\x02\x0c\x9f\x1b \x0d\xed\x91rM\xbcX,\xd2\xe1p`'\x0c\x04\x7fY\x80\xe4\xb2\x08\xa8\xd5j4\x9b\xcdh\xb1X\xf0\x06\x06\xbf\xfb\x82\x00D\xa8\xd5jQ\xb7\xdb\x8d7\xeeh4\xa2\xe9t\xcamD\xdc\xbd\x00\x1c9i\xeb~v\x82E\x0c\x06\x03b\x8c\xc7c\x16\x81\xe6r+\x00\xdb\x8d\x8f\x8b|\xeb\xect:\x94\xcf\xe7i\xb5Z\xa9q\x16\xebB\x01B\x02FL\xd7\xfaw\x85B\x81\x1dH\x1a\x0f\x1c\xb1p\x008\x82\x04J18\x8d\xb9u\x00\xd4\x8a,\xdfu\x14\xf1\xe7\xf3I\xfb\xfd\x9e\xca\xe5\xb2r\x08\xcc\x0b\x11\xda\xe6\xa0\x8c\x91\xfb\xfa|>'F\xb5Z%\xc6\xeb\xf5b\xf2\xf1\x93\xb9^\xaf\x9b\x90\x14\xa1.71\xfe\xb6\xd3\xeb\xf5\x98(\xbf\x8d\xf2{Q\xdc\xd7l6\xa9\xddn\xff4\x17\x88>Fh\x10}y\x09S\xf5{_\x18\x86,\x00\xce\x85S\xc8\xdc\x01l#v@\xca\xbf\xe62q\xc0y\x0a\x81M\xac\xae\xa5\x80\xb1\x98>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" -qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x09\x0aa\xba\xa7\x00p\x00i\x00c\x00o\x00n\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x08\x06`Gg\x00z\x00o\x00o\x00m\x00.\x00p\x00n\x00g\x00\x08\x0f\x9eY\x87\x00p\x00i\x00c\x00k\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0aa\xa4\xa7\x00s\x00i\x00c\x00o\x00n\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" -qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0a\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x07\x00\x00\x00\x03\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x92\xa6\x00\x00\x00P\x00\x00\x00\x00\x00\x01\x00\x00\x15\x06\x00\x00\x00|\x00\x00\x00\x00\x00\x01\x00\x00\x8c\x1f\x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00\x8f\xcc\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x02\xa6\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x002\xdd\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x01\x00\x00\x9bh" +qt_resource_data = "\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xa4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xab\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" +qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x08\x0f\x9eY\x87\x00p\x00i\x00c\x00k\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" +qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x01\x90\x00\x00\x00\x00\x00\x01\x00\x01O\x99\x00\x00\x01\xa8\x00\x00\x00\x00\x00\x01\x00\x01X[\x00\x00\x01>\x00\x00\x00\x00\x00\x01\x00\x01\x0a\x08\x00\x00\x01\x5c\x00\x00\x00\x00\x00\x01\x00\x01%\xb7\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00/\xdd\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x01\x00\x00\xda\x08\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xda\x00\x00\x00N\x00\x00\x00\x00\x00\x01\x00\x00\x1f\x82\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00B=\x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00R#\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00b\x05\x00\x00\x00\xde\x00\x00\x00\x00\x00\x01\x00\x00\xcae\x00\x00\x01\x0e\x00\x00\x00\x00\x00\x01\x00\x00\xe9\x82\x00\x00\x01&\x00\x00\x00\x00\x00\x01\x00\x00\xf9s\x00\x00\x01x\x00\x00\x00\x00\x00\x01\x00\x01?\xd6\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x01\x00\x00q#\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x01\xc4\x00\x00\x00\x00\x00\x01\x00\x01t;" def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 04911f54..e9b4e3ea 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -224,7 +224,7 @@ class PickDlg(QDialog): filter_icon = QIcon() filter_icon.addPixmap(QPixmap(':/icons/filter.png')) zoom_icon = QIcon() - zoom_icon.addPixmap(QPixmap(':/icons/zoom.png')) + zoom_icon.addPixmap(QPixmap(':/icons/zoom_in.png')) # create actions self.filterAction = createAction(parent=self, text='Filter', From 4990e33d27d94d58e2568c6fc03bb9f279b94576 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:32:56 +0200 Subject: [PATCH 0468/1144] added utility functions for pick handling --- QtPyLoT.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index c621e664..ec388f08 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -80,6 +80,7 @@ class MainWindow(QMainWindow): self.filteroptions = {} self.pickDlgs = {} + self.picks = {} # UI has to be set up before(!) children widgets are about to show up self.setupUi() @@ -349,6 +350,15 @@ class MainWindow(QMainWindow): def getData(self): return self.data + def getPicks(self): + return self.picks + + def getPicksOnStation(self, station): + try: + return self.getPicks()[station] + except KeyError: + return None + def getPlotWidget(self): return self.DataPlot From 06d6060a9fc157bde5a05bb0e764fb7f5fb03240 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:37:54 +0200 Subject: [PATCH 0469/1144] added method to add picks to the picks dictionary and warn the user if he/she is about to overwrite pick information --- QtPyLoT.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index ec388f08..6f0981d4 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -503,8 +503,33 @@ class MainWindow(QMainWindow): else: print 'picks not saved and closed dialog' - def updateStatus(self, message): - self.statusBar().showMessage(message, 5000) + def addPicks(self, station, picks): + stat_picks = self.getPicksOnStation(station) + if not stat_picks: + stat_picks = picks + else: + msgBox = QMessageBox() + msgBox.setText("The picks for station {0} have been " + "changed.".format(station)) + msgBox.setDetailedText("Old picks:\n" + "{old_picks}\n\n" + "New picks:\n" + "{new_picks}".format(old_picks=stat_picks, + new_picks=picks)) + msgBox.setInformativeText("Do you want to save your changes?") + msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) + msgBox.setDefaultButton(QMessageBox.Save) + ret = msgBox.exec_() + if ret == QMessageBox.Save: + stat_picks = picks + elif ret == QMessageBox.Cancel: + pass + else: + raise Exception('FATAL: Should never occur!') + self.getPicks()[station] = stat_picks + + def updateStatus(self, message, duration=5000): + self.statusBar().showMessage(message, duration) if self.getData() is not None: if not self.getData().isNew(): self.setWindowTitle( From 5d2900510e5b986c788aec476fbc922a28dc6796 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:39:01 +0200 Subject: [PATCH 0470/1144] added methods for drawing picks to the main window --- QtPyLoT.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6f0981d4..cca4f982 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -371,6 +371,13 @@ class MainWindow(QMainWindow): return statID + def getStationID(self, station): + for wfID in self.getPlotWidget().getPlotDict().keys(): + actual_station = self.getPlotWidget().getPlotDict()[wfID][0] + if station == actual_station: + return wfID + return None + def addActions(self, target, actions): for action in actions: if action is None: @@ -528,6 +535,38 @@ class MainWindow(QMainWindow): raise Exception('FATAL: Should never occur!') self.getPicks()[station] = stat_picks + def drawPicks(self, station=None): + # if picks to draw not specified, draw all picks available + if not station: + for station in self.getPicks(): + self.drawPicks(station) + return + # plotting picks + plotID = self.getStationID(station) + ax = self.getPlotWidget().axes + ylims = np.array([-.5, +.5]) + plotID + phase_col = {'P': ('c', 'c--', 'b-'), + 'S': ('m', 'm--', 'r-')} + + stat_picks = self.getPicks()[station] + + for phase in stat_picks: + + picks = stat_picks[phase] + colors = phase_col[phase[0].upper()] + + mpp = picks['mpp'] + epp = picks['epp'] + lpp = picks['lpp'] + spe = picks['spe'] + + ax.fill_between([epp, lpp], ylims[0], ylims[1], + alpha=.5, color=colors[0]) + ax.plot([mpp - spe, mpp - spe], ylims, colors[1], + [mpp, mpp], ylims, colors[2], + [mpp + spe, mpp + spe], ylims, colors[1]) + self.draw() + def updateStatus(self, message, duration=5000): self.statusBar().showMessage(message, duration) if self.getData() is not None: @@ -554,6 +593,9 @@ class MainWindow(QMainWindow): self.data = Data(self, evtdata=event) self.dirty = True + def draw(self): + self.getPlotWidget().draw() + def closeEvent(self, event): if self.okToContinue(): self.closing.emit() From dd8766277af34c93efb0b5802e102cee30b1851d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:39:51 +0200 Subject: [PATCH 0471/1144] last four commits might not work without the imports coming with this one --- QtPyLoT.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index cca4f982..9f1533ed 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -33,7 +33,8 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ QFileInfo, Qt from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ - QDialog, QErrorMessage, QApplication, QPixmap + QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox +import numpy as np from obspy.core import UTCDateTime from pylot.core.read import Data, FilterOptions From 3dd02d7f0c056283c0dabf2f9c09af13fec8a23f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:44:06 +0200 Subject: [PATCH 0472/1144] do not print information to standard out but into the status bar (recognizable for the user) --- QtPyLoT.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9f1533ed..ba6f4ffa 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -501,15 +501,15 @@ class MainWindow(QMainWindow): wfID = self.getWFID(gui_event) station = self.getStationName(wfID) - print 'picking on station {0}'.format(station) + self.updateStatus('picking on station {0}'.format(station)) data = self.getData().getWFData() pickDlg = PickDlg(self, data=data.select(station=station), station=station) if pickDlg.exec_(): - print 'picks accepted' - self.getData().applyEVTData(pickDlg.getPicks()) + self.updateStatus('picks accepted ({0})'.format(station)) + self.addPicks(station, pickDlg.getPicks()) else: - print 'picks not saved and closed dialog' + self.updateStatus('picks discarded ({0})'.format(station)) def addPicks(self, station, picks): stat_picks = self.getPicksOnStation(station) From e051be8a06f862866487646c1933f32c5e05eea9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:45:42 +0200 Subject: [PATCH 0473/1144] code cosmetics and give some hint on the usage of the overview window in the status bar --- QtPyLoT.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index ba6f4ffa..d77e2c3d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -124,7 +124,10 @@ class MainWindow(QMainWindow): # create central matplotlib figure canvas widget self.DataPlot = MPLWidget(parent=self, xlabel=xlab, ylabel=None, title=plottitle) - self.DataPlot.mpl_connect('button_press_event', self.pickOnStation) + self.DataPlot.mpl_connect('button_press_event', + self.pickOnStation) + self.DataPlot.mpl_connect('axes_enter_event', + lambda event: self.tutorUser()) _layout.addWidget(self.DataPlot) openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) @@ -581,6 +584,9 @@ class MainWindow(QMainWindow): "PyLoT - seismic processing the python way[*]") self.setWindowModified(self.dirty) + def tutorUser(self): + self.updateStatus('select trace to pick on station ...', 10000) + def printEvent(self): pass From 5507a22865c9c4938d665809be0f7cd8c4e4180e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:46:27 +0200 Subject: [PATCH 0474/1144] actually draw picks into the overview window --- QtPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index d77e2c3d..a2dec7c9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -511,6 +511,7 @@ class MainWindow(QMainWindow): if pickDlg.exec_(): self.updateStatus('picks accepted ({0})'.format(station)) self.addPicks(station, pickDlg.getPicks()) + self.drawPicks(station) else: self.updateStatus('picks discarded ({0})'.format(station)) From a8fe4b741930c103d212ce313c432e95977b6f5f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:47:55 +0200 Subject: [PATCH 0475/1144] give variable declaration a sense --- QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a2dec7c9..2a547fb6 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -438,11 +438,11 @@ class MainWindow(QMainWindow): self.plotWaveformData() def adjustFilterOptions(self): - filteroptions = None + filteroptions = self.getFilterOptions() fstring = "Filter Options ({0})".format(self.getSeismicPhase()) filterDlg = FilterOptionsDialog(titleString=fstring, parent=self, - filterOptions=self.getFilterOptions()) + filterOptions=filteroptions) if filterDlg.exec_(): filteroptions = filterDlg.getFilterOptions() self.setFilterOptions(filteroptions) From b8aabfce99e46e045567c8c8bedb85eb97ff33eb Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 10:49:42 +0200 Subject: [PATCH 0476/1144] implement changes proposed by deprecation warning from matplotlib --- pylot/core/util/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e9b4e3ea..b8f2e784 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -10,7 +10,7 @@ import numpy as np from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvas -from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg +from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT from matplotlib.widgets import MultiCursor from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout, \ @@ -217,7 +217,7 @@ class PickDlg(QDialog): def setupUi(self): # create matplotlib toolbar to inherit functionality - self.figToolBar = NavigationToolbar2QTAgg(self.getPlotWidget(), self) + self.figToolBar = NavigationToolbar2QT(self.getPlotWidget(), self) self.figToolBar.hide() # create icons From b8c92ed5519f90cddaedd24ce78b087f575b311e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 11:02:46 +0200 Subject: [PATCH 0477/1144] preparing to parse the picks to the picking window --- QtPyLoT.py | 3 ++- pylot/core/util/widgets.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 2a547fb6..319ad8db 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -507,7 +507,8 @@ class MainWindow(QMainWindow): self.updateStatus('picking on station {0}'.format(station)) data = self.getData().getWFData() pickDlg = PickDlg(self, data=data.select(station=station), - station=station) + station=station, + picks=self.getPicksOnStation(station)) if pickDlg.exec_(): self.updateStatus('picks accepted ({0})'.format(station)) self.addPicks(station, pickDlg.getPicks()) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b8f2e784..df7bf40c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -158,14 +158,18 @@ class MPLWidget(FigureCanvas): axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) class PickDlg(QDialog): - def __init__(self, parent=None, data=None, station=None, rotate=False): + def __init__(self, parent=None, data=None, station=None, picks=None, + rotate=False): super(PickDlg, self).__init__(parent) # initialize attributes self.station = station self.rotate = rotate self.components = 'ZNE' - self.picks = {} + if picks: + self.picks = picks + else: + self.picks = {} self.filteroptions = FILTERDEFAULTS # initialize panning attributes From 960f9ca71a5c8a7a5dd8366737f13c3d5b4d66ed Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 11:21:06 +0200 Subject: [PATCH 0478/1144] use only the high resolution icon --- QtPyLoT.py | 4 ++-- icons.qrc | 4 ++-- icons/{pick.png => pylot.png} | Bin icons_rc.py | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) rename icons/{pick.png => pylot.png} (100%) diff --git a/QtPyLoT.py b/QtPyLoT.py index 319ad8db..b568dd7d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -108,7 +108,7 @@ class MainWindow(QMainWindow): self.startTime = UTCDateTime() pylot_icon = QIcon() - pylot_icon.addPixmap(QPixmap(':/icons/pylot.ico')) + pylot_icon.addPixmap(QPixmap(':/icons/pylot.png')) self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(pylot_icon) @@ -629,7 +629,7 @@ def main(): pylot_app = QApplication(sys.argv) app_icon = QIcon() - app_icon.addPixmap(QPixmap(':/icons/pick.png')) + app_icon.addPixmap(QPixmap(':/icons/pylot.png')) # set Application Information pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") diff --git a/icons.qrc b/icons.qrc index 5f25e229..0d56e892 100644 --- a/icons.qrc +++ b/icons.qrc @@ -1,7 +1,8 @@ icons/pylot.ico - icons/printer.png + icons/pylot.png + icons/printer.png icons/key_E.png icons/key_N.png icons/key_P.png @@ -13,7 +14,6 @@ icons/key_V.png icons/key_W.png icons/key_Z.png - icons/pick.png icons/filter.png icons/zoom_in.png icons/zoom_out.png diff --git a/icons/pick.png b/icons/pylot.png similarity index 100% rename from icons/pick.png rename to icons/pylot.png diff --git a/icons_rc.py b/icons_rc.py index e6ee0972..0c4e7f2f 100644 --- a/icons_rc.py +++ b/icons_rc.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Di. Juli 7 10:05:17 2015 +# Created: Di. Juli 7 11:18:37 2015 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xa4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xab\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" -qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x08\x0f\x9eY\x87\x00p\x00i\x00c\x00k\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" -qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x01\x90\x00\x00\x00\x00\x00\x01\x00\x01O\x99\x00\x00\x01\xa8\x00\x00\x00\x00\x00\x01\x00\x01X[\x00\x00\x01>\x00\x00\x00\x00\x00\x01\x00\x01\x0a\x08\x00\x00\x01\x5c\x00\x00\x00\x00\x00\x01\x00\x01%\xb7\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00/\xdd\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x01\x00\x00\xda\x08\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xda\x00\x00\x00N\x00\x00\x00\x00\x00\x01\x00\x00\x1f\x82\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00B=\x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00R#\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00b\x05\x00\x00\x00\xde\x00\x00\x00\x00\x00\x01\x00\x00\xcae\x00\x00\x01\x0e\x00\x00\x00\x00\x00\x01\x00\x00\xe9\x82\x00\x00\x01&\x00\x00\x00\x00\x00\x01\x00\x00\xf9s\x00\x00\x01x\x00\x00\x00\x00\x00\x01\x00\x01?\xd6\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x01\x00\x00q#\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x01\xc4\x00\x00\x00\x00\x00\x01\x00\x01t;" +qt_resource_data = "\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xa4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xab\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" +qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" +qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00B=\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x01O\x99\x00\x00\x01\xaa\x00\x00\x00\x00\x00\x01\x00\x01X[\x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x01\x0a\x08\x00\x00\x01^\x00\x00\x00\x00\x00\x01\x00\x01%\xb7\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00/\xdd\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\xda\x08\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xda\x00\x00\x00N\x00\x00\x00\x00\x00\x01\x00\x00\x1f\x82\x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00\x9b\x7f\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\xabe\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x01\x00\x00\xbbG\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x01\x00\x00\xcae\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x00\xe9\x82\x00\x00\x01(\x00\x00\x00\x00\x00\x01\x00\x00\xf9s\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01?\xd6\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x01\xc6\x00\x00\x00\x00\x00\x01\x00\x01t;" def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) From d53a3dad047d2201509fec00f3dffca5b3cc8c65 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 11:22:19 +0200 Subject: [PATCH 0479/1144] make the test work again --- testPickDlg.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testPickDlg.py b/testPickDlg.py index 9c83f206..e49f7675 100755 --- a/testPickDlg.py +++ b/testPickDlg.py @@ -1,9 +1,15 @@ #!/usr/bin/env python import sys +import matplotlib + +matplotlib.use('Qt4Agg') +matplotlib.rcParams['backend.qt4'] = 'PySide' + from PySide.QtGui import QApplication from obspy.core import read from pylot.core.util.widgets import PickDlg +import icons_rc app = QApplication(sys.argv) From ca1b41511c7b69d69d25609d0bc24ab3ea0e9ca7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 11:23:01 +0200 Subject: [PATCH 0480/1144] draw picks from main window to the picking window --- pylot/core/util/widgets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index df7bf40c..ee117b44 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -212,6 +212,9 @@ class PickDlg(QDialog): # set plot labels self.setPlotLabels() + # draw picks if present + self.drawPicks() + # connect button press event to an action self.cidpress = self.connectPressEvent(self.panPress) self.cidmotion = self.connectMotionEvent(self.panMotion) From cf944358dd400f35b2c237950547993165024247 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 11:23:29 +0200 Subject: [PATCH 0481/1144] removed unnecessary apply button --- pylot/core/util/widgets.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index ee117b44..2796b6a5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -264,8 +264,7 @@ class PickDlg(QDialog): _innerlayout.addWidget(self.multicompfig) # add button box to the dialog - _buttonbox = QDialogButtonBox(QDialogButtonBox.Apply | - QDialogButtonBox.Ok | + _buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) # merge widgets and layouts to establish the dialog @@ -278,7 +277,6 @@ class PickDlg(QDialog): self.selectPhase.currentIndexChanged.connect(self.verifyPhaseSelection) _buttonbox.accepted.connect(self.accept) _buttonbox.rejected.connect(self.reject) - _buttonbox.button(QDialogButtonBox.Apply).clicked.connect(self.apply) # finally layout the entire dialog self.setLayout(_outerlayout) From 6078338757f4a0dadefb76c1348ed42867408aaa Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 12:14:18 +0200 Subject: [PATCH 0482/1144] added a splash screen --- QtPyLoT.py | 8 +++++++- icons.qrc | 1 + icons_rc.py | 8 ++++---- splash/splash.png | Bin 0 -> 40452 bytes 4 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 splash/splash.png diff --git a/QtPyLoT.py b/QtPyLoT.py index b568dd7d..020a4d31 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -33,7 +33,7 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ QFileInfo, Qt from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ - QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox + QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen import numpy as np from obspy.core import UTCDateTime @@ -627,6 +627,9 @@ class MainWindow(QMainWindow): def main(): # create the Qt application pylot_app = QApplication(sys.argv) + pixmap = QPixmap(":/splash/splash.png") + splash = QSplashScreen(pixmap) + splash.show() app_icon = QIcon() app_icon.addPixmap(QPixmap(':/icons/pylot.png')) @@ -639,9 +642,12 @@ def main(): # create the main window pylot_form = MainWindow() + splash.showMessage('Loading. Please wait ...') + pylot_app.processEvents() # Show main window and run the app pylot_form.showMaximized() + splash.finish(pylot_form) pylot_app.exec_() diff --git a/icons.qrc b/icons.qrc index 0d56e892..de3a672a 100644 --- a/icons.qrc +++ b/icons.qrc @@ -17,6 +17,7 @@ icons/filter.png icons/zoom_in.png icons/zoom_out.png + splash/splash.png
help/index.html diff --git a/icons_rc.py b/icons_rc.py index 0c4e7f2f..b87c5eca 100644 --- a/icons_rc.py +++ b/icons_rc.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Di. Juli 7 11:18:37 2015 +# Created: Di. Juli 7 11:48:47 2015 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xa4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xab\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" -qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" -qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00B=\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x01O\x99\x00\x00\x01\xaa\x00\x00\x00\x00\x00\x01\x00\x01X[\x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x01\x0a\x08\x00\x00\x01^\x00\x00\x00\x00\x00\x01\x00\x01%\xb7\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00/\xdd\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\xda\x08\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xda\x00\x00\x00N\x00\x00\x00\x00\x00\x01\x00\x00\x1f\x82\x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00\x9b\x7f\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\xabe\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x01\x00\x00\xbbG\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x01\x00\x00\xcae\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x00\xe9\x82\x00\x00\x01(\x00\x00\x00\x00\x00\x01\x00\x00\xf9s\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01?\xd6\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x01\xc6\x00\x00\x00\x00\x00\x01\x00\x01t;" +qt_resource_data = "\x00\x00\x9e\x04\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x80\x00\x00\x01\x00\x08\x06\x00\x00\x00#\x95\xc7f\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x17\x12\x00\x00\x17\x12\x01g\x9f\xd2R\x00\x00\x00\x07tIME\x07\xdf\x07\x07\x090\x11\xbal\xcd}\x00\x00 \x00IDATx\xda\xec}w\x94\x5c\xe5y\xfe3w\xee\xf4\x99\x9d\xb2\xb3;\xdb\x9bV\xbb*+\xd1-\x0c\xa2\x83\xb0\x0d\x1cSbL\xe28v0v|lp\x0eq\x09\x04\x88c\x1cl\x9c\xe08\x80K\x82c\xe3\xb8!sL\x5c\xe4P\x02\x18\x04\x085TWm{/\xb3\xd3{\xbf\xf3\xfbC\xbf\xf7\xf3\x9d\xbb\xb3\xb3\xbb\xd2\xae\xb4\xda\xfd\x9est\xa4\x1d\xcd\xcc\xce|\xed}\xbe\xe7m\xaa|>\x9f\x07\x07\x07\x07\x07\x07\xc79\x8c|>\x8fT*\x85\xed\xdb\xb7#\x95J!\x97\xcb\xe1\xb6\xdbn\x83$I\x10\x04\x81\x0f\x10\x07\x87\x02|Wppppp\x9c\xf3P\xa9T\xc8\xe7\xf3\xc8\xe7\xf3P\xa9T\xc8\xe5r|P888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0\x0e-\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0* \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqppp\x02\xc8\xc1\xc1\xc1\xc1\xc1I \x07\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqpp\x02\xc8\xc1\xc1\xc1\xc1\xc1\xc1\x09 \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x9c\x00rppppp,%P!h\x0e\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\xe3\x9c\x07w\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xc1\x01\x95J\xc5\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\xe5\x0aI\x92\xb8\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7J\x05W\xfe888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83c\xb9A\xe9\xfe\x95$\x89\x0f\x0a\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\xcb\x9d\x00\xcaI\x1f\x8f\x07\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x10@\xe5\xcf<\x16\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\x02\xb9\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a \x7fr\xc5\x8f\x13@\x0e\x0eN\x0098888V\x08\x09\xe4\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\xfe\xe4\xa4\x8fg\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x08 A\xa5Rq\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95F\x029\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x91?N\x00988\x01\xe4\xe0\xe0\xe0\xe0X!\x04\x90\xc7\x00rpp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x09 \x95\x82\xe1\x0a \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0cR\x9e+\x80\x1c\x1c\x9c\x00rpppp\xac(p\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15F\xfc\xf2\x12'\x80\x1c\x1c\x9c\x00rpppp\xacH\x22\xc8\xc1\xc1\xc1\x09 \x07\x07\x07\x07\x07'\x80\x1c\x1c\x9c\x00\xf2!\xe0\xe0\xe0\xe0\xe0Xn\xe0\x9d@888\x01\xe4\xe0\xe0\xe0\xe0X\x81\xe0\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x86\x5c.\xc7\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eN\x0098888\x96-x+8\x0e\x0eN\x0098888V\x18x\x0c \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0c\x5c\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x85\x81+\x80\x1c\x1c\x9c\x00rpppp\xac \xf0:\x80\x1c\x1c\x9c\x00rpppp\xac@p\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a\x03\x8f\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\x13@\x0e\x0e\x0eN\x0098888\x963\xb8\x0b\x98\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x04\x95J\x85L&\xc3\x07\x82\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95\x04\xde\x0b\x98\x83cf\x88\x8b\xf5\xc6$\xbds\x09~\xf1o\xb9\xf2\xbf\xf3\xf9<\xfb7\x07\xc7b\xedm\xf9:+\xb5\xc7\xe9y\xf4\xb7r\xbdrpp\xfc\x09\x14\xb3\xc8K\xd8,\xdf\xb3S~n\xe6\xf3y\xa8\xd5\xeai\xf6\xfc\x9c$\x80\xf9|\x1e\xd9l\x16*\x95\x0a\x92$A\x92\xa4i\xc6\x82c\xe1\x17\x92\xdc\xb0\x0a\x82P\xf0X\xb1\xe7pp\x9c*\xe9\xa3}M\xc6\xaa\xd4\xfe\xce\xe7\xf3\xc8\xe5r\x10\x04\x81\xad?A\x10\xa0V\xab\xa1R\xa9\x0a\x0e>\x0e\x8e\x85\x14\x1e\x94\x84j\xa9\x93>I\x92\x98ZI{\x8a\xdb\xcd\xe5\xb76%I\x82J\xa5*8\x17\xe9L\x14\x04\x81\xfd9\xe7\x08`6\x9bE6\x9b\x85$I\xc8d2H\xa5R\xecg2\x14\x1c\x0b\x07Z\xb8$IB:\x9d\x86$Il\x7fg\xb3Y\xf6\xff\xc5@\x97\xc1|>\x0f\xadV\x0b\xbd^\x0f\xbd^\x0f\xadV\x0b\x00\x9c\x04r,\x0a\x01\xa4\xbf\x97\xba\x0b8\x9b\xcd\x02\x002\x99\x0c\xb2\xd9,2\x99\x0c\xd2\xe94r\xb9\x5c\xc1e\x8bcy\x81\xe6W\xa3\xd1@\x14E\x98\xcdfh\xb5Zh\xb5\xda3J\xfc\xc5\x85\xfa2\xf4'\x1e\x8f#\x1c\x0e#\x1c\x0e#\x16\x8b!\x99L\x22\x97\xcbq\x02\xb8\xc0 \x05EN\xfch1i\xb5Zh4\x1ah4\x1ah\xb5Z\xf6\x98(\x8aP\xab\xd5\x10E\x11*\x95\x0a\x1a\x8d\xa6@1\xe4\xe0(\xa5R\xa4\xd3i\xa4\xd3i\xb6\xbf#\x91\x08\x92\xc9$\xbb\xe8\x15\x83Z\xadfF\xd8h4\xc2n\xb7\xa3\xbc\xbc\x1cv\xbb\x1d\x1a\x8d\x86\x0f,\xc7\xa2\x13\xac\x5c.\xb7\xa4/\x1a\x99L\x06\x89D\x02\x89D\x02\xa1P\x08\xe1p\x18\x89D\x02\x99L\x86\x13\xc0eL\x00\xb3\xd9,DQ\x84\xd5jEyy9\x9cN',\x16\x0b4\x1a\xcd\x19[\xaf\xe2B~\xa1X,\x86`0\x88\xee\xeenx\xbd^\xf6E\xe4j\x15\xc7\xc2\x19\xe4h4\xca\x16\x92\x5ce\x15E\x11\x06\x83\x01&\x93\x09v\xbb\x1dF\xa3\x11F\xa3\x11z\xbd\x1e:\x9d\x0ez\xbd\x9e\x91C\xadV\x0bA\x10\x18)\xe4\xf3\xc4QLU\xc9\xe5rH\xa7\xd3\x08\x06\x83\xe8\xeb\xeb\xc3\xe8\xe8(\xbbP\x90\xb2\x5c\x0c\xf1x\x1c{\xf7\xeeE,\x16Css3\xd6\xaf_\x8f\x96\x96\x16\x08\x82\x00\xbb\xdd^\xf2\xb5\x1c\x1c\xa7z9\x96\x9f\x93K\x95\x00\x92r\x9eN\xa7\x11\x8dF1>>\x8e\xae\xae.\xe6\xa5!\xdb\xc9\xb1\xfc\xce\xd2\xfd\xfb\xf7cxx\x18v\xbb\x1d\xabW\xaf\xc6\xfa\xf5\xeb!\x08\x02\x13e\xce\x19\x02H_(\x9b\xcd\x22\x16\x8badd\x04\x95\x95\x95\xb8\xfa\xea\xab\x0bT*\x8e\x85_Hr\xf7\x1b\x11\xc0L&\x83\x89\x89\x09\xc4\xe3qLNN\xe2\xd8\xb1c\xc8f\xb30\x1a\x8dp:\x9d\xb0\xdb\xed0\x18\x0c\xd0\xeb\xf50\x99L0\x9b\xcd0\x18\x0c\xd0\xe9t\xd0h4|\xce8f\xdc\xe3\xf1x\x1c\x13\x13\x13p8\x1c\xd8\xb4i\x13[+\xa5\x0e\xabl6\x8b\x1bo\xbc\x11\xdb\xb7o\xc7\x0f~\xf0\x03\x08\x82\x00\xadV\x0b\x9b\xcd\x06\x8b\xc5\xc2\x14i\x0e\x8e\x85Z\xab\xca\x8b\xf2RU\xd1\xc8\x0d\x98L&\x11\x0c\x06\x11\x0e\x87q\xe7\x9dw2e\x88\x0b'\xcbw\x8dn\xd9\xb2\x05G\x8e\x1c\xc1\xd3O?\x8d#G\x8e \x16\x8b\xa1\xac\xac\x0cF\xa3\x11:\x9d\xee\x8c\xb9\x81O\xfb\xe4\xa5\xa0\xc6t:\x8dx<\x8e\xa9\xa9)\xe8\xf5z\x98\xcdf\xee\xf6]dP\x1c\x95r\x9c+++\x0b~\x0e\x06\x83p\xbb\xdd\xd8\xbf\x7f?\xba\xba\xba \x08\x02\x0c\x06\x03l6\x1b\x1c\x0e\x07\xacV+\xca\xca\xca`6\x9ba4\x1a\xd9\xe2#U\x90\x83\x1fX\x14\xd7\xeb\xf3\xf9PYY\x09\x8b\xc52\xeb\xfe\xa6\xb8\xbf\xae\xae.<\xf7\xdcs\xe8\xe9\xe9\xc1\xaaU\xabP__\x8fh4\x0a\xbb\xdd\xce\xd60\x07\xc7b`\xa9\xc7\xd1e\xb3Y$\x93I\x84\xc3a\xf8\xfd~\x18\x0c\x06fS9\x96'T*\x15\xa6\xa6\xa6\xf0\x9b\xdf\xfc\x06===\x98\x9a\x9a\x82\xdb\xedF0\x18\x84\xcb\xe5bb\xce9A\x00\x05A@:\x9dF2\x99D,\x16C\x22\x91\x80N\xa7c_\x94\xe3\xcc,\xa8b\xb7`\xca$\xb3\xd9l\xb0\xd9lhooG>\x9fGgg'zzz\xb0o\xdf>\x08\x82\x80\xd6\xd6VTTT\xc0\xe9t\xc2\xe1p\xb0\x9b\x08\xbd7w\xd3\xadl\xe4r9d2\x19\xc4\xe3q\xc4b1\xa4\xd3\xe9Y\xf7w>\x9f\x87 \x08\x18\x1d\x1d\xc57\xbf\xf9M\x9c8q\x02\x1e\x8f\x07\x91H\x04\xf1x\x1c\x99L\xa6d\xf2\x08\x07\xc7\xa9^V\x94?/U!\x82\x12&\x13\x89\x04\xdb[\xb4\xaf\xf8y\xbb<\xd7\xa5J\xa5B8\x1c\xc6/~\xf1\x0b\xec\xdc\xb9\x13###,|.\x1e\x8f\xb3\x98\xd53\x85\x05q\x01\x93\x8b\x88\xbe\xc4\x5c\x82\xbb\xb9:\xb8p\xe4Oy[\x98\xa9\xd6\x1a=o\xe3\xc6\x8d\xd8\xb8q#6o\xde\x8c\xb7\xdez\x0b/\xbe\xf8\x22\xea\xea\xea\xd0\xda\xda\x0a\x97\xcb\x85\xaa\xaa*8\x9dN\xe6\x1e\xa6\x0cb\xee\x16^\xb9kL\x92$\xc4\xe3qD\xa3\xd1Y\x89\x1f\x91\xbf}\xfb\xf6\xe1\xa7?\xfd)\x0e\x1e<\x08\xaf\xd7\x8bL&\xc3\xfePl\x167t\x1c\x8bmx\x97\xaa\xad!\x17o&\x93A \x10(i7\x17\xab\xae\xee|\xf6\xdfB\xfe\xee\x95\xb4\xef\xe5\x84\xde\xeb\xf5\xe2\xfb\xdf\xff>\xde|\xf3M\xf4\xf7\xf7#\x16\x8bA\xadV\xb3\x90\xae3\x9d0\xbb \x04\x90\x0cD4\x1a\xc5\xd8\xd8\x18\xbc^/_\x00gx\x81\x9d\xca\xf3\x5c.\x17\xee\xb8\xe3\x0e\xdcz\xeb\xad\xf8\xe7\x7f\xfeg\xbc\xf2\xca+hkkCss3\x9a\x9b\x9bQUU\x05\x87\xc3\x01\xb3\xd9\xcc\xe6\x9a\x97\xedX\xb9F4\x99Lbjj\x0aUUU%\xd7X<\x1e\xc7[o\xbd\x85\x17^x\x01\x87\x0f\x1f\x86\xdf\xefG<\x1e\x9fv\x18\xf23\x80c\xb1\x94\x16\xc2Rv\x01+\xe3\xe7\xa7\xa6\xa6f=\xbb\x17\xf2\xbb\x9c\xa9\xb1Q\xce\x89\xbc4\xd4\xd9\xba\xcc\x9e\xa95\xa1R\xa9\x90L&\x11\x8dF\xd1\xdd\xdd\x8d\xd7_\x7f\x1dG\x8f\x1eEWW\x17b\xb1\x18KP\x22q\xe5L\x8b,\xe2B.\xa6\x5c.\x87H$R\x92\x00F\x22\x11\xe4\xf3yLNN2\xe6;\xd3\xfbQ\x8d\xa4RFi)\xdd\xeeN\xf5\xf3\x9c\xcakT*\x15R\xa9\x14\xaa\xaa\xaa\x10\x0e\x87a\xb5Z!I\x12\xca\xca\xca\x90\xcb\xe5`6\x9b\xa1R\xa9\xa0\xd5jg,\xd2+7\xc4_\xfb\xda\xd7\xb0{\xf7n\xfc\xe4'?A\x7f\x7f?.\xb8\xe0\x02\xb4\xb6\xb6\xa2\xbe\xbe\x1e.\x97\x0b6\x9b\x8d\x05\xebs\x12\xb8\xf2@\xfb;\x1e\x8f\xc3\xe7\xf3\x15}N.\x97\xc3\xc0\xc0\x00^y\xe5\x15\xec\xd9\xb3\x07'N\x9c\xc0\xc8\xc8\x08b\xb1\x18;p)\xcb\x8d\x07\xb8s\xccv\x86.\xc4\x85s\xa9\x87\x19\x90\x1b8\x1e\x8fc||\xbc\xe8\xf9\x1c\x8b\xc50<<\x0c\xb7\xdb\x8d\xde\xde\xde\x19\xc9\xd3\x5c\xec\x88\xdc\x15y*\x9f\xf5T^W,\xc9\xebTm\xe5b\xb8H\xe7\xf3YJ=\xaf\x98\xc7M\x10\x04D\xa3Q\x04\x02\x01\x8c\x8f\x8fcdd\x04\x83\x83\x83\x88F\xa3,\x0c\x86\x88\xdf\xd9h\xda .\xd4\x00\x92\x01\x88\xc7\xe3,FH\x8e\xd1\xd1Q\xf4\xf7\xf7\xa3\xbb\xbb\x1b\xf1x\x1c\x1e\x8f\xa7(\xdb\xa5\x85\x9fJ\xa5\x90N\xa7Y\x11c\xe5\x00+[K-$\xb9:]\xe26\xd7\x00\xce\xf9\xc8\xfa\xb4\xf9\xe4\xef-\xaf\x01HcAn[\x8b\xc5\x82\xf2\xf2r\x18\x8dF\x94\x95\x95\xa1\xac\xac\x0cv\xbb\x1dv\xbb\x1d:\x9d\x8e\x1d\xac\xf4\x1e\xd9l\x16\x9b6m\xc2\xea\xd5\xab\xf1\xf4\xd3O\xe3\xc5\x17_\xc4\xfb\xdf\xff~\x04\x83A\xa4\xd3id\xb3Y\xf6ZN\x02W.\xb2\xd9,\x82\xc1`\x01\xe9K\xa7\xd3\x98\x9a\x9a\xc2\xfe\xfd\xfb\xb1\x7f\xff~\xf4\xf6\xf6bhh\x08n\xb7\x1b\x89D\xa2\xc0\xad!\xdf\xb3<\x0c\x84C\xde\x05\x83J\x9f(\xcf\xceP(\x04\xb7\xdb\x8d@ P\xd0UF\xaf\xd7\xa3\xbd\xbd\xbd\x80`\xccG\x01<\x1d2\xb4P \xfb\x96\xc9d\x0ab\x00\x01\xc0\xef\xf7\xa3\xbf\xbf\x1f;v\xec@\x7f\x7f?\xc2\xe10\x82\xc1`A\xfd\xd6S\xb5u\x0bE\x8a\xe5cX\xec\xf7SR\x8b\xb2\xc5\x9d\xb2\x1d\x9arLf\xfa^D\x98\x8b\xa9\x8a\xa5~.5N\x0b\xd5v\xaf\xd8{\xe4r9$\x12\x09V?\xd5\xe7\xf31\x8e$O\xf6\xa0j\x0ag\xfaL\x5c\xd0\xfa\x0b\xc4\xce'&&\xd8\x97\x0f\x06\x83\xe8\xe9\xe9\xc1\xc1\x83\x071::\x0a\x9f\xcf\x87d2\x09\xaf\xd7[@\xee\x94\x93F\x93\x12\x8f\xc7\xd9\x82 \xb9\x5c.\x87'\x93\xc9\x05'p\x8bI\x0eO\xf5\x90\xa0X<\xe5\xe3t\xf8i4\x1a\xe8t:\x18\x0c\x06$\x12\x098\x1c\x0eH\x92\x04\xb3\xd9\x0c\x93\xc9\xc4J\xc0TVV\xc2f\xb3\xc1n\xb7\xa3\xa2\xa2\x02&\x93\x09\x06\x83\x01\x82 \x97\xcb!\x95J\xe1\xc6\x1bo\x84\xc5b\xc1\x0b/\xbcPP\xec7\x97\xcb\xc1\xe1p\xc0h4\xb2R\x05\x1c+\x03tI\xc8f\xb3\xf0z\xbdH&\x93\xd0\xe9tH&\x93\xe8\xec\xec\xc4\xee\xdd\xbb\xd1\xdb\xdb\x8b\x91\x91\x11LNN\xc2\xe7\xf3!\x12\x89\xb0RE\xb4v\xc9\xc0s\xf2\xc7\x89\x1f)$\xa2(B\x14Ex<\x1e\xbc\xfd\xf6\xdbx\xeb\xad\xb7\xf0\xf6\xdboc``\xa0 ^T\xbefr\xb9\x1cZ[[\xf1\xd6[o\xc1\xe9t\xce\xe8:+\xe5j\xccI9\xa8p\xf6[\x12\x92]\x8bF\xa3\xe8\xec\xec\x84 \x08\x18\x1f\x1fGoo/\x06\x06\x06\xd0\xdd\xdd\x8d@ \x80d2\x89D\x22\x01\xe0d\xf1\xe8\xb3I\x5c\xf3\xf9<#\xecr\xb5\x8bHL>\x9f\x87\xc9d\x9aF\xc0\x95s\xa1\xd5j\x0b\xc6\x9fBMJ\xa9k\xc5\xbc\x834\x1er\x01\xe9L\x9d1\xf4{\x8b\xfd>\xba\xacd\xb3YV\xec;\x95JM[\xcf\xf2\x8bM\xb1\xb8\xfe%O\x00I\xae\xcf\xe7\xf3\xac\x04L:\x9df$.\x9dN#\x16\x8b\xc1\xe7\xf3\xb1\xfaG\xca~\x87$\x8bR\xab\xa9L&\x03\x8dF\xc3\xda\xe3H\x92\x04A\x10\x90J\xa5\xd8k\xa8v\xddb\xa8t\xa7\xb3\x80\x16r\xf2h,\x09T`\x9bz\x0aR\xc9\x16Q\x14!I\x12L&\x13<\x1e\x0f4\x1a\x0d\xab+\xd4\xd3\xd3\x03\x87\xc3\x01\xbd^\x8f\xf2\xf2r\xd4\xd7\xd7\xc3j\xb5\xa2\xb6\xb6\x16N\xa7\x13\xa2(\xb2[h:\x9d\x86F\xa3\xc1\xc5\x17_\x8c\xbd{\xf7\x16\x14\x9c&\xc5P\xa7\xd3-\xf9\x0a\xfb\x1c\x0b{\xc0\xc9\x0f)\xba|%\x12\x09$\x93I\xa6\x12+[X)\x0fF\xa5r\xcf\x13\x8bV\x06\x94\xe7m,\x16\x83\xc7\xe3\xc1;\xef\xbc\x83_\xfd\xeaWx\xe9\xa5\x97\x0a\xd6\x09u6\x92\xaf!\xe5:\x9a\xcb\x99-\xef\xb1+\x7fN\x22\x91\xc0\x1f\xff\xf8G\xd4\xd4\xd4\xa0\xa3\xa3cV;\xb2\xd8\xe3\x92\xcb\xe5\x10\x8dF\xf1\xf2\xcb/#\x16\x8b!\x93\xc9\xa0\xaf\xaf\x0f\x1e\x8f\x07^\xaf\x97u\x06\xc9d2KfNi\xaf\x13\x91#\x11\x81:R%\x12\x09&\x14P\xe7)R\x04\xe9\x5c\xa1\xccW\xf9x(\xc3\x96H\x80\x90\x17\xa4\xa7\x0eC\xc4\x17\xe8w\x93\xa7\xecL\xf7\xd4-\xc5\x15\xe8<$\x1e\x93\xc9d\x0a\xfa>\xcb/\xc6g\x1a\x0bF\x00\xc9(\x00@\x7f\x7f?\xf6\xec\xd9\x03I\x92\xe0\xf3\xf9\xd0\xdf\xdf\x8f\xfe\xfe~LNN\x22\x1c\x0e#\x9b\xcd\xb2\xa0pb\xef\xc4\xe0\x05A`\x841\x97\xcb\xb1Vr\xc4\xa2\xcf\x04\xab_\xca\xea\x04\xa9n\xb4\x80\xa8`(\x19g\xb5Z\x8dX,\x06\x95J\xc5\x5c\xbd\x1a\x8d\x06\x16\x8b\x05\x81@\x00eee\x98\x9c\x9cD__\x1f\xecv;\x1c\x0e\x07\x0c\x06\x03V\xaf^\xcdnc\xe1p\x18\xa2(B\xaf\xd7\xa3\xb5\xb5\x15G\x8f\x1ee\xad\xfeh\xa1\x92;\x98\xc7r\xad\x1c\xd0\x9as\xbb\xdd8r\xe4\x08\x04A\xc0\xd4\xd4\x14\x06\x07\x071<<\x8c\xf1\xf1q\xf8|>D\xa3\xd1\x02\xd5X\x0ey\xb8\xc2R\xdfk\x1c\x0b\xb3fH-\x02\x80\xd7_\x7f\x1d\xdb\xb6m\xc3\xee\xdd\xbb\xb1k\xd7\xae\x82K-\x11\x05\xf9e\xa2\x14\xc6\xc6\xc6fue\x16S\xa0T*\x15N\x9c8\x81t:\x8d\xf1\xf1q477\xc3n\xb7\x9fUrL\xf5\x00\xdf|\xf3M\x18\x0c\x06\xa6\xa2\xc7b1\xa4R)\xa4R\xa9\xb3\x9681\x1b\x04A`\xaa\x1d\xa9\x80\x94\xd8\xa0\xd5j\x19\x09$\xe2M\x82\x05\x915\xa5\xfd \x92K]R\xe89\xd4\x8aR\x9e<#\x9fc\xb9r\xb6XY\xd3\xf3\x15|\xe8\xf7\xcb\x9b5\x14\x9b\xc7\xb3U\xfaG\x5c\x8cA\x10E\x91)D\xd4aB\xde\xda\x86\x9e\xa7V\xab\x11\x8f\xc7\xd9\xe4\x91\x9f\x0f\xe5\xe5\xe5\xf0\xf9|0\x9b\xcd\xa8\xa8\xa8\x00\x00x<\x1ev\x135\x9b\xcd\xe8\xec\xecd]\x1bh.m6\x1b\xdb\xcc\x1c+\x07t\xa9 %\x98\x0es\xba\x8c\xc8\xd7\xecL5*9\x96\xbf\xe2\x97\xcdf\x99\x8a\xf7\xf4\xd3O\xe3{\xdf\xfb\x1e\x06\x07\x07\x91J\xa5\xd8\xda\xa0\xb3C\xaenY\xadV\x5cs\xcd5hkkCCC\x03\xfbc\xb5Z\x0b\xde_\xa3\xd1\xa0\xa2\xa2\xa2\xc0\x16\xccu}%\x93I\xf6\xbaR\xc9\x86gb\x9c\x88\x10\xe4r9\xf4\xf5\xf5!\x99L\x22\x95J!\x99L2\xb5\x88D\x10\xb9z\xb6\x14m\x14\x8d)\x9d\x11\xe9t\x1a:\x9d\x0e\xd9l\x96\xf5\xa8'\x8f\x16u\xbc(f\xd3\x88\xf0\xd2\xbf3\x99\x0c\xb2\xd9,\xe3\x04t\xb1P\xd6\xbc\x95\x7f\x9e\xa5r\xd6(\x95k%9%\xe1\xe6L\xb9}\x17\x8d\x00\xcewP\x22\x91\x08\x93ri\xc1\xd3m\x87\xfc\xe4rW\x92\xfc\xef3Y,q\xa9\x93Be\xf6\x10-(\x22\xe3Z\xad\x96\xf5\x07\xa6q\xa6\x9f\xa9\x83\x8b\xc9d\x82\xcb\xe5B(\x14b\xad\xe2\xd4j5\x8cF#\xdb|\x07\x0f\x1edq\x86\xd4K\x98nv\x5c\x05\xe4\x06\x9f\x83C~&\x85\xc3a<\xf5\xd4Sx\xf4\xd1G\xd9Y\xa44\x86z\xbd\x1e\x95\x95\x95\xb8\xf3\xce;q\xc3\x0d7\xe0\xf2\xcb/\x9f\xd6\x1d\x86\xc8Q\xb15\xb6\x10Y\xc2\x14^s6\xce1y\x22\x9f \x08\x18\x1b\x1b\x83Z\xadf\x1e0y\xa8\xd4R\xdc\xefJ;$'\xaa\xe4\x0e&\xb2N\xa4\x8cD\x84L&3\xcdU+O\xf2 \xc50\x9b\xcd2AH9&\xa7\x9b\x88y6\xc6l\xa6X\xfe\xb3\xa1\x02\x8a\x8b\xb5\xf9U*\x15\xeb\x0c\x92L&\xd9d\xc9\xcbI\xe8t:\xa4\xd3\xe9\x82\xe0H\x8au(\x15GT,\x93h\xa5\xa9\x7f\xf2\x9b\xa0|\x1c\xe4A\xa7\x14\x17\x91\xc9d \x8a\x22\x0b\xdc\xcff\xb3\xcc}\xeb\xf5z\xa1\xd7\xeb\xd9\x1cP\x1b\xbfd2\x09\x8dF\x83p8\xcc$\xfd`0\x88}\xfb\xf6\x15\xf4\x10\xa6x\x0d\xde6n\xf9\x13f\xf2\x18\x8b\x5c.\xc7\x08!\x05\xd2\x92{\x97\xdc\xec\x14\x9bID\x906\xb3Z\xadF&\x93\x81Z\xad\xc6\xd0\xd0\x10\x8e\x1e=\xcaJ\xcb\x18\x8dFF\xfe\xb8+\x98+\x7f\x1c+\x0ft~h\xb5Z<\xf2\xc8#\xf8\xe1\x0f\x7f\x08\xb7\xdb\x0d\x00,\x06L\x92$\xacY\xb3\x06\x8f<\xf2\x086o\xde\x8c\x86\x86\x86\xa2kh\xb1\xcf\x10eAe\x95J\x05)/\xcd\xba\xc6\xe5\xee\xec\x85T\xff\xc8\xcbb\xb5ZY\xf8\x8d\xd2\xce\xcd&\x06\x9cm\x05\xb0X\xfb=\xa5r)\x0f\xfb\x22\xc5\x8b\xc2Fh.\x94\xe4\x8f\x04!\xb2M\xc5\xde\x93\x88\xe5\xb9$>(\xe7\xd4`0\xc0f\xb3\xc1h4N\xcb\x8a>\xa7\x08\xa0\xbc\xae\x8d\xdb\xedFUU\x15\xe2\xf18K@ \xd7$\x11\x0br\xf5\xc6b\xb1\x82x\x07\xb9\xaa\xa0\xcc>,\xa64\xae$\x05p\xa6\x1aF\xc5T\x18\xf9\xd8\x90\x22H\xb7(\x9dN\xc7\xe4v\x8b\xc5\x02\x95J\x85H$\x02\xbd^\xcfn\xc5\x92$\xb1\x22\xbe\xf2\xf2\x1d\x9d\x9d\x9dp:\x9d\xacw0\x91@\xee\x0a\xe6d\x90c\xe5\x81\x5c\x97\xd7]w\x1d\x06\x06\x06\x0a\x5cz\xe9t\x1a\x97]v\x19\xbe\xff\xfd\xef\xe3\xbc\xf3\xce\x9b\xb1T\xc6\x99\x5c\x83rwr>\x9fG^*}\xc9>r\xe4\x08\xc6\xc6\xc6\xb0a\xc3\x06\xd4\xd6\xd6.\xd89G\x99\xb1F\xa3\x11.\x97\x0b\xf5\xf5\xf5\x05\x04j&\x22\xb8\x14I\x8c2\x16Oi\x93\xc8\x0bEv%\x95J\xb1\xd2f\xa4\xd6\x91:H\xc9\x1f\x14\x06&O\x08*V6\xee\x5c%\x7f\x04\xb5Z\x0d\x97\xcb\x05\x8b\xc5rn\x13@\x22\x81\x82 \xa0\xa1\xa1\x81\x05\xd7\xca\xbf4e\xf1\xa4R)\xa6\x14\x92\x8f_\x1e\xf3\x07\x00z\xbd\x1e\xf5\xf5\xf5X\xb5j\x15+[B\x81\xe6\xf2E\xb7\x9cI\x87|<\xe8\xdf\xd9l\x16\xa1P\x88e[\xd2f\x19\x1d\x1dE4\x1aE8\x1cf\x8f\xd1\xa6Q\xb6\xdf\xa1C\x862\xb8r\xb9\x1cL&\x13{\x1e5)'\xd7\xb1^\xafg\x85J\x83\xc1 \x0e\x1f>\x8c\xea\xeajTWW\xc3b\xb1\xb0\x98A^\x16fe+A\x8be\xd89\x96\x16\xc8\xdd\x9bJ\xa5\xf0\x9d\xef|\x07\x0f>\xf8 \x04A`e\xbb\x00\xe0\xaa\xab\xae\xc2?\xfc\xc3?`\xcb\x96-,\xd6\xeel\x94[Q\xaeKe\xd8B)\x050\x99Lb||\x9c\x950\xab\xad\xad]P2 \x08\x02\xccf3jkkq\xe9\xa5\x97\xe2\xa7?\xfd)\xc6\xc7\xc7\x0bB\xa1\xe4b\xc7R =r\xd7\xab<\xb35\x93\xc9 \x99Lbxx\x18\x03\x03\x03\x18\x19\x19A$\x12a\x17\x82L&\xc3\x1a\x09P9\x18\x22\x85\xc0\xc9Lp*\x1cO|\x80\x94?9\xf9\xd3h4\x8c\x17\xd4\xd5\xd5\xa1\xbc\xbc\x9c%\xa5\x15k\x16q\xb6/\xc5\xca\x905\xb9\xdb\xdfn\xb7\xa3\xaa\xaa\x0av\xbb\x1dV\xab\x95y\xde\xcei\x17\xb0\xb2\xb1\xb1\xcdf\xc3\xc0\xc0\x00#\x17\x82 0\xb7/\xc9\xbdJ\xe5\xcf\xe5ra\xf3\xe6\xcdX\xb7n\x1d\x9cN'\x1b\x1cr7\xcao\x99+A\x01T\xc6\xaa\xd0x\x11y\xa6\x04\x9ah4\x0a\xb7\xdb\x8d`0\xc8Z\xcf\xf4\xf5\xf5\x15,D\xb9\x9cN\x8a \x118yk\x1aJ\xceQ\xab\xd5,k\x8e2\x81\xfb\xfa\xfa\xd0\xd5\xd5\x85\x9a\x9a\x1a8\x9dN\x94\x95\x95\xb1ZP\xdc\xf0\xaf\x1c\x92W\x8a\xf0qEpy\x93\xbf\xa3G\x8f\xe2\xe3\x1f\xff8\x0e\x1c8p\xd2\x98\x88\x22\xd2\xe94\xd6\xacY\x83'\x9f|\x127\xdcpCA\xf8\x892\xb9c1\x8cm1\xa3\xaf\xfcY^/\x0e\xc0\x8c\x0a\xa0\xbc$\x8d \x08\xec\x02\xbc\x90g\xbbZ\xadf\x09xZ\xad\x16eee\xa8\xac\xacd\xd51\x8ay\xc0\x96\xc2\xbeR\xc6\xda\x11YK\xa7\xd3X\xb7n\x1d&''\xd1\xdd\xdd\x8d\xfd\xfb\xf7\xa3\xbf\xbf\x9f}\x07\xaa\xe8\x91\xcdf\x99`@\xff&\xce@\xb1\x7fD\x80\xe5j\xa2\xcdf\xc3\xd5W_\x8d\x8e\x8e\x0eTVV\xc2j\xb5\xb2J#d\xb7\x96\xd2\x19\xa4\x8c\xcf\x97\xab~T-\xc5j\xb5\x16\x10\xc0sZ\x01\x94\x7fqb\xf8\xb4\x89\x08\xf2\xac\x1e\xba9\xc8\x0d\x8b\xddn\xc7\x07>\xf0\x01\xac[\xb7\x0e---p\xb9\x5c\x05\x04C\x9e\xf6\x7f\xb6\xea\xe7\x9c\xe9\x03\x97\xc6\x94\xfe-'p\x14\xcfG1\x13\xd1h\x14\xd1h\x14\xc1`\x10>\x9f\x0f\x13\x13\x138|\xf80:;;Y\x11m\xda\xb4\x82 \xb0\xc2\x9a\xc9d\xb2\xe0\xa6I%d\xe8\x00\x94\xbb\x81s\xb9\x1c\x0e\x1f>\xcc\xfa\x05;\x9dN\xe6\x0a\xe6*\xe0\xf2\xc5\xd9l\xe4\xceq\xf6A1\xc1/\xbf\xfc2\xfe\xec\xcf\xfe\x8c\x91\x22:\xe7\xef\xbf\xff~<\xf1\xc4\x13\x05\x86\xf8L\x9f\x07\xb3%&)\xeb\xc9\x96Z\xcbD\x16\x17\x83\x00\xd2\xb8Q\xd7\x0c\xadV\x0b\xab\xd5\xcaD\x11%i\x90\x17d_*kAN\x02\xc9\xae\x93\x08A\x84&\x9f\xcfctt\x94\xa9w\xe4\xe6\xa4\xf8P\xbd^\xcf:w\x10\x1f\x90\x8b\x14\xf4\xddM&\x13n\xbf\xfdv\xb4\xb7\xb7\xa3\xb9\xb9\x99\xb9M\xa9\xde\xadR%]*c\xa4\xbc\x84\xd0\xe5\x83l%%\x80P\x99\x9c39\xbf\x8b\x1aqK\x0bV\xde\xdaE\xb9`\xc8%,\x1f\xa4\xcd\x9b7c\xcd\x9a5X\xbf~=\x93x\x0d\x06\x03\x0b\x10V\xca\xe0\xcb\xd5\x18\xcd\x14`+\x1f/\xda(tsR\x12A\x9f\xcf\x07\x8f\xc7\x83\xa6\xa6&\x9cw\xdeyx\xe7\x9dw\xd0\xd7\xd7\xc7Z\xea\x91\xbb\x86\x12v\xe8\xbd\x89\x94S\x89\x04y\x0b>\xfa{jj\x0a===hllDuu5/\x0e\xcd\xc1\xb1\x8c\x89\x1f\x19\xd7\x1f\xfc\xe0\x07\xf8\xdc\xe7>W@\xec\x9a\x9b\x9b\xf1\xb3\x9f\xfd\x0c\x9b6mb\xc1\xfcg\x03\x92$\xa1\xaf\xaf\x0f555\x05IjJ\xdbS\xaa=Y\xb1\xf3V\x10\x04\xd6\x86m!]\x8bt\xd1&\x85\x94b\xb3g\x22\x0f\xf2\xdf\x7f\xb6\xec\x9e\xd2\xcd*\xff\x9b*|P\xd7)I\x92\x10\x0a\x85\xe0\xf5z\x0b\x08\x9a^\xaf\x07\x00\x18\x8d\xc6\x82\xf6v\xca,_\xb9\xbd\xdb\xb2e\x0bZZZ\xb0f\xcd\x1a455\xb1\xdfA\xea\xdfR\xb49J\x05P\xe9\x0a&[)o\xe8p&!.\xf6\xa1Q,iA\x1e\xd8)\x7f\x8e$I\xa8\xae\xae\xc6\xda\xb5k\xd1\xd8\xd8\x88\xba\xba:TUU\xc1b\xb1\xb0\xf6g\xf2\xc5\xb6\x12\xdaH\xcd\xa5\x06\x94\xfc@\xa3MDD\xb0\xba\xba\x1a\xc1`\x10\x1e\x8f\x07UUU\xa8\xae\xae\xc6\xae]\xbb\xb0w\xef^\x84B!6\x1f\xf4|Z\x94\xb4A\xe5E*S\xa9T\x81\x0b>\x16\x8b\xa1\xbf\xbf\x1f\xa3\xa3\xa3hjj\x82\xcb\xe5\x82\xc9d\xe2\xb1\x80+\x84\x10p\xac\x1c\x90\xd1\xfa\xd4\xa7>\x85\x1f\xff\xf8\xc7\xac\x96[.\x97\xc3=\xf7\xdc\x83g\x9ey\x86]H\xcf\x06\xf9#\x92v\xe4\xc8\x11<\xf0\xc0\x03\xa8\xab\xabCmm-V\xb5\xb4\xe0\xaf?\xf5)\x5c\x7f\xddu\x8c\xfc\xc9E\x07\x22-\xb3\xa9\x89\xe4)Y\xac\xb1%5H\xab\xd5\x16\xcdt]\xaa(\x16_Iud\xa9\x8e\xdf\xf8\xf88\xea\xea\xea055\x05\xa3\xd1XP9\x82TARe\x95\x8d\x1f\x08\xabW\xafFSS\x13\x9a\x9b\x9b\xd1\xd8\xd8\x88\xca\xcaJ\xa6\xfe)\xc3\xc1\xce\x15^\xa0$\x83g\x03\xe2\x99\xfe\xc2\xa4\x08*\xb3Wi\x00\xd6\xae]\x0b\xbb\xdd\x8e\xca\xcaJ\xd8l6\x98\xcd\xe6\x82\xfa8+\xf5\xe0-\x059\xd9\xd2j\xb5\x8c\x10f\xb3YX,\x16X\xadV\xd8l6\x94\x97\x97\xc3f\xb3A\xab\xd5\xa2\xbc\xbc\x1c\xff\xf7\x7f\xffW@\x02\xd3\xe94+\xd0I2}>\x9f/\x88\xd1\xa4\xc3\x8a\xc8\xe0\xe0\xe0 \xc6\xc7\xc7\xe1v\xbbY\xa2\x8e\x9c\xacs,_,\x85:[\x1cgf\x9eU*\x15\xee\xba\xeb.<\xff\xfc\xf3\xec\x02\x98L&\xf1\xcc3\xcf\xe0\xd3\x9f\xfe\xf4\x9f\x0c\xcaY*\x05E\xc6\xf4\x8f\x7f\xfc#\x00`tt\x14\xa3\xa3\xa3\xd8\xbd{7~\xfb\xbb\xdf\xa1\xa9\xa9\x09\x9f\xfb\xdc\xe7\xf0\xf9\xcf\x7f\x9e\x95\xb5\x9a\xcbE\x86\xce\xd23q\x9e\x9d\x8bY\xad\xcaq\xa1\xd86\xe0d(\x97\xcb\xe5b\x7f\x02\x81\x00\xb3\xe7\x0e\x87\x83%0R\xbc#\x91\xf1b\xed\xd2jjj\xe0r\xb9PUU\x05\x87\xc3\x01\x8b\xc5\x02\x83\xc1pN\xf3\x82\xa50\xcf\xe2\x99>H\xe80Q\xa6\xe2\x13(\xde\xcfl6\xc3d2\x9d\xb5&\xdd\xe7\xf2\xa2\x22\x05\x8e\xba\x80h4\x1a\xe8\xf5z\x18\x8dF\xe8\xf5z\xd6\xbe+\x9f\xcf\xe3\xa5\x97^b}\x99\x95-\x87\xe4\xe4\x9c$jy\xcb\x1aA\x10\x10\x0e\x87\xe1v\xbb\xe1\xf1x\x10\x0a\x85PQQ\xc1:\x85ppp\x9c\xdb\xa0K\xfa\xddw\xdf\xcd\xc8\x1f\xa9,o\xbf\xfd66o\xde\xccz\xbd.\x05\xec\xd8\xb1\xa3\xa0\xf6,p\xb2\xf2DOO\x0f\xee\xbd\xf7^\xdc\x7f\xff\xfdx\xf8\xe1\x87\xb1v\xedZ\xe8t\xbaY\x0b\x06\xcf\xd4\xb7\xb5\x94\x9a\xc3m\xd0I{a2\x99`\xb1X`\xb3\xd9\xd8\xdfv\xbb\x9d\xf5]\xa6\xb2dF\xa3\x91\xcd\x13\xcd\x87\xbc\xc4\x9bV\xab\x85\xc3\xe1`\xbc@\xa7\xd3\xb1\xe2\xe2\x1cK\x90\x00\xca'Q\xf98\xc5\xac\xc9\xff\xc8AA\xa2D\xfcVB\x92\xc7b\x93A\xea\xd8!\xef\x0dL\x99\xd8\xf1x\x1c\xaf\xbe\xfa*#\x7f\xca\xc0^j\xe5C\x89 D\x02\xe57\xfe\xa3G\x8f\xe2\xfc\xf3\xcfG0\x18D\x22\x91@YY\x19\x0b\x9c\xe6X\xfe\x04a>\x8fs\x9c{g\xc8_\xfe\xe5_b\xeb\xd6\xad,h\xbf\xac\xac\x0c\xef\xbe\xfb.\xd6\xad[\xc7\x12\xfdf[\x1f\x8b}\x86Sm\xd9\xef~\xf7\xbbx\xf1\xc5\x17\xd1\xdf\xdf\x8f\xde\xde^\x1c9r\x84\x91A\xbd^\x8fl6\x8b\xaf~\xf5\xab0\x9b\xcd\xb8\xe9\xa6\x9bp\xfb\xed\xb7\xb3\xb8\xbbb%D\x94\xa5\xc9\x94\xe7\x1a\xfdL\xf1\xd4\xfc\xe2\xfb\xa7\xf9 \x976u9\xb1\xdb\xed\xa8\xad\xad\x85\xc1``%\xc6\xa8\x12\x88<\xa9\xb1Xm[\x12/\xc8\x8eq^\xb0\x04\x09\xa0r\x03\x15K`\xa0\x85A\x05\xa1\x95\xc5\x9c\xc9}H$\x83O\xf2\xc2\x80\xb2\x8d\xe8\xdf\xf9|\x1e\xf1x\x1c\xe1p\x18\x93\x93\x938t\xe8\x10\x80\xc2\x12\x09\xf2lk\x22{\xf1x\xbc\xe0\xff\x04A\xc0\xe4\xe4$B\xa1\x10\x82\xc1 \xe2\xf18#\xf9\x9c\x00.op\x17\xf0\xf2\xc7'>\xf1\x09l\xdd\xba\x95\x15\xe9\xd5\xe9t8t\xe8\x10\x1a\x1b\x1bg\x8d\xb7\x92\xb7\x06;S\xeb\xb1\xaa\xaa\x0a\x97]v\x19\xae\xb9\xe6\x1aD\x22\x11\x04\x83A\x04\x02\x01\xfc\xe2\x17\xbf\xc0\x81\x03\x07\xd8%8\x1a\x8d\xe2W\xbf\xfa\x15^{\xed5<\xf0\xc0\x03\xf8\xd2\x97\xbe\xc4\xea\x9d\xceDdU*\x15|>\x1f\x92\xc9$\x02\x81\x00\x9a\x9b\x9ba\xb1X\xe0\xf1x\xf0\xf5\xaf\x7f\x1dO=\xf5\xd4\x92RC\x97\x0a\x11$\x8f\x13\x95\xbb\xa1\xf8Q\xaf\xd7\x8bL&\x03\xb7\xdb\xcdb3\x8b\xad\x17y\x1b9\xce\x07\x16x~\x16\xe3\xc6(\x872\xd1CN\x0e\x8b\xc9\xebt\xa8\xcc\xd4\xfd\x83\xe3\xf4H\xa0V\xab\x85^\xafGEE\x05\x1a\x1b\x1b\xb1j\xd5*\xb4\xb7\xb7\xa3\xbc\xbc\xbc\xb06\xd6\xff\x9f\x17\xbau\xa9\xd5j\xe4r\xb9\xa2\x1d?(\xf3.\x12\x890\x02\xc8\x15\xa0\xe5\x0f>\xc7\xcb{^\xbf\xfb\xdd\xef\xe2\xa7?\xfd);\xc7\x8dF#zzzPSS3\xa7\xcb\xb9 \x08x\xf5\xd5W\xb1k\xd7\xae3\xb6^\xa8\xb2D\x22\x91\x80Z\xad\x86\xd3\xe9\xc4\x1dw\xdc\x81\xfd\xfb\xf7\xe3\xbd\xf7\xde\xc3\xc6\x8d\x1bY\xfc\x1fp\xb2\xa8\xfd\x97\xbf\xfcel\xd9\xb2\xa5\xa4\x1d#\xbb\xb4o\xdf>\x1c?~\x1c}}}\x18\x1e\x1e\xc6\x81\x03\x07\xd0\xd6\xd6\x86\xef}\xef{x\xf2\xc9'9\xf9S\xcc?\x09=\x82 \xa0\xae\xae\x0e\xabV\xadB[[\x1b\xf3\xf0%\x12\x09X,\x16&P\xc8\xcb\xc5\x15\xe3\x16\xe4\xd5\xe2\xbc`\x89\x12\xc0\xb9lt\xb9\xebW9\x91\x9c\xf8-.\xa8\xf8\xa4\xd9lf$\xb0\xb9\xb9\x19\xf5\xf5\xf5Lu\xa59\xd0\xeb\xf5H&\x93\xac\x04\x0c\x91\xbfb\xca\xad\xc7\xe3A8\x1cF4\x1ae\xdd]8\x96\x0ff\xda\x8fs\xc9R\xe78w@1\xc0\xaf\xbf\xfe:\xee\xbb\xef>f\xbc\x8dF#v\xef\xde\x8d\xda\xda\xda\x92nN\xf9Zx\xea\xa9\xa7\xb0e\xcb\x16\x5cv\xd9ep\xbb\xddg\xa5t\x09)J\xd9l\x16\x17]t\x11\xf6\xed\xdb\x87\xe7\x9e{\x0e\xeb\xd7\xafg\xdfW\x14E\xbc\xfa\xea\xabhoo\xc7\xdbo\xbf\x0d\x00\x05$Q\x0e*\xb1%I\x12|~\x1fn\xbc\xf1F\x04\x83A\xe4\xf3y|\xe5+_\xc1\xce\x9d;\xf9\x22\x92\xd9y9\x014\x99L\xa8\xa9\xa9\x81\xd1hD}}=DQdmH\x95\xf6\xbf\xd8:\x91W\x0b\xe1g\xce\x12$\x80r\xf5h.\x13T\xcc\xa8PM;>\xc1\x8b\x07J\x10\xb1Z\xadp:\x9d\xa8\xad\xadEcccAp-\xb9J,\x16\x0b\x8cF#s\xf5\xd0\x86Vf\x7fMLL \x1e\x8f#\x91H\xb02\x0b|\x0e\x97\x8f\x224\x9f\xb9\xe4\xf3~n\x9f\x0d]]]\xb8\xf9\xe6\x9b\x99\xfbN\x92$\xfc\xcf\xff\xfc\x0f\xd6\xacY3\xeb\xdcR\xec\xf0\x87?\xfca\xfc\xed\xdf\xfe-T*\x15t:\x1d.\xbd\xf4R$\x12\x89E\xbf\xd8\x17\xf3(E\xa3\xd1\x82f\x04W\x5cq\x05\xbe\xf1\x8do\xe0\x8b_\xfc\x22\x8b\x0b\x14E\x11\xc3\xc3\xc3\xd8\xb2e\x0b\xfe\xe5_\xfe\x05\x1a\x8dfZ\xfc\x9f\xfc;\xaa\xd5j\xd8]\xe1/\x0f\x00\x00 \x00IDAT\xac6<\xf5\xd4S\xec2\x9cN\xa7q\xd3M7!\x1c\x0e\x17\xd4\xbe]\xa9{\x83\x92<\x88\xd4\x95\x97\x97\xa3\xa6\xa6\x06\xf9|\x1e\xc1`\xb0\xa0\x86\x9f\x92\x03\xcc\xa4\xc0r,a\x02(\xdf \xf2\x94\xf6\x996\xd1L\x87\x01/$\xbc\xf8j\x8e(\x8a\xd0\xeb\xf5(//\x87\xcb\xe5Buu5\x1c\x0e\x07{\xdcl6\x17\xa4\xe6\xd3\xebf:\xc0\xd2\xe94\xc2\xe10\xe2\xf18S\x009\x11X>\xeb\x85\xf6%\xc7\xf2\xc7G>\xf2\x11\x96\x91\x99H$\xf0\xd4SO\xe1\xc6\x1bo,j\x98\x8b\x91\xad\xcd\x9b7\xe3\xf7\xbf\xff=#\x94\xa9T\x0a\x7f\xfd\xd7\x7f\x0d\x83\xc1\xb0\xe8g\x82\xf2\xfd%I\xc2\xe0\xe0 \xdez\xeb-\xec\xdd\xbb\x97\xd53\x15\x04\x01W]u\x15~\xf2\x93\x9f\xa0\xbd\xbd\x9dy9R\xa9\x14\xfe\xfe\xef\xff\x1e\x0f=\xf4\x10\x8b{\x9ciO\xa4\xd3i\xdcu\xd7]\xb8\xef\xbe\xfb\x98\xda\x15\x08\x04p\xe5\x95W2b\xa9\xfc,+)n\x96\xba\xc6\xd0w\x0e\x85B\x08\x85B\xa8\xaa\xaaBCC\x03\xcb\xfe-v\xc9,\x96@\xca\xed\xc99B\x00\xe5\x13z*\x0b\xbeTaN\x8e\x85\xbb\x9dQ\x86\xb0\xddnGEE\x05\xacV+\xabF/\x8a\x22l6\x1bk\xe7C\xedvH\xceW\xcek<\x1e\x87\xcf\xe7\x9b\xd6\xf6o\xb6\xf5!/\x05\xa4|Ly\xa0\xf3\x03\xe0\xd4\xf7a\xb11\x9ei\xbc\x8b\xa9(3\xdd\xcc9\x96\xcf:\x01\x80\xc7\x1e{\x0c\x9d\x9d\x9d\xcc\x13\xf3\xc9O~\x12\xf7\xddw\x1f\x0b\xd0/\xf5Z\xaf\xd7\x8b\xcb.\xbb\x0c;w\xeed\xcf\xb5Z\xadx\xeb\xad\xb7\xf0\x8f\xff\xf8\x8f\x05\xdd\x84\xce\x04\xf9\x93?\x9e\xc9dX\xa9*\xf9z\x06\x80\x17^x\x01\x0f?\xfcpA\xd7\x8fo|\xe3\x1b\xf8\xf2\x97\xbf\xccb\x9fg\xda\x0f\x99L\x06O>\xf9$\xb6l\xd9\x82\x5c.\x07\x8dF\x83C\x87\x0e\xe1[\xdf\xfa\x16\x0b\x99\x01N\xba\x8e\xfb\xfa\xfap\xe8\xd0!\xd6\xf9b%\xd8\x18\xea\xf3\xabR\xa9PSS\x83\xb6\xb66\x96\x8c8\x1fn\xc0\x15\xc0s\x8c\x00\x16c\xf1\x9c\xc9/-UG\x1e\x0fh\xb3\xd9PQQ\x81|>\xcfZ(\xe9\xf5z\x16#Cs\xa9\x8c\x13\x94oPR\xff(\xad\x9f68\x15\xa5\xa6^\x91\xd4\x09F\xfe\x98\xfcg\x0a\xe4\xa6\xee$\xd45F\xdeA\x86\xa3\xf8!)o\xcc>\xdb\x18\xd3\xf8R_n\xf98+\xc30\x94\x8a\xfdL$\x91\xe3\xdcD&\x93A__\x1f\x1e~\xf8a\xf6X{{;\xfe\xf3?\xff\x93\xd5b+u\x96D\xa3Q\x5c}\xf5\xd5\xe8\xec\xecd\xc5\xe8\xdb\xda\xdap\xf4\xe8Q\x5cq\xc5\x15\xec\x1c9\x955M\xeb9\x9dN\x97\xdc\xfbs\x89S\x0d\x06\x83\xcc;A\xeei\xbf\xdf\x8f\xaf\x7f\xfd\xeb\xf8\xcdo~\x03\x00\xac+\xc7\x13O<\x81G\x1ey\x04v\xbb\xbd\xe8\xa5\x96\x08_:\x9d\xc6\xb6m\xdbPSS\x83L&\x03A\x10\xf0\xe8\xa3\x8f\xa2\xab\xab\x8b}\xa6@ \x80\xae\xae.\x0c\x0f\x0f#\x14\x0a\xad(;#o\x137\xd7\x02\xdc\xa5\xe6\x90c\xe1pFR\x96x\xb9\x88\xa5M\x02\x0d\x06\x03,\x16\x0b\xccf3\xacV+\xacV+\xeb\xb3\x98N\xa7\xe1\xf3\xf9\x10\x8b\xc5\xd8\x0d\xac\xd8A\x9b\xcb\xe5\x90L&\x19\xf9\x937\xf3&2A\x07@\xa9,\xf0b\x87\x87<\xfbK\xde\xa1\x84g\xdc\x15\xee1\x1ag\x22x\xf2\xdaZ\xb3\x1d\xac\xf22L\x1a\x8d\x06:\x9d\x8e\x198\xf9\xf3h.\xe4e~\xf2\xf9\xfcY\xeb\xfd\xca\xb10\xe4O\xab\xd5\xe2\xe6\x9bofk@\x92$|\xf7\xbb\xdf-I\xfc\x08\xa9T\x0a\x1f\xf8\xc0\x07p\xec\xd81\xd62\xed\xa2\x8b.\xc2\xae]\xbb\xd8\x1e\x9do'\x0d\xba@\x16\x8b7>U\x15\x10\x00\xa2\xd1\xe84%\x89>\xe3\xad\xb7\xde\x8a\x83\x07\x0f\xe2\xfc\xf3\xcfg$\xf6\xbf\xff\xfb\xbf\x99\x0b[\xa9\xdc\xd1\xfe\xa21\xfa\xe1\x0f\x7f\x88\x9bn\xba\x09\x92$!\x1e\x8f\xe3\xcb_\xfe2~\xfd\xeb_\xb3\xf2Y\xa4bq{\xc89\xc1\x8a\x22\x80\x1cK\x9b\x00\xeat:\x98L&\x98\xcdf\x88\xa2\x08\xbb\xdd^P\x0f\x8bj8\xe5r9v\x80)\x89C.\x97C,\x16c7u\xf9\xe3\x92$\xb1\xe4\x10\xca\xa2\xa3\xdb|\xa9\x9b\x9d\xbc96e\x22\xd2\xe7\xa4 m\xder\xeeO*\x09\x91py2\x0e\x11\xee\x99\x0e\x5c*\x08\x9e\xc9d\x0a\xaa\xf7\x93\x0a,\x8f\xff$\xe2\xa7\xd7\xeb\x0b\x94`y\xed\xb3\xd9\xdc4\xdc\x03\xb0\xf4\xd6\x8eF\xa3\xc1\x13O<\x81\x13'N@\xab\xd5\x22\x9dN\xe3\x9e{\xee\xc1\xf5\xd7__r\x8f\xd1\xff\xdds\xcf=\xd8\xb1c\x073\xec\x97\x5cr\x09\xf6\xec\xd9\xc3\x8a\xfd\x9e\x0ab\xb1\x18\x02\x81\x00\x8e\x1c9\x82\x1d;v`\xdf\xbe}\xe8\xea\xea\xc2\xc7?\xfeq<\xfa\xe8\xa3\xa7\xdcy#\x93\xc9L\xfb>\xf2\xb3j\xfd\xfa\xf5\xe8\xea\xeaB{{;[\xaf[\xb7nE}}=\xae\xbc\xf2\xca\x0276\x9d\x83\xf4\xf3\x87>\xf4!\xdcz\xeb\xad\xf8\xedo\x7f\x0b\xb5Z\x8dm\xdb\xb6\xe1\xd5W_\xc5\x87>\xf4!\xd6\xee\x8c\x8aK\xaf4;\xc3\xc3GV \x01\xe4\xedq\x96>\x04A`\xad\xe2L&\x13$IbE^EQD(\x14B.\x97c\x07\x98\xbc\xf2\xbfrn\xe5\xaa\x13\x1d\x90*\x95\x0a\xc9d\x12\xd1h\x14\xe1p\x18^\xaf\x17>\x9f\x0f\xa1P\x08\xc9drNY\x85\x00\xa0\xd3\xe9X\xb2\x0a\xf5\x89\xd6\xe9t+~\x8d\xc9\xdd\xbe\xd1h\x14\x81@\x00~\xbf\x1f\x13\x13\x13\xac3K)\xd0\xeb\xf2\xf9<\xccf3\xea\xea\xeaP[[\xcb\x02\xb8\xa9\xd9\xba\x9c\xf4'\x93I\xb6\x0e(K4\x95J1e\x96\xe3\xdc2\xce\xb1X\x0c\x8f?\xfe8#\x7f\x8d\x8d\x8d\xf8\xe1\x0f\x7fX\xb202\x91\xfe\xa7\x9f~\x1a?\xff\xf9\xcfY\xb0\x7fKK\x0b^{\xed5\xe4\xf3y\x18\x0c\x869}\x06\xf9\x05b\xe7\xce\x9d\xd8\xb6m\x1b^{\xed5\xec\xdd\xbbw\xdas\x87\x87\x87g,!6\x177a)\x22B%c\xda\xda\xda\xf0\xea\xab\xaf\xe2\x86\x1bn`\x17\x96'\x9f|\x12k\xd7\xae\x85\xd5je\x04R\xe9\xc1\x08\x06\x83\xb8\xfb\xee\xbb\xf1\xca+\xaf0\x97\xf1_\xfd\xd5_\xc1\xe7\xf3!\x1a\x8d\xb2D\x11^\x22\x8bcE\x10@~\xdb_\xfa\x87\xbf\xbc\xca:\xb9=jjj`\xb7\xdb\x11\x0c\x06\x11\x8b\xc5\x98\xb2D\x07d\xb1\x04\x0e:\x10)\xbe\x86\x0eiR\x04\x03\x81\x00\x04A\x80V\xab\x85\xc1`\x80\xc3\xe1`\x8d\xc1\x8b)\x0c\xa4j\xa5\xd3itww#\x14\x0a\xe1\xbd\xf7\xdeCkk+#\x1f\xd4\xd2n\xa5\x13\xc0l6\x8bD\x22\x01\x9f\xcf\x87\xb1\xb11\x0c\x0f\x0f\xa3\xbe\xbe\x9e5M'\xb7y1\x8c\x8f\x8f\xa3\xb7\xb7\x17{\xf6\xecA<\x1e\x87V\xab\xc5\x95W^\x09\xa3\xd1\x08\xb3\xd9\x5c\xe0\x02T\xb6f\xcc\xe7\xf3\x88D\x22\x10E\x11f\xb3\x99\xb9\x9e\xe7c\x909\xce>\xbe\xf5\xado\xc1\xe7\xf3\xb1y}\xf6\xd9ggu\xeb\x8b\xa2\x88]\xbbv\xe1\x0b_\xf8\x02\xdb\x8b\x00\xb0g\xcf\x1e\x94\x95\x95\xcd\xf9w\xa7\xd3ih\xb5Z\xec\xda\xb5\x0b\xf7\xde{/\xba\xbb\xbb\x11\x89D\x0a.\xa7\x94l\x01\x00###L\xad>\x15\xcc\xd4\x83^N\x02\x01\xe0\xfa\xeb\xaf\xc73\xcf<\x83\xcf|\xe63,\xce\xef\xa1\x87\x1e\xc2\x8f\x7f\xfcc\xc4\xe3q\x88\xa2X\xe0\xc1\xc8\xe5r\xd8\xb9s'4\x1a\x0d>\xfb\xd9\xcf\xe2;\xdf\xf9\x0e\xb4Z-\xfc~?\xfe\xf5_\xff\x15\xb7\xdcr\x0b\xc6\xc7\xc7\x91\xcf\xe7\xe1\xf1xPYY\x09\xa3\xd1\xb8b\xce..\x04-s\x02\xa8\xec!;\xdbm\x8bci\x90\x07r\xb3R\xdd?\x8dF\x83\xf7\xbd\xef}\xe8\xea\xeab\xaa[(\x14\x82\xc3\xe1\xc0\xc8\xc8H\xc1\x5c\x17K\x04\x90\xf7\xfe\xa4\xf5\x90N\xa7\x91L&\xd1\xdd\xdd\x8d\x8f}\xecc\xf3\xfe\x9c\x17^x!\xa2\xd1(\xee\xb9\xe7\x1e\xa6J\x10y\x5c\xc9\xbd7i\xbc\xd3\xe94\xa2\xd1(\xbc^/\xba\xbb\xbbq\xf3\xcd7\xa3\xae\xaenN\xef\xb1f\xcd\x1a\xc4b1x\xbd^tvv\xc2\xe7\xf3\xe1\xf2\xcb/G8\x1cFee%\xfb=\xf2\xf9T\x1e\xec\x82 \x14\x0a\xf1,\xbds\x0c\x92$abb\x02?\xfe\xf1\x8f\xd9<_{\xed\xb5\xb8\xf4\xd2Kg5\xda\x99L\x06w\xddu\x17DQ\x84$IH&\x93\xd8\xbbw/l6\xdb\xbcT\xf9h4\x8a\xbf\xf8\x8b\xbf\xc0+\xaf\xbc2MA\xa6z\xa5uuu\xd8\xbcy3\x1a\x1b\x1bq\xe1\x85\x17\xb2\x8b\xdfL{\xa2\xd4\xef\x9ek\x11\xe1\x5c.\x87O\x7f\xfa\xd3x\xf1\xc5\x17\x99Kwrr\x12\xff\xfe\xef\xff\x8e\xaf|\xe5+\x88D\x22\x05\x1e\x91\x91\x91\x11\xd6&\xf3\xba\xeb\xae\xc3\xd6\xad[111\x01Q\x14\xf1\xf8\xe3\x8f\xe3\xa2\x8b.b\x97\xb0\x89\x89\x09\xf8|>\xb4\xb5\xb5a\xd5\xaaU\xcb\xde\x8b\xc1\xfb\x85\xaf\x10\x05P\xbe\x88\x95\xea\x1f\x9f\xec\xa5y+#\xb7\x04\x05\x5c\x9b\xcdf\xb4\xb5\xb5\xa1\xae\xae\x0e\xdd\xdd\xdd\x88\xc7\xe3\xd0\xeb\xf5\xd3\x0e\xdc\x992\xbc\x8b)r\x94!\xec\xf3\xf9\xf0\xf6\xdbo\xe3\x8a+\xae\x98\xf3\xe5\x80n\xd7\xdb\xb6m\x83\xdf\xefG4\x1a\x85\xc3\xe1@EE\x05\xca\xca\xcaX\xab\xba\x95z\xd1\xc8f\xb3H\xa5R\x08\x04\x02\x18\x1c\x1c\x84\xddnG]]\x1d+\xbfP\x8a\xf8'\x93I\xfc\xe4'?\xc1\xb3\xcf>\x8b\xde\xde^\x04\x02\x01\x5cs\xcd5\xacN\x9a\xbc\xdb\x8b8\x96>\xf9S\xa9Tx\xe1\x85\x17066\xc6\xce\x80\xcf\x7f\xfe\xf3sr\xdd>\xfe\xf8\xe3\x18\x1a\x1ab??\xf6\xd8c\xb8\xf8\xe2\x8bY\x9c\xf0l{Z\xa5R\xe1\xd9g\x9f\xc5\xa7?\xfdi\xe4r9\x18\x0c\x06\x16\xae\xd0\xda\xda\x8a\x1bn\xb8\x017\xddt\x13\xae\xbf\xfe\xfa9'\x18\xcd\xc5\xc6\x90\x87b\xb6\xe7\xaa\xd5j\xc4b1|\xeb[\xdf\xc2\xde\xbd{166\x06A\x10\xf0\xc6\x1bo\xe0\xc6\x1boD{{{\xc1\xfb\xd0\x05\x88\x92`>\xf3\x99\xcf\xe0k_\xfb\x1a\xb2\xd9,\xfc~?\x9e}\xf6Y|\xecc\x1fC*\x95\x82J\xa5bY\xd7\xabV\xad\xe2\x8b\x91\xe3\xac\xe1\x8c\x97\x81\xe1\xd9?K\xebfFA\xc9j\xb5\x1a\xe9t\x9ae\xc0\xe9t:\xe6\xa6\xb0Z\xad\x88\xc7\xe3\xecfL%E\x94\x07)\x95O(X`\xff_]\xa4\xf7\xdf\xb6m\x1b\xb2\xd9\xec\xb4l8\xb9{Q\x99iz\xf0\xe0A\xec\xd8\xb1\x03^\xaf\x17\xbd\xbd\xbd\x18\x1c\x1c\xc4\xf8\xf88\x02\x81\x00\x8bG[\x89\xa0\xb9\x88D\x22\xf0x<\xf0x<\xb8\xe5\x96[\x0aJ\xf7(\xdd\xb64']]]x\xe0\x81\x07\xf0o\xff\xf6o8~\xfc8\xc2\xe10\x0c\x06\x03\x9a\x9b\x9ba\xb5Za4\x1a\xa7\xc5z*/x\x84p8<\xef\x0b^\xb1\xf7\xe48s\xc8d2P\xa9Tx\xe4\x91G\xd8\xfa\xb8\xf0\xc2\x0bq\xfb\xed\xb7\x97\x9c\xcbl6\x8b\xf1\xf1q<\xfa\xe8\xa3\xec\xb1\xf7\xbd\xef}x\xf0\xc1\x07Y\xdch)P7\x90?\xff\xf3?\xc7\xddw\xdf\x8d\x5c.\x07\xadV\x8bD\x22\x81\xc6\xc6F<\xf7\xdcs\xd8\xb1c\x07\xbe\xff\xfd\xef\xe3\xa6\x9bnb1\xa6\xf2\x18\xd4\xd3!\x81\xa9T\x8a%\xab\xcd\x16\xa2\xa4\xd3\xe9\x90\xc9d\xf0\x85/|\x81\x85\xc7\xa4\xd3i\xfc\xf2\x97\xbfD2\x99d\x09V\xf2\xefE\xfb\xeb\xe2\x8b/\xc6\xa6M\x9b\xd8{\xed\xd8\xb1\x03~\xbf\xbf\xa0\xd6 \x9d\xa9\x00\x10\x89D\xd0\xdf\xdf\x0f\xaf\xd7\xcb\xc5\x12\x8e\xe5A\x00y\x0c\xd0\xd2W\x00Ia\xa3@\xfe\x8d\x1b7\x02\x00\x0c\x06\x03k\x17\xe7\xf5z\x91\xcdf\xa1\xd7\xeb\x0b\x5c\xbcJ\x90\x0b\x99\x0a\x7f\xca{\x0aS\xad\xc1\x81\x81\x01\xfc\xd3?\xfd\x13N\x9c81k\xdfgA\x10p\xe4\xc8\x11\xec\xde\xbd\x1b]]]\x88D\x22\xf0\xfb\xfd\xe8\xe9\xe9\xc1\xd8\xd8\x18|>\x1f\xe2\xf1x\xc1A\xbc\x92\xc8;eW\x87\xc3a\x0c\x0f\x0fc\xc3\x86\x0d\x05m\xfb\x8a\xcd\xb7\xdf\xef\xc7\xcf\x7f\xfes<\xf8\xe0\x83\xf8\xedo\x7f\x8b\xd1\xd1Qf\x10\x9b\x9a\x9a\xd0\xd4\xd4\x04\xbb\xdd\x0e\x93\xc9\xc4\xe6\xb2\xd8\xd8\xcac\xfd\x94*!\xc7\xd2_;:\x9d\x0e\xbf\xff\xfd\xef\x11\x0e\x87\xa1\xd3\xe9\x90\xcdf\xf1\xd5\xaf~uV\xf5L\x14E|\xf1\x8b_d\xff\xd6h4x\xec\xb1\xc7\xe6\xd4\xbf]\x92$\x18\x0c\x06\x9c\x7f\xfe\xf9\xd8\xbau+{~:\x9d\xc6\xc3\x0f?\x8c\x81\x81\x01|\xf4\xa3\x1fe\xa1\x07\xf2s`.=\xe2\xe7\x12s.I\x12FGG\xe7\x1c\x9f.I\x12:::p\xf5\xd5W\xb3\xdf\xf1\xde{\xef\xa1\xab\xabkZ\xc2\x9b\x1ceee\xf8\xc8G>\xc2>\xfb\xc0\xc0\x00\xc6\xc6\xc6\x8a\xc6\x1f\xe6\xf3y\x0c\x0c\x0c\xe0\xc4\x89\x13\xe8\xef\xefG:\x9d\xe6\x8b\x94\xe3\x8c\x80\x97\x81Y\xe1\x86\x80n\xd7\xa4&Q\x00\xb7$I\xa8\xa8\xa8@6\x9b\x85\xd9l\x86Z\xadF&\x93\x99V\xdbO~\x90\xe9\xf5z\x88\xa2\xc8\x12\x07\xa8\xcc\x8cV\xab\x85\xc5bAee%\x9a\x9b\x9b\xf1\xc6\x1bo`\xff\xfe\xfdhoo\xc7\xb5\xd7^\x8b\x86\x86\x06TUU\xc1h4\x22\x9dN#\x9dN#\x91H`hh\x08\xef\xbd\xf7\x1e\x8e\x1f?\x8e@ \x80x<\x8et:\x8d\xfe\xfe~\x8c\x8c\x8c\xa0\xa9\xa9\x09\x15\x15\x15\xac|\xcdJ\xaa\x0bH\xf3E\xf1{\xa9T\x0a\x1b6l(J\xce\xf3\xf9<\xbc^/\x8e\x1d;\x86\xe7\x9f\x7f\x1e{\xf6\xec\xc1\xd0\xd0\x10\x22\x91\x08S\x82DQ\xc4E\x17]\x84\xea\xeaj\xb8\x5c.F\x00\xe5FWi\xe4\x94\xe5,\xb8\x9awn\x80\xc2>\x1ey\xe4\x11\xb6\xaf\xadV+n\xb9\xe5\x16\x96\x94Q\x0cj\xb5\x1a\x13\x13\x13\xd8\xbau+\xf4z=\x92\xc9$n\xbb\xed\xb6Y\xcb\xc5\xc8\x09\xe4e\x97]\x86\xc3\x87\x0fC\x14Ed\xb3Y\xb4\xb4\xb4\xe0\x97\xbf\xfc%6m\xda\x84t:}Z1\xbdsq\xeb\x0e\x0e\x0e\xe2\xfe\xfb\xefGMM\x0d\xbe\xf9\xcdo\x96\xfc}*\x95\x8a\x85X\xdc}\xf7\xdd\xd8\xb1c\x07+L\xff\xcc3\xcf\xe0G?\xfa\xd1\x8c%]\xe2\xf18>\xfa\xd1\x8f\xe2\xf1\xc7\x1fG(\x14\x82$I\xf8\xfd\xef\x7f\x8f\x07\x1ex\x80e\xcc\xcb?w8\x1cF6\x9bE0\x18D2\x99\x5c\xb6u5\x8b\x85\x85\xf1K\xe32!\x80|\x22\xcf]\x02H\x99u\xf2\xfaW\xf9|\x1eF\xa3\x91\xd5\x0a\xa3\x8e\x11\xf2b\xce\xf4>\x1a\x8d\x06\x16\x8b\xa5\xa0e\x1c\xbd\x8fF\xa3AYY\x19\xaa\xab\xab\xd1\xdc\xdc\x8c\x9e\x9e\x1e\x1c\x8f\x91\x91\x11\x84B!LNN\x22\x99L\xb2\xcf499\x89\xbe\xbe>444\xc0\xe5r\xc1f\xb3\x15(\x8f+\xc1\x80Sfv(\x14\xc2\xd4\xd4\x14\xaa\xaa\xaa\xa6)'4?\x07\x0f\x1e\xc4K/\xbd\x84\x1d;v\xa0\xaf\xaf\x0f^\xaf\x17\x91H\xa4\xa0\xd9}[[\x1b:::P]]\x0d\x9b\xcd\x06\x83\xc1\xc0\xc63\x97\xcbM\xeb\xef]l\xbf\xf3\xf0\x8es\x03j\xb5\x1a\xfb\xf7\xef\xc7\xe1\xc3\x87\xa1\xd3\xe9\x90J\xa5\xf0\xf8\xe3\x8f\x9f4\x083\x5c\xa2(\x1c\xe3S\x9f\xfaTA\xf8\xc6\xcf~\xf63d2\x99\x92D\x8aH\xe5\xad\xb7\xde\x8a}\xfb\xf61bu\xfd\xf5\xd7\xe3\xb9\xe7\x9e\x83\xd3\xe9D6\x9b\x9dS\xd1\xe9\xd3\xd93^\xaf\x17\xf7\xde{/\xd2\xe94zzz\xf0\xd8c\x8f\x15t>Q\x92\xd5\xae\xae.LLL\x00\x00\xca\xcb\xcbq\xe7\x9dw\xe2\xd9g\x9f\x85V\xab\xc5\xe0\xe0 v\xef\xde\x8d\xcb/\xbf|\xc6\xd7\xd7\xd5\xd5\xe1\xd6[o\xc5\x7f\xfd\xd7\x7fA\x14E\xbc\xf3\xce;\x88D\x22\xd0j\xb5E\xdb/R\x9f\xe2\x85\xaa\x13\xc8\xcb\xb0q\xcc\x86\x05s\x01\x173\x06|\xf1-}\x15I\xde\xb5C\xa3\xd1\xa0\xad\xad\x8d\x110jh\x1e\x8f\xc7Y\xbc\x9d\xb2\x06\x96<\x19\xc0`0L\xeb\x19L\x06\xc7h4\xc2\xe1p\xa0\xbe\xbe\x1e\xad\xad\xad\xb0\xd9l\x98\x9a\x9a\xc2\xf0\xf00\x0e\x1e<\x887\xdex\x03\xbbw\xef\xc6\x9e={\xb0\x7f\xff~\xec\xdc\xb9\x13\x03\x03\x03\x08\x87\xc3\x88\xc5b\x05\xbf+\x9dN\xa3\xb7\xb7\x17\xa3\xa3\xa3p\xbb\xdd\x08\x85B\xb3\xb6\x89Z\x8e\xea\x1f\xd5\xfd\xf3z\xbd\xb8\xe2\x8a+\x8a\x1a\xde]\xbbv\xe1\xe7?\xff9\xdex\xe3\x0d\xf4\xf6\xf6\xc2\xe3\xf1 \x12\x89\xb09'R}\xfd\xf5\xd7\xc3\xe9t\xa2\xb2\xb2\x92\x95\x8eQ\xba\x92\x95Y\xfe3\xa9&\x1cK\x1f?\xfb\xd9\xcf\x00\x9c\x8c\x89\xd3\xe9t\xb8\xe3\x8e;X\x1b\xb3\xa2\x86B\x10p\xfc\xf8ql\xdf\xbe\x9d\xc5\xf3\xde{\xef\xbd0\x99L%\xe7\x9cb\xfc\xbe\xf4\xa5/\xe1\xa5\x97^b\xcf\xbd\xf2\xca+\xf1\xd2K/\xc1\xe9t\x96$\x9e\x0b)BX\xadV\xac_\xbf\x9e\xfd|\xe0\xc0\x01<\xff\xfc\xf3E_322\x82\xbe\xbe>6&\xa1P\x08w\xdcq\x07\x8b\x0bT\xab\xd5\xf8\x8f\xff\xf8\x0fx\xbd^$\x12\x09\xa6\xa4\xcb\xf7\xc1\xa4{\x12\xdf\xf8\xc67\x0a\xbe\xdfK/\xbd\xc4.\xd5r[\xa9\x8c\x87>\x1d\xc4\xe3qvy^\x0a\xe0g\xc2\x0a \x80\xf31\x12\xa5\x16\x87\xbc\x84\x0c\xc7\xe2\xaa\x7fD\x12R\xa9\x14\xfc~?\xee\xb8\xe3\x0eF\xd8\x08\xe1p\x98\xa9x\x94\xe1V\xac\x9f\xa3N\xa7\x83\xddng\xc9\x03r\x02(W\x01].\x17\x9a\x9b\x9b\xd1\xde\xde\xce\x8a\x10\x87\xc3a\xf8\xfd~x\xbd^\x8c\x8f\x8fcbb\x02\xb1X\x8c\xd5\x0fL&\x93\xec}(\xeehhh\x08\xc3\xc3\xc3\x18\x1d\x1den\x13e\xff\xda\xe5:oT\x1f1\x1c\x0ec||\x1c\xd5\xd5\xd5(//\x9f\xb6gz{{\xf1\xe2\x8b/\xe2\xe0\xc1\x83\x18\x19\x19\x81\xc7\xe3A\x22\x91(p\xdfK\x92\x84\x0b/\xbc\x10UUU\xa8\xaa\xaabsH\xf3]l\xaf\x16#\x86\x1c\xe7\x0e$I\xc2\x9e={\xd8\xcf\x1f\xfe\xf0\x87a\xb3\xd9fu\xe1n\xdd\xba\x95\xc5\xdc\x02\xc0\xfd\xf7\xdf_\xb2\xc7/%\x85l\xdf\xbe\x1d\xdf\xfe\xf6\xb7Y\x0cq[[\x1b\xb6o\xdf~F3\xc7\xa9\x06\xe9\x83\x0f>\x88\xd6\xd6Vv\xce\xfd\xeaW\xbf\xc2\xb1c\xc7\xa6\xadc\xea\x19L \xd2{\xf7\xddw\xb3\xe7MMM\xe1\xd7\xbf\xfe5\x0e\x1c8P\x90\xd0A\xc8\xa43\xa8\xa8\xa8\xc0\xa6M\x9b\xd8\xe5\xf9\xcd7\xdf\x84\xc1`(8\x7f\x95\x04\xf0t\xf7S\x7f\x7f?\xf6\xee\xdd\x8b\xce\xce\xce%aG\xb9\xabw\x05\x11@9\xa1S.>\xf9\x02\x98)\xb0w\xa6zc\x1c\x0bo\x04\x88H\x84B!\xc4\xe3q\xd4\xd7\xd7\x17(z\xc1`\x10\xe9t\x1a\xc1`\x10\xe1p\x98e\xff\xca\xfb\xce\x12\x0c\x06\x03*++\xa1\xd5j\xa7\xa9G\xd4\xc7\xd7h4\xa2\xbc\xbc\x1c\xf5\xf5\xf5hllDKK\x0b\xe2\xf18#\x81\xe1p\x18\xd1h\x94\x156\xa6vfT\x80\x9a\x0aVS\xfb\xb2c\xc7\x8eatt\x14\xe3\xe3\xe3\x88D\x22\xec0]\x09\xf3\x16\x8dFY\xe1\xe7\x0f~\xf0\x83\x05\xbdy\x01`ll\x0c\xbf\xfb\xdd\xef\xb0\x7f\xff~\xf8\xfd~6>\xcan-j\xb5\x1a\x17_|1jkk\x0b\xd4?\xb9\xd2[\xea`\xa7B\xd4\x1c\xe7\x0e\x8e\x1f?\xceH\x0f\x00\x5c}\xf5\xd5E\x09\xbf\x1c\x89D\x02\xaf\xbf\xfe:\xfb\xf9\xb6\xdbnCccc\xc9\xd7\xa8T*V\xe7O\xab\xd5\x22\x99L\xc2j\xb5\xe2\xddw\xdfea\x05\x8bE6f\xfa?\xa3\xd1\x88\x87\x1ez\x88\x95.\x12E\x11_\xfa\xd2\x97\x90\xcf\xe7\x91\xc9d\xe0\xf7\xfb155\x85X,V4\xeb\xfd\xe2\x8b/\x86^\xafg!\x18;v\xec@8\x1c\x9e\x96\xb8!\xb7a\x9f\xf8\xc4'\xd8\x99:66\x86\x91\x91\x11F\x9a\xc9s1W\x028[\xa2\x15\x95\x9d\x11E\x11\xd1h\x14\xd1htI]^g\xeb\xfd\xce\xb1\x0c\x14\xc0\xb9L\xba\xbc\x91|\xb1\xd7s\x17\xf2\xe2\x92\x08\xca\xfa\x0d\x87\xc3\x98\x9a\x9aBEE\x05\xaa\xab\xab\x0b6\x22\x95|\xa0\x96mD\xb0\x8a\x19\xfc\x8a\x8a\x0a\x18\x8dFV\x97\x8f\x12\x08\xe4\xf3-\x8a\x22\xca\xca\xcaPUU\x85\x86\x86\x06\xacZ\xb5\x0aF\xa3\x11\x99L\x86\xf5\x08\xa6^\xb6Tf\x81\x0eF\xeaTB\x17\x07\x9dN\x87\xc1\xc1A\x0c\x0d\x0da||\x9c\xb9b\x96\xb3\x0aH\xea\x1f\xc5\xfe\x0d\x0f\x0fc\xe3\xc6\x8d0\x99L\xd3\x9e\xb3\x7f\xff~\xf4\xf7\xf7#\x14\x0a1\x82\x9f\xcdf\xa7\x8dOGG\x07\x9a\x9a\x9a\x98\xfaG\x89<\xc5\x0c\xd9L\xfbu\xa6va\x1cK\x13\x87\x0f\x1fF0\x18\x84J\xa5\x82\xcdfc\x85\x9fK\xad\xbb\x9e\x9e\x1e\xd6\xef\x17\x00n\xbc\xf1\xc69\xc5\xdc>\xf1\xc4\x13\x18\x1f\x1fg\x04\xe9\x97\xbf\xfc%\x1c\x0e\xc7Y\xeb\xe0\x93\xc9\x9cT\xe5\x1e~\xf8a\xe6\xb6\x0d\x87\xc3\xb8\xef\xbe\xfb\xa0\xd1h\xb0w\xef^\xbc\xf7\xde{\xacs\x91\x92\xa0\x94\x97\x97\xe3\x82\x0b.`\x8f\x1dD\x22\x11f\x8c\xe4c\xe4\xf7\xfbq\xf8\xf0aLLL \x10\x080w\xba\xb2\x0b\x82N\xa7\xc3\xc6\x8d\x1bQ]]\x8d\x8a\x8a\x0a\x98L&F\xb2g2\xce\xc5B6\xf8E\xed\xdc\x00\xed\xa7W^y\x85\xcdeMMM\x01\xa1)\xb6\xe6T*\x15\xdez\xeb-\x00'c\xd9\xca\xcb\xcb\xf1\xa1\x0f}h\xd6\xdf511\x81\xef}\xef{\xec\xb1\xbb\xee\xba\x8b\xbdn\xb1\xd4\xbf\xb9\xf4\x16O\xa7\xd3\xe8\xe8\xe8\xc0u\xd7]\xc7\xe2\xf9\x9e\x7f\xfey\xbc\xfa\xea\xab\xac\x9da1\x12\xa6R\xa9\xa0\xd3\xe9p\xd1E\x17\xb1\xc7\xfa\xfb\xfb\xe1\xf7\xfbg\xfc<*\x95\x0a\x06\x83\x01UUUl\xff\x1d\x04\x83A\xdcv\xdbm,\xf1C\x8eP(\x84\xb1\xb11\x0c\x0e\x0e\x22\x1c\x0e#\x91H\xb0\xd8\x18\xb9\x92D\xf3q\xfe\xf9\xe7\xc3b\xb1\xc0f\xb317m\xa9\x182\xa5\x0aX[[\x8b\xe6\xe6f\xf4\xf6\xf6B\x14EV\x9a\x86\xe6]\x14E\xe6N\xa1\x9e\x9c\xf4\xb7(\x8a\x08\x87\xc3\xe8\xef\xefg\xad\xcc\x9cN'+G\xb3\x1c\x08\x09\x19\x85x<\x8e`0\x88\xc1\xc1A\x96Y(\x1f\xe7\xc9\xc9Itww#\x16\x8b\xb1\xb8\xa0T*UP\xb3\x91\x08\xde\xe5\x97_\x8e\x8a\x8a\x0a8\x9dN\x94\x95\x95\xb1q.\xa6\xd8\x92\xa23\x9b\xd2R,\xa4\x83c\xe9\xac!A\x10\x0a2y\x01`\xdd\xbau3v\xf1\xa0\xd7\xec\xdc\xb9\xb3\xe0\xf1\x8b.\xbah\xc6\xe2\xcf\xb4\xc6\x9ey\xe6\x19\x00'=:\xf5\xf5\xf5X\xb7n]\xc9.#\xb3|\xf8\xb90\x89y\xbfm*\x95\xc2e\x97]\x06\x8b\xc5\x82H$\x02\x00\xd8\xb9s'ZZZ\x98\x9d*v~\x85\xc3a\xdc\x7f\xff\xfd\xd0j\xb5\x88\xc7\xe3,\xfe\xae\x94\x22J\x9d\x8e(<\xc5\xe3\xf1\xa0\xbd\xbd\x9d\xb9h\xe5\xe5\xd3\x02\x81\x00\xd4j5\x12\x89\x04\xecv;\x9cN';\xc7i\x8c}>\x1f***\x10\x89DX8\xd5\xd8\xd8\xd8\xb4\xb9\xa40\xaa\x85\xdc\x97\xd4\x13Y\xa7\xd3!\x97\xcbadd\x04\x83\x83\x83\x00\x00\x8b\xc5\x82\xb6\xb66\xbe\xe18\x01\x9cy\xf1\x10\x01\xd4h4\xc8\xe5r0\x18\x0c,K\xd4\xedvcdd\x04\xfd\xfd\xfdp8\x1c,\xa1@\xd9\xe0\x9e\x03\xd3T\x1d\xfa\x9b\x5c\xa0\x99L\x86e\xd5\x86\xc3ax\xbd^\xf4\xf4\xf4\xc0n\xb7\xe3\xf6\xdboGUU\xd5\xb4\xb9\xc9d2\xd8\xb1c\x07v\xef\xde\x8d@ \xc0\x0a1\xa7R\xa9\x82Vp\xf4\xfb\xd7\xad[WP@X\x19\xffW\x0cD\xe4\xacV+\xaa\xab\xab\xd1\xd0\xd0\x80\xd5\xabWchh\x88\xa9}Db\xe5\xb1\x80\x14\x9fCa\x04\xf4\x1d\x0d\x06\x03:;;\xd1\xde\xde\x8e\x91\x91\x11\xe6\xd2\x9c\xcbg9W\xd4?r\xfb\x8c\x8c\x8c\xa0\xbe\xbe\x1eN\xa7\xb3\xe0`\x97$\x09\xfb\xf6\xedCww7+\xfbB\xea\x822\xcbp\xed\xda\xb5hjjBuu5\xecv;t:]\xc9\xd6Ss\xddo\xbc\x17\xf0\xd2?+\x0e\x1e<\xc8\xc8\x0f\xc5\xa5\xcd6g\x87\x0e\x1d\x02p2\xc1\xa0\xae\xaenN\x8a\xce\x1f\xfe\xf0\x07\xd6e\xe4o\xfe\xe6o\x0aT\xc7y\xe3\x14\xd6\xdf|\x5c\xc1\x9b7o\xc6\xb6m\xdb\x00\x00o\xbf\xfd6>\xf9\xc9O\xb2\xf2S3\xbd&\x1e\x8f\xb3R1\xb3\x91\xabL&\x83\xea\xeaj\x98\xcdf\x84\xc3a\xd6\xfb\xf7\xf2\xcb/G4\x1a-\x88\xe3U\xa9T\x18\x19\x19\xc1\xf0\xf00\xb2\xd9,\x9cN'6m\xda\xc4\xcef\x82\xd7\xebE\x7f\x7f?\xe2\xf18\xb3\x89>\x9fo\xda\xe7!\xaf\xcdlc_\xac\xc7{\xb1\xc7(\x16qdd\x04\xeb\xd6\xadCYY\x19\x02\x81\x00\xfb\x0cn\xb7\x9b\xd5YT\x8eY\xb19\xe1\xe7\xc5\x0a#\x80\xe4\x02\xa4\xd6?z\xbd\x1e\xa1P\x08\xa2(\xb2\x1e\x89\xc7\x8f\x1fg\xeeD\xca\x94\xa2 \xf5\xb3U>`)\x1e\xe8\xf2\x929D\xfa\xc8\xddKeU\xa2\xd1(\x82\xc1 <\x1e\x0fB\xa1\x10.\xbf\xfcr\x5ct\xd1EE\x8b\xf9\x0a\x82\x80\xbe\xbe>\xbc\xf3\xce;\x18\x1b\x1bC0\x18D4\x1ae\x99eJW\x82 \x08\xd8\xb0a\x03\xaa\xaa\xaa\xe0p8X\x06\xb0\xb2\x06\xa0r\x93\xd3!e4\x1aQQQ\x81\x86\x86\x06455\xa1\xae\xae\x0e\x03\x03\x03\x05\x87\x18)}r\x12(\xbfDP}\xb1L&\x83\xc3\x87\x0f\xa3\xa9\xa9\x09\xf5\xf5\xf5p8\x1c\xcc\xady.\xabR\xf2\xd8?\xaaQv\xfb\xed\xb7O\xcb\x8e\x9e\x9c\x9c\xc4\xd1\xa3G\xe1\xf1x\x98\xfb\xb7X\xe6\xaf(\x8a\xe8\xe8\xe8@MM\x0d*++QVV\x06\x9dN\xc7\x0e\xf0\xd3q\x17\x15\xdb\x97|\xaf.-\x02\xd8\xdf\xdf\xcf\xce\x0cy\xdb\xc7R\xaf9|\xf80\xbb\x84o\xdc\xb8\xb1\xe4kT*\x15\xc6\xc7\xc7Y\xf7\x1e\x00\xb8\xf9\xe6\x9b\xd9\xc5n\xde\x18\x0d\x03\xbf\xef\x06D\x01@\x91\xdfI\x0f]\xdb\x044\x9b\xe7E(T*\x15b\xb1\x18\xae\xb9\xe6\x1al\xdb\xb6\x0d:\x9d\x0e\xe3\xe3\xe3s\xda\x03\xf3\x11\x22\xb2\xd9,\xacVkA\xb9$\x9f\xcf\x07\x00\xac\x16k\xb1q\xa7\x18\xe7t:=\xed\x1c\xcdd2\x18\x19\x19\x99V[\xb7\x18\xf9\x94\xf2\x12\x90/>|\xf3\xdd\xbb\xb1X\x0c\x83\x83\x83\x88\xc7\xe3\xe8\xee\xeeFmm-s\xfb\x92::\x97\xb9^\x8c\xe4\x0f^?x\x09\x11@r\xdb\xc9'\x99\x8c~,\x16c\x1d\x1d\xa2\xd1(\xecv{A?Z\x8dF\x83\x13'N0\x22\x13\x8f\xc7QSS\xc32\xaf\xe6\xbb\x01\x97\x0b\x94\x07\x13);D\xfc\xa8TK,\x16+\x88\xa3\xd4h4hnn\xc6\x95W^9-XY\xbe\xd9\x8f\x1f?\x8em\xdb\xb6\xb1\xae\x11\x81@\xa0@IR\x06+\xd7\xd5\xd5a\xed\xda\xb5,\x8e\x8c\xdc\xbf\xc5H\xbf\x12\x94\xf9Ku\x01\xa90\xf4\xd8\xd8\xd84\xd7\xb5\xc1`(\xd8\xe0\x82 \xb0\x18R*\xe3@1<\xc3\xc3\xc3hhh`I)\xa2(\x9e\xb3\x8d\xd5\xe5\xb1\x7f\x91H\x04\xe3\xe3\xe3\xacc\x87RQ\xa7\xde\xca\xa4\xda\x16+\xfa\x0c\x00UUUX\xbf~=\x5c.\x17S\xffHe?\x9dCw9\x97\xdfYN\x04p``\x00Z\xad\x16\xe9t\x1a\x8d\x8d\x8dszMgg'\x8b\xdd\xa6Vj\xa5\xd6Kgg'K\xe42\x18\x0c(//?\xf5\x0f\x9e\xce\x01\x13Q@3\x03\x01\x14p\x92\xdc$\xb2\xa7\xe4\x06N\xa7\xd3\xd8\xb0aC\xc1c===hnn>\xed\xf5L\xaf\xa7s\xd3f\xb3\xb1\xc7\x02\x81@\xc1\xe5n&2\x94\xcb\xe5\x10\x8b\xc5X\x17\x95R\xf6`\xa6\xef'\xc3E\x87\xb0\x00\x00 \x00IDATe%\xe4uy\xa80s\xa2\xcf\xc0\xc0\x00\xb4:-\xaa\xab\xaaY\xd8\xc7\xc4\xc4\x04&''\xb1f\xcd\x1aX\xadVF\x5c\x93\xc9$k\x8fG\xb1\x88d\x8f\xb3\xd9,\x92\xc9$\xccf\xf3\xbc/\x80s\x1doyMZ\xb2gT\xab\x96B\x7ff\xb2\x9f\xc41\x96k\xac2\xc5\x89\xce\xf5\xb2%\x9e\xce\x22\x97\xdfJ\xe4\x7fK\x92\x84\xf2\xf2r\x8c\x8d\x8dM{>%#\x10,\x16\x0b3\xea:\x9d\x0e\xe5\xe5\xe50\x9b\xcdp:\x9d\x08\x87\xc3x\xf3\xcd7\x11\x08\x04P[[\x0b\xb3\xd9\xcc\x12\x00V2\x01\xa4\x80`\x22eJ\xa5\xc7\xe1p\xa0\xac\xac\x0c\xcd\xcd\xcd\xa8\xaa\xaaBmm-s\x9f*\x95#\x8a\x19\xe9\xed\xed\xc5\xb6m\xdbp\xe4\xc8\x11\x8c\x8c\x8c\xc0\xef\xf7#\x1a\x8d\xb2\xd2\x02\xf2\xa4\x0a*\x7f\xf0\xfe\xf7\xbf\x1f\x0d\x0d\x0d\xa8\xac\xac\x84\xd9l.(\xcf2\x97uE\xae\xdc\xf2\xf2r\xd4\xd5\xd5\xa1\xad\xad\x0dG\x8f\x1e\x85\xdf\xefg\xc9&\xa4l\xca\xe3\xcb\xe41\xa4\xa4&\x93[\xe6\xe8\xd1\xa3hhh@MM\x0dS\x8d\x89 \x9e\x8b\xf3M\xfb\xc5\xef\xf7#\x10\x08\xe0\xca+\xaf,\xd8\xdc*\x95\x0a^\xaf\x17\x9d\x9d\x9d\x98\x9a\x9ab\x074\x112\xf9\x9cK\x92\x84M\x9b6\xb1\xba\x7ff\xb3\xb9@\x95P\x92E\xa5\xda\x5c\xaa\xec\x07\xc5\xf0\xce\xe7f?\xdb\xfbr,<\x01\x9c\x9a\x9ab\x17\xaa\xe6\xe6\xe6\x92\xc6\x97\x1e\x0b\x87\xc3,\xde\xaf\xb6\xb6\xb6`\xee\x8a\xd9\x84\xd1\xd1QFz\xea\xeb\xeb\x0b.p\xf32\xf6*\x15TR\xfed\x0c\xa0<\xd9\xa9\xe0C\xaaNK\x05\x92$\x09f\xb3\x19f\xb3\x99\xd5\xcc\x1b\x1e\x1eF[[\x1b\x8bC\x9e\x8f=\xd4\xeb\xf5\xd3\xceK\xba\xa8;\x9d\xcei\x040\x91H`rr\xb2d\xdd\x5cj\xc7y*H\xa5R\xc8J\xd9\x821\x97'z\x09\x82\x80\x13'N\xa0\xab\xab\x0bZ\xad\x16\xa3#\xa30\x9b\xcd\x88F\xa3\xec\x1cN&\x93\xb8\xe4\x92K`4\x1a\xa7\x95\x95\x917\x05\xa0\xc7\xe3\x898\xca\xca\xca\x98m\x9a\xc9\xfd{\xaa\x9c\x83\x0a\xff\x93\xfd\xa0nV\xd9l\x16\xb5\xb5\xb5X\xbf~=\xcb\x19\xa0\xf5@\x9d\xa3B\xa1\x10\xf4z=.\xb8\xe0\x82\x82\x18\xd6\xb9\xac\x9bd2\xc9<\x5ct\xf1\x96$\x09\xb1X\x0c\x16\x8b\xa5\xe0}\x8a\xbd\x9f\xfc\xff\xe8\xbbP\x85\x0d\x9b\xcd6\xe3\xe5z\xa6\xcf\xa6t\xd1\x07\x02\x01\xf4\xf4\xf4@\x92$\xd4\xd4\xd4\xa0\xa1\xa1\x81\xbdvrr\x92\x09it\xe6\xe7\xf3y\x88\xa7K\xa2\xe4\x85\x9ciQi\xb5ZLNN\xa2\xa2\xa2\x02\xa9T\xaa\x80\x14P\xe0\xbe|Ah\xb5ZVC\xce\xe1p`\xcd\x9a5\xb8\xfd\xf6\xdbQYY\xc9\xcaY\xe4\xf3y\x84B!\x04\x83\xc1eY\xe6c\xae\xaa*\x15T\xb6\xdb\xed\x8c\x04\x91+\xddf\xb3\xc1d21uL9\xb7\xca\xf8\x0e\xbaA\xed\xde\xbd\x1b/\xbe\xf8\x22\xba\xbb\xbbY/\xdeh4\xca\x02\x9c)+\x8d\x16S.\x97\xc3\x86\x0d\x1bp\xc9%\x970\xf7/\x91\xf3\xf9\xb8[)\x09\xc8f\xb3\xb1X\xc0\xf3\xce;\x0f/\xbf\xfc2\x04A@*\x95b\xea\x94\xbc\x9c\x01\xb9\x82\xa9\x86\x17e\x03\xe7r9\x0c\x0e\x0ebpp\x10\xf5\xf5\xf5\xac\xbb\xc5\xe9(\x5cg\x1b\xe9t\x1a\x91H\x04>\x9f\x0f:\x9d\x0ek\xd7\xae\x9dv@P1l\x8f\xc7\xc3\x92?h\x8f\xc8\xc3\x04jjjX\xe1g\x87\xc31\xad\xed\xdbL\x87\x8d\xb2m\xa3\x92(\xca]\xf4\xb3\x19w\xba\xec\xc9K@q\xb7\xcd\xe2\x83\x92/.\xbc\xf0Bx\xbd^\x0c\x0f\x0f3\x028\xd3\xe5\xa8X\x92\x07)A\xa5\x14\x06\xb7\xdb\xcd.\xab.\x97\x0bf\xb3y\xda\xf3\xe7<\xe7\x1a\x0d \x08\x80\xa0\xa6E\xaf\xf8\xfb\xf4\xc6\x85\xca\xd4\xd4\xd4\xd4\xa0\xbb\xbb\x1b\xc0\xc9,\xddS\x09\x1bQ\xab\xd5\xa8\xa8\xa8\xc0\xd4\xd4T\x81}\xa2\xcb\xac\x5c\x09\x0d\x04\x02\x8c\xa0\x95\xb2e\x92$\xc1\xef\xf7\x17\x9d\x8b\xb9z\x10\x90GQ[\x90\xcf\xe7Y\xb9/\xba\xd4{<\x1ex<\x9e\x82\xe7\x06\x83A\x9c8q\x02k\xd7\xae\x85\xdb\xed\x9e\x95\x90\x0c\x0f\x0d#\x93>\x19\x92\x13\x08\x04\x10\x8dF\x17d\x8f\xabT*\x04\x83A\xb8\xddnv\x86\xc8\xc7\x98\xe2'\xa9\xbe%\xd9\xb8\x9e\x9e\x1e\x8c\x8e\x8e\x16(\xad\xe1p\x18v\xbb\x1d\xa2(\xa2\xb5\xb5\x15&\x93\x09\xbd\xbd\xbd\xe8\xe9\xe9\x81\xd1h\xc4\x05\x17\x5c\x80\xb2\xb22\xf6\x9d\x8e\x1f?\x8e\xc1\xc1A\xf6\xfc\x96\x96\x16\xf4\xf4\xf4```\x00\xd9l\x16:\x9d\x0e\x97^z)L&\x13\xb3\x95\x9d\x9d\x9d\x98\x98\x98\x80\xc1`@{{;\x5c.W\xc1\xda\x9f\x9c\x9c\xc4\x81\x03\x07X7\xa5\xf5\xeb\xd7#\x9b\xcd\xc2\xeb\xf52QDN`'''\x11\x89D \x8a\x22\xf3p\x112\x99\x0c:;;\xd9\xba\x9a\x9a\x9a\x82N\xa7\x83\xcb\xe5BWW\x17\xba\xbb\xbb\x91\xcf\xe71::\x8a\x0b/\xbc\x10f\xb3\xf9$\x97\x90W\x15\x9f\xaf\x14K\x03\x9f\xc9d\xe0v\xbb\x11\x0c\x06\x99\xf4O\x8bK\xe96\xa4/\x1b\x8f\xc7\x19Y$\x03m2\x99\x98q\x22bSYY\x89\x9a\x9a\x1a~\x82\xcfC!\x9cOl\xca\xeb\xaf\xbf\x8e\xed\xdb\xb7\xb3\x96jtS\xa0^\xbc\xf2\xac_\x9a?\x83\xc1\x80K/\xbd\x94\xd5\xa4\xa26l\xf3%\x80\xf4\x19\x92\xc9$\xa2\xd1(\x04A`\x89!\x1e\x8f\x07\x82 0\xb5\xd8h4N\xcb2%7\xb2N\xa7c\xaen\xbf\xdf\x8f\xee\xeenX\xadVh4\x1a\x84\xc3a\xd8l\xb6\x19\x13BJ\x8dU\xa9[9\xfb7\xf2,\xbeF\xe9bQ\x12ny(\x84\xf2&^p0#\x8f\x5c\xf6O1\x9d~\xbf\x1fCCC\xac\xa5\x94\xfc\xb9\xd1h\x14\x87\x0e\x1d\xc2\xe4\xe4$\x02\x81\x00\x8b\x87T\x96\xeb\xc9\xe7\xf3\xd8\xb8q#r\xb9\x1c\x22\x91\x08\x9bgRm\xe5\xdd\x08\xe4\xddz\xe8p\x09\x85B\x98\x9c\x9c,X\x17\xc5\xc2\x8f<\xf20\x8c%Q;\xe0A^#\x14\x84~X\xad\xd6\xe9$\xf4\x14\xd5\xe4\x5c.\x07\xbb\xdd\xce~\x8e\xc5b\xa7D\x00\xc9\x86)\xd73\xd9C\xa7\xd3\xc9\x88\x22e\x1d\xcf\xe5\xf3Ro\xf4\xf9|7\xaa\xd9(I\x12\xeb\x92\x14\x0e\x87Q^^\x0e\xadV\x8b\x9e\x9e\x1e\x0c\x0f\x0f\xcf\xd9;2<<\x8c\x91\x91\x919y\xde\xc6\xc7\xc7\xd9s\xb3\xd9,+F_\xcc^\x95R\xb8\x8a)p\xa4.[,\x16\x88\xa2\x88L&\x03\x87\xc3\x81\x96\x96\x16D\x22\x11\xf4\xf5\xf5\xb1\x8b\xb0\xd9d\xc6\xbb;\xdf\x85\xd7sR-t8\x1c\xec\xfc\xa2$\x1eI\x92011\x01\x8b\xc5\x02\x8f\xc7\x03\xb5Z\x8dP(\x84\xae\xae.ttt\xc0`0\xa0\xaf\xaf\x0f'N\x9c`cz\xf8\xf0atvv\x16x\xd3\xb2\xd9,v\xee\xdc\x89\xf6\xf6v\xf6\x9a\x89\x89\x09\xa8\xd5j\xa4R)\x1c8p\x00\xe7\x9dw\x1etz\x1db\xd1\x18&''\x0bzBG\xa3Q\xd6jQ\xadV\xa3\xaf\xaf\x0fn\xb7\x1b\xf5\xf5\xf5\x08\x87\xc38z\xf4h\x81\x08\xd2\xd9\xd9\x09\xb3\xd9\x8c\xc6\xc6F\xb8\x5c.\x0c\x0e\x0e\xb2\x5c\x0a\xaa\xa4\xf1\xce;\xef\xa0\xba\xba\x1a~\xbf\x9f\xed\xdf@ \x80\xbe\xbe>\xb4\xb4\xb4`jj\x0a\xe2{\xef\xbdw\xda\x12:\x19\x15\xb7\xdb] \x9b\x0b\x82\x80d29\xad\x03\x01\xb9\xecHB\xcdf\xb3L\xb1\x92+\x18\xf2N\x10\xa7BN\x973\x8a\xcdQ\xa9\x22\xbe\xf2\xc5\x9aH$\xd0\xdd\xdd\x8d\xd7^{\x0d]]]\xac\xf4\x8e\xcf\xe7C<\x1eg\xca\x1f\x91\x08e\xa0\xf1%\x97\x5c\xc2\xfaL\xba\xddnD\xa3\xd1\x02\xf7\xef|n\xfatK\x8dF\xa3H&\x93\x10E\x11\xb5\xb5\xb5\x98\x9c\x9cd\xe4\x93.\x15\xa4\x80\x12\x11$\x05\x93n\x83d\xb0\x8e\x1d;\x06\x9b\xcd\xc6n\x8c\x0e\x87\x03:\x9d\xee\xd4\xb3\x10K\xcd\xc3I\xe6\x87\x93\xb6?\xbf\xa0sKk?\x12\x89\xc0`0`\xcd\x9a5\xd3J:\xec\xdb\xb7\x0f\x03\x03\x03\xacc\x0b\x91?e1Y\xa7\xd3\x09\x8b\xc5\x82d2\x09\x9f\xcfW\x90}/\xff]\xc5\xe6-\x97\xcb!\x1e\x8f\xc3\xe3\xf1 \x91H\x14\xb8W(\x0bq\xae\xc9#\xd1h\x14\x1e\x8f\x07###\x88\xc5b\xacn\xe4B\xee\x83\xd39\xcf\x94\xef9/\x17 \xad\x85\x05\xfc\xfc\xf3\xfd\x0c\xb3\xc1\xe3\xf1\xb0\xf3\xa0T\xc1v\xb9\xaa%_GtY\xcbKy\xa8\x04\xd5\x8cdj\xcd\x9a5\xc8d2\xa8\xab\xabC2\x99\x9cV\x1ajN\xdfI\xa5\x82\x86B]\x00H\xb9?\x15\xa0\xcff\xb3\xa8\xac\xac<\xf9\x19\x16`\xdb\xc9\x09\xca\x5c\xd6\xa3<\xfb^\xfe\x98^\xaf\x9f\xf6\x7fd\xcb\xe4*\xde|\xe64\x9dN\xb3\xec\xe1\xb9\x90\x7f\xbd^\x0f\xab\xd5\x0a\xaf\xd7\x0bI\x92p\xe2\xc4\x09v\x8eR\xa1\xfeD\x22\xc1\xe2\x0a\xe5\x7f\x94\x97V\xf2\x10\xcc\xa7&/}\x7f:\xbf)1\xb1\x14\xf1\x9fK\x0b\xbfP(\x84d2\x09\xa7\xd3\x89\xd6\xd6V8\x1c\x0e\xc4b1\xb8\x5c.V\xcagdd\x04\x89D\x02CCC'\xd5\x5c\x9f\x1fz\xbd\x1eMMMhnn\x86\xd7\xeb\xc5\x91#G\x90H$\x98-\xa1\x84I\xb5Z\x8dL&\x03A\x10\xe0\xf3\xf90>>\x0e\xa3\xd1\x88\xbe\xbe>\x08\x82\x00\x83\xc1\xc0B\x8dr\xb9\x1c4\x1a\x0d\xca\xcb\xcbQQQ\x81\xf1\xf1q\xf8\xfd~\xec\xdb\xb7\x8fy\xc9\x8cF#\xf4z=\xc2\x91\x931\xd9\x9d\x9d\x9d\x90$\x09\xf1x\x9c\x952\xa3P\x1c\x9f\xcf\x87h4\xca\x08\x5c,\x16\xc3\xe8\xe8(\xc6\xc7\xc7Y\xc2lee%\x9cN'2\x99\x0c\xb3\xd7\xc7\x8f\x1f\xc7\xe8\xe8(\xa2\xd1(\xab\xefZQQ\xc1jwNMM1\xd2N\x89\xb6\x14\xe2\x15\x0a\x85 RP\xef)\x1dx*\x15rR\x0e\xb9l\x0eSSS\xac\xce\xd3L\x86DyP\x90A\xa6\xbaB\xe1p\x18Z\xad\x16\x1e\x8f\x07\xef\xbd\xf7\x1e\x1c\x0e\x07\x93T\xe9\xbd\xb8\xbbhn\xd2?\xfd-w\xdd\x05\x83Atuu\xe1\xd0\xa1Cl\xe1P\xcd\xc5X,\xc6b\xfe\xe4\x0a\x92R\xb5\xda\xbcy3n\xb8\xe1\x06\xac^\xbd\x1a\x8d\x8d\x8dp:\x9d\xd0\xeb\xf5,\x93t\x0eV\xb2P9P\x01\xd9\xcc\xc9\x1b\xae\xdb\xed\x86\xd3\xe9\x84(\x8a\x98\x9a\x9a\xc2\xc8\xc8\x08ssS\xbc#\x1d\xa2D\x00I)\xa6D\x0f\x8aY\x09\x04\x020\x9b\xcd\xac\xc4\x0c}Nf\xb4\xf2s$\x0bE\x94\xbd\x85\x9c\xa7bcF\xb13\xd1h\x94\xd5c\xbc\xed\xb6\xdb\xa6\xed\xa7P(\x84\xc3\x87\x0fchh\x08\xe1p\x98\xa9\x7fr\x97=\xe1\xfd\xef\x7f?.\xbd\xf4Rttt\xa0\xb1\xb1\x91%\xed(U\x19e\x8c(\x19 \x9f\xcf\x87\xee\xeen\x8c\x8d\x8d!\x14\x0a\xb1\xf7&W\xc2\x5c\xe1r\xb9\xd0\xde\xde\x8eK.\xb9\x04\xd5\xd5\xd5\xc5\x95\x9c%~\xd1Z\xca\x9f\xf5t\x09\xafr\x1d\x96\x95\x95\xb1\x90\x0b\x9a\xbf\xab\xae\xbaj\xc6:y\xc0\xc9\xba\xa0\x0f?\xfc0S\x92\xf5z\xfd\xa9\x8d\xa3J\x05\xa1?\x08\xf1\xf8\x11\xe4E\x81\xb9C#\x91\xc8\x9fTh\xb5l\xfd\x9e\xa2@@FY\xeeY\x98M5\xa63'\x10\x08L\xebU\xae\x0c\x85 \xc1CIv\xe6J\xfe\xe9RN\xaf\xb5\xdb\xed\xac\xf4J1\x98L&TTT0\xe5\x87~\x9fF\xa3a\x15\x1d\xe4kE\xadV\xa3\xb1\xb1\x11\x91H\x84\x95\xb6\xa1\xef\xb2f\xcd\x1a$\x93I\x1c:th\xd6\xd0\x0e\xf9\x19B\xef-\x0f\xe1QzB\x8a\x89\x143\x8d7u\xaf\xaa\xac\xacD}}=\xda\xdb\xdb\xa7\x9d\x1b&\x93\x09eeeH$\x12\x18\x1c\x1cd\x8aYMM\x0d\xd6\xacY\x03\x95J\x85\xba\xba:H\x92\xc4H]:\x9df\xe4O\x14E\xb8\x5c.VP\xbb\xab\xab\x8b\x11D\x97\xcb\x85\xf5\xeb\xd7C\x14E\xd6q\x8a\xcahi\xb5Z\xd4\xd7\xd7chh\x08\x13\x13\x13\xc8\xe5r\xb0Z\xadhii\x81\xc9d\xc2\xe0\xd0 \xfaz\xfb\x18\xc1s\xb9\x5c(++\x83\xd3\xe9Dyy94\x1a\x0d+\xd7F\xbd\xee\xa9\xd4N*\x95\x82^\xafG\xc7\x86\x0eT\xb9\xaa\x98\xfdkhh`\xe1N\xe1p\x18\x82 \xa0\xbc\xbc\x1ck\xd7\xaee\xe3B\xed\x01\x05A\xc0\xaaU\xabP^^\x8e\xa3G\x8f\x22\x1a\x8d\x22\x14\x0a\x9d\xf4\x04\x9cn\xd1F2V\x1a\x8d\x06n\xb7{\xc6x\xab\x99\xfa\x1b\x92\x8cK\xb7\x8c`0\x88\xc9\xc9I\xe6\xdaknnF{{;/0{\x0a\x86@\x92$x\xbd^\x8c\x8d\x8d\xe1\xd8\xb1c\x18\x18\x18`\x01\xb4>\x9f\x0fSSS\x88\xc5bl\xf1Q\xf0\xb2\x92@\xd0|vtt\xe0\xe6\x9bo\xc6\xea\xd5\xab\xd1\xd2\xd2\x02\x97\xcb\x05\xab\xd5zJ\xae_\xe5:\xa0x\xbfx<\x0e\x9f\xcf\x87\x81\x81\x01\x16B@EG)\xf1\x85bH)\x03\x8cjJR'\x0a\xb5Z\x8d\x9e\x9e\x1el\xdc\xb8\x91\xb5\x86\xd3h40\x99Lls\xcc\xd9 -\xa0\xb27\xd3<\xc9\x7f&E\x9dZ\xbey\xbd^\xb8\x5c.vp\x91\xbb\x96b\xffN\x9c8\x81P(\x84P(TP\xaeG\xfe\xbe\xb5\xb5\xb5\xe8\xe8\xe8@ss3\xea\xea\xeaX\xd2\x8e\xbc\xf3\x87\xf2\x10\x97\x7f&*(-/\xf2]\xcc}=\x97\x84\x0e\x0a\xdc\xa6\xc3\x9a\x5c9\xcbI\x89_*\x17\xc0\xd3\xfd\x0e\xa4\x8a\xe9\xf5z\xf6oR\xa3g*\x1e=\xd3\xef?\xe5q2\xa4N\xc6\x01jN\xfe.\xaa\x22!wK\x9f\xcew'\x8c\x8d\x8d\xb1KeEE\xc5\xac1\xe6r\xaf\x83\xfc;\x16ss+\x09\xe0|\xc7C\x1e\xca!I\x12\xaa\xab\xabYu\x8cb\xef\xa3\xd3\xe9`\xb7\xdba0\x18\xa6%\x90\x14K\xc8\xb0Z\xad\xd8\xb0a\x03+\x1bF\xcf\xd7\xe9tLY\x93\x87m\xd1z\x90\x8f\x11\x85\xd8P\x22\x0d\xbd\xbe\xa6\xa6\xa6\xe0s\x9e\xca\xfcP\xfc\x7f*\x95BYY\x19\xea\xea\xeaf\xf4\x80\x99\xcdf\x16\xa7Hjnuuu\xc1\xf3\xeb\xeb\xeba\xb5Z\x99+\xfe\xe8\xd1\xa3H\xa7\xd3hmmEkk+K\xaa#\x95\xb0\xa9\xa9\x09\xabV\xadb\x99\xcd---\xd3~\xb7V\xab\xc5\xea\xd5\xabQSS\x83L&Sp\xae556\xc1l2#\x10\x08\xc0d2\xa1\xaa\xaa\xaa \xf9\x0e\x00\x0c\x06\x03K\x96\x02N\x16hw8\x1c\x88\xc7\xe3\xb0X,'\xd5n\xd9w\xd0\xeb\xf5X\xb5j\x15\xacV+&&&\xa0\xd5j\xd1\xd0\xd0\xc0~gee%6n\xdc\x08\xaf\xd7\x0b\x8b\xc5\x82\xda\xdaZh\xb5Zd2\x19\x9c8q\x82er\x8b\x0b\x91\xea\xae\xcc\xe6\x9bO:7\x19t*2,\x08\x02\xdcn7#$\xc7\x8e\x1d\xc3\xf8\xf88+5B\x81\xeb\x92$\xcd\xe8v\x5c\x8c\x83\xf3l\xba\x9e\x95\xf5\x9f\x8cF#\x8b\x9d\x8b\xc7\xe30\x99L\xcc\xfd\x97H$X\xaf\xd8\xbe\xbe>\x04\x02\x01&gS-\xb9P(\xc4\xdc\xbc\x941ZL\xf5\x93+m\xe7\x9f\x7f>\xee\xbc\xf3N444\xa0\xa1\xa1\x01\xe5\xe5\xe5\x8cP\x9d\xae\x11$y\xdah4\xb2\x1b^[[\x1b\xba\xba\xbaX&\x9a\x5c\xa2\x97\xc7\xa8\xd1\xcd\x8d\x94\x86L&\xc3n\xcd\xc7\x8e\x1dCSS\x13jkkY]@r\x19\xcf\xc3\xaf\xb7h\x0a`1C@.\xf1D\x22\x81@ \x00\xaf\xd7\x8b\xff\xd7\xde\x99G\xc7]\x97\xfb\xff=\xfb\xbeg\xb6\xccdk\xd2\xa4{KiYZ\x0ae\x97\x02r\xa5\x02z\x11\x11\x5c\xa8\x82\x0a\xcaQ\xef\xfdq\xc5\x8b\xc7\x85\xe3\x82\x22\x02\xea\x11\xf1\x82\xe0\x02\x17/(\xdb\xbd*{i\x11*]\xd26M\x97\xecK\x93\xc9Lf\xdf\x7f\x7f\xc0\xf3\xf1;\x93I\x9a\xb4\x93t\x92<\xafs\xe64\xcd2\x99\xcc\xf7\xf3\xfd|\xde\x9f\xe7\xf3<\xefg\xfd\xfa\xf5cDR4\x1aE[[\x1b\xfa\xfa\xfaD\xee\x1f\x89?\xba\x86R\xe1N\xe6\xd1&\x93I\xf4\xe2\x96\x1e5O\xf4\x9a\xc6\xfb\xbe\xf9\x18e\x9bO\xa2\x94\x9e\xc7\xe9t\xa2\xbb\xbb\x1b\xc0{\x89\xeb3~=\xb3y@\x9e\x07d\x80R&\x87\x1c2\xe42YdSi@\xa5\xfeg\x95\xf0qN\xd1\xc9d\x12\xa3\xa3\xa3\xc2\xb8\xda\xe7\xf3\xa1\xaa\xaa\x0a}}}\xe3\xce\xfb\xc5E\x08\xf4\xb9RF\xca'*\x00\x8b\x85\x86\xd3\xe9D,\x16\xc3\x91#G\xa0V\xaba\xb5ZEZ\x07U\xe3Ses0\x18,x\x9d4g\x93\xe0\x90\xcb\xe5\xa2\x1bT\xb1\x08!\xf4z=\xccf\xb38\xa1kiiA\xfb\x81v\xc4\xe2\xff\x8c\x9a\x9a\xcdf\x18\x0c\x86\x02oB\xeabB\xa7\x0d':6\xb4Z-jjj\x84\x10+\x85\xcb\xe5\x12\x913\xaa\xbc.\xae\xb0\x95\xc9d\xa2\x98\xc9`4\x88\xe0\x82\xddn\x87\x5c.\x87\xd3\xe9\xc4\xa9\xa7\x9e\x8aX,\x06\x95J%r\xc8\x8b\xafk)aM\xaf\xad\xb8\xc8\xb5\xba\xba\xba\xa0\x96\xe1X\x91z\xb9\x5c.*\xee'\xdaL\x93\xa3Cq~\xbc\x5c.\x87\xdf\xef\x87\xd7\xeb-\xb8\xfe\xd5\xd5\xd5\xd0\xe9t\x22\xe0\xa2,\xc7\xe2}\x22\x17\x96r\x13h\x80P\xdfYz\xc8\xe5rtww\x8b\x81^UU\x85T*\x05\x85B\x01\x9b\xcdV\xf6\xc8`q\xa9\xf6T\x05\xe0t\x08E\xa9\xb0\xa6|,\xda\x0d%\x12\x09$\x12\x09\x1c=zT\x88\xc2@ \xc4!\xe5\xec\xd0\xcf\x90Ap:\x9d\x16\xc7\x85R\xe1Wl\x1a\x9c\xc9d\xb0n\xdd:\x5cv\xd9eX\xb0`\x01\x1a\x1b\x1bE\x05R\xb9\x8c\xb9\xe9\x18B\xa3\xd1\xc0b\xb1\xc0\xe7\xf3\xa1\xb6\xb6\x16---x\xe3\x8d7Dd\x8f\xc6\x09]#\xda\x11K\x13\x99)\x12\x98\xc9dp\xe8\xd0!\x1c:t\x08~\xbf\x1fN\xa7S\xb4<\xab\xd4h2\xbd\xff\xe4\xfbw\xf4\xe8Q1\xe1\x16\x8f\xcfC\x87\x0ea\xfb\xf6\xed\x08\x04\x02\xc2\xf7\x8f\xc4\xb1t\x822\x9b\xcdX\xb3f\x0d\xdcn\xb7H\xa9(\xe7{p\xa2\xe3\x9d\xc5_\xe5\xb3l\xd92\x1c>|X\x1c+\xcd\xe8\x86X\xad\x00\xbcFa\x04-3\x00\xe9L\x10\xb9l\x0ei\x87\x06:\x8b\xfe}u\xa0\x9c\xb2\xa5\x10u$\xda\xb3gO\xc1\xe7\xcf<\xf3L477#\x18\x0c\x8e\xdb\xdb\xb6T\xbb\xcb\xe2B\x17:^\xa4 \x07\x15~\xe4r9q\xb4>U\x9f<\x8dF\x03\x83\xc1\x80\x85\x0b\x17\xc2h4\xc2d2\xc1b\xb1`\xfb\xf6\xed\xe28Z\xab\xd5\x8a#j\xe9\xdaa\xb3\xd9\xb0j\xd5\xaa)\x1b\xe4\xd3\x111u&\xa9\xaf\xaf\x7f/H\x13\x8f\x89\xb5\xd2\xe9t\xc2\xe5r\x89\x9c\xb5|>/,\xde\xa4]\x9cJ5\x05\x98\xec\xdf\xeev\xbbE\xd7\x94\xf1\xae\xb3\xc3\xe1@KK\x0b\x0e\x1e<(\xe6\xce\x89\xda\x0f\xca \x83\xddn\x1f\xf3\x9a\xacVkIk\x96\x89^\xf3Tr\x5cO\xf4\xeb\xc5\xe3n\xa2kW\xfc\x7f*F\x02\xcal\x04]\x5c\xb41\x99\xc4pir1\x0d^24\x8eD\x220\x99L\xa2\xa4\x99\xbe\xa6R\xa9`4\x1a\x0b\x0c \xcb-\xb4*\x19\xba\x91\xc8u]zdC\xc7\xb8\x94\xd7Bm\xc4\xa4Q>\x12H\xd2hQ\xf1\xce\x86\x8eW\xaf\xb8\xe2\x0a\xac[\xb7\x0e\x8d\x8d\x8d\xa2}\x98\xd4\xf3\xaf\x5c\x0b8M\xc6\x06\x83A\x94\xc0755a\xef\xde\xbd\xc2\xe3\x89Z\xdb\x19\x8dF!\x0a\xe9\xef&s[\xaap\xa4\xe3\xef\xc3\x87\x0f\xa3\xae\xae\x0e~\xbf_\xb4\x16\x9cr\x14p\x86\xa0\xe2\x8dD\x22\x81P(\x84\x81\x81\x01\xacZ\xb5j\xcc\x0d\x9eN\xa7\xf1\xf2\xcb/chh\x08###\x22\xf2K\xc7\xc7\xd2\xe3\xa2u\xeb\xd6\x89]\xa2\xd9l.HR?Q\x91Wj\x93\xc4\x82n\xeeE\x12\xd7\xae]+:e\xbc\xf5\xd6[\x93\x9e\xdb\xcbB\x8d\x19\xb8e-\xbd \xa4GF\xd0\xb7#\x8bx\x22\x01\xcb\xcaf\x98\xab\xdf\x8f\x92\xc8e\x80\xe4\xf8q\xb2\x0b'\xd9`\xd1}e\xb7\xdb\xe1\xf5z\xa1V\xab'\xb4^1\x18\x0c0\x1a\x8d\x18\x1c\x1c\x14\xe3\x9e\x12\xf8\x09\xca\xd1\xa2H\xd4\xc0\xc0\x80\xf8ZUU\xd5q\xcdA4\xe7\x1a\x0c\x86\x82\x96k\x14\xed\xa3\xaf\xc9d2a\x15&\x15\x80\x14\xf9\x9a*\x1e\x8f\xa7\xc0\xc6\xc4f\xb3\x89\x82\x22\x99L\x06\xbd^\x0f\xab\xd5\x0a\xbb\xdd\x8e\xfe\xfe~\xe8t:8\x9dND\x22\x91\x82\xf9\xedx\x03+\x1e\x8f\x07\x0e\x87\xe3\x98\xf3\x8bB\xa1\xc0\x82\x05\x0b\x84\xc5\x11\xcfG\xc7\xb8\x07\xa6S\xa0LdGPj'\x90\xcb\xe5D\xb5\x93B\xa1\x10\xc9\xfc4\xf0\xa5\x09\xeat|\x5c*\x17\xe3d\x89\xb2\x99BZ>/\x15qd\x15@\xd1=iO`\x8a\x1eJm]J\x09?\xfa\xbc\xcdf\xc3\x87>\xf4!,^\xbc\x18\x0b\x16,(0|\xa6\x1dd\xb9o.2R\xb5\xd9l\xf0z\xbd\xa8\xa9\xa9\xc1\xe2\xc5\x8b\xf1\xf2\xcb/\x8b\x1cQJ\x98\xd5\xeb\xf5\x22e@\xab\xd5\x22\x99LB\xa5R\x89\xf4\x00\xaa:;p\xe0\x00\x9a\x9b\x9b\xd1\xdb\xdb+\xfa\x15Wb{8\xba\x06\xc9d\x12\x91H\x04CCCH$\x12X\xb3fM\x81\xb0\x92\xc9d\xd8\xbf\x7f?\xda\xdb\xdb\x11\x08\x04D\x82xq\xbf_\x00\xb0Z\xadX\xb5j\x15\x9cNgEF\xff\x98\xd9!\x00\xd7\xacY#\x16W\xdaLN5o\xf3\xb87\x07y\x14\x1c\xed\xcae\xef\x07\x19\xb2\xef\x15\x1fJ\xbf6U\xc3a\x12\x80/\xbf\xfc\xb28\xed8\xed\xb4\xd3\x0a\x1c(J\xfd\x1dF\xa3\x11\x8f\xff\xf6ql\xdf\xb6\x1d+V\xac@ss3\x1a\x1a\x1aD\xb4\xab\xb8\x15&\xbdw\x03\x03\x03\xe25R$k\xaa\x11@i\xe4Q\xfa\x9e\xfa|>\x04\x02\x01q2F\x22\xd3h4\x0a[\x1b\x83\xc1P\xb6\xb1Q\xaa;\x13\x00\xb4\xb4\xb4 \x99L\x8a<\xbbR\x1e\x80S\x9d7hS?U\xfd\xc1\xe2\xef$\x09\xc0\xa9F\xd3\xc8\xe8\x90\x84\x0a\x09\x1c\x8aZ\x91\xe8!\xf1\x22\xcdq:\x9e\xc5l\xb6-\x5c\xd2\x1b^\xba\xd0\x17w\x06\xa1\xef\xa1(\x90T\xec\x15G\xfa\x8a\xf3Q\xa8\xfd\x8fV\xab\xc5\xda\xb5kq\xce9\xe7\xa0\xa1\xa1\x01\xb5\xb5\xb5\xf0\xf9|\xb0\xd9l\xa2sD\xa9\xe3\x8frE\x01U*\x15t:\x9d\xc8\x05\x5c\xb0`\x01\xf6\xef\xdf\x8f\xa1\xa1!\xf1\x1a\xa9j\x9c*\xces\xb9\x9c8\xfaU\xab\xd5\x05\xbd(\xfb\xfa\xfap\xf0\xe0A\xd4\xd4\xd4\xc0\xe7\xf3\x15\xb4\xac\xab$\x11H\x1b\x1a\xca\xe1\xff\x22\xf6\xb4\xee\xc1\xeb\xaf\xbf\x8e\x05\x0b\x16\xe0G?\xfa\xd1\x98(`\xf1<\x1b\x08\x04\xc4\xef^\xb0`AAq\xcdT#\x80\xc5\xe2\x91\x0a\xf2\xe8$\x05x/wo\xcd\x9a5\xd8\xb9s'\xacV+\x1a\x1b\x1b\xcb\xb2\x0e\x01\xff\xec\xe0\x05@x3\x02\xef\xe5\xfd\x9ds\xce9\x00PP\x10R<&\xa6{\xdc\xb2\xf8\xab \x01x\xac\x9b\x92\xbaM\xd0\xc7\x94\x04O\xc7\x95\xc59j\xf3I\xdd\x97\xaa\xb4,\xf5\xb5R\x1f\x97\x12\xe1\xa5\xaa\xd6H|\xacX\xb1\x02\x1b6l@SS\x13jkk\xe1\xf5zE\xc9\xbaN\xa7\x13\xc7\x87\xd3\xf9\xdeS. u\x07\xa9\xa9\xa9Acc\xa3HrN&\x93B\x80\xd2$-\xf5\xb2\xa3\x9e\xa5\x1a\x8dF\x08\xc3\xdd\xbbw\xa3\xb9\xb9\x19\xfd\xfd\xfd\x22\x8aYi\xb9\x80T\xedL&\xcdj\xb5\x1a+W\xae\x1c\xb3h\xbe\xf3\xce;x\xfb\xed\xb7E\xcf_\xa9\xf1\xb3\xf4\xda\xba\x5c.\xb4\xb4\xb4\x08\xf1\xae\xd7\xebO\xb8Z{*\x0b\x043w\xe6\x1f\xb3\xd9\x8c\xfa\xfaz\x1c9r\x042\x99\x0c\x7f\xf8\xc3\x1f\xb0y\xf3\xe6I\xfd,\xdd\x9fO<\xf1\x04>\xfc\xe1\x0f\x9f\xf0\xdc=\x9e\x00:\x9e\x0d\x88F\xa3\xc1\x8b/\xbe(\xd6\x1e\xb7\xdb-\x92\xe6\xc7\x13\x11r\xb9\x1c\xfd\xfd\xfd\xe8\xe9\xed\x11Q\xc3\xb3\xce:K8ZH+\xe4\xa5~\xb8\xc3\xc3\xc3\x05\x82h\xd1\xa2E\xa2\xe5\x1a\xcd{\xe4\x91;\xd9\x08`1\xc5\x95\xa5\xc0{\xa7\x00\xeb\xd6\xad+{\xb1\xa4\xd4\xdaG\xa5R\x8d\xe9\xb7~\xac*j\xde,\xceq\x01(\xbd\xd0\xe3%G\x92\xc5\x04E\xfeb\xb1\x98\xc8c\xa3\xe8\x9f4\xf27\xdf\x0c\xa1K\xd9s\x90`\x9b(\x97b\xa2\xf7\xa9\xf8\x98D\xadV\xa3\xa1\xa1\x01\xe7\x9cs\x0e\x1a\x1b\x1b\xe1t:\xe1\xf3\xf9\xe0r\xb9`\xb7\xdb\xa1\xd7\xebE\x83\xed\x99h\xdbE\xbbh\x83\xc1\x00\xb7\xdb]\x10\x05\xa4\x8a6\x8a\x02R\xc4O.\x97\x17t\xa6\xa0\xb6p\xf4\xbd###\xd8\xbf\x7f\xbf\xa8\x086\x9b\xcd\x22\x87\xb1\x12D \x8dq\x8a\xfeuwwc\xed\xda\xb5b\xf1\xa0\xeb\x18\x8b\xc5\xb0s\xe7ND\x22\x911\x95\xbf\xc5cb\xf1\xe2\xc5\xa8\xad\xadEUU\x15,\x16\xcb\xb4\x19a3s\x1b\xda|\x5cv\xd9e\xb8\xef\xbe\xfb\xa0T*\xf1\xc6\x1bo\x1cS\xf0\xd3\xa6\xf2\xe7?\xff9\xbe\xf4\xa5/!\x9f\xcfc\xfd\xfa\xf5\xa2\xe2\xf4D^\xcfD\xfd\xc6\xa7\xb2.\xf4\xf4\xf4`\xfb\xf6\xed\xe2\xff\xeb\xd7\xaf/\xa8\xf8,57(\x95J\xec\xda\xb5K\xa4]\x00\xc0\xd2\xa5K\x0b\xc4Y\xa9\xea\xe0\x9e\x9e\x9e\xf7\x0c\xb4\xdf\x7f}\xabV\xad\x12\x96id\xca\xadR\xa9\x84?\xdbd\x04\xf0T\xafa9Q\xa9T0\x99L\x18\x1e\x1e\x86V\xab-Y=<\xde\x5cW\xaa\xb9\x03o\x1e\xe7\xa8\x00\x9c\xe8\xe6\xa4.!T\xb0@\xc9\xec\xa9TJ,\xf2\xc5\x1d\x0d\xe6\xd3\xce\xa1\xd8\x5c\xb3\xd4\xc7\xe3\x09;i\x84Oz\xf3\xd1\xee\xcd\xe5r\xa1\xb1\xb1\x11+V\xac@CC\x03\xecv;\x9cN'\xdcn\xb78\xee\xd5\xe9t\xc2\x98r&#<\xd4#\xd8b\xb1\xa0\xba\xba\x1a\x0d\x0d\x0dhhh\xc0\xbb\xef\xbe+\xf2\xfb\xd2\xe94t:\x9d8\xf2\x966\xe8V\xa9T\xe2\x98\x98\xc4\xe1\xfe\xfd\xfb\xb1h\xd1\x22\xf8\xfd~8\x1c\x0e\x98\xcd\xe6\x8a\xc9\x05\xa4<\xcdp8\xfc^{\x1e\xa5\x12---cr}\xde}\xf7]\x1c:t\x08}}}\xa2\xf2\x97\x22\xe4\xd2\xcd\x82\xd1h\xc4\x9a5k\xe0\xf5zQUUU\xf6\xdc?\xde\xcd\xcf\x1fh\xc3y\xc9%\x97\xe0\xbe\xfb\xeeC6\x9b\xc5\xf0\xf00^y\xe5\x15\x9c}\xf6\xd9\x13.\xf4\x1d\x1d\x1d\xb8\xe5\x96[\xc4\xbdy\xd7]w\xe1\x81\x07\x1e(\xcb\x9c(\x9d\xcf\x8a=\xe6\x8e5O\xd1\x91\xf43\xcf<#\x04\x97Z\xad\xc6y\xe7\x9dW \xe4J=\x8fL&\xc3[o\xbd%:c\xd8\xedv\xd1\xe3\x976\x94\xc5\xf7\x99\x5c.Goo\xaf\x10\x80J\xa5\x12\xd5\xd5\xd50\x9b\xcd\xe8\xe8\xe8\x80B\xa1\x10\xc5\x19\xc1`\xf0\x98bn\xaa\x86\xdf\xd3\x91\xab\xadT*\xd1\xdc\xdc\x8c\xe1\xe1aaf<\x99y\x82\xe7\x8ay*\x00\x8b/<\xe5\xb2\xa5\xd3i\x11\x05\x0c\x87\xc3\xa2B\xb5T\xb1\xc2|c\xb2;\x5c\xe9Q\x83t\xd2\x96\xe6\x0b\xaa\xd5j\xd4\xd7\xd7\xa3\xa9\xa9I\xf4\x0f\xa4\xbe\xcbv\xbb\x1dV\xabU\x1c\x15R\x0b\x9b\x89&\xc2\xe9^t\xc8\xcb\xca\xe3\xf1\xc0\xef\xf7c\xc9\x92%8p\xe0\x80\xb0\x06J\xa7\xd3\x88\xc5b\xd0j\xb5\x05\x93\x0fM\xe6\xd9lV\xfcK\x15\xc1\xed\xed\xed\x22\xa7\x91\xda\xc3\x9d\xec\x5c\xc0\xe2\xe8\xdf\xe0\xe0 \x9a\x9a\x9a\xc6x]\xa5R)\xbc\xf3\xce;\xa2Y{<\x1eG\x22\x91(\xc8\xfb\xa3\xeb\xb4|\xf9r\xd4\xd5\xd5\xc1\xe9t\xc2b\xb1@\xab\xd5\x96=\xfa'mIW\xca\xfb\x8c\x99;\x9b\xd0|>\x8f\xe5\xcb\x97\xc3\xeb\xf5\xa2\xaf\xaf\x0f\xb1X\x0cO?\xfd\xf4\x84\x02P.\x97\xa3\xa9\xa9\x09_\xf8\xc2\x17p\xef\xbd\xf7\x02\x00\x1e|\xf0A|\xeaS\x9f\xc2\xca\x95+\xcb2\x1eK\x1d3N6\xbf\xec\xd0\xa1C\xf8\xe5/\x7f)\x22j\xa7\x9f~:\x96/_\x8e\xc1\xc1A1\xef\x15\xcf\x0b\xf9|\x1e\x81@\x00;w\xee\x14\x9f[\xbat)<\x1e\x8f(H,NI!\xa1\xd6\xd3\xd3#^oMM\x0d\xdcn\xb7\xe88\x22\x97\xcb\xe1v\xbb\x91\xcdf'\xf4Y,\xd5\xa5\xe7d\xe2\xf1x\xe0\xf1x\xf8&a\x018y!C\xc2$\x91H\x88\x9eyT\xf9H\xbet\xd2\x82\x06fr\x93\xb4T\xa8)\x14\x0a\xd1\x9e\xa6\xaa\xaa\x0a---\xc2\x10\x93|\xa3l6\x9b\xf8\xbf\xc9d\x12\xa2\x8f\xec\x0fNv\x22-\x19;\x9b\xcdfTWW\xc3\xe7\xf3a\xe1\xc2\x85x\xe7\x9dw\x0a\x8e\x81\xa5cJj\x05Cm\x8f\xe8H&\x16\x8b\xa1\xbd\xbd\x1dMMM\xf0\xfb\xfd\x222v\xb2\xa3\x80\xb4\x09\xa2c\xddH$\x823\xce8\xa3\xe0~\x91\xc9dhmmE__\x1fz{{\x11\x0e\x87E\xf4\x5c\xbaQ\xa2\x8a\xf8\x0d\x1b6\xc0\xe1p\x88#\xfcJ\xf6>df\xc7\xfcRSS\x83\xb3\xcf>\x1b\xbf\xfb\xdd\xef\x00\x00/\xbc\xf0\x02\xee\xb8\xe3\x0e\x98\xcd\xe6\x09{\x80\xdfv\xdbmx\xec\xb1\xc7022\x82l6\x8b\x0f\x7f\xf8\xc38p\xe0@A\xa1\xd6\x89\xdc;\xe3E\xc2&\xaa\x08V(\x14\xb8\xe3\x8e;DG\xa1l6\x8b/\x7f\xf9\xcb\x18\x1d\x1d-\x88\xb2\x95\xca1\x1c\x18\x18\xc0\xe1\xc3\x87\xc5\xd7\xd7\xae]+r\x01\xa5U\xc0\xd2S\x9aT*\x85\xce\xceN\xf1<>\x9f\x0fn\xb7[XY\xa9\xd5j\x98\xcdf\xe1i:\x91\x88\xe5\xfb\x98\xa9x\x01(5\xb4\xa5\x85J\x0ay\xfeQ'\x0aJd\xa7\x9c\xbfb\x8cF#\xf4z\xbdh\xc6,\xbd\xc1\xe8f\x9f1_\xaacL\x94\xd3\xf5\x9c\xc5\xff\xd2b\xafV\xaba\xb1XD\x95'\xb5\xca!\xaf>\x12vz\xbd\x1eF\xa3\x11\x16\x8bE\x1c\xef\xd2\xd7(\xff\xa4\x12\x84\x9ft\xa2#\xbfG\xb7\xdb-\x8c\xa1)\x0aH\xa6\xd7\xe4AE\x16\x15\xf4\xbeP$\x90\x0a$\xb4Z-:;;\xd1\xd5\xd5\x85\xbe\xbe>\xd4\xd6\xd6\xc2j\xb5\x8a\xca\xd8\x93\xf17\xd315\xf9\xfeuuua\xf1\xe2\xc5\xa2I;]\x8bl6\x8b\xed\xdb\xb7\xa3\xb3\xb3S$\x93K;\xb7H\xef\x81s\xcf=\x17UUU\xa2\xf2w:\xa2\x7f\xcc\xfc\xe4\x8a+\xae\xc0\x13O<\x01\x00\xd8\xbd{7\xb6n\xdd\x8a\x8b.\xbah\x5c\x11\x96\xcf\xe7Q__\x8fo}\xeb[\xb8\xe9\xa6\x9b\xa0\xd5jq\xe4\xc8\x11|\xf0\x83\x1f\xc4\xb3\xcf>{B\x9bC\x12n\xa5N\x96(\xa7\x8e\x9c$\xa4si2\x99\xc47\xbf\xf9MaO\x92\xcb\xe5\xf0\x95\xaf|\x05Z\xad\x16\x89DB<\x7f\xb1\xd8\xcaf\xb3\xd0\xe9tB\x00+\x14\x0a\xe8t:\x9cw\xdeyH&\x93\xef}\xbfB>&\x07\x90\xd6\xc1\xbd{\xf7\x8a\xcf577\x0b\xf3\xfaK/\xbd\x14\x89d\x02&\xa3\x09\xd1h\x14\x0a\x85\x02\xd9lv\xdc\xf9h.\x09\xc0\xf9\x96\xd3?g\x05`\xb1MI>\x9f\x177\x98\xd4z\x82nX\xda\x9d\xd1\xf1/U\xfe\x16[\x95\xb8\x5c.\xac\x5c\xb9\x12\xd5\xd5\xd5\x22\x99]\x9a\xa0+\xad\x08\x9e\xe8\xa6\x99\xe9\x08\xdct\xdc$RGu\x12\xc1\xd4\x06\x8d\x1eT\x8dE\xff\xeat:!\x02\xe9c\xfa:y\xe6I\x05S\xa5\xa1P(\xa0\xd7\xeba\xb3\xd9\xe0\xf3\xf9\xe0\xf3\xf9\xd0\xd2\xd2\x82m\xdb\xb6\x89\xd7Lm|T*\x95h)(\xad\x10V\xa9T\x05\x1b\x8b={\xf6\x08c\xe8\xaa\xaa*\xe8t:\xd1\xc7r\xa6\xa1\x08\x1e\xf9\xfe\x85B!\x9cw\xdeyc*\x7f[[[\xd1\xd9\xd9\x89\xbe\xbe>D\xa3Q\x912Q\x5c\x1ce4\x1a\xb1b\xc5\x0ax\xbd^\xd1\xaaO\xa3\xd1L\xeb}q\xacj?fv,\xc4\x93\x19#\x1f\xfd\xe8G\xf1\xa5/}I\xb4g\xdc\xb2e\x0b:::D\xd5}\xa9y1\x93\xc9\xe03\x9f\xf9\x0c\xb6n\xdd\x8a\x87\x1f~\x18j\xb5\x1a\xcf=\xf7\x1cn\xb8\xe1\x06\xfc\xeaW\xbf\x9a\xb25\x8cT`\x8d'\x1ah\xdd\xc9\xe7\xf3H&\x93\x05\x7f[$\x12AGG\x87\xc8\x0d>\xf7\xdcs\xb1a\xc3\x06qZ \x15\x80\xd2M\xb7L&CGG\x07\xb6o\xdf.\xe6\x94k\xae\xb9\x06N\xa7S\xf4\x11V\xc8\x15\xc2\xa7P\x1a\x08\x19\x1a\x1aBww\xb7\xd8\x88m\xda\xb4I\xbc\x1e\x9a\x8b)7\xf0X\xf3\xf7\xa2\xfd=\x00\x00 \x00IDAT\xf0\x5c\x12J\x5c\xf81\xc7\x22\x80\xd2\x1085D\xa6\xddQ\xf1\xe0%\xdb\x8b\xe2\xfe\xa5\xf4=K\x96,\xc1Yg\x9d\x85\xfa\xfazQ\x98@\xc7\x93\xf31\x0c^\x9coE\xb9&\xb4\xd3%\x11#\x8d\xe6\xd1C*\x14\xe9{\xe89*\xf9&$qj4\x1a\xe1r\xb9\xd0\xd0\xd0\x80\xce\xceN\x1c|\x18g\x9ey\xe6\x98\xdc\xa3d2\x89\xdd\xbbw\xe3\xc0\x81\x03\x88F\xa3B\x00\x16wq\x01\x80SN9\x05>\x9f\x0f\x1e\x8f\x07V\xabU\xb4\xeac\x98\x13]\x88I$~\xe3\x1b\xdf\xc0\x96-[\xa0\xd1h\xd0\xd9\xd9\x89\xff\xfe\xef\xff\xc6\x95W^9\xae\x88\xa4{\xf3\xfe\xfb\xef\xc7\xae]\xbb\xf0\xf6\xdbo\x03\x00\x1e~\xf8a\xc8\xe5r\xfc\xe2\x17\xbf\x98\xb2\x08\x9c(\x02H\x7f\x8f^\xafG<\x1e\x1f\x93\x9f\xeap8\xf0\xf3\x9f\xff\x1c\xb7\xddv\x1b\xb4Z->\xfb\xd9\xcf\x8a\xcaT\xe9\xf3K\xdf\x97\x5c.\x07\x9b\xcd\x86;\xef\xbc\xb3`\xa3\xff\xe3\x1f\xff\x18\x7f\xfb\xdb\xdf\x0aOgd\x85\x01\x11\xb5Z\x8d\xd7^{M<\xaf\x5c.\xc7\xe5\x97_>\xee|\xa7\xd1hD$r\xbc\xf5u\xael:\xb8(d\x8e\x08\xc0R\x17\xd1n\xb7\x0b\x13g\xe9\xf7Q\x7fZi\xd4\xaf8\xfaWWW\x87\xf3\xcf?\x1f\x8b\x16-B}}=<\x1e\x0f\xf4z\xbd\x88fHo\x84\xc9V~\xcd\x85\x1bF*\x02\xa5\x0f\x12\x83R\xab\x96b\xaf<\xcaw\x99m\x93\x085.\xb7Z\xad\xf0x<\xa8\xaf\xaf\x87\xdf\xefG0\x18\x14\xd5\xb3\x14\xfd\x94&]K{\x03\x03\x10f\xc9\x0a\x85\x02\xbbv\xed\x12\xc50\xe4\x96?\xd3Q@\xca\xfd\x0b\x87\xc3\x18\x1a\x1aB.\x97\xc3\xb2e\xcb\x0a\x22\xe42\x99\x0c\x87\x0f\x1f\xc6\x8e\x1d;\x10\x0c\x06122\x22<3\xa9\xdf&a4\x1a\xb1t\xe9R\x11\xfd#\xdf?6Ee&\x8a\xf8\xb5\xb7\xb7\xe3\xd0\xa1C8\xf7\xdcs'\xec\xeeC\xc7\xa97\xddt\x13\xbe\xfa\xd5\xafbtt\x14j\xb5\x1a[\xb6l\xc1\x95W^9\xe1\x18\xa3\xcd\xe7\xd3O?\x8d\x0d\x1b6\xe0\xd0\xa1CP\xab\xd5x\xe8\xa1\x87p\xf0\xe0A<\xfe\xf8\xe3\xf0z\xbd\x93\x9a\xcb\xa5\x1b\xa4\x89\xc4\x03m\xfe\x8a\x85i.\x97\x83\xc1`\xc0\xb6m\xdb\xd0\xd6\xd6\x86D\x22!\xee%:U\xa2y\xc0j\xb3bxx\x18.\x97\x0b\xaf\xbe\xfa*v\xef\xde-:\x12\xdd~\xfb\xed\xc2\xfa\xa4\xb8\x08O\xda\xf7V\xa3\xd1\xe0/\x7f\xf9\x8b0\xd5^\xb7n\x9d(N+e\x17c2\x99D.\xa2\xc1`\x80\x5c.\x17=\x84I 2LEF\x00\xa5\x13\x0cE(\x8aof:\x9e\x8cD\x22\x05v/4\xc0\x95J%\xce>\xfbl466\x0a\xdb\x0e\xab\xd5Z\x90\xcb4\xdfv\x0d\xc5\x13\xccx\xbbA\xe9\xce\xb5\x94Y\xeal\x14\x024&\xccf\xb3\xe8\x0e\xb2h\xd1\x22\x1c\x9f\x18\x1fmmmH\xa7\xd3\xa2\x18\x86\xf2\xff\xc8\x08\x9a\xc6\x1e\x1d}\xd3\x04\x1a\x8f\xc7\xd1\xde\xde\x8e\xc6\xc6Faz\xad\xd7\xeb\xcbR\x998\x19\xa4\xbe\x7f###\x88\xc5b8\xe5\x94S\xc6\x5c\xdf\x9e\x9e\x1e\xbc\xf9\xe6\x9b\x08\x85B\x08\x85B\xc2$]\x9a\xfbJ\x1ekg\x9f}6\x1c\x0e\x87\xb8_\xa6\xbb\xeb\x874RY\x09\x05X\xcc\xd4\xb0X,x\xec\xb1\xc7\x84\xe0{\xe0\x81\x07p\xf7\xddwOx\x1cK\xd7\xfb\xd2K/\xc5\x05\x17\x5c\x80\xff\xfb\xbf\xff\x03\x00\xdc{\xef\xbd\xb8\xfe\xfa\xeb\xb1l\xd9\xb2\x09E[>\x9f\x87\xcf\xe7\xc3\xce\x9d;q\xd9e\x97\xe1\xe5\x97_\x86\x5c.\xc7\xd0\xd0\x10>\xfd\xe9O\xe3\x87?\xfc!\x1e~\xf8a\xac]\xbb\xb6 \x80P<\x8e\xa5\x11\xb6\xf1\x8e\x80K\xad\x17t2@\x1e\xa0\xa5\x02\x14\x14\x01\xa4\x8a\x5c\xbb\xdd\x8e\xfb\xef\xbf\x1f;v\xec\x10\xdf\xf3\xaf\xff\xfa\xafp\xbb\xddc\xde'\xe9\x06<\x9f\xcf\xc3b\xb1\xe0\xbf\xfe\xeb\xbf\x0a:|\x5c{\xed\xb5\x22-\xa5\xd4f\xd7b\xb1\x88cs\xa7\xd3YpoI\xd3w\xe6\x0a\xbcy<\xc9\x01\x96\x99\xbe\xd8T\xf9H\x85\x1b\xc5a\xffE\x8b\x16\xc1l6\xc3j\xb5\x8aH\x86\xb4R\x93\x1f\xf3\xefHO\x1a\x05\xa4N%\xcb\x96-\x13\x13<\xd9\x0aIm\x84h\xb2\x97\x16\xc5\x90(R\xab\xd5\xa2\x22\xb8\xb7\xb7\x17\xc3\xc3\xc3\xa2\x0b\xcdtOH\x94\xea\x90H$D\xdb\xb7\xea\xeajX\xad\xd61\x11\xb5m\xdb\xb6\xa1\xb7\xb7W\x1c\xfd\xc6b1Q8\x22\x15\x93k\xd6\xac\x11\xb9\x7f\x16\x8b\x05:\x9dnZ+\x9b+\xa5\xea\x9e9\xfe\x0d\x88\xdf\xef\xc7\xc6\x8d\x1b\x91N\xa7\xa1R\xa9\xf0\xbd\xef}\xef\x98Q5\xba\x17\xd5j5\xfe\xe3?\xfeC\x88,*\x88\xa0\xfb\xefX\x9bV\xadV\x8b\x97^z\x09\x9f\xfb\xdc\xe7\x0a*n\x0f\x1c8\x80\xd3N;\x0d\xcb\x97/\xc7\x1f\xff\xf8G\xf4\xf4\xf4\x8c\xc9\x87S*\x95\xa2\x8bOq\x0el\xf1\xeb,\xfe\xdd\xe4*`\xb3\xd9D\xbe\x1ed\xe3\xdf\xa72\x99\x0c;w\xee\xc4-\xb7\xdc\x22\xee\xa7\xea\xeaj\x5c~\xf9\xe5\x13\xfe\x9d\xb4\xd9\xe3LD\xffJEP\x98\xd9\xb1\xa1\xca\xe7\xf3\xf8\xc1\x0f~P\x10Q{\xf0\xc1\x07E\xca\xc4\xb1\xae\xff\xd9g\x9f\x8dO\x7f\xfa\xd3\xc2\x7fs\xef\xde\xbd\xf8\xc4'>!*\xf1\x8fu/g\xb3Y\xfc\xf4\xa7?\xc53\xcf<\x83u\xeb\xd6\x89\xa8\xb8N\xa7\xc3\xee\xdd\xbb\xb1y\xf3f477\xe3\xd2K/\xc5-\xb7\xdc\x82{\xef\xbd\x17\xbf\xfd\xedo\xf1\xdcs\xcf\xe1\xa5\x97^\xc2\xc8\xc8\xc81\xffF\xe9\xd8\xac\xad\xad\xc5\xc6\x8d\x1bq\xd6Yg\xc1\xe1p\xfcs3\x8d\xd2\xfd\x84\x95J%\xc2\xe1\xb0\xc8\x8d\xa4\xf7\xe8\x96[n\x81\xc9d\x9a\x94H\xfe\xdb\xdf\xfe&6\x96\x00p\xc3\x0d7\x1c\xb3\xe0E\xa1P`\xd1\xa2EX\xb6l\x19\x00\x14\xa4\xa6\xd0I\xc7l\xe5D{73\x15.\x00'\xb3\x18H\x8f\x8dJ-\xb6dUB\xbbK\x1e$\x0cM\xaa\xd4\x7f\xd2\xe5r\xa1\xb6\xb6\x16\x0b\x16,\x80\xc9d\x12\xd5\xe4\xe4+Y||D\x132E\x12\xc9^f\xef\xde\xbd\xe8\xed\xedEww\xb7\xc8\xb1\x9b\xce(`q\xee\xdf\xe1\xc3\x87q\xca)\xa7\x88\x02\x1d\xe9&\xe9\xed\xb7\xdfF[[\x1b\x82\xc1`A\xee\x9f\xf4\xbe\xc9\xe7\xf3\x22\xfa\xe7\xf5za\xb5ZO\xda\xa6\x89\xf3\x01g\xd7B\x9cN\xa7\xb1z\xf5j,\x5c\xb8\x10\x89D\x02J\xa5\x12\xdf\xf9\xcew&5\x8f\xd3\xe9\xcd\xfd\xf7\xdf\x8fe\xcb\x96!\x95JA\xa9T\xe2\xf1\xc7\x1f\xc7]w\xdd5)\x11I\x82\xea\xe2\x8b/\xc6_\xff\xfaW<\xf5\xd4Sp\xbb\xdd\x88\xc7\xe3P*\x95\xc2\xd4\xfd\xa5\x97^\xc2O\x7f\xfaS|\xf1\x8b_\xc4\xb5\xd7^\x8b\xcd\x9b7c\xcb\x96-hkk\x9b\xd0\xec\xb9\x18\xaa\x8a\xa7\x1c;\x00%\xc5\x1f\x09\xacd2\x89K.\xb9\x04###\xc2\xcc\xf9\xa6\x9bn\xc2\xca\x95+\x85\xdf\xe8D\x1c=z\x14/\xbf\xfc\xb2\xf8\xffE\x17]\x84\x85\x0b\x17Njc&\xcd\xdd\xa5>\xec\xf4\xda\xf4z\xfd\xac\x1dw<7\xccq\x01H\x15\xbd\xc7\xf2\xe5\xa3\x5c\xacR-\xa4\xa4\xed\xccJ5\xd6f\xe6\xb7\x08\x94\xe6\x02\xd6\xd5\xd5\xa1\xae\xaeN\x1c=I\x8bB\xa4\x91*z\xd01\xb0\xd4S\xf1\x9dw\xdeAOO\x0f\x06\x06\x06D\x7f\xdd\xe9\x8a\x02f\xb3Yd2\x19D\x22\x11\x11\xd9;\xed\xb4\xd3\x0a\xda\xaa\x01@WW\x17\xb6n\xdd*,b\x8a}\xff\x08\x9dNWP\xf9KQ\xf3\x99\xcc\x95e\xe17;!\x91v\xd7]w\x89\xb1\xd9\xd9\xd9\x89\xfb\xee\xbboR?O\xc6\xeb/\xbd\xf4R\x81\x0d\xd3\x9dw\xde\x89G\x1f}tR\xe2LZ\xa8u\xf9\xe5\x97\xa3\xbf\xbf\x1f\xcf?\xff\xf8\xa0\x98\x7f\xa4\xc7\xcb\xd2\x00\x85\xf4\xf7=\xf8\xe0\x83\xc2\xc7\xb4\xa1\xa1\x01\x9f\xf9\xccg\x8e\xeb:iuZ,jY\x84E\x8b\xdf\xb3D\x9b\x8dE \xc5\xc6\xd8\xcc\x1c\x15\x80\x93\xbd\xc0\xc5\xe2\xb08\x12\xc8\xdee\xccD\x22P\xab\xd5\xc2\xe1p\xa0\xa6\xa6\x06MMM\xa8\xae\xae\x16\xbez$\x04\xa9ZV\xba\x18\xa8\xd5j\xf1\xf3tL\x9aN\xa7\xd1\xda\xda\x8a\xae\xae.\x0c\x0e\x0e\x22\x1c\x0e\x1f3\x99\xfdx\x05 \xf5\xbc\x0e\x04\x02\x88F\xa3X\xbe|\xb9xm\xe4C\xb6\x7f\xff~\x1c8p\x00\x83\x83\x83\x08\x04\x02\xc2\xa7Lj\x94N\x0b\xcb\x19g\x9c\x01\xaf\xd7+r\xfff:\xfa\xc7\x85 \xb3_\x04\x9aL&l\xd9\xb2E\x88\xb0\xd6\xd6V<\xf5\xd4SSZ\xac/\xbc\xf0B<\xf2\xc8#B@\x01\xc0\xd5W_\x8d\x1f\xfd\xe8G\xc2~i2\xaf\x85\xbaw\xe4r98\x1c\x0e\x5c{\xed\xb5\xb8\xe7\x9e{\xf0\xe7?\xff\x19\x7f\xfd\xeb_\xf1\xdak\xaf\xe1\xb5\xd7^\xc33\xcf<\x83\xe6\xe6f\xa4\xd3\xe9I\x09\xc0\xe2\xc0\xc4x\xe2D&\x93\xe1\x8e;\xee\xc0}\xf7\xdd'N\xa9\xf4z=^\x7f\xfdux\xbd\xde1\xebSq\x852}|\xcf=\xf7\x08G\x81t:\x8d[o\xbd\xf5\xb87L2\xc8\xe0v\xbb\xd1\xd2\xd2\x02\x8b\xc52'\xc6\xdcd\x8d\xc7\x99Y&\x00'\xbb\xf8\x14W\xfe\x16G\x0a\xa7#\x02\xc3\xcc\x0d(\x11\x9ar\x01\xfd~?\x96/_.\xc4H,\x16\x13\xc7D\xa5\xa2\x80d\x8cM\x1fS\xc1\xc5\xe1\xc3\x87\xd1\xd5\xd5\x85\x91\x91\x91\x92\xfd\xab\xcb\x11\xfdK\xa5R\x18\x1d\x1d\xc5\xe0\xe0\xa08\xc6.\xe6\x9dw\xde\xc1\xf0\xf00\x06\x06\x06\x90L&\x0b\xbc2\xa5G_\x8d\x8d\x8dhnn\x86\xd3\xe9\x14\x95\xbf3\x19\xfdc\xe6\x0e\x9f\xfd\xecg\xe1\xf7\xfbE\xf5\xea\xf5\xd7_?\xa5{ \x97\xcb\xe1c\x1f\xfb\x18\xee\xb9\xe7\x1e\xc4\xe3qqzs\xdbm\xb7\xe1\x9b\xdf\xfc\xe6\x94\xac\x95J\xd9\xbe\x00\x80\xc9d\x82\xc7\xe3A]]\x1djkkE\x04p\xa2\xe78\x96\x07\xaa\xd4P:\x91H\xe0\xdf\xfe\xed\xdf\xb0u\xebV\xf17%\x12\x09\xec\xdf\xbf\x1fUUU\xc8d2c\x8c\xf8\xa5\xcfA\xddG\x8e\x1c9\x82{\xef\xbdW\xe40644\xe0\x92K.\x19\xf75\xcc\xb7\x0d\x07\xb5\x80e\xe6\xa0\x00\x9cj\xa8\xb7\x94\xad\x09G\xff\x98cA\x15\xc1v\xbb\x1d\xd5\xd5\xd5\x22\x12H\x96\x11\xa9T\x0a\xf1x\xbc\xa0\x070\x8d+*\x02!k!\xa5R\x89\xd1\xd1Q!\x00)\x0a(=J>Q\xa4\x95\xbf\xa1P\x08}}}\xd8\xb0a\xc3\x98{\xa1\xbd\xbd\x1d\x07\x0f\x1eDWW\x17\x82\xc1\xa0\xa8 \x94F\xff\xe8\xde\xa0j\xc6\xaa\xaa*q$\xc6>\x99\xcc\xf1\x8cM\x00\xf8\xc9O~\x22<5\xe5r9.\xb9\xe4\x12\xe1\x957\x99\x8d\x7f*\x95\xc2\xad\xb7\xde\x8a\x9f\xfe\xf4\xa7b\xbc*\x14\x0a|\xfd\xeb_\xc7\xd5W_\x8d\xde\xde\xde\xb2Dv\xa4y\xe1\x13\xdd\x9fr\xb9\x1cN\xa7SD\x09\xc7\xab@\xd5h4hmm\xc5\x96-[\xb0o\xdf>\xf1\xdcUUUhkk\x83\xd7\xeb\x1d\xb3NI\xc5\xa5\xf4y\xe5r9n\xb8\xe1\x06\x91\x8f\x0c\x00\x9f\xff\xfc\xe7\x11\x8f\xc7y\xa0\x15m\x18\x8aE!3\x07\x04`\xa9\x0b9\xd5s\x7f\xce\x13`&\xb3\xe0h4\x1aX,\x16x\xbd^\xd4\xd6\xd6b\xd1\xa2E\xc2\x0c\x9a,%\xa4\xc7\xc0R\xe3b\xaa2\xa7#aZ\x04\xba\xba\xba\xd0\xd9\xd9\x89@ \x80X,V\x96#N:\x9a\xa6\xe8_ww7\xbc^/\xbc^\xef\x98\xe7omm\x15\xbe\x84\xd1hTD\xff\xa4\x0b\x1d\xb5\x8c\xab\xaf\xaf\x87\xd7\xeb\x15\xb9\x7f\x95d\x974\xd5\xfb\x9d9\xb9Q\x99t:\x8d\x0f~\xf0\x83\xf8\xe8G?\x8aT*\x05\x85B\x81W_}\x15?\xfb\xd9\xcf&\xbd\xa9\xa0\x88\xdc\xe7>\xf79<\xf5\xd4Sb\xa3\xa6R\xa9\xf0\x87?\xfc\x01\xeb\xd7\xaf\xc7\xd0\xd0\xd0\x09\x8fQi\xca\xc4D\xbd\x80\x95J%\xea\xeb\xeb\xb1r\xe5J,]\xba\x146\x9bM|\x9d\x0c\xe35\x1a\x0d\x1ex\xe0\x01\xfc\xbf\xff\xf7\xff\x10\x0a\x85D\x8b\xb6\xfa\xfaz\xfc\xe3\x1f\xff\xc0\xc2\x85\x0b\x0b|\x0a\xa5\x05\x8a\xd2\x8fi\x1c?\xf4\xd0Cx\xe9\xa5\x97\xc4\xef\xd9\xb4i\x13\x1a\x1b\x1bgu\xe5n\xb9\x84^\xa9{\x9d\xd7\xfa9&\x00y\x82gfj\xd1\xa2\x8a\xe0\xaa\xaa*\xf8|>\xf8\xfd~444 \x95J\x15\xe4\x06\xa5R)1aK;\x04\xd0q\x17=\x12\x89\x04\xf6\xed\xdb'*\x82\xc3\xe1pY*\x82\xa9\xb0#\x16\x8baxx\x18\xbd\xbd\xbd8\xff\xfc\xf3\x0bz;\x03@OO\x0fv\xef\xde\x8d\xee\xeen\xf1\xbb\xa5\xa6\xcfR\xdf\xbfSN9\xa5 \xf7O\xab\xd5VD\xf4O\xfa^M\xf6\xbe\xe7h\xff\xc9\x87\xf2\xf4\xee\xbd\xf7^\xb8\xddnqDw\xe7\x9dw\xe2\xc8\x91#S\xda\x98\x01\xc0\xbf\xfc\xcb\xbf`\xcf\x9e=\xa2u\x1c\xf0\x9e\x0dJUUUY\xd6\x83\xc9\xf4\x02\xa6\xfb\xba\xb9\xb9\x19MMM\xa2\xea\x99ZF\xbe\xf2\xca+X\xb0`\x01\x9e|\xf2I1\x0e\xa9\x08\x85\x22\x7f\xc5\xbfS\x1a\x01\x94\x1e\xfd\xa6\xd3iD\xa3Q\xfc\xfb\xbf\xff\xbb\xf8\xba\xd3\xe9\xc4u\xd7]\x87\x5c.7'r\xf7\xca\xad\x07\x8a\xfdL\x999,\x00y\x92g\xca\x0d\xb5E2\x1a\x8dp\xbb\xdd\xa8\xaf\xafGCC\x03\xf4z=\xb2\xd9,\x92\xc9$\x92\xc9$r\xb9\x5c\x81Y4\x89@\x95JU\x109S\xa9ThkkCWW\x17\xfa\xfa\xfaDE\xf0\x89\x1c\x03\x93\xf8K&\x93\x08\x85B\xe8\xea\xeaBSS\x13\xecv\xfb\x98\x1c\xc5]\xbbv\xa1\xbd\xbd\x1d\xa3\xa3\xa3\x08\x87\xc3\x22\xff\xaf\xb8\xd2\xb1\xb1\xb1QTC\xda\xedv\xe1\x13V\xe9\x13~\xf1\xff)\xcf\xb7\x9c\xb9\x96\xcc\xf1\xa3T*QUU%z\xfa\xcad2\x0c\x0c\x0c\xe0\xfa\xeb\xaf/\xb0V\x9a\xec5onnF \x10\xc0\x86\x0d\x1b\xb0j\xd5*\xfc\xecg?C2\x99,\xcbZ0\x99\xcdN\xa9~\xc1\xf9|\x1e;w\xee\xc4\xe5\x97_\x8es\xce9\x07\xa3\xa3\xa3\x05\x91\xbc\x8f}\xeccx\xe4\x91GJ\xaeY\xb4i,6l\xa7t\x92\xdbo\xbf]\xf4\x1c\xcf\xe5r\xf8\xcew\xbe\x03\xaf\xd7\x0b\xbd^?\xa6\xc77\xc3\x81\xa2y#\x00\x19f: \xd1\xa6\xd7\xebEEpcc#\x5c.\x970L\x96V\xf3f\xb3\xd91\x91\x03\xa91\xb4L&C2\x99\xc4\x9e={\xd0\xd9\xd9\x89\xc1\xc1AD\x22\x91\x82<\xc2\xe3\x15\x80\x14\xfd\x0b\x87\xc38\xe5\x94S\xc6$\xba\x0f\x0c\x0c\xe0\xad\xb7\xde\xc2\xe0\xe0\xa0(B\xa1\xdf+\xcd\xfdS*\x95X\xb5j\x15jjj\xe0t:a0\x18\x0a|\x0dgj\x92&\x9fO\xfa\xdcd+1\xa5\xcf#=Zc*\x87\x8f}\xecc\xb8\xf4\xd2K\x85\x98y\xe5\x95Wp\xc3\x0d7L\xaa\xc3\x87\xf4\xba\xd3&\xeb\x85\x17^\xc0\xd6\xad[\x91\xcb\xe5\xa0\xd1h\xca\x22\x0a\xc6\x1b\xebTAL) 4\xbe\xe2\xf18\x1ey\xe4\x11\x5cq\xc5\x158\xf5\xd4S\xf1\xa7?\xfd\x09j\xb5Z\xe4\xd7.[\xb6\x0cw\xddu\x17\xae\xbb\xee:\xc4b1Q\x8d\x5c,\x00\x8b#\x80\xd4g\xfc\xdb\xdf\xfe6\xfa\xfa\xfa\xa0P(\x90N\xa7q\xc3\x0d7\xe0\x93\x9f\xfc$\x1a\x164\xe0\xb4\xd3N\x83V\xab\xe5\x81\xc5T\xe6\xc6\x8f\xdf\x02f\xd6\xee^\xde\xf7\xf5\xb3X,p\xbb\xdd\xa8\xa9\xa9\xc1\xa2E\x8b\xd0\xdb\xdb+\xaa\xf6\x92\xc9$\xf4z\xbd\x10-R?@Z\xa8(\x7f)\x93\xc9\xe0\xc8\x91#8|\xf80jkk\xe1r\xb9`2\x99\xc4\xf7L\x05\x8a8R\xf4\x8f*\x7fkjj\x0a\x16,\x99L\x86\xdd\xbbw\xa3\xa3\xa3\x03###\xe2\xf8\x97\x8e\xb1\xa5\xa2\xcb\xeb\xf5b\xc5\x8a\x15p\xb9\x5c\xb0Z\xad0\x18\x0c\xd3\xde\xf6\xedD\x16\xec\xf1\x16\xeab\x01\xc9T\x16\x7f\xfa\xd3\x9f\xe0\xf3\xf9088\x08\x85B\x81G\x1f}\x14\x8d\x8d\x8d\xf8\xc67\xbe1\xe5\x13\x9d\x135.\xa6\xfb\xa08\xe7N\xea\xfbIb/\x97\xcbA\xa9T\xc2`0\xc0b\xb1\xe0\x8d7\xde\xc0\xfd\xf7\xdf\x8f\xe7\x9f\x7f\x1e\x81@@\x8c5\x95J%\x8a4\xbe\xfc\xe5/\xe3\x8c3\xce\x80N\xa7\x13]H\xc6\x9bkJmZ\x1e{\xec1l\xdb\xb6M\xbc\xd63\xce8\x03\x0f<\xf0\x002\x99\x0cjkjy0\x1dC\xcc3'y\x0d\x9d\xc9\x8b\xcf\x83\x80)'\xd2(\xa0\xddn\x87\xdf\xefGcc#\xdcn\xb7\x88\x04\xe4r9Q\xd5+\xed\x17L\x22\x90\x1e\x9434::\x8a\xf6\xf6vtuuahh\xe8\xb8*\x82i\x9cSnP(\x14\xc2\xd0\xd0\x10V\xaf^=\xe6\xf5\xf7\xf7\xf7\xe3\xad\xb7\xdeB__\x1f\x82\xc1 \xb2\xd9\xac(^\xa1\xc8#5\xbe_\xb7n\x1d\x5c.\x17\x9cNg\xc5W\xfeR5i\xb1\x10\xa4\xbcF\x8e\xfcU.\xc9d\x12;v\xec\x10\xd7K&\x93\xe1[\xdf\xfa\x16\x9ex\xe2\x89\x19\xa9\xda\xa4\xe7\xdf\xb6m\x1b\xda\xda\xda\x10\x0a\x85\x10\x89D\x90H$\x84):U\xda\xc6\xe3qD\x22\x11\x8c\x8c\x8c\xe0\x9dw\xde\xc1SO=\x85\xaf}\xedk\xb0\xd9lX\xbf~=~\xf3\x9b\xdf`tt\xb4\xe0\xf9\xb5Z-n\xbe\xf9f\xf4\xf7\xf7\xe3\xa2\x8b.\x82B\xa1\x10=\xeaK\x8dY\xfa\xbf\xf4s*\x95\x0a\x7f\xfe\xf3\x9f\xf1\xa3\x1f\xfdH|\xde\xedv\xe3\xf1\xc7\x1f\x17Ef\x0cS\xe9\xcc\xd8(\xa5\x89\x84\xcdc\x99\xb2\xee`\xde\xaf\x08\xb6Z\xad\xf0z\xbd\xf0\xfb\xfdX\xb2d\x09zzz\xc4\x91\x0c\x1d\xf7\x90\x8b>E\x14\xb2\xd9\xac\x88\x06\x90\xe5E.\x97C{{;\x16-Z\x84\x9a\x9a\x1a\xb8\xddn\x98\xcd\xe61\x1d\x00hL\xd3sI\x05\x0dE\xee\x92\xc9$\xc2\xe10\x06\x06\x06\xa0\xd5j\xd1\xd4\xd44&\x82\xb2o\xdf>\xb4\xb5\xb5!\x12\x89 \x16\x8b\x89~\xc4\xd2$\xe9|>\x0f\xbf\xdf\x8f\xa5K\x97\x8a\xe8\x9f^\xaf\xaf\x88\xca\xdf\xf1\xaa0\xd5j5\x22\x91\xc8\x18\xa1G\x02\x90\xc49o\x0a+\x0f\x8dF\x03\x97\xcb\x85g\x9f}\x16\x17]t\x91\xe8\xdet\xf5\xd5W\xe3\xc9'\x9f\xc4\x87>\xf4!\xa4\xd3\xe9i\xebJ\x91\xcdf\xa1T*q\xcd5\xd7\xa0\xb3\xb3\x13\x00\xe0\xf1xPUU%r^GGGE\x8e\xef\xe8\xe8(\x8e\x1e=:\xe6o {\x1a\x8a\x10\xaeX\xb1\x02\x9b6m\xc2\xed\xb7\xdf\x0e\x87\xc3\x81\xe1\xe1a\xb1.QZ\xc2x\x1b\x13\x12\x80\xb9\x5c\x0ef\xb3\x19\x8f=\xf6\x18>\xfe\xf1\x8f\x8b{0\x95J\xe1\xe1\x87\x1fF}}=\x0f\xa0In\xde\x999.\x00yrgfb\x22!_@\x87\xc3\x01\x9f\xcf\x87\x86\x86\x06\xd4\xd6\xd6\xa2\xa3\xa3\x03\x0a\x85B\x08\xbcd2Y\xd0-\x83\x8eu(\xe7\x87\x04a(\x14B[[\x1b\xfc~?jjj`\xb5Z\xa1\xd5jE\x94\x90\xc6\xb64*X,\x00\xd3\xe94\xe2\xf18\x82\xc1 \x8e\x1c9\x82\xab\xaf\xbez\xcc\xc47<<\x8c\xad[\xb7bdd\x04\xc3\xc3\xc3\xa2\x0d]q\xee_>\x9f\xc7\xc6\x8d\x1b\xe1p8\xe0r\xb9`6\x9b\xc5\xeb\xa94\xb2\xd9,\xd4j5\xc2\xe1\xb0\xe8\x11K\x11L\x8a.\xa5\xd3\xe9\x13\xca\xaddf\x86\x0b/\xbc\x10w\xdf}7\xbe\xfa\xd5\xafB\xa3\xd1 \x99L\xe2\xaa\xab\xae\xc2\xa3\x8f>\x8a\x8f|\xe4#\xc8f\xb3\xd32\x06i\xdd\x18\x18\x18\x80\xd9lF4\x1aE\x7f\x7f?\xfa\xfb\xfb\xc7_\xc8\xde\xcf\xe5\xa5\xfb2\x99L\x8a\xcfo\xd9\xb2\x05\x9f\xf8\xc4'\xb0p\xe1B\xd8\xedv\xf1;hSW,\xf2&\x12,\x1a\x8d\x06\xbf\xf9\xcdo\xf0\xdd\xef~W|.\x95J\xe1\xf6\xdbo\xc7\x07>\xf0\x01.z<\x0em\xc0\xef\xd9\x1c\x13\x80\x5c\xe2\xcd\xcc$r\xb9\x1cj\xb5Z\xf8\x02RAHOO\x8f8*\xa2c^\xda\xf1\x17\xb7s\xa2\xa4u\x1a\xbf\xfb\xf6\xedCSS\x13:;;\x85\xd92\xe5\xdbI\xab\x00\x8b'/:\xbaM$\x12\x08\x06\x83\xe8\xee\xee\x86\xdb\xed\x86\xcf\xe7C.\x97+Xp:::p\xe8\xd0!\x04\x02\x01\xc4\xe3\xf11]?\xe8\xf9\x1a\x1a\x1aPWW\x87\xea\xea\xea\x93\xd2\xf3w\xbc\xfb\xbb\x18\x85B\x01\x93\xc9\x84\x91\x91\x11h4\x1ad2\x99\x82\xe35\xe0\xbdN-\xd4\xadE\x1a\x05\xe4\x05\xa0\xf2\xc8f\xb3\xf8\xcaW\xbe\x82X,\x86\xff\xfc\xcf\xff\x14\xd7\xe8\xba\xeb\xae\x83J\xa5\xc2\xe6\xcd\x9b\xa7\xe5\xf7\xaaT*Q\x80\xa5T*\x8f\xb9\x8e\xd0}H\xe3\xd2\xeb\xf5\xe2\x9ak\xae\xc1\x8d7\xde\x88%K\x96\x881{\xac\xce R\x93\xe9b\xb1\x22\x93\xc9`6\x9b\xf1\xf4\xd3O\xe3\xee\xbb\xef.\xd8\xe8}\xf1\x8b_\xc4E\x17]\xc4\x91\xad2\xcc!\xcc\x1c\x10\x80\x0c3\xd3\x02\x90\xfav\x92/`CC\x03\xf6\xed\xdb\x87\xbe\xbe>(\x95J\x11\x05\x94\xb6\x80#\xd1\xa2\xd1h\x84A4M\xe2\xa9T\x0a{\xf6\xec\x81\xcf\xe7\x83\xd3\xe9\x84\xd1h\x84Z\xad.\x10_$\x06\xa5\x93>\xf5!\x8eD\x22\x18\x1a\x1aB__\x1f\xae\xb8\xe2\x8a\x82D\xf6|>\x8fh4\x8a\xbf\xfc\xe5/\xa2\xf2\x97\x16<:V\x96.z\xcb\x97/GMM\x0d\xaa\xaa\xaaD\xf4o\xa6s\xe8\xc6\xeb\xa4 \xfd\x1aE[\x13\x89\x84\xe8\xb2\x22\xed\x05+\x93\xc9\xd0\xdd\xdd\x8dP($:\x9dX,\x16\x16\x80\x15\x0a\xf9c~\xe3\x1b\xdf\x80\x5c.\xc7\x9dw\xde)6P\x1f\xfe\xf0\x87\xf1\xc3\x1f\xfe\x10\xb7\xddv\xdb\x98\xd6\x9e\xe5\xc0h4\x22\x91H\xa0\xad\xad\x0dmmm8t\xe8\x10\xc2\xe10\xa2\xd1(\x86\x87\x87\xd1\xd9\xd9\x09\x9b\xcd\x86\xc5\x8b\x17\xc3\xe5ra\xc1\x82\x05\xa8\xaf\xaf\xc7\xc8\xc8\x08\x14\x0a\x05\x96.]\x0a\xa3\xd1\x88d2)\xa2\x83\xa5\xe6\x8d\x89\xc6x\xf1\xe7\x9f\x7f\xfey\xdc}\xf7\xdd\x22\xbf/\x93\xc9\xe0\x9e{\xee\xc1\xcd7\xdf\xcc\xe3\xf78`#\xe8y&\x00\xf9b3\xd36\x90\xdf\xaf\xd4\xb5X,\xa8\xae\xaeFCC\x03\x1a\x1b\x1bq\xf4\xe8Q\xd1\x98=\x93\xc9@\xaf\xd7\x8b\x89\x87\xf2\x00)\x82(\xb5\x83\x91\xc9d\xe8\xe9\xe9Akk+l6\x1b\x8cF#T*\x15\x9cN\xa78>\x92&\xc4S\x04\x22\x99L\x8a~\xbf]]]\xf0\xfb\xfd\xa8\xab\xab\x1b\x13M\xd8\xb9s'\xda\xda\xda\x84=\x0cY\xd6\x14\xe7\xc5\xb9\xddn\xac\x5c\xb9rL\x0e\xd4\xc9\x16\x80\xd2\x1d\x0c\xa3\xd1\x88\xcd\x9b7\xc3\xeb\xf5\x8eY$v\xef\xde\x8dW^y\x05\x07\x0e\x1c\xc0\xc8\xc8H\x81\xaf\x99\xb4\xf27\x97\xcb\xe1\x82\x0b.\x80\xddn\x87\xdb\xed\x86\xc5b\xa9\xa8\xe8_q\x15\x9f\xf4c\xb5Z-\x8cv\xe5r9\x0c\x06C\x81\xa7\xa1B\xa1\xc0\xe0\xe0 ^~\xf9e\xa4R)\xc4b1\xd4\xd5\xd5\x89\xc8\xeaLu6\x99/\xd0F&\x9f\xcfC\xa7\xd3\xc1h4\xc2h4\x8a\xb6\x89\x93\x8d\x04R\x84|\xe3\xc6\x8d\xd8\xb5k\x176m\xda\x84\xfd\xfb\xf7C\xab\xd5\x22\x9dN\xe3\xdak\xaf\xc5#\x8f<\x82\x9f\xfc\xe4'hjj\x12\xc2\xbf\xdc\x22\x89\xee\xdf1'J8~1A\xa6\xd2V\xab\x15o\xbf\xfd6n\xbd\xf5V\xbc\xf6\xdak\x05\x91\xeds\xce9GX\xbd\xb0\xf8cX\x00\x1ec\x91\xe0I\x9c\x99i\xe4r9\xb4Z\xad\xc8\x05\xf4\xfb\xfdX\xb4h\x11^}\xf5U$\x93I1iS^ y\x04\xd2\xcfR\x84M\xa3\xd1\xc0h4\xc2\xe7\xf3\xc1\xe3\xf1\xe0\x82\x0b.\xc0\x1bo\xbc\x81w\xdf}\x17\x89D\x02\xdd\xdd\xdd0\x18\x0cP(\x14H&\x93\x88F\xa3\xd0h48\xf3\xcc3\xb1|\xf9rq\x9c)]$R\xa9\x14v\xef\xde\x8d\xa3G\x8fb``@T\xfe\x16\x1f\xff\xe6\xf3y\xb8\xddn477\xa3\xba\xba\x1av\xbb\x1d:\x9dND\x1cO6\xd2\xfe\xc3\xc5]=\xe8\x08\x8e\x84\x9e4:H\xef\xb7\xc3\xe1@SS\x13\x94J%\xb6m\xdb\x86\xde\xde^,^\xbc\x18\x1e\x8fG\x1c\xad\xf3\xdcQ\x1e\xa1Dc0\x91H \x9b\xcd\xc2b\xb1\xc0\xe3\xf1\xa0\xba\xba\x1a2\x99\x0cz\xbd~JG\xee\xf4\xbd\x0b\x16,\xc0\xbb\xef\xbe\x8b\x9bn\xba\x09\xbf\xfe\xf5\xaf\xc5xx\xfe\xf9\xe7\xb1l\xd92\xdc|\xf3\xcd\xf8\xc1\x0f~ \x04h9\x8f\xf5e\xf2\x7f\x0a\xc0rT\x93\xe6r9\xe1\x12p\xe3\x8d7\xe2\xb7\xbf\xfd-\xe2\xf1\xb8x\x0fS\xa9\x14\xbe\xf0\x85/\xe0\xee\xbb\xef\x86V\xab-(\xe8b\x8e/0T\xfc13\xc7\x04`qd\x80afJ\x00R;\xa8\xaa\xaa*\xd4\xd4\xd4\xa0\xbe\xbe\x1emmm\x18\x1c\x1cD&\x93A\x22\x91\x00\xf0^Q\x02\xe5\x0dIs\x7f\x8cF\xa3\x10\x91\x16\x8b\x05MMMX\xb6l\x19\xd6\xacY\x03\x00\x18\x1c\x1cD2\x99DWW\x17\xd2\xe94\xccf3|>\x1f\x5c.\xd7\xb8\x13[6\x9b\xc5\x1f\xfe\xf0\x07\xbc\xfa\xea\xab8|\xf8\xb0\xb0\xa7!3\xdb\xe2\xe2\x8f\xb5k\xd7\xc2\xeb\xf5\xc2\xe5r\xc1b\xb1\x9c\x94\xca\xdf\xf1\x88\xc7\xe3\xa2x\xa6\xb8C\x8aF\xa3\x11\x06\xd0\x14\x0d\xccf\xb3\xd0\xe9t\xd0\xeb\xf50\x99L\xf0z\xbdhhh\xc0\xb9\xe7\x9e\x8b\xef}\xef{\xd8\xb3g\x0f\x8cF#\x02\x81\x00\x82\xc1 R\xa9\xd4\x94:\xaf0\x13\xcf\xbf===\xd8\xbbw/\x12\x89\x04t:\x1dZZZD\xd5<\x09\x9f\xe3\x19[r\xb9\x1c\x0f?\xfc06o\xde\x8cO}\xeaS\x18\x1c\x1c\x14\xe3\xff\x87?\xfc!~\xfc\xe3\x1f\xe3\x17\xbf\xf8\x05\xae\xbc\xf2JX,\x96\xf2\x09@\xc8D\xb1\xd0\x89\xe4\x95+\x95J(\x95J\xc4\xe3q\xbc\xf0\xc2\x0b\xf8\xf9\xcf\x7f.>O\xb8\x5c.\xfc\xfa\xd7\xbf\xc6\x05\x17\x5c \xc6$oN\xca?FY+\xb0\x00d\x98\xb2 \xcd\x05\xf4z\xbd\xa8\xaf\xafG}}\xbd\xa8\x08\xa6\x02\x8fT*U\x90\x0cO\x22\x8c\xaay\xa5\xf9m\xa9TJ\x1cu\x91\xd0\x93\xf6\xf5\xa5\x9f\x97V\x12RD,\x99L\xe2\xcd7\xdf\xc4\xae]\xbb088\x88H$\x82`0X\xb2\xf8\x03\x00\x9cN'\x16/^\x0c\xaf\xd7[q\xd1\xbf\xe2\x05Pz\x0cN\xef\x81\xb4\xad\x16u\x8c A\xa8\xd3\xe9\xa0\xd5jQ]]\x8d\xc6\xc6F\xa4\xd3iQ\xe5)\xad\x96f\xca\x0f\xb5s\xeb\xee\xee\x86\xcf\xe7\x83\xcdf\x83\xc1`8\xee\xe7#\x8b\x9fG\xa8\x91U\x00\x00\x1daIDAT\xcb/\xbf\x1c\xbbv\xed\xc2\xd7\xbe\xf65\xfc\xeaW\xbf\x12\xddwr\xb9\x1cn\xbc\xf1F<\xf7\xdcs\xf8\xfd\xef\x7f/\xa2\xee\xe5\x18\x7f\xc5\xde\x7f\x93]o\xa4'S\xad\xad\xadx\xec\xb1\xc7\xf0?\xff\xf3?\x18\x1a\x1a\x12v1d\x22}\xf3\xcd7\xe3\xfb\xdf\xff\xbe\xb8\xef\xa7\xab\xf3\xc9|\x15}\xac\x15* `2\x9dj\xbed\xa2.\xc3L\xf7\xa0~?\xbf\xcfl6\xc3\xe5r\x89c`\xb3\xd9,\xc4])\xcf=\x8aj%\x12\x09\xa4\xd3i\x84\xc3a\x0c\x0e\x0e\xe2\xc8\x91#hoo?\xe6\x22 5\x89\x96V\xc8\xbe\xf2\xca+x\xf6\xd9g\xd1\xde\xde\x8e\xfe\xfe~Q\xf8!\xb5O\xa1\x85I.\x97c\xc9\x92%\xa8\xab\xab\x83\xc7\xe3)\x88\xfeUZ\xe4\x81Z\xbeI\x0bSH\xe4Q!\x00\xe5VJ;\x81\x00\xefuA\x19\x1a\x1a\x82B\xa1\x18\x131\xa5k\xc2\x8f\xe3\x7f\xd0\xfbH\xe9\x0d\xdf\xf9\xcew\xf0\xc4\x13O\xa0\xab\xab\x0b\xb1XL\xe4\x9fR\xf4\xf9\xb8#\x08\xef\x1b5\xbb\x5c.<\xf4\xd0C\xd8\xbe};N?\xfdtQu\x0f\x00\x9f\xfd\xecg\x91\xcb\xe5\xcav\x0c,\x15\x80S][d2\x19^x\xe1\x05l\xdc\xb8\x11\x17^x!~\xf9\xcb_\x8aqH\xe3\xf3\xfc\xf3\xcf\xc7\x8e\x1d;p\xdf}\xf7\x89#_\x16\x7f\xe5\xd3\x08\x13\x8d7\xd6\x0a\xb38\x02X\xaep.\x87\xd8\x99rD\x01\xa5\xb9\x80\xb5\xb5\xb5hll\xc4;\xef\xbc\x03\x00\xa2\xe8\x22\x99L\x8a\xcaS\xa9\xb1s2\x99D*\x95B8\x1cF0\x18\xc4\xdbo\xbf\x0d\x97\xcb\x05\xb7\xdb=\xa9\xc5@.\x97c\xdf\xbe}\xd8\xb9s'^}\xf5U\xf4\xf7\xf7#\x18\x0c\x22\x12\x89\x08\xb3d\xa9\x08\xa5\xdfKy\x84.\x97\x0b6\x9b\xad\xa2*\x7fK\xdd\xef\x91HDD\x82\xa4\x22Pj\xa7Cy\x82$\x88\xbb\xba\xba`\xb5Z\xf1\xd2K/\xe1\xe2\x8b/\x86\xdb\xed\xe6\xfb\x7f\x9a\x04z<\x1e\xc7\xa3\x8f>\x8a'\x9f|\x12\x9d\x9d\x9d\x88\xc5bH$\x12H$\x12\x22\x1a^\x8e\xdfC\xd7}\xf5\xea\xd5x\xf3\xcd7\xf1\xfc\xf3\xcf\xe3\xe3\x1f\xff8\xd6\xacY\x833\xcf<\xb3\xac\xd7\x956Jt\x0fOE<<\xf1\xc4\x13\xb8\xea\xaa\xabJ\xf6\xa8\xf6\xfb\xfdx\xec\xb1\xc7\xb0a\xc3\x06\xf1\xdc\x5c\x90\x84\xb2\x8d\x0f\x16ys\x5c\x00N$\x0a\xa7\xb2\xcb\xe4\x1b\x8e)\xc7\xa4\xa3P(`6\x9bEw\x90%K\x96`\xdf\xbe}\x22\xc7,\x99L\x8a\x9d?\x1dO\xa5R)\xc8d2\xe8t:D\x22\x11a\x1f\x93L&\xf1\xe8\xa3\x8f\xe2\xd4SO\xc5\x8a\x15+\xa0\xd5ja6\x9b\x0b\xc6j6\x9bE(\x14B__\x1f\xda\xda\xda\xb0o\xdf>\xb4\xb6\xb6\x22\x12\x89`xx\x18\xa1P\x08\xb1XlL\xcf_\x22\x97\xcba\xfd\xfa\xf5p\xbb\xddp:\x9d\xa2\xebG\xa5\x0a@z\x9fI\xc8J\xefy\x8a\xa6\xd2\x91\x1fE^3\x99\x0cl6\x1bzzz\x10\x89D\x10\x8dF\xb1z\xf5j\xb4\xb4\xb4\xc0h4\x0a!\xce\x9c\xf8\xbc{\xe8\xd0!\xfc\xf1\x8f\x7f\xc43\xcf<\x83\xfe\xfe~\x91r maX\xceD|\xba\xe7R\xa9\x14>\xf0\x81\x0f`pp\x10\xdd\xdd\xdd\xd0j\xb5e\x1fs\xd2H\xfbT\xb8\xf8\xe2\x8b\xb1d\xc9\x12\xb4\xb6\xb6\xbe\xb7\x00*\x95X\xb2d\x096n\xdc\x88\x8f|\xe4#X\xb3f\x0d\x1f\xf7\xce\x80\x1e`\xe6\xa8\x00\x94\xde\x98\x94\x0fT\x5c\xe1\xc8032\xb8\x95Jh4\x1a8\x1c\x0e\xf8|>\xf8|>,_\xbe\x1c\xaf\xbf\xfe\xbaXD\xa8\xe2T\xaf\xd7\x8b\xf1I\xc6\xc5\xe9tZ\x88\xbfp8\x0c\x97\xcb\x85@ \x807\xdf|\x13~\xbf\x1f\x1e\x8fGT\x11SqIGG\x07\xfa\xfb\xfb\x0b\x04_8\x1c\x16\xc7\xceT\xf8!\xb5\xe6\xa0{\xc2d2a\xf5\xea\xd5p\xbb\xddp8\x1c\xc2\x1c\xb9\xd2\xc4\xdfxG\x8e\xd2\xaf\x93\xc8\xc8d2B\xd4\xc5b1h4\x1a\x04\x02\x01\xa4\xd3i\x04\x02\x01$\x93Itww\xc3\xe1p\xa0\xaa\xaa\x0a\xb5\xb5\xb5\xd0\xe9t\xc2\xf2\xc6h4B\xa7\xd3\x9d\x94\x85\xa7\x9c\xe2\x88\x9e\xebx\xaf\xe5d_\x7f2\x99D(\x14\xc2\xc1\x83\x07\xf1\xc6\x1bo`\xc7\x8e\x1dhooG(\x14\x12Q-i\xc7\x95\xe9\x18[j\xb5Z<\xb7\xdf\xef\x9f\x96MG\xa9\x8e1\x93y\x8fL&\x13\xd6\xad[\x87\xbe\xbe>|\xees\x9f\x13\xf3\x82Z\xadF$\x12A&\x93)\xbb`e\xfey}H\x0b\xb0G\xf0\x1c\x15\x80\xd2\x5c&\xbaY\xa5\xf9K\x1c\xddcf2:\xa5P(`4\x1a\x0b*\x82\xf7\xee\xdd\x8b\xd1\xd1QQ\xdc\xa1R\xa9D\xa7\x0a\xeap\x00\xbc\x17\xd1\xa3j]\x83\xc1\x80\x9e\x9e\x1etww\xc3\xe9t\xa2\xbd\xbd]\xfc\x0e\xb2\x8c\x19\x1c\x1cD.\x97C<\x1eG\x22\x91@4\x1aE4\x1a\x15Gm\xf1x\x5c\x88?\xa9\xef\x1fAG\xbf\x1e\x8f\x07V\xabU\x1cMWb\xee\x9f\xf4\xbe\x9e\xa8\xa7*E\x03\xb3\xd9,4\x1a\x8d\xb0\xcbI\xa7\xd3\xd0\xe9t\x18\x1a\x1a\x12\x1dN\xc8\x9f.\x16\x8b\x8d1\xd0\x9e\xca\x22_\x1cY\xad\xa4H\xc7tZ_P46\x12\x89 \x10\x08```\x00CCC\x22\xed\x80~7\xe5eR\x9e\xe6t\xbd\x96\xe9B*`\x8f\xe7\xfd\xbc\xf7\xde{\xf1\xb3\x9f\xfd\x0c\xb1X\x0c[\xb7n\x15\xc7\xe2F\xa3\x91\xd7\xa7i\x9e7\x8aSm\x989\x22\x00\xa5\x8b\xc2\xf0\xf00Z[[\x91\xc9d\x90N\xa7\xd1\xd1\xd1\x81\xd1\xd1Q\x84\xc3a$\x12\x09\x11\x0d\x91\xee\x06fb\xe2`\xe6\xe1\x00\x7f?\x0ah\xb7\xdb\xe1\xf5zQSS\x83\xc6\xc6F\xbc\xf5\xd6[\xa2Wm\x22\x91\x80\xc1`\x10BE\xea\x0bFQl\x12\x88z\xbd\x1e\xa1PHT\x19\x93P\xa4\xde\xb7\x99LF\x88<\xe9d\x97H$D\xe5\xb14\xfaG\xd8l6\xb4\xb4\xb4\xc0\xe7\xf3\x89\xca_\x8dFS1\xc7\xa1R\x93\xeaP(\x84}\xfb\xf6A\xa1P \x18\x0c\x8a\xc2\x96p8,\x22\xa7T|@\xa2'\x97\xcb!\x1a\x8d\x0a[\x18\x00\xc2k-\x14\x0a\x89B\x97t:\x0d\x83\xc1\x80\xae\xae\xaec\xce\x09\xb3\xcdG\xecD\xc5\xd6\xb1~>\x9b\xcd\x22\x95J!\x93\xc9 \x12\x89 \x16\x8batt\xb4\xc0V\x87\x22\x80\xd2\x0d\xfal\x9bs\xc7+\x02\x99lT\x89\xa2\xf6\xf4^p$jz\xaf\x15\xcdu}}}hmm\xc5\xc8\xc8\x08\x06\x06\x060<<\x8c\x91\x91\x91\x82\xd3\x96\xe2\x8d1\xfd\xcb}\xc2+\x5c\x00J/\x18%\xcf\xd3b\x11\x0e\x87\x0b\x92\xe0\xc73\xc0\x95F\x10\x19\xa6\x1c(\x14\x0a\x11\x05\xf4x<\xa8\xaf\xaf\xc7\xc2\x85\x0bq\xe0\xc0\x011Fe2\x99\xc8\x01\xa4\x1e\xb6T!IQ\x12\x95J\x05\x99L\x86H$\x02\x8dF\x83d2\x89`0\x88|>\x0f\xbd^/\x8e8\xa59\xaft\xdcKQ\xb0x<.\x04e\xf1D\xb6h\xd1\x22,X\xb0\x00.\x97\x0bV\xabU\x1c\x81V\xd2\xbd@\x133\xe5:\xaaT*\x84\xc3a\x0c\x0d\x0d\xa1\xbf\xbf_\xe4\xf4\xd1\x91x\xf1u\x00 r,\xa5\x0b\x84B\xa1@(\x14\x12\xdf\x17\x08\x04Dk\xbc\xa9F\x99\xa6k\x81\x98-\x0b\x0fy3R\x11\x13\xe5]J\xe7U\xe9b:[\xf3-O$\x02Hs\x02\xa5'\xb0\xb0\x98\xfey\x83\xdc\x15\x82\xc1 b\xb1\x18B\xa1\x10zzz\x10\x8dF1::\x8aX,&r\x89KE\xf0\xf9\xfa\xcc\x82\x08 M2\xc3\xc3\xc3\xd8\xb7o\x1f\x86\x87\x87E\x97\x04Z\x1c\xa4\x11\x02i\x04\x90&'\xee\x02\xc0\x94\x1b\x95J\x05\x8dF\x03\x9b\xcd\x06\xbf\xdf\x8f\xfa\xfaz\xd4\xd5\xd5a\xf7\xee\xddb\x13B\xd1\xbbX,V\x10!\xa1\xc8\x1e\x99\xe5*\x95J$\x93I\xa8T*\xf1 \xe3c\xe9B\x22\xb5\x96\xa1^\xc1\xd9lv\x8c\xfd\x0c\x15\x9c\xac]\xbb\x16n\xb7\x1bUUU\xd0\xeb\xf5\xa2%Z\xa5-\xba\x00\x10\x0e\x87\xd1\xd6\xd6&&n\x9a\xc4\xa3\xd1\xa8\x10\xbc\xc5\x02\xf0X\xd1\xbcb\x11CsA%L\xfc\xb3i\xf1\xa1\xb1%\x1dk\xd2\xb6\x9c\xd2\x08\xdal\xed\xc4p\x22>\x80\xe3\x8d5f\xfa\x85z \x10\xc0\x9e={\xd0\xdd\xdd-\xf4\xc0\xf0\xf0\xb0\x08\x08\xd1\xc6\xb1x\xbc\xd2ub\x11X\xc1\x02\x90\x8eyd2\x19FGG\x11\x0c\x06E\xd4\x83\x16\x06\xca\x85\x92\x1e\xb5\x95\xf2\x0ed\x01\xc8\x94;*\xa2R\xa9`4\x1a\xe1p8P[[\x8b\x95+Wb\xef\xde\xbdb\xa1$\x1b\x93\xe2\xb1'\x97\xcbE\xb50\xf5\x1a&\x93[\xaa\x10\xd6\xe9t\xc8d2\x05\x9b\x17\xb2\x92\xa1EY:\xfe\xa5c=\x9f\xcfc\xc9\x92%\xa8\xaf\xaf\x87\xcb\xe5\x82\xd9l\x86N\xa7\xab\xc8\x8d\x10\xbdn\xca1\xa3\xa3\xf1H$\x22\x8e\xb8\x8b\x0b[&\xbb8\x17\xe7\x0d\xce\xf5]\xfft\xe7\xdeI7\xd7\xf4\xb1TXOV\x8cW\xe2\xfb&\xed\x05|\x22\x22\xb6\xf8~\xe7ugz6$t\x8d\xfa\xfa\xfa\xd0\xdf\xdf\x8fd2)R\xc2h\xfe\x95\xce\x1d\xc5\x91\x7fi\xee S\xa1\x02\x90\x16K\x85B\x81H$\x82P($\xaa\x1e\xa59O\xc5\xea\x9e&)\xba\xa19A\x94\x99\x96\x81\xaeTB\xa7\xd3\x89\x5c@\x9f\xcf\x87U\xabV\xe1\xf5\xd7_\x1f\xd3\xcd\xa2xA\x90V\x0c\xd3\x18\xd6j\xb5H\xa5R\xa2\xa0A\xa5R\x15$\xd9\xd3\xc4GB\x90\xa2\x80\xd2\x85\x99\xee\x97\xf5\xeb\xd7\xc3\xe9t\x8a\xca_i\xff\xdcJ\x8b\xba(\x95JD\xa3Q\xd1U\x85v\xee\xe3\x1d\xdd0'O(\x8d'\xa4\xa9\x05\xdal\x8e*\x8d\x97\x03x\xbc\x82\x99\x05\xe0\xf4\x5c'\x9a\xc7\xe4r9\x22\x91HA\x0a\x98t\xde(\xbe~\xc5u\x01\xe5\xec%\xcdL\x83\x00\xa4\xe31\xbd^\x8fL&\x83\x91\x91\x91\x82\x04\xd0\xc9F\x05t:\x9d\xc8}\xe2\x1b\x92)\xe7\xf8\xa4\xee \x1e\x8f\x07uuu\xc2\x0f,\x18\x0cNj\xc1\x91\xf6\xec\xcd\xe7\xf30\x18\x0cB\x10R\xa7\x03\xfa\x7f\x22\x91\x80Z\xad\x169qT\x85Y\xdc\x0c\xfd\xcc3\xcfDuu5\xdcn\xb7\xe8\xfaA\xf9\x86\x95\x04\xfd}\x1a\x8d\x06\xf9|^\xe4?J\xff~\xa6\xb2\x04\xe0xc\xf9D\xfa\xffV\x8a\xb0(\xb7@\xa19\x82)\xffuR\xa9T\xd0\xeb\xf5H$\x12\x18\x1e\x1e\x16\xf3E\xa9`\xd0\xb1\xc6,U\xb03\x15$\x00I\xfc\xe9t:\x98\xcdf\x11\x15)\x95\x13u,\xccf\xb3h/\xc5\x17\x9a)'\x0a\x85\x02:\x9d\x0eV\xab\x15~\xbf\x1f\xb5\xb5\xb58\xe3\x8c3\xf0\xdcs\xcf\x8d\x9b\x08^*\xd2@\xc9\xe3\xd4\xf3T:V\xa9\xe2W\xa7\xd3\x09\xc3gz\x14\x1f\x8f\xeat:,_\xbe\x1c\x1e\x8f\x07UUU0\x1a\x8d\x15\x99\xfbG\x93\xb8F\xa3\x81\xd5j\x85V\xab\x15\xc7\xe2'\x9a\x83\xc5\xcc\xdcbLcY\xab\xd5Vt\x87\x99\xa9\xfc-'*\x109\x028\xbd\xd7I\xadVC\xa7\xd3\xc1b\xb1@\xa5R\x89\x22\xb0RQ\xbf\xf1\xae\x8b\x5c./\xf0\x06\xadDk\xacy+\x00)<\xabV\xaba2\x99\xe0\xf3\xf9D\x94`*\x0b\x02\x0d\x88\xaa\xaa*\x98L&\xa1\xf8yQa\xca\x05mT,\x16\x0b<\x1e\x0f\x9a\x9a\x9a\x10\x8b\xc5\xf0\x8f\x7f\xfc\x03}}}\xc7\x8c\xa4H\xad4\xc80Zz\xc4!\x15v\xe4\xf5G\xd1\xbfb\xdb\x17\x00X\xbat)\xea\xeb\xeb\x85\xef\x9f4\xfa]i\xc2\x99L\x99\xa9HE\xfa~0\x95\x8f4\xb7\xcal6\x8b\xcd\xc6l=Z\x1b/8 \xb5\x0d\x99\xaa\x00daQ\xfe\xeb#\x97\xcb\x85\x03\x834Mf2\xe3\x95H\xa5R\xb0Z\xad\x228\xc4\xd7\xa9\x82\x04 \xf5d\xd4\xe9t\x22\xc1~\xfd\xfa\xf5\x22G\xaa\xb8\x92\x87vo\xd2\xfc*\x95J\x05\xadV\x0b\x93\xc9\x04\xaf\xd7\x0b\x87\xc3!\x12\xe1\xf9\xec\x9f)\xe7\x8e\x94z\x04\xdb\xedv,X\xb0\x00\x00p\xd5UWa\xc7\x8e\x1d\xc2\x8bJ\x9a\xabJ\x95\x94\xa9TJT\xaee\xb3Ya\x19\xa3\xd5j\xa1\xd1h\xc4\xf8\xa6/\x04`:\x9d\xc6e\x97]\x86\xd3O?\x1d\xa3\xa3\xa3\xa20N\x1a\x18\xa2\xebI\xa2P\xa3\xd1\xc0`0\xc0n\xb7\x8f\x19\xb3\x9c\x1eVA\x02\x90&\x18\xab\xd5*\x12\xc5\xddn7\x12\x89\x84\x88\x10\x94*\xb9\x97VB\xaa\xd5j\x18\x0c\x06\x98L&q<\xc1\xa1^f:&&:\x8a\xa0vkf\xb3\x19\xe1p\x18\xb1XlL\xa1\x07\xf0\x9e\x17\xe0\xc8\xc8\x88hS\x16\x08\x04\xa0\xd7\xebQ[[+\xbe/\x9dNC\xaf\xd7\x17\xb4{\x93\x8e\x7fi\xe5/\x80\x82\xca_\x8a\xfeU\xea\xa4F\xef\x99\xd1h\x04\x00h\xb5Z8\x9dN\xd1\xd5G:\x91K==\xf9\xde\x9dY\x8a#`\xd2\xe3PZP\xb5Zm\xc1\x11\xf0l\xcd\x03,\xfe\x9b\x8b\xff\xfe\xc9\xfeM\x1c\x01\x9c>\xc8D\x9f\x8e\x80u:\x1d\x5c.\x17\x12\x89\x84h\xb7Y\xea:HuA\xf1\x98\xa5\x9ck\xa6\x82\x04 -\x10\x00`\xb1X\xa0\xd7\xeb\x0b\x8e\x87\x8e\xe58O\x17\x9bz\xb1\xd2\x83U>3]\x82\x86\x04\x17%\x18\xdbl\xb6\x92-\xda\xf2\xf9<\x22\x91\x08\x06\x07\x07\x11\x89Dp\xf0\xe0A\xa8\xd5j,^\xbcX\x08\xc8@ \x04$\xfd,\x89\xc0\xe2\xa3\xdf\x5c.\x87e\xcb\x96\xa1\xa9\xa9\x09n\xb7[\xecj+}!\xa6|\x5c\x12\x0e\x16\x8b\xa5\xe0\xfe\xe6hJ\xe5\x08\xa2R\xd11\x8a\xe2\xd2f[\xadV\xcf\xfaH\x0a\xb9L\x9c\xe8s\x14\x7f\xccc\xb7\xbc\x22\x10\x80\xf07\x95\xea\x82\xf1\xdeo\xe9\xc6E:fU*\xd5\xac\xdf\xb0\xccI\x01(\x15\x81$\xe4t:\xdd\xa4\x0bA\xa4G\x15\xec\x03\xc8\xcc\x04\x14\xb5V(\x14\xc8f\xb3\xd0\xeb\xf5\x05bFZ\xf0A\xad\xca\xe8hw\xf9\xf2\xe5\xb8\xfe\xfa\xeb\x11\x0e\x87q\xe4\xc8\x118\x1c\x0e\x1cW\xc5\x95\xbfk\xd7\xae\x85\xd7\xeb\x85\xd3\xe9\x84\xc1`\x98\x15\xb9\x7f\x0cS\x89\xd0=u\x22-\x08K9S0\xcc\xbc\xba\x8f\xf8-`\xe6;\x94\xaf\x07@\x18\xe3\xe6\xf3y$\x12\x09D\x22\x11\x0c\x0f\x0fC\xa3\xd1\xa0\xa5\xa5\xa5`\xb1\x88\xc7\xe3hkkC$\x12A,\x16C4\x1a\x15\xfe\x7f\xd2\xe8_>\x9fGuu5V\xacX\x01\xa7\xd39k*\x7f\x19\xa6R!\xcf\xccr\x1e\x01\xf3\xbd\xc8\xb0\x00d\x98y\xb8\x98\x90\xcd\x00\x1d\x07e\xb3Y\xe1\xff\xd7\xd5\xd5\x85\xb3\xce:k\xcc\xcfuvvb``\x00\xc1`\x10\x91H\xa4\xe0\xf8Wj\x7f\x94\xcf\xe7q\xe1\x85\x17\xc2n\xb7\xc3\xe9t\xce\x9a\xca_\x86\xa9\xd8\x85\xeb}\xe1&\xad\xd6\x97\x1e\xe5N\x16\x8e\x002,\x00\x19f\x1eS\xdc\xc1\x82L\x9cC\xa1\x10zzz`4\x1a\xd1\xd4\xd44\xe6\xb8\xa9\xad\xad\x0d}}}\x08\x87\xc3\xa2\x8b\x08\x89?\x22\x97\xcba\xe1\xc2\x85\xa8\xa9\xa9\x81\xd7\xeb\x85\xcdf\x83N\xa7\xab\xd8\xb6o\x0c3[6mr\xb9\x1c\xf1x\x5c\x08\xc0P($\x22\xf9\x93\x15\x91l\x04\xcd\xccg8\x07\x90a$bM*\xfe\x06\x07\x07\xd1\xd5\xd5\x85\xeb\xae\xbb\x0e\xb9\x5c\xae`\xb1\xe8\xed\xedEkk+\xba\xba\xbaD\xf4Oj\xfd\x22=\x8eZ\xb3f\x8d\xc8\xfd3\x99L\xa2\xcb\x0d\xc30'\xb6i\x8bF\xa3\xd8\xb3g\x0f\xd2\xe94\x06\x06\x06\xa6l\x0c\xcdG\xc0\x0c\x0b@\x86\x99\xc7\x90`\x93\x8a\xbf\xfe\xfe~\x1c\xe8gs\xb9\x1c\xd2\xe94\x12\x89\x04FGGq\xf4\xe8Q\x1c\x9f\xc7\xf0\xf00\x06\x07\x07100\x80\x03\x07\x0e\xe0\xc0\x81\x03\xe8\xec\xec\x14\x85&\xc1`P\x08?\x12\x7f\xd2\xc2\x0f\x85B\x81L&\x83\x8d\x1b7b\xc9\x92%\xa8\xa9\xa9\x81\xc3\xe1\x80\xc1`8\xa6Pe\x18\xe6\xf8\xe6\x84\xf16qS\xb9\xdf\x5c.\x17\x16/^\x0c\xadV\x0b\x8f\xd7\xc3o,\xc3\x02\x90a*\x09\x8a\xe6e\xb3YD\xa3Q\x0c\x0d\x0d!\x9dN\x17\xe4\x00\x92\xa9\xeb\xd0\xd0\x10\xe2\xf18\x86\x87\x87\x11\x0e\x87E\xa4 \x9f\xcfC\xa9T\xa2\xbf\xbf\x1f\x81@\x00n\xb7\x1b\xfd\xfd\xfd\x88\xc7\xe3\x08\x85B\x88\xc7\xe3\xe8\xed\xedE$\x12A\x7f\x7f?\xa2\xd1(b\xb1\x18\xc2\xe1\xb0\xb0\x9e\x91\xb6{\xa3\xe7\x94\xc9d\xc8f\xb3X\xbat)\xce>\xfbl\xf8\xfd~x\xbd^\x98L&\xf6\xfdc\x98\x93(\x10'\xb5\x00*\x95X\xb8p!\xbfa\x0c\x0b@\x86\xa9\xe4\xc9\x9c\xaam\xa9\x02W&\x93\xc1h4\xe2\xe8\xd1\xa3\xe8\xee\xee\xc6\x8b/\xbe\x88\xa3G\x8fb``\x00\x89DB\xe4\x0a\xd2sd\xb3Y\xb8\x5c.(\x95J\x8c\x8c\x8c \x91H@\xa1P\x08\x91\x17\x89D\x90\xcdf\x91J\xa5\x84\xc7 \x09?i\xab7\x12\x7fd\xf8\x5cUU\x85\x8b.\xba\x08\x0b\x17.Dmm\xad\xc8\xfd\xd3h4,\xfe\x18\xa6\x82\x05\xa0t#\xc70,\x00\x19\xa6\xc2\x90\x8a\xae\x5c.\x87@ \x80\xbf\xfd\xedop:\x9d\xc8\xe5r\xe8\xec\xecDww7\xf6\xee\xdd\x8b`0\x88t:\x8dT*U\x10\xa9\xcbd2\x00\x80\xde\xde^$\x12\x09qTL\xad\xe0\xf2\xf9\xe8\xf5zTUUA.\x97\xc3l6#\x1c\x0e\x0b\xc3\xe7l6[\xb0\x00\x94\x8a\x00\xd01\xb1\xb4\xb8C\xa1P\x88|\x22\x8dF#\xa2{Z\xadV|\xacR\xa9\xa0\xd1h\x0a*|9\xc2\xc00'\x17\xa3\xd1\x08\x83\xc1\x80\xba\xba:ttt\x08\xafO\x99\x9c\xab\x80\x19\x86\x05 3\xa7v\xfcj\xb5\x1af\xb3\x19\x1a\x8d\x06V\xabUt\xfc\x18\xaf\x95S\xa9*`\xea\xe0AB\x8e\xa2\x80$\x04\xe9\xb8Y\xadV\x8bJC\x85B\xc1G\xbd\x0cS\x81P\xe4_Z\x11,\xcb\xf3}\xca0,\x00\x999\x83L&\x83Z\xad\x16\x22M\xa7\xd3\x89N\x1d\xc5\x22o2\xcf%E*\xf0H\x0c\x96:6f\x18\xa6\xf2\xe6\x851~\x9f\xb2\xf7\x1f\x0c\xc3\xb0\x00d\xe6\xcedO\x11\xba\xe2N\x1f\xe5x\xeeR\xe2\x90a\x98\xcaG\x9a\x8f+\x93\xc9 \x97q~.\xc3\xb0\x00d\xe6\xfc\x84\xcf0\x0c\xc30\xcc\x14\xd7Q~\x0b\x18\x86a\x98\xd9\x0cG\xee\x19\x86\x05 \xc30\x0c3\x8f\x05 \xe7\xee2\x0c\x0b@\x86a\x18f\x1e\x8bA\x86aX\x002\x0c\xc30\xf3D\xf4\xb1\x08d\x18\x16\x80\x0c\xc30\xcc<\x13\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb8\xf8\xe3\x1c@\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe62\xd2N \x1c\x01d\x18\x16\x80\x0c\xc30\xcc<\x11\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb1\x18dA\xc80,\x00\x19\x86a\x98\xb9\xbe\x90qkH\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe60\xc560\x1c\x11d\x18\x16\x80\x0c\xc30\xcc<\x15\x83\x0c\xc3\xb0\x00d\x18\x86aX\xf41\x0c\xc3\x02\x90a\x18\x86a\x01\xc80,\x00\x19\x86a\x18\x86a\x18\x16\x80\x0c\xc30\x0cS\x99P\xe4\x8f#\x80\x0c\xc3\x02\x90a\x18\x86a\x18\x86a\x01\xc80\x0c\xc3\xcce8\x02\xc80,\x00\x19\x86a\x98\xf9\xb6\x90\xc9\xe5\xc8\xe7\xf3\xfcF0\x0c\x0b@\x86a\x18\x86a\x18\x86\x05 \xc30\x0c3'\xe1#`\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe6*2\x99\x8c#\x80\x0c\xc3\x02\x90a\x18\x86\x99\x8f\x22\x90a\x18\x16\x80\x0c\xc30\xcc<\x16\x83,\x08\x19\x86\x05 \xc30\x0c3\x0fD\x1f\xa1P(\xf8\x0da\x18\x16\x80\x0c\xc30\xcc\xbcZ\xd4\xe4\xbc\xac1\x0c\x0b@\x86a\x18f\xce#\x8d\x00\xf2\xf1/\xc3\xb0\x00d\x18\x86a\xe6\x99\x00\xe4\x08 \xc3\xb0\x00d\x18\x86aX\x002\x0c\xc3\x02\x90a\x18\x86\x99{\x0a\xb0\xb4\x18d\x18\x86\x05 \xc30\x0c3g\xf5\x1f\xe7\x002\x0c\x0b@\x86a\x18f~\x09@.\x02a\x18\x16\x80\x0c\xc30\xcc\xfc\x15\x80\x9c\x03\xc80,\x00\x19\x86a\x98y,\x06\x19\x86a\x01\xc80\x0c\xc3\xcc\x87EM\xc6\xcb\x1a\xc3\xb0\x00d\x18\x86a\xe6<\x05Q?^\xd5\x18\x86\x05 \xc30\x0c3\xbf\x04 G\x00\x19\x86\x05 \xc30\x0c3\x1f\x162\xb9\x1c\xf9|\x9e\xdf\x08\x86a\x01\xc80\x0c\xc3\xccGdr.\x02a\x18\x16\x80\x0c\xc30\xcc\xdc\x16|2Y\x81\xf5\x8bB\xae\xe07\x85aX\x002\x0c\xc30\xf3A\x042\x0c\xc3\x02\x90a\x18\x86\x99\xaf\x8b\x1a\x1bA3\x0c\x0b@\x86a\x18f~\xc1\xd1@\x86a\x01\xc80\x0c\xc3\xcc#\xf2\xf9<\x0b@\x86a\x01\xc80\x0c\xc3\xcc7X\x002\x0c\x0b@\x86a\x18\x86\x05 \xc30%\xf8\xff\x82\xe5f\x8a\x9e\x05\x9f\x09\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xa4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xab\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" +qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x06\x07\xa7(\x98\x00s\x00p\x00l\x00a\x00s\x00h\x00\x0a\x08\x94\x19\x07\x00s\x00p\x00l\x00a\x00s\x00h\x00.\x00p\x00n\x00g\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" +qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x16\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x11\x00\x00\x00\x05\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xac\x00\x00\x00\x00\x00\x01\x00\x00\xe0E\x00\x00\x01\xbe\x00\x00\x00\x00\x00\x01\x00\x01\xed\xa1\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x01\xf6c\x00\x00\x01l\x00\x00\x00\x00\x00\x01\x00\x01\xa8\x10\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x01\x00\x01\xc3\xbf\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x00\xcd\xe5\x00\x00\x01$\x00\x00\x00\x00\x00\x01\x00\x01x\x10\x00\x00\x00J\x00\x00\x00\x00\x00\x01\x00\x00\x9e\x08\x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x00\xad\xe2\x00\x00\x00z\x00\x00\x00\x00\x00\x01\x00\x00\xbd\x8a\x00\x00\x00\xc4\x00\x00\x00\x00\x00\x01\x00\x019\x87\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x01Im\x00\x00\x00\xf4\x00\x00\x00\x00\x00\x01\x00\x01YO\x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x01hm\x00\x00\x01<\x00\x00\x00\x00\x00\x01\x00\x01\x87\x8a\x00\x00\x01T\x00\x00\x00\x00\x00\x01\x00\x01\x97{\x00\x00\x01\xa6\x00\x00\x00\x00\x00\x01\x00\x01\xdd\xde\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x17\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x12C" def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) diff --git a/splash/splash.png b/splash/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..7f843a121c31c021239d9bad4f5674e5275d0deb GIT binary patch literal 40452 zcmZs@2RzmN`#*ljCVM3zdu1miE1P6xuk0i{*)x0Zglw`BvIz+xBs*kNQL;C`>wSOL z_y2hO-RE|@kK?@G@7McvUDxw^KA+c#P*+p9j7NiqLZL1zDavZ1Q0M_D6dDQ#3!Y(0 z>9L0YV415Z$f7Qge{x!jli(R#Cq;c%6pD%z`4`P5`z!n%3foQTo*ecZHa0FFNt6A6 zFABwgQj(R{_V~8p`z+aL#q(V7*`M9?tm>NE(-}I=oLs{<2=ap8UZ&MDS6viw=X3Y5 zcOrDg#R#{cwa=Uv2~Y{LSZcWLs%o^Xj!un%b5mRQi8pV}h z-hDfJ&ZQ%2u&u`!NYfnmjB(kyp`RWmk_K}Fw<_!5gUC)%Fd_Apyp>EnCm|2M! zBany~I}F`N*9rN`+)*#l{%YI? zWWdH|3!p{5GEbf1jnF@JAYV%G{Qu_{|9-v$_kVxz&mW;&{y9E3i~4n2!vDX6AZLIP zAB=o0Wnw==bN%lz&{CAUumgjG$)w;931e6AloA_|!yt#i#_mzZ)IEo@{nx1?@AB`r zv1pY3d7i=?L4^0;Gwjbo^(oQEVZ-JA>rfV$#8N9m;*@aQf4@70EPCVrd2>7lf0wQ} zxA{L zW5o9$i}SCAt3t-xsz`{B_YOkBQ)fLxo%dv60ZRbe* zKB-J{&(&!8Ijrt@z8?GaX#X^mIpD~LZpxmLGX*b^Svs-cQFoBV0#)M7Tf89kzj{?` z5G9;H^L_7imyVtfLa3~`KoC_(N)zYkl;vuc;1Nm?#)gp7B|o<-Ns>@Xd|{yE9XUO) zQZLM+6r^0pDMIyAl^i4ROmq#$OgKYMI3t8v>64{^Vxi`SjQ}|>t~OFEX0v z_ikNIQO49DAL@G&yfh=xz^o)AmKDmZWbbcNqrZRAC6(6UBSBQ5gWqaG1P}NDm5JfP zjfYmymezzXk}-ge5=bYgp5lh@Uw;a@G#YhSMOk==F%SIQ+L`#S~D5I{Yt1_nbJEJ$WGhoW8o!at0f|m->Adgyu{TJ+!>SbfZawM=w zG%-a|{LAy~g_;E;kKj?)#*~1Q*vs7fgCf|V!diaPR_KF6eSS*|t$Xt|nwU@`?w&ld zawDGl0$!SQRjdeEba>>d<19E)!w>1Ip=9k=s;HVNZdr}zV|H?$o}!s*Y}}OiLNo(c z3@aC9{BrfUL(TZY|NL?AXf@fFTehR<`EX^~R;Z6M(a4HIjszQ7j(8!3zGdzTWxc2Ya)6e6%PX~Fk{Ee@35nP@Z+KPcAns%mPBlT{XoY%9k;K{QT4H zi>;8)posO%5}Yj=8JUv^?c}>hWQJUYgk|=^QCA&XNT%x@dx-r0UV>AHi_RFpit746 z{s=avL0Mnaw&sK3PnTb)vXT&Bkr0MCuVpwgyGFjbAI5KG%Ja$horNg8Mdm?XHHUj-udwG%#WG? z%UVk`l}($IqNu3oZ^qmy)}1?dUc7u6H2qtikNSE4in;lqs5A;wT1qTSULai-?kZzK zfLgJIY4Xx%?$_q^W_`>xQ}%DC>}?$#6(q71Y;4%Pyu98z&Pm_|1&ojDXlrY?|8)DX z;Pf^Fr}fsekqY}|7g0-rD{!Men)4To{M0{qCXpS+fJ1%lTGy?%qkEYm&ggV;*a?al zaBI?4i!8TsuzN~Obdn``A6yS2WhINim^OS@dFa_JK2fM#C?i7K&Fi;?zcJHD#>B+L z?ZZPCSMy>_Fa*;XEh#Ge{YRsq=0>pwr{(*ehA&FSY3?{Kb;nFLI5P=5evhvI?uF0$ z{*dw4uU}ujeZx!Z4=F9>6v|=KN2}yUms7-`DpR zPv$NpzWeOl+dVjd>>ypCQxpG2FpIC50VAG3R*0r+a+12mgx3^h;}MwEw`0UZ^`~Kp zaK>xB;&9FX+@@u_kIVM)Vc9dG%%5Z@or&p>$PqJ3G7w4<4{g z=!w(y7&^9wP?>n3g%Qz*&TT4)(`CC;Zwm%kR3%e>&~!)RT3RbEDzd)QOtsydSyH7j z7||j$I@R>~Rmn|A!11uJW8{ZtXJaNz{jVv+(aGuRQkhp#tEe+Nuc$_GFUs%~8S#WE zGHWKHKFwoJWvJ{Y z1kdk&#|&1^W*-_$&@SmIlZ>7ZbzF z9tyJ*z^$Axl=_pLB%eeY*E1Ew&KnJx`a2e!+HHlZH8E+qF1aZ|z4+_Zs>qrI)d9Z$*x2~^XSR8GdO9j0fduyVhqayS zq!Dj5<=(mXw!3ckM9_0E=dsI>%Xx^?wVdrwd#qQ~|J|Q08+^7(Lt=aBa3=@lFud9kptzCgT5F$7o&M94Ct z&0B8$s8KQk;Kj7zQX8eH3OTaZ@}+!xifite9e#JV%su6j$6YPKLUb-@KUK5%cfF&| zB$~=$Tj*@uPEo#RIJ6ygud3mkyw z7mwUH!aJeG_ipbc;%M?=A;pDKR7A>UsK-GhU+6MK7Nvugy!M2fGPq}{`%Y3S;ghA#+cAfe{rZVIy#z@pKqaayLxgdv44d)Mt*C% zpBKSjNxeChl|)cWOe)i!V)a@Is938#sw7KbA_)}JghR8EtFLirV*E79{j7WB!QPpw z7!XfYP)vCDBh`;*NyQtg2ctK-D1L}D(+Al9%n`cE_mT@qo+eb&qv9PiE#l@qywww=rB%o-akLeM5Hhj>W zdrXU8W36}tJpf7Q({)ceVQW`Co_G_aa)Si($SRM#ySv-^sTHMG4y0_gOp!nUDDW;q zw{CSh)Mr0%QQbq$LFs~sE6VGEvaQKcpjlFrD#CviL)xtLT1i0iBxLg|xXg?9f-n8%%L*et|pg-d#c}7pogJT7wXlY`(1PCd!W*lr~ZEH4OMo1#BW zY^dJk6isbgOdGIHk32xKtM}Xq9V}l0Z2xaLHFw`Q=vU|%xenY!YVO^;mrg8d4e-C4 z<`3uO<>j?getzo9lBBfXq}dy4W1bmK47qN1Zx2EIKnyhxkU)u!#=`5Bo+^W>aTT=XA6}pVGr#xm6Wm#BNZS2D+qoo3#%iDV20P=DK}G5wc9C;Yc1dtluVqmG ziVQosdBx(W-OXrurfDB+B05Xw&{35a4(>*n%UIdLcl}sHIvq=lOb6b7oWd zHZxo4G9hcVT0ykzb5&Nsfd^dow;8(NE(k~~in11GgnJ5i+ZXAb*W{u{M%5lY;G|e5 zNK|E=vh3|wg7gj8FKAvd@+muof;M-OZDEQ3c1Dg5?Xq++IaNZhB|{`5{>+ed&1c{B zJ+It}^(k8Qt8GvrV&sFJL_Ra0LH`19jAEC#$m8;NVTU6!aVv&82Aq+ZE15WNjMT$F zef)^wx0_GDFgGoi26hoP!T>5DXWZmG570(;nagW+K;11o7uJ% zlDI2xYk#hljWQEuWBg3HQGRMDVZBWq1uOy1b(%tD(?gzLaf8N0Lg3F(li^$Cnx=HcvPfUjkME*GS)0a`)SO8kf^=q)=#J6z} zlp7ivn)leYYKYiM+6ua14&H&L?v+0qA=($8`ps(%^bcdc4i2zlWj?`tt9Npz(1nBO zgt05?gS;(I@^T!;_3C?F-;=^u8kr^7*GA3w+M&GK3aRx+N)+Sdw7iLdM&ZE&ip4Dv z6)A=ZW)ggty>9cWAP?-u(wxr{+(klfm@%D-Xym)JsgEY(85QflT$rDy@l`;U@7;mf(Rq$pQ?wJsP>5L4`>C&yyFkHe`W{KtsJAEVaba#}xTRF{hb znG|DS5JOIGZXo7FUz?EC7TnMx^29h(x*cp3gbuv1UacJkm!VAH>b6{crc#)X_>Vb2 zZn^5=R)$}#uUxw(CqyGBLTep2T%=JqmwPuH@L_D*e4rmyiu{5l;OaHM%UcOCo_ewtfInLT>x6h&(?x=j~_cjAzeiJm+qb( ze3{&3^BzrxJ1!Gt;x#vudKuu4pzpqP=@RvoE7HuQ52=EnzoEfZ7o}zlY425a;-tOr z6Ob$^dNg7y6iqccHkRAclF3p?@tD%Nj<>5X{$}I!9N@0fQapgLI%O%I-v=L2y{)dQ z8n8ydIulh817AIFZ>#dk(2C2$oz(%odWUO3OkAaPxz*>G&Lxee-Cg>{gT%84<0Z7L z08q{NJ{b%_UZvocZOl}QWFQgod`zP}s8Ke?{Ww-2@Z|XT%h*`R%a=6JvH^guGEkrS zTS=R!8~N8+&dD>WEngvSaf$vygpgLm>FbMdMqqHp%k_iVvVBNz%{bkaK8z4(wBV0^ z!k=Gxs&B$e*dC3S)509XfU|+#5^vKC$qv^8iVYOz*X8AwWBiex?WrH$Yj%Gl(Fe4R zoIWcX8`35LX8}+-+ZVg^M*>t7GpP&KL8&T#*?<+hJh=KXUDKG@SV9Dw+SOJoGg;QB ze!Wr=%>q3vkpDj5`WRk4eJSS+_>S? zXoQJz_tn0C-LJLGpO@0Qlh1FkIgt-3@RLDFghz)NF;qg}GDLh1`NVGD)+c%L?OhPV z+Yc{$Evx5?lvpf(ppu&dT^ng(r+q%gybWzG-YVz(cGr~s@}Tp+G=B$g+o{0$#?X0m z0;Aa4X4{3I_|``<%+N#CZ0s5qR*v=FRD*t5N~&Pg*9MIV0$@eT#`$imGnx#ZdMYQdp81i08UV*2+>5M(;M%V ze6P@CG3K-=XnFo+CD@*YB|tqrQ&6-mipE(5U+4Qbxkj^3EZp=30~xn0243FOYxBF4 z7zcup_E9B7c#%+g^%;nq!JrN9n&4O@7~94+Soc>ZG~@OVDQ#@fJW%% z&8j)+Uvrx8M3phsf8wzkayb5QV!h6@pR2+BNSbbv!#|c;>Ghj8-QQ{-4^K{pH#LcA zKX}jqIbz>tyQ_}$o8mwrR`RJ-{ykaQw@pnnunz$80ug}x%^Tz9x6d|Bp37Vprr-53 zNPZ{1BFCv!q8*4Y0lXA+clr7Gwsv*^9D;f+7pnreo)bHj)5Z@Uj}}XW0J`oePc?*I z`_92ku+1yaP-Dd*tRtpG_vKF{;(yyOy)G-e`{cD#PKZUryC<*vV zWb=j(7j#B`0`i^#65@h;vwmJ|X1Oc3DR+pNA+3$|D?luNn$~&5FHUzh(1GmR`Ybp! zKazdEn@!NC&xtA5y&LC$%7ij=b0dd!oNV>J<#*CNms?VTn|(5~_V;MW|5Zxu%YM1g z!LNeOud<@_EBc&sCxz!N+0%en^G6Rv)vOVQ1+(yJwlF1Zng9N3XJ(y0C~atBIe zaEFuHPEaA=&|O!oXKqG*-s-)3=|Ap;=+~(Y)W+vjSjI8M6H_6Q8}J{HaGBzzJ%Mc z?MxW(!e_5m9?Xr&s{EXNZl~&P+57j2{DpT3?&| zvh&FoCGUp<>1I`WW;IijbMDPqd&Wi8mh$-t17rhXEgv&<%0fC9%vz+1N2(SVX;tD; zeQX*>B3?2xNBM?oJ(=3{J81UU>Y^HkX6^fT6IxN1=Lyo#xdPi;>92(@YI<5SX1DCpn0HN*eTRoMf)D)@Ma~M5L{GoBN!!Wn=0CvXc2A5v z6Y$6CT3Uj|{f!Jb9(!ZDNTVuPXY#rl?tFkjMd--SkJp~f1VYhWYCZVg*&TCL|CHXT z`t?~_+o_fPRCQs&HKyiwz(CVV`qBFO`cl);b;FW;86OYb^xgFK*zRQ$J<|FtiF*f- z-J3UrWjYlyhK5&n{sJcese@JhQ-2r566NyPVvAV>X?vr+TjRQ}XhP=U4W8GB1ylCc z%x@bTuK@B>eOf3Ax&p98I4@wZpD zos0cx-juLzfh%<1{2ue|+qXmqwIc(MUnRwFt@BBrOgpw+Mqx5*5OP+rD3SX3(L1zk$-@Q!Ce~+#%d}%K z>-G_2;*_T-8t}reOG*NVZDoCZ@5pLcK2c2BZ<`cL(8XV{86;&6aM`ovA=Cc+TK(Q_ ziH<02W#TG>3V`6S&E+gCETo-oG%AB|1kI%D-j`4vYyW$ystllUB`CfX!1p0ci(?eZ zZd~5+bI87IY;4?cw3?BAqL#>fI&^VL1W-$GeMWQRZD zPQ3KFpxuT0Av*gU!*=q1y6~`S7#c5g%b{H?dnDXMc3$2vXrBQR0QKTMV=qiqusX7C zE!Ga85x7r?OD&WriJruI$UaNYc8`L@Hc4tix+3%6QLXz3v&e1tA}XN*fXdQA+r=HG z|Fa4I%fTIQf7k?VpRp)?fI9QtYyHN?Y;)R}AO!}RuJKa}zwM;#oYvMXPf;Ms z&(Ak6=!ov@5=VJwKDrMi@66K);|54(FTPRWU^qe7Dqvo6%FV^$klNJ!(WvgASn9JZ@Bkzr%Ltx?iH0mt&n+f;=k8^s1_U2oTU-^cGdw2OVE$X%5o+KB}HMJD2814<1(Wzk1XXRl+Vf`&l$~NDM8Y zht8V-I<(JZJH7Y)NDpS8(c^#N-7|m`(a+ONaQoRG%b{9>ihp>}xQoL$%-QFI7)MEx zuX-)@N;r?aumTW4f=KezNqQpA(P!!E;dWZvXY*{EZ$uzs-j_IHuCt#CGUCC84*zZ# z;ez4ZF(rOFnp#sV0DY$)U#^cInX^n%|Jo_jEP?zGTvsQY8ZAQa&`#0r8jYf~*vjnX zz41VSj>xuV3JP4HkHnAx`_DZuN~XK6CDCNbj_R5h>=oIaUrntynm!|}7kH1)`#rm4e`2v;Xxa^#sRbHdV$EXX>*%hCBAKHS&y_P%|- zBYBZDX3q6A6*K}P-@P$}YU=ti{q396?hg$k=Prs3TG5o7_tr(T)Y4{7TqCqDV+DV{ zE&bU%J5=B1b#6Jj+&o+efGR>t}sce9QOC#uisI9IYQqB{KUQG|$~o zI>mwQPJh^r?X)q;KQ8b)FN)^_K+j3KEJwD>O;VImw)=g`+^$X0My+4U| zL^1}@s$`&kxrqTF1L-t84+oTGb)qOckrfwF_W+KFyF6)kxhXAfJv~|iUSXNIFE4-E zrz9h*MO4^|@#|Uv!;|kWf9`YQCOCNt5R&{!Cp6;3fjx>>dXR+29e2UJSUjhg5u?08Mj9hSm z74D2YdxZyL6qMIkTG60ux82R=-uwRe_3J~A@sIrC8BeC9Ueyzc?HaqY<30G5 z4D2eyO2c{wImpv*K8A-jrDiZZ`d>e7tBV3i(0oaDI(lYdN13_?rkRr3iX zoEK;N(MVn#4e7u2S5d)7UaO$+{<3k*6U+KKtex78Yj1eO;L9 z)u?oZj+JYA2<3pZ|dU%)p#M4ygAy|o#l1CnFl=vnc| zBjEftzSZI%eD~&UYHlWpkj3LEPPRuwfdZ~tAwltZG}uH99c>;s&K|)b@!%n!RYC5- z&D=1$574n#fTA6qr#IgL#+%7TS2oacBu=J=7H`iT5xz6Haq1^|zGr$tQ9t8?4}DR> z&>A!ih{GU%xWRmohX><^EBfn=>$j($L|HtF$9AdFoRx%>ebBmz>~Zr_Lw53@&IJ%; z6+4WmR50QX&I|TH3r$dEMW~WE-wlhM^GqlO*Yk$i|1_@A6)6*Y8uhl`dej1~89WCf zI9!wS&cLnwH*Xx&HmjgZ2CD!#RovX&L4{iYidq3smX#ID$(`*ZZMqFm_{j0LxLaKb z5}6;Go1;sfZ^ccCn|lAQZP@*)VsrBw)R$+oPpQBT!>j~oZL;|puf6bei}sPNP-NR- zR9ib&WsAT)=_0Sfr%#_wws;BxfOTF7p{kCWQ}xap-F)?!WC4`O`~JrxNDq;&^327B z^a84NY$f0=xRIE237`^=v+gvlzv+a|gP)%tO@n;va4BvHa-n(JTd<~pB=fGe_TgCR zgZ}SP9Va1`#F*;u!&83IXKxgGaxv9oU>ao%Si?(l0h7+o&VsrOxMYNICS9NYZ$ala6{o>pi|S*K8M%qvO0zdfh@?s!0Y~5g3&1BV z+&vgDM|>Fez-(ULt`!}xj$*qaJU$ypZV9`+n`W$Vag`E3wXsxAcKHJMEM5H z6p>dQ8qjXdx{qaNt$jt?`hTLTaWH`8a`aPGtRR&Rb`VJxU}fS=~!Us-Q& z?~ao%nBj?L5>{nDfij+%q~og3l8QW%T!Noyt5Z+ z^wvm!qch=<)d5fvrDd%b|WNUh-l`vIqL|7=S|+I*?-mySwEP zTMtrb{^7riG91kLi?*ppN3I8!Xh*V?4N&HRq7vV0`8R~uCorT3;NvL(ELnkcOI)4p zO}Ufx#VFqHqI(&W-oN2`{o2}c#u>6oP{a|kAYc+zPV{$?#;!=CJPE#h)kP^i%$GWk zxq1s71()`iYHiO8h)qyo@QqrO-77(KH)A~r5T5Ih7sK*mje&N zyLa#Ap&Mrre)8?Po0zWwf2*tWI(S|e&rdcL?%lim{%F84_0*R$B{D>B#`jzW~IZkP5(G-I*$OW&;63mApe8)6UZQebfdbb zH={yAwp5k`t_S!q4jH2>ra#?2)`iv&IH)na{8*B0^TAz-e}wkJ94p$^IQe{OV3117 z%7$lVqF=qDqZRjvCuxBY6A~7d$sc}e{}sI6kkElQMO6KC)#@Q3QPGI^?G>Q`2xQHn@Zi- z_%b*NK?m#>OzXe5zG-aVw(~dE^%5-W$-!pNLgzml8nhm$9N9uRf<}4lv`bpq$J)|| z-9vu+f|dg5iL#N3D+q!H^^@QH>Dk`IQsdgI;E|uQ!Wmgw!#_u+1mUO%GJwt%sFc5x z7I74A5*mqP&_AfEs-jSR{r!kxaS^h12p|(^OA$VzpSR7Ydtzd2YyliAT}baL4TJ;8 z!Vt$_>}utvc{Q^aLrTd$^}lug9E$0RMkR>lvvtFlLg{<$UC_lVDq=|Z>}=`cdh}be zsVi`axVb7SD!?g-*p&YMe_*bZv_F3Wi8YE^!;uj?*hW54z+^)ZtJTGFIag}nP1^^ z$N<4yZrb+h%xAtG4Izl-Oa%o5f?$UTvjU)gHsg#Pg|A&Ub_ks?5kMC(Disy-@;+bq(N9 zm0t81kt%D_hA;acTSy@`61jSU-b_B*MWa>OKfY zpSBNG7Cl837Qe2jGJN$0f}v*Z-o1MZ6K&@=LBdmy|04aVKoHVtJ5t3~js|YcJ?}7M z7x7QQ`0I9{g;;X_%tj3k4zfH{Xe@h%Ng^ehtHmR6g#Lo#?Xw~;3tD_fjBrJWX;^J= zD^am~fmQ;VGJJ<+wYum#86<^j$H~P77OUbCh0&e~TOmX_0ICm>i=b;k=-EK%oQ7?a zDTIsMt+fMQ;^}CeR9p1KJ_HqDX=w?fWrNQccZV`YUVmW?48*~ht)6IR_Vxq_$%$-o zxHrNQeGWGu=BZ45_(7M&AC+wUYWT~nSY{L!6EgdtA`uFq736T+Xu;8qaEB*HFm7@e zIJw@5*V>#qYtV#4UIIOF5nzJVL(C{SVe4bb{NeC0Ip9t-%)Q0Qz53>(HvQ4Io2@8ip){}qP>&mZ-E@GHR zk&;CsNAi!p=%!?BVzYiWSVchI-l*EtlHgM{;Yzi=n26JNd^xNWVp;|eD5O+;Va61n zoYW_>ZzHdqMn>X+ETNMYM~bt&b0d-w5Gtq}AyC%J4QlZ~n+LP{9!`--owju#q5Lmx z87k~7wXlv3X@bj_AA*G4Xr5Ywr}&G$>-;j{1#s>{WRAS5fmK9YwB&gFLD>V=#WD94 z6tG{ueEH&7Aws;1U`qAmX_5OwS`{*IzalPX<-(u=Ym1O?|mz}GPFK!h?=XpQ%+ZAE@#X;v- zj)1~wzDCSXZ`mJ6HIZy#RyEY~c^=?^c3Jy)qWYV&&^~l=;H&385e7d|mRikSWi>+B zC5Xl%M5F)EdgZbKxg1D6I^_+8g_s~q+P_!IWqcYymqz~)3_-qXN{I*zqr_LdwYAdZ z9_|BB(1NRGP75Qo#fx#{r z;X}FKGpOIix+b0>16B};Xt^A9t3cu+EGCN0VhRe$RxXjR?fBeHe*Qp&Ti>W^{?ioI z>QFV>*0@V~zj)-wk00$oV)iT^z4AZzMEL9StcU!hMd1D&`ROz)M^)8xd~&jIaAI>R z4%7rVx*)ySU%<-7|K?YIopkX&-8?;im>uH3T})v36m09@z_6B#u-8g|UFenSyytj@ zw0$aBTHuH5d2&5BBWimYBQ_-ClID5%DTwYLECm)2tH}OoZtnF|ArR%2DI$dbfTtFa z`a?cyJ^432HDp^8cD3A;Z=pgx9xo4+&wsedb#d*T8hif>QdMBA4R1l*0mAD6APcD& z|C5OWbG$aX=EaP|E!Szzn7!P)VR{#qE+uO&jp&^+-EH~#m%vj69Z31UMOCLq-4^qM z7n>7>t)bxjh`Q)s+d;Yjl|kMCwgh>Ut%Cy(4i(2fXmkiwjs|W;PYgP*AGw7OchCJ5 zdqL|!gB}1n0oWoHhV_Gb2b%8(Z;OeU38aJMlF+}x2hidP!OwCQM>z`~5HiyM2H^zdLOk<#Kg%V_Y!-YT2lP?ge}m!?gl?4m(~mEq zFd6}UCk&@-#AV{JCMi*#bY1TIK!4tAnfNpow3N{4-`zLrX9S)Q&N|!z%iIDe+}cuN z<;#007+(NqdPsG3^%XK497u3*KMBJw)z;P`CK(`JCfE9{YevFZZ$77zOGqI9MMe|V z^<`uv7;etL?34GIZuf_@Bpp)`V8N!qPsVv!4-gmpqaQX8!1`S!X6;f>e5L(yWh-Jc z1+*(@ea<-+ZO#x}qgnlSXiw$i)>02Rw;=I3k3G;$(KfSHLxkV7u|LhA?^(AJK=wp5 zl80MOh~hsyJY4l-?Z_wsl7tL+>k<8vZu-6NZS+8?-k}%$D{sB_?4Z^QTO;O($A!i3 ze0MX!W3cb<*OuKFv*N8Ky1Ly1P3pR2rgEWPC~uebeL#%lcvXBfjRSYcetk2-3#7`R zcbL-+fI1xo{EB9Yw)6y!r|YI7bC4qQ2OR9SYnkV{_X{HX8WGkAP~+Lz8REy&-?9+} zBL_bqPjJEzQd+eOJUN6M0ZIRZc@4qJYJutobI`_)YRZn9W7U)M!j`jk_UtTBA6_= z&;M+m$5KqZ`WXbBPS@gjII0xto1TH%v27bC8K;sJn3utM4_uWZGwFs5RfKF2i|?nU z*Hn)QdT+Km&b`l@`r=9vEix$nQLuK{wz`Cf`-&5oPDYtE$bnu6g&`X-go3TAMog=( z06jL{QD>X_j!T(lKOgXR0N6k`n{l6h!o77jR-OGhLgWz=B1T@&xF)F^6J>Xt&yUu? zlLf{%{##E@GC-HOVwwmU+e8vLg+-wsHz6=+dfdQNx=EOl4cqU+4tX0CTE_a$;3Rt zjIWy91;G|S%dbll;|Tzam~DD0=)RRR0YJHCek+Qn$+mlO=w0_K&($j^wmV{o8?D~K zxR-~i#^y4BbGzqJw66p`CnqP626`f6larJ08VmSJLuS%O z$@GgyU?>(D&Vo@16bcRq0?qpc5wuLtEn>7$TOqiEFnh(VGWy#c8K{6w4pUd$xl&W} zuP%*fUpvk)$^kTBH5S_W5U}g+?ryt7{Sxq93J)HPy=w&fHqs;_or)AgJ05?rW=W8a z)9%Scg`uvxl-`vKT>{x<&^(Z)?#q{eu`*o?TifswUs>~&ssQrryALsTuYo)Q@dO|_ z7EsRW*UWw!QQc8VmlLFShTN}KTr+Q$#*BNi|NHKYut za1%4`9oKT5b!?&f`&E()dYDh2h`I^~gl)nIuc12$bbz4jpcXj@NEmzEYT5C8)e%p- z$8(8%e9p(HBjc7MBJZG3h}A;ksP}&Lww7QkD@-hD^4BDk!3Z1l;Q%I}%8j2KAUvlK z&5iJ-2PN8o!#kj7fvDj30g~24NMt*HTf1I4U~ges-s4@>!%pc8 zOALO9yU^#tT|6OhsLvSF*N)Y%5?xs9!ea_pMlTqb1Op>9D`3{Ai|C1?xA`O6mz>A2 zSMxx^jDzmScA}{4@uyi8s|P^JLR5q6A3O>?ZKp0qZo_hNQAc<|0T4b&iwM@>yU>e+ z?wxL>tQ*9DGx_%6C1{D~okaAo-d1DfxFn5Sw-Tb!09g*OA9$V+juSdP%rI&WUl&sL zHd%GwD!6g>I?bn&=LY)Z+}zM0f#-COW~C^&;+mg3&NNglBV$QG zZ0jG;p%BG-s>|U!_2o-#n^>}FQVDZPM8?w?LK$mv?C>&BaS(fj{dC=B;G6*7yMDX0 zL&6BRpm9Ar0PkofUO&ZTD;j;r)dp!bV zeyC^Z$Dcp);FxkfNfZ-HYcz>oCNh!qz$KA#neJsvkM;xE>e@9}?G1NtX-NYh)j@IM zVF zl0c~(DJ4xTE@HRn(JM4j3k}rdJlF)OH9420_{E`6 zK#ORm#F5ATswQS^rOF=$@!-14Ey6T4iof?2e52$#CO`p5L(F(Eq^5PDXeF6-_nBzg zoW2@{6Vw`TOhQuy(vPdxJ(vcJnk~|?D;!sWA61qGs0pCOl-whFHHb#0AtiVTjoc}5 zhPk?U8B48r1a{5c4hzJb=ingmdr!5p*mcCZrm6>OamR`St3Wc0cSLpRQKe|^n-y)c zb;^ak{f`&Gie>q!=W9OKydfax-ih5PEi2WZ>;VfnI5ZIf0ZO>MQBp7S<2Z$?aLYKD zjRo@Rv-M63Z-DDHN6&i()i&B?sWSf7)!85UQy-TLbVUsARkOSrWK5e=UiOF97e-v7 zV`A=kORz8zgUBlQ3pNDI9e{BN{1TA!JwUJ^!NCAs3?IhwNKrZhXaxagj8@8pJwSed zDQRF}0IAZ*?a$^+VT!!+^Y`yA$OZ^jhYSScR!pE+eMR3cz~l7)dw$SHfBEudFrGf2 zvgctR5=eV0_htr}vjO96Ma5+p1PPhjwCQCnuDkq8`R!lm8I5Uy(Sx2|cFN>NXgEBt0u zx`EyGcW}*Ga{9!DTO2JDUQAWGy1JU!okoWr*2(?7vaYVq>`&y>b6{%ay?(8)XX*W<|vy2U5usUZF|HZR@lq$$X>%!`*;jVK@Z6M7uPATz&q%k2I)sI(G*b zoXo(x+5jVB8v6RZefpq!0tbN+++n4^YVoEN)~s&&cQi%<*DN^eEd}_Ws*2Y49TGN4 z{{|i%nN9^YOrQ5VOlPBKa&W~^YJnj$^zv1wUY0bz1Kxz4qXt6`Tu^+f^x7d66CpSi zLt2V6#r#)tOD0-lP)R72)ulBTQGhk(PEN#-Tg#ifezF_cT!k=&=mrTGwlb)xS?*Hm z{PuA@or#jRai2Od0I%yU*o1ZRSNxt<4U4IMlAp-lXS`c;yA}>$N zUm2*^z~>91i;S$i)=R|ZkS-+00?!yv8CdVFYdpX0tckDDTxP%t$SIPmJUrR$4(Wrn z9L<4|$@X6ESuNbF;ORz)ozYS4K;OLnawFW!80gVCDb-eq%pIEtR9R7tUc~VV8is)D z^&5GcM!m)9Jj7oGtziXABLl;T7Cim?RZ2Vo(PgG2i7Sa@I2bSqMr9dSrp*ZeuVL1~ zJ@ix0i$%7Q_bgKlzyd_a#!|9Z0F?t~^*r`G7_EY7K~R7|uK>5yM@%ZCfa+?2{VarM z2i&%Q3RCQ`3(O$F!M=h8OEdA}0HwVq1&O8?F9JHuq(H+r;?eE+mJMYJ3}teP1c8yiTqFxax=L;@cu09a(I31|!Gzk|Vg1T(g9r$KIoX~;-~XtS<4 zVor&^SsMhJb_oGyP3K#HRjqACh&wS~LK>7=3MsqEF{RnWv`vnc% zy`FjBzHhyiQsVeV2i_9eQn84!B_OCl(FhU)5S^pz`#2qrBvE z?pSw_>1_OYw^5}vyHqG5z2w2Fk0kw_{f(NT>gUa%iaeY1tzQ-^_6igl0xkvdf-|fX z=JCkYOhrR4T$2qSukhXP)g#yZQE9$b_rr4L1?mjnh14I;BKD(38Or9M^LRJRs1e0G03N<^}{iA~L}Evzdh# za5|umQ+kX5IC1|o6S2SVzW)-KfY)Poz?i@U1PYdc&duwZ`7kvUMSL$Ag?x>1JBLZb(Z1&Ro$LXbGBjqpkfwgzu+=77T-I^W-Ou*v}bK<6<7 zKm8(b=Fh&7id~_opEhR5zz>2cR?&(}XpI|+G6U8%s#S?x)Z1#r#WHGXf{_CnB{1yM z^-_@;nRuNr@kXqJ<-4&%Yj?%zZ2IUEaIA8+SkjC>9IH0XIv`Vw2b(?)$8IVy^7$a% zgJ%M=I_wq8r5I0u~I(#UsjM8`JeffIrJE@Dn7h_@Q@!&rb+~5v}YY-FRQ#3*)ZU8!$8syi^PoA(*G9J7)wn9ZOqpcXG5l z+#5?-OcmVp`&B$&V~j;x+QE@Q2}Vrw0_mALG0A7W>mI9_ zdoIh^v52?gpPZ(%P5il-mDkr_tc`=eihL_u1-}ip3|w6CNCZ&rFfa`YJlt2EJ{xEf zfM14iWg9cfriXR_SUvz>p%d#}2}}y{ip(!=&1c`sJ2l*2k8%T;Z6VQqD!1Iup$~FzasBZU6?cP29uWAu>#l=+fivHa}m+OVq6W2Br^(h6Sv06Bx*iK|VGFI56%cH$t6iby0P+ zsn5Mv-%S4o>+y5;NJPJaj~j4x)oNk7mM4o+;wX|Oiqyx&KnH|U{*k~}&HCN8D>J@a za%)ItvAKnOl_2N^V-_vH>!yo-7sI`PHV3-Xf5aSrm$W+zAZmf!3T zu2@C0Utxfyf&>h>^Exm}2duguj22uPzd!@4!qbQWdQDeKCX3&&L0|Ts8*Zh`wNw~_ zipPK1R`#aJu3OBry|*a`{FZ!LiF;mWB{BKRn7NWTTZbgea)FI^nG!x_Yunb^pS{YH zl9Jc|qyOUG%_2cIVjR`=2X6>M7&`7Mxvwgp|I}2gteyT{zN^d+%rJbK2j5DVUp7#r z6xWs?%R%y}sv=2He4bL5yIBo58^*b5#oTPizYrc>k=eXKw4mW76B$7+7<21sWodai z;zIs5Jzbo26{;-=>Okp$<-#&j9}p|}g)hNOAk4QYCVo^jhH3TX`{Z)SK2>i93nYmw zd^`u#QxKTPog@2f0dRqcgDfKm(!ebM+iLshQDCp-hxsoI$6hDY9Wd?5&c%f&qwonh zut&KjGiit@q zOdc@O!~mt2wTqGIFf1>i14^<(y?3`qS^DwS#AucJXyA5$(^>uY*ug;_KmaeFH(n^< zMMQ#&iCF;Nf|{Dz4DB5tE!eJ*I_$SJ{#GYSeerR3bDIY%WjFBA!(YFm!_Z;bn3wV! z(-pBCk2Vi+Lb(56RiKoSl2hQ=3NcsYxD;)ttjUH45eaq1ex@OzHWbG3Kozlf{XE5R zBd7Y-hr0K-@l2HQAS23 zgzRLNq>OB`6Ox_K6crH?Wn^WS$Ze&CC@LWl83~oPy;4S0$oL(XzMt>+kKgm^^?9DB z5BL2Z*L7a!d7Q^_oY@gzE&M)imUT#iZ)f|MYQLvkw$Te352FR?TY?+U7-YOUBZ~Vi zGB~KEReMKv;AFvtxgA_yUXm-j{Ofk}HNZRo=#2HL%T8li?Ze>U0e{ck6uZWt9`BAI z29oQwTRMil4XxuZ9c?`u8^ad*-b9e)X$7oly1ddG=1k@SB@#7&XyBq=VKyYlJOd=v8 z(7OX@stP!S(6#m0XZXP|;RyNo5q3~E@V7x$8hr3%BvahCdrbq^jFq>o^_Gy6VxUFK zKuEYQdc$+jj3gB`&W~IXZXP;qd-=lTk;mzYzcLUtBd=thYn41^*x` zk*l`U!F9`M2WG!zP!*ZP|VX?$$Pp2a{<}=FpyHW3#;(%-CQ$Ht$&*Sr^(HEJ*u} zZ6dgfmV1lXr>7Er?rahV+P3aV{%!!Ct7~UqVAbTrj(EMRCGp-@R0_NX6q@ByoBmAB z`+vSTG4`->Pg~LG@iQp;%~(a&%tZP z)P(dp^!7>l;1@n3AF#97S1o;oOOTn!C0@BcIcpblceR{_Ly6;_My+7GfCYxEok9@;5bL|Qg0_V$_|^pfF`^0}^XD837h zHPrPMMGPBpo;A4^D`g&bcUS*={&Y>x;^nJXn_WR-r9H0k%}FDVMO1k!AbwO%A*_<2 zjs5I#^0&&R?-eG#bfKoabWjNE3jT8J5~zgGu;k~ZM=Y5@Y&uyVS#Vg~aZkbBvib*j zXE&0%t>VtY`$S??^rK;X)^5*$a{Nx`*rkq<&1DOS`|v0c?4nB5^_&yrPCCiq7G%Qjc-{sAqm0y_aFZGcHG8fPhcz2$)2&+~T z5UAPM|NV7KtUAs-uDVdO?tMqrrM+}1JoZJINK&i{9caLY)S@?gbotRR57;qPfWq-C zVf2ChTC-KLE6e9&rMmfIgMKHdz1M5)`-_i!kmKFuZ^bLw zJap0OE~Q-Ck#4g*W!R7v2fzEJ<;sx@=T6;7yPv~)O88^(A>(I*Eh>|SFs%^cVUGUv z>7m7w#vToBWxH&F6`TR!CVH0a+MA&grM(P=P$(03HFzm5a!^t_;97c=Y;M1e5b_xQ z2m(W)$J<-t>-*Z-WKW6Y7Ze0;(dCEn=jiDt zS{(@!_liNn1VsRlu-L$>@Qp^AzD)q`<|MeT7d>B@C_zrfyT`x5^EpF)t@?W)@65HSwe@# z(VA@2UF^DTZl)8jE=KPwDC=P)8ewED)N?6sCJSttl=i$pU7S$0O;OQt?CaC&-f){w zYooL`G7gW4czd>r$aeA3>VN2G^9^oiSJH4+iy3d){Xf{ov4g?qKOBG!HW^Z;O`L$?`D6 zt_{h+n%;Ew>6HzwRNXgKRYB7>K>t0P)fEQUF<%T$I=tpbRsmW8j+`Q#L2G8NO2BTZ z>P3xfadJnN`8`Od+*wEjo2& zD{{?m7`o_~c!*+;Yd(kvLiBP@VLWH$H*bGi3GQB(@cXG6HoLZPdb)6`weEB`c8K89 zKYBoarRr>Xv+Hxh!k4lu=V9WK5|i=I-Qe=*xU`(vgAYgGBm~*jv!2>W@(3e&P4!dW zj{FilNaO>lh8j6$;Zu}b$~#VU5dj`Nh9qY>_c{3~J%&pSJ0KY()z*%>_GsA?koLJ`ccx($SbBlPrUtN7Jq~F^l*cutXsEEv*dOe zr^wSYsEv9Q&4dr%VpDVPGo4MJ^(SYLr<}GR(hbJ_>n}Vrd01q6YX=7fB_ON=B2=E3 z@dcS*-{xknx_ACr&zSJas(kJC`k~km#+AGgHgQ86Cj^^4mD6jFJlt&Cw%+dXYwk14 zGsiN;VlBRN>dZz=n%l(UMe8vS{ zDUaNd?ZmM<4w74DULGmsdUPu-76z;jU8hGC7*i({aVomwWWVP8@01VX6j0J(VQ4SY zsWRCl4`%peHkb9)(!COILqdgmD;X#efeWi1>C>cYM5le97ZHfak7QzZL;(V7Hi!Ch z3b2kW)+4>mtI-JGvTITBcgg58g3Qn}aV-%YQ^Xu5RMs=l)r{E{I;g9AHd_b<)%L_O z{u~mf_t|phI7&v+vO~s!7~FV&70u%JE_yW|dD^l5`RiVemA^{$%#Fus3ZRcZ=+kJm*ym3aPknrBD~+|mFmbPA#JJ4HPlAa~{>%VB zP=(pqvyk7?q1X$Ye$HR$J2{{?FZCtESY?84*If7NjPcJq(<#d7EC73-MQ*7Od&gJ$ zY4eqVIu16}n)vde;U8?K&x>Cv8z|Z6O7`Uhl%Z=uAMDmj)uQ&VYxe7l+wph(C(L{-ZwK$mOwFT;<+b69jZWo?W zfPAphXGd&}pf#&8mQLpO&b{&Q#MDzPFl^t-n+#iFUU{jAT~i>+Gh*S?nKRDl$v92i z7~zSmE%X{Bfl4;9vewyYqNkRqFX5>o#no7{)=}PT{YQUt5-(AaEF=g;nAd!~J@+T! zJraiEM))ltf1>tGY1*}D&cDdJFHMk2a;PmrIlATMw3OmDu|$0+MhvnKP^#K;447)H z^S^bar>Fm6F6j(BhxC{|$=#*lu33JF>L(Yvaf<@S?WXw_IE)D4!}9iK>zyiJZTH=J zwCp(F;7v-}&_K5IMvFqVm*4X(^re2*ZeST`f0g;(w)jn5@5&wSA;xDXTSuGwPiCSz zB*6s&#eXeiobXDc5HnY&oD>mKc_Z}Rsfa*I=yafOQT1f3Lh|Qd@K&+Qm3}5|9kR@~ z=t)Vb=_amrZmojA7Jyzr^B?h&K3k>vgwmXyA72hT@(|~f=f0l%bAV(W$ndB?lLh>P+zG;WQXf|f>zE*V}tzJu5I3H0gjj6p^XtHn*q&hLMm zZtYFpno#>E)m9pgZUwAC`Z?@K)qn}0)C$7MQoHts-B@vFn|F=zwkMbVpI|k*8j~KDe<2nJtq)f zK{~ye)j%>X)U5Rs- z8uNX%(P_cWYj;3~Lu_~kCmp-1LkW7!7=1p+S>J`CCpO3urVXg%wH+iYJ4utXx2nzjDy++2y)v3m0SIxd zbib7~)c9Z;bh|6Zh4udhshCDgu_Px{pV4>0RFqNQB`>YgX$ir5>h?nF>9Zl%3|aeI z&V~u?ztU>(N6RG~^8MDEL( z;*7ev9ewIrT%1!CCc?3d!YGjl5d-EusBK}@1V&Rt!~6J&ZSAs(GYhEa9T5eodOHq8 z;?~f`$SN*27mhdBQK~z39%@T&>}KlEGs@J!rnx?-p{gLxq@fW1jfcm+L=aYtEn8B{ zpeej>kDnXaPmCQOX#gzXs3TPvhEd_G39lNn&}?X~NlF?sBO$#C+D#7#4!# zi3R@jIK#s=HBBG&(%i9qL}GVc~_B=0h$59Ou~Gko^`ENrH8iv8J}6r(cAi!C z9nhmU&&f0qyU{U%as{|7{=9B$3r6QZyuV*G^^rUMels`^!P*wr z-&%JvHhz`mT^|A;mA49 zJ07a8r=@D**9!5^59v(gY&oY_a_BQ8`rsaFzCD-kD;xi^OFmN+}?>REO{*i;15E>9Z)&Y-qErG_HA z!D9qRTzVkk@V!T%2AEH=S=Mt_=b5ZySi5eW#+fsv@55kkq*u;&HBx-rxYgtz>io|C zxB%m}?E1FTvUP$%ZLCU)nz>(0C%QI$itJg)nc8WPB=robUMO-9(-gJ3_(@r2yR_q4 zk3>q^H?avt4Ycl-STpQ5v|VCGMA*|qVI6^6K_|k_fkuQi(?qxAQkD?U)pB=vwYsW4 zmYZhzuUlKs!`umBIa2d`YGJ9kVw`yg5s#p9<$-C=K+5MVczPmt>V+`}avBW^*BMWm zAakSb^w^82ODu2iE&1l0!f_x>PdK1=eP>nD=KbItgKQU0G=@vuj(cI*ev=T-)InBF zM1adEgrba!pBR3m+-FD=qty+J0j#OQ#~+{Z?S#_!WX2+I>6g>%vDb*YGory_c+#LV z89l9Q%@<*IDP^aovt%D*F(+^5ZdvVGrVS$N$AKhJ-PZWt2vu_KWz}CkK-T&~& zGeVu|=E-UvB#zyP(OvijuwQ+KGB1%jLItWvlymV{8*|oYhu17Sx)2fSE}eYgnL`!G z8^lyZQ*h(xCgr_U-ztw{VxfknH!-jQRc*5}`xo>Xt7o&z7>qyuvCL}iLZFV6d23gC z?7EndiAvOk;W@m_=fSjS0HYJCHSxYS9xT|vdWk^d;Z&9d9)Zg@K*WV+M zCoz6nb^EPNY@=ABSbsaRhE_oY;MCBn`$&|qSWPbTpN(ZmSh(w5 zu5n!h4-6i9Cm>d7{rc)yY0FKDa7+o7C6H^>%E;3%=g)z=M9wX2J#B4m&8dRvp=1f^ z{Wdm?m!IT4A1q8@VH@i?)aid8LS%m|niuv9Ke* z86jZ{qm)@na)ivgcW9QBR!R@Y9J=;tgcNfi|6K$yZU zZQlRza%y9a*VeUC=8Z655Tz5i-X~+I#Z=X^{y~|;g6sH3NF! z{80ITU9;Oii2dG=(>pNtgNOg<(W8KpsXP~dF4i+#eZjZkTsz&mm^wlrL}%3T?A?x$ zMzd6PcNb`|qDfK6qzp(8DG+z#485B$SQ!RYp>R)}n!-q1?ZRK5csmwn2$;c%I zGwYS~R!wh!ER$0{q{|P2(i2d;82j)Vbo_ZmVlgZ*ZiuVhswIaEm!Iui>xbDRgvv~f= zF*Irb>dG@u20iJqmvQbH1f-UtkG?ZWO|WIHNqc ziH}1;JCcZ|YDT%Nff5)t@S)wfuy2g?#7o)_n)x1hU?M6FT8k4uw z5eH!uOP60>GM7lCMjCsrIx;aLweY1+0%lg46?`teoq5RaY_x`yKsHcpi|`1&AD*htk{-2GTMMypkDvJ89UPH}AeJzue z4@H>`gOk(GRtK1{n)*R#tkqCcbZ-$7z$l_>uMYGAPzVvwu8k5CF{id*I>y{oo@y;I z4Q74TFj#x6Q#fTq03&4CkA}N%zJLV32Z}+2_zZ-To6+D zw|#w**8cvQ9CBLhFYR1$OG7_?XdzpuFS_0?LP-mT zulZ=kR0Z-9!C5Gs(E>33m>WJxbErcD-aTF!uAlj;4xcw15t@Nth9hWUqK6(9I4Nu&b`Zeu<1HFB zEHvbH5>ieP=N1{R8|^chF)*D>ZAv`Yy=}+FxYMYZ!;3y3XMqvNaWhrp7b9EH(LT@8 zj?WI);XmhX)?;XM^VCd#GI5n}QC(CqB5q@*-_hA{k+Wt!pMFHfsodW?e|Z996&b(* z)!SpAs7T}s$ z7)IM>diYV?0LhIJ6mCFWB@&;1mpbepjR~a)+hoVQ6-A~y_d02;NMRpczUW?Ka zsD1IiiI+1d{%KWhPoWZf{nvv{nu17sh*B56S$IzHkL0R2D@>PfWBHf|ETV}?Z^Nrb zd9CnvAWlR}A_;!^)OjCzfnVjT=xCAJT2=Tz@;TbK`ym8+gmDDYGwJc<8G0lbF*U9| zltxp~A$fAqRe829RG`?N?y8{(&NQvCLeNskUNN`1gECly*cof6(Nq!NR8nnJ2e``> zgg!ol77pMaawwI-6>S{&*IIZf*-v~tT+of(^qfR1yb;(S=&h{ksjBhmfpJrJ^p4Rg zNX$iz^3lE{dKaIkUu8$kJwxTr`J+N%&>?2$0C9emZ8Mas2g#0f;xSD{^u=eFsJc{Z z5sK)O={5TnxO5ovglL~Pv52u@yfKF_p{Z=-8ugLl`&h!)p>N<=4(T53I_V z|9%MKC#_%KU@@ecB8j$ zO>+OCssmN03Z%s{pTru1TKLvUK0@{_}Pt{0twoS zLtk%ryg)_jDg0trYSYB!J(OvfiV3PH!o*4XQ6GDE;{()LdD2TkS2#@JEl6TockfRM98nvWE0}&3w+9V+(<*T_v_Rx*MXrbJ0EM@Tc(W9Id zA+2r}nv2;?8y;M~jbf)`g!55gqD!*E+rQVhKQc|l$|Dlt=^-7}h^3b8`F-~1IEh4E zZ6H8iX?yiEPoyetoF}kJoIyw#WZE zbFbn$7CxUYnz_owKZ~&Y)Qo(HdUp=hUR8A{6O%t<4t+#hj61LD)a%`3xI)d?2Mj*~ zynX0Y|FK6QcKLqSJ7%|wP<(?Axq7rxBf9XSARq<$3aSCT&pG>+11XesG}uA++LL=H z^g%Ex6qSJM&k1XZ;QT@;n;cOGAx%+&GM?$|j%8{%T!?fBWTuHHIk_!not3D6a|HfC z#%!rMWjt%Q=Xb@R+v3YhKc|S zk2h=m?!wC{+T6d7(UL&8qJB%Y(XK%KK|0py{y+6(SLHK=3I^6zny;ft;G55FcL%&N zhm88k^kcN&p-M#~ew*mYKMi+Rz~?P>yA;Lu%Ap392U>EN8};N1ic=ln)(9G4RDvms zKbZe@-dZ7CEm40j>vmY=kl_GW)P%60H6G5pEPmQRBpx|GG4w)%<@@QsDZI6?C?FdE zBm~ea1s4m-+KP&bOf7bQ{BgRQ!*ZA-U}90cr8MnPQqx41g#?i5O=o8tT;zzc2>hG2 z6k1;Wu6P{8B5-0s`$lV8V($APRd5KU8f|1!n~m}Vo&hc;87k3`p((TiUqv5RPMnsA z=ZyY6>k@aVtw-{_ufR9*z!Wd+x2Ip7saOWa%2PYl+`4Vg&r^McC~IAn1zK~_)X+MC zQ^!_1S;jEQa?-W&O3Jtgds2{;g~ z^mt&?*CD4K>bzBNv zCGAEREN}OpueBI>5z|TQ`fk+j>z`l6R#Idopp{G2ihPYe?XBNf2EaF1H=C)d+1wNh z6bI~uHA+mk5qtp`L6uwc$B;fk$!sb{BJeQSv0PHnwUOQ#WWmVidZv@3i*l%qBfp$b z0mIiD-CPLc+Q0t(p~>}qg6o?T%}Wa|B;^@|Z>gk&^>iNj(;6HUaE_>u?dQ18p(vgE z_lDA-(YL>PPFs>2k$_ginIDB))V8n0b0LP0>xW=GWI=ZVx zpFhO%PXmBBWMub0+!3?OtQS(Ou!n&gU(8x!S6 zz~r?~_%oMu%{;K#e5_)|6}|z)k}DYfn4RAYqO4~tr@WwwK`NCok;Ol+>;Lt~>0TuB z+*+>8=EF_z#d5+!nw?Yck2-uUV0Z}B>g-cgY*zPOR>E7QCXRW-pV-J zln0Ir_^pdzy@$RqnKp%*XcZXgC?|`gO>L@u)^o52J+fT|6;I60-#okEtQP4fgJDYD zm-w}T_N~65ckFBGc4><5$wRJAf*bACFc1i=MX3C6a8j}28G(Y7Bf)>Y`9kz5vB67p zjxZ%e)de9vf`Y>X8^A$Y39&C8Do9vKk$yY2HI@lUOl}ucoNcPv1|B_R?@r(<6U2b! zfaj?Ft_)K6cb`xqtyr}2M)DO9q@h(seL2Ktz-AaBozp(d(Qwzr^&*i{KoGEvzRQyLHF0<4nl?7^vIa5Bx6{VXp1!ih0Y@pZGe(yIt!Os-goi;wt<+Yx5Osw; zJxC|vNp-$u@B#?d_&b}s(yaEv%X8v){n`jl6oGybJnq5sCs^}0T8_lF4}7 z%OvTufO3ef6zH3pQ^#|?hF*+h3(SK|$PpegbYJiXy=44LA~rA)6IQ;Cw2mu?lX1qG zVC}$N>ku}?x0(>|nu+K93#ybm0NIF5bIDl z*EIG1QF0I(@)e=XILWtfjqNOW^T0|9HJ3IX6jCuyK&Y)LJL6xffsPq|=_f@zSnR}0lnw`Q>R z(U)UrG@UeD_2wm4P}>HTy&h%>FknXDSvkULLqZJJJ(+k0025^`oLjnwb%U=ChX1rL z?&bV|mKY)daE%U()*^o42Y6Qm4jb540|y_+$@)7apw0*O3M3IVNS5AE99<5RKyV0@ z=BL_2%M66sePGH2*G&X(klRE{(#7`*H^lxa$O}(?H$E^XZObKf2 zl(Ix-wTZ;^%}Y|}n9GwTwPu14$E=P(NrPZbdf{k-Tx0#IkX_GVo_<)9?MY8Xh1vdi zXZ7?mixd2dGyqulAA3ZPlLaEXN;G(hC1T~muL9_r~=_ z0+!TwWQHx@^SL(a(a=|EJ*1i)iKxIdL41Y@;CoiPAO~U==re zk0pYv79)^AJ;5;S@Q0@pj~ta>zfDWHZP;EpDbI83hi1A1b=Qtpti?j81?&~!t;~YY zz;EH<^`ORM*M+_FGzh;^7FRR+UdS?i+D}`ZV58X4I#pFnPd}`!H z-xTQ6))aj~LGSavmui~c1nZcp;RGZP0PqK~PeWym#;4NPP5f%J+lPxJe3=H85Cg19 z_z4l0%Kw>4i-U*p$Uz)5kfmW73Nv;rkT|dyqOFBVnL?TsD^~Fm57YAnsU<%#EU+7n zKS9WY5Q%G@@^22=dvaz>VJO(<%coIPGB4kKJ}(p0LQpB}0%i_kognc9Z6dt>o8hUg zsasxaV=4;7W3ciA?KI+HczhI{S%^QGMGDUhVv`3u``!P{g#l;O3dN`dXTYly&I9b# zm7fK}M>}Oj^_2o-)pOe{Z*vm=00I-0W11Cx+z%xEI#y2$r=6`Re9m!QdTFV|inGM# zZaw`Jl9duSHoJ4b_ClK>>uzo*;7ixmVivLoOkP~f;3V`)UAQ@dxt+&334;w|=CqJ9=WokM@3oHP= zBpAkk>+nfPNzXnF(AOEAdJ2e_@Pi_WIW*ZwwLU)ZqF?lTrKLAC|x+_uO3I`}h? zvFfBnJYIb_`w7sKdrB+kH}_1G`K&h2UsC~h%q|>OgwrH8Q%_F}ad8>>1vW9*$7u;j@Bk50eR9io zM}S4K{Ph|Zbn-wt091+aiIkI&qu}xvNj#R`7av-U(2u2yG|4RgO@eZP3`a{PWb?$= z7R5O1SK#rX`Br6RM(^7FNIZxJhztTVKKIQWSGuNmk+g{d00{JQS5DKzZd%_;D(UA+ zwR+8>*WrDMl8O2_qX9LQ!=`8&V@#xX8H1!SH``J90&OXQ7sAJl8dSBdB?~DoDfb1M z=}{1oYWdu`r#BgLX=C7N63c8Xc=?uZ&dgB|#CT7|y^jC=D<>n_lHE)b)ld%(z#4S2 zQQd@rHsc^;b$@yp1p+XxEq2Tp0qWUW*+Xw~mf2|Re92v|Q2(s{j{u$;5h`zUW{I? zC>zVWJ^9j~^V}Uy2>}PW>mC&@RO#o2e{KY*R+6zSV23-SZzNT7K)hvVga;5eTq=aK z080sw0as7e0YL%9@FdH6f>ijx=b1DU(SbFc-uS>cGgJ?5-Jova^u_>8@vw!xHht%9 zD)gAlb^Cy>^;_!iI8PZnr49}R3K$*^*_^}GF};DW|LP92kjBJ#4Tmw&%NJQ0_GOq& z&6Ei-jICFEVk@lzq~|?cjf_<1y-iQ8rVxk`6?MaDW+~1f^8@<<_S^Y%(?d|iMx2=g zW*Y4yJau~~;m{7b$c_`8=l+BX*>eTuL~fvO9K zyc*C7mQkofBoS#a&oA=AmVjUfUY#;LHDqQ5^i8FocIMZr;WlB3PSO(+n247f53?Y9 zI76{ZK0{8Yfo}+U=v%MdrAh=Ed>M<=)zl2m7Bun5+wB`H9QNe#V^j^qjIkSi7No`l ze*>@5oyE{&lF}1raL5hOj0J-le+1XrMR)G*IQQ$@TjJ0#fjjCYGr_QJF90ttzSEJo zRo{EHv6c!Mc2)N6)@}XYH-G<~CyRg#lAHK!8yj*zYBiUymo3Fem@)5uW8eB{@w)6R z6EP&eT4!RB6BB=T!8$DLi0i5wkbQ^)VFlL)WvL^7Jmv<$#lHu0B2ey2Vih}Xy~gyy z1HX$x2x%=6G=f&b4+&4e0k|2U(;}-QhAsA>w>g5?2?PS#4C;tZ4x`%H483~&G2e*X zwm5z*J4}#79*9B|85QL2q*H}fKEgWV##d?6<$kCW<|bkSYX~roMHY8b;Hab3D^35$ zg9zcf#?P-Nxd4Ja=z$0Mz1npDS`b_3BA1u!xSEPm3a}d#x==TTkxGduH2D9#3gUAZ zxJgO)nv$^E!nV6!MqaKgQ6Gf{pm_ok`tOpmuiz#5h?VBe9t&iz6M8RdhrMmWDo-de zpK)xEO7Rv5V92Wf^QUK+W1CEjY@M{PBo zK048D1@ZNGxEgP2on-Ny$d$0e@K2m=(7phGya8q(RwIIE_0Jb>3L1Is*AqhX(t5(u zM|=wUUF-v~CIL=h=<@gTrReT-E^~iUE(m$epG2HFeJ`M+M|;nEgSC(iYEu~ zCsMfhk9uWrOa{Zo7tAG*c=0C=2N!(L=`t1SjH8v(W-Id8I%md!_lvWuoN^A92Lv1v z!GGXM32fZ-GkVmTd5qsqP|CSmvpfz!gMu`hTr`wSPoEx`TXp~PS{x%l5SGZ7YN$jN z)^X|_o$#>@Q_UaaSJ$chM;7AUv(Jh2E7Nc0NoXxYxIuzj*r=D+j+-VSYlBgJhBQ}X zl$};yb>24iCJI{tGO#ib3j+uY1b+ktZ0kR0mOkM9G-+5v!XajGo>&|% zO+EvY6SD|7>w4gz+mYvQuMd*Y-(wD0?U)>N>`nHRiZX4suZy`jBNB%OZ=ufAnu-^-Nw`# zu~E;I9dkGV3c`}>A7=$L9H2f=VbQFVv%SFkvw>-$kBT(i!3oWWVDm^>6;coo!4u;> zZ`t-#jYGa)8AUodSXfvv#m&BNsx{7M;uJ;Tq*M-5h(@Lw)^jH}zh<1@2O!3+X>L=}OI4WU=~9wh!;eSY~p{GZCH++#1H5Q8!&CaL)cqCdPDtVbtd2lLy*Oaa3)jE_L z087*vQ@p>5RRq+YqUry>soC%XCA(MlsU1S-1+sDIUcc0)Cjy>xJ?pQC4^C%~VwThj z(0!{{OYtxpc|42z{k?f`5~YU*t6AQ%*}$cFUtHTgEkdn7Gta{voQ!NF%BVF@Ve-Q z)ZRjQPGO4P9OeXo0?o!Lp2%@Z3K_TCmRjzs;-X+5!rui|7LF(!B}zcf$H8xjy2fB9 z(WdsY_syh*vSohHKFEchA3D2+KL)&9a^~S)uyzTREcLHWe3^FE`L`Z0*hUl;y?UXx z>9doevo+dX-j`DI=QfK?Aug@*x%kOF)zT+!N z5m&hluC-h$ngN;#4iZeXEMcfnpvBLe_BC~Epg(E>H!5%hz~F~D);Q*SEJM_)yPU0i zSD{|U=4{PI&eMIrbXqH&K5ZROX@9(i=*knh!BqKWfv6oxXSe=V$q;kA*fW-ksnMa@XO^5WT?*qkfI3c z+$y_r>GbioIqYzHOa_gANr(0l7!T~-Gv71B2cr|t(tY}InnW_n37{E{IC!fg^{?L6 zPsFG!eT#bsi>)2kX1(=*cW2FXu8QjY?i+&%eEiFT>L*W>-|SNE0Aac%OOC?x~Hpp#T%deR(}B zJIwz@?-Ta5K_CgUW#+o2Ai{FVwKZ&Qn3pv2xQDXI%d7b2+*^+aoQKbA*7D<%5$g$C z5YoWVq9<=?g1B279v@Q}M99Z11Eg8qiuoNQhkiKeJAcHhta6+&T00x^;w*LtC$j zjmZO>qtDpJk2EinTDTUuxbSje4=+vHu6oUk=%7=|q&I|Xb1Sv}rmTBPeIg|%?Qkw$ z&1O~#W=-O|nW8U6_bVB5UprgWc46->`2)ch>9{xXd^+;*%I1$SAceya>}lb+L}KX!BqCa*UhaxP)iltdqeQcmH>z2d^WyKkA4Xuo|u`zCLm5wdiCi=OERq%5iq< zCxzpNTHKVeem4D>P-||Bo0ROxJ)!GGJsMuh$jfez+Gh9iR?zGpGa6k~Vq<}ees~<{ zm^onT(sbwIygjIc5z+j}G%aYjbvX9ju_8SjP#uwkAm2Y03P|ofcEn5Z%2v{+|43Gdwe{)?Fh(MsL z_s%ZlTSySOLqKKV<+08qznq;@;r|OXQQ9u%zNuqwSu2HRG@sz9TgJld07lRQ zA+wg@ZEF%+sqys0?o3v|Ibw5SZ#haq=>qRO_|p&R!tO4;ILF-OryAE9Q;f7?b(z?$ z>ExC^NxdRa_~Xsz^jZ|X_eVdMnf85ab&7xK0_u3^puJq`nqB*96Dx^@SjR{xAmnEb zDPUQ6^ zdL^0>pu&K};W00`Guo!uHL8AI9xC=mmgh2}58WfRvj}1ZTns66?h)o@ohoJ~wTr2B zu@3P-#1{gHumb%XSRzOb_0dqs47C0GuZ=G^%xkB=E*pkUBEfgTJSzP3L-0?GV@mI( zlN4aCh^aE#HGAGMT%{LyP8;j!n&~E)JPvbhq-4M?N#_if-Nes}hA_+a$bn~|hECvULp1j{I*YmO7ni4cXqL~uI+Jd+AOHJHJ;5S*rAdJ*h^Khyk19|6rI!<}JS zmsnX|FVhr`4noM6iOXgProe+ES%d2^JqiNH znZb-0()g=YyK3%@J0_zXi24Ffn@&Ym=YKH)NrJ|b2sHnZA>mDr_+sb6lWRyjt|=7M z&l<%{-cT-E#GTq+PaxyOi}0VX0N~}iO%(Al8qM_yXrV=K%ca;9RD*-V9SS>n~6!c#U=vcsVzICL89i zy|~>|ERmYPppx!p=cy(0eCbIAQBR>P#zRIGMBs2_Dh-eSCQDPMV9OFh#q>S| z&6+^6CY`V>>{tLZr1J@s2pq$Tz%~gNSRA0`L--nS@jU(H`jY^eCWR_ISv-JA3cEGv z;hKw)~^L%*y+>?54c zBsV4^8_?W9`$H1+dmcxV7^~<5f`gTv*jVXE0#|&vLV;|skFm`QPaW$N1r*L%SjedX)r&)K1=FfvzaLU3uu|3i_BQrFmuXwrwLr7T zz{D-BdGzSr`gu4kNG2Gig07OIoM36@(O8*vZ03Xn5Gwnv=8Ns7m+3AP~_c>?qR zL_(nMtoe;Da+Dx6R&YFdS{UB#LK+0958MdcFyW|7>-o*K9nS1^hF1|>!WfA-8@y3G zZnP92726`gEMHe6xU}Nq1!#v6zsPhp(hCQ2ge{BpMgQpR8~K!27RkW4}8P%>oQZ@CN{r zr>#LkhTHM2XIcc@U#@a+%QTk}>t2W86J4OsYJtXE(~9ac9yF z9@|NRW>agp>c3Gz@LugwZ{3l9LUNSLWNy(ZZr``V77nn`0OPALVE(M_iD&d=lnZVd zG)-WqaC$-y-7i75iMeT6@0UZLJC>q}m-~$h{=H!Mp$CRtg1X1~W zd!|lN%8qo`iQ2ejNRq(RV3IDQK!cAoMte!xybZ>6cpqrwkk3(vmQ=>mmFFV8fV@to z5Ka#!z$~b(=8X(qL$eae3|b?oLTjDQ!(`O*_Vsb4 z2*;G4$Cu;$?2Gs_wta1oO63+3oKyWSSmcL5|8GZFs#>Q`Jy4q6U@W7*czwvH11>oZ zGFZXHA`6sqBzGuLP#Pn%!=VhS75)jGyT-KXo0!Qc9#_|G7^lFFvf+Gp(9Pdl6j#VB zeMSF{fE=@xC=C(BAhaVbHbfzWAnJ}w3N!m{zN=>>OEU_S!yQx}tQnZL;RWGIptwR; z?cXG7{9pV%{_Pu&2h+Y69gSY)7G_q^oV`vy44XVuBU9;K$?AWnDWml+oJN=|ppNGD zk*z|!tC*e-9@)CTkdhEVc`aL1QnowOB+OqBS~IGj&0S0XJD5z;>c%MHzrDIjMwA;P zI{_m?BL;FlLxFK>+U=mVKf71AYr=UGHa-g z^mm79Jkxo~Y<}LP&)$Yjs&VpM1NK1bc6g6n0G~o|4?r5D4Yh*DS!a}7=yMrn`51bE!BII9yBv93`6AUXlH&qDJ*hcW#Nfii6-0hh zg#-VEkHRkxluh^Y;)jv?;NSf3-^#Ta3GmYV@6WUS-_IBExTx2Yt&J~!R1xcWSoiVC z$&buon&rQLS#I;DuYBpmCeQGdP!#3o#FqaaS1P5CLEwM>&(f1!y4qAf#=;UFJkNg@ zbLbcUAfEcamk_X0O`7(<&z|E9N?Z2d?f6P9$5UK-@=cet_Pl6Z_usdu(3`5-z5Vaw zD(M7t@Vx(hHb0l#Bi+`=NHSTq73;plitJ7f#{JYSC z$aU-0<^Q|1XRZIe`X(Wq9^``mUCuUgZ%fy|^tGF&7lZp Date: Tue, 7 Jul 2015 14:21:11 +0200 Subject: [PATCH 0483/1144] switching components enabled in overview window --- QtPyLoT.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 020a4d31..e0ff36d3 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -33,7 +33,8 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ QFileInfo, Qt from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ - QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen + QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen, \ + QActionGroup import numpy as np from obspy.core import UTCDateTime @@ -145,6 +146,12 @@ class MainWindow(QMainWindow): print_icon.addPixmap(QPixmap(':/icons/printer.png')) filter_icon = QIcon() filter_icon.addPixmap(QPixmap(':/icons/filter.png')) + z_icon = QIcon() + z_icon.addPixmap((QPixmap(':/icons/key_Z.png'))) + n_icon = QIcon() + n_icon.addPixmap((QPixmap(':/icons/key_N.png'))) + e_icon = QIcon() + e_icon.addPixmap((QPixmap(':/icons/key_E.png'))) newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, @@ -225,6 +232,35 @@ class MainWindow(QMainWindow): phaseToolBar.setObjectName("PhaseTools") self.addActions(phaseToolBar, phaseToolActions) + # create button group for component selection + + componentGroup = QActionGroup(self) + componentGroup.setExclusive(True) + + z_action = self.createAction(parent=componentGroup, text='Z', + slot=self.plotZ, shortcut='Alt+Z', + icon=z_icon, tip='Display the vertical (Z)' + ' component.', + checkable=True) + z_action.setChecked(True) + + n_action = self.createAction(parent=componentGroup, text='N', + slot=self.plotN, shortcut='Alt+N', + icon=n_icon, + tip='Display the north-south (N) ' + 'component.', checkable=True) + + e_action = self.createAction(parent=componentGroup, text='E', + slot=self.plotE, shortcut='Alt+E', + icon=e_icon, + tip='Display the east-west (E) component.', + checkable=True) + + componentToolBar = self.addToolBar("ComponentSelection") + componentActions = (z_action, n_action, e_action) + componentToolBar.setObjectName("PhaseTools") + self.addActions(componentToolBar, componentActions) + # pickToolBar = self.addToolBar("PickTools") # pickToolActions = (selectStation, ) # pickToolBar.setObjectName("PickTools") @@ -351,6 +387,9 @@ class MainWindow(QMainWindow): def getComponent(self): return self.dispComponent + def setComponent(self, component): + self.dispComponent = component + def getData(self): return self.data @@ -408,11 +447,26 @@ class MainWindow(QMainWindow): title = 'overview: {0} components'.format(zne_text[comp]) wfst = self.getData().getWFData().select(component=comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title) - self.getPlotWidget().draw() + self.draw() pos = self.getPlotWidget().getPlotDict().keys() labels = [int(act) for act in pos] self.getPlotWidget().setYTickLabels(pos, labels) + def plotZ(self): + self.setComponent('Z') + self.plotWaveformData() + self.drawPicks() + + def plotN(self): + self.setComponent('N') + self.plotWaveformData() + self.drawPicks() + + def plotE(self): + self.setComponent('E') + self.plotWaveformData() + self.drawPicks() + def filterWaveformData(self): if self.getData(): def hasfreq(kwdict): From 173724ee5a47ea8eccfac2e0660c45597a3409a2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Jul 2015 14:27:29 +0200 Subject: [PATCH 0484/1144] make use of the __version__ variable other than updating the RELEASE_VERSION file --- QtPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index e0ff36d3..048e08dc 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -692,6 +692,7 @@ def main(): pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") pylot_app.setOrganizationDomain("rub.de") pylot_app.setApplicationName("PyLoT") + pylot_app.setApplicationVersion(__version__) pylot_app.setWindowIcon(app_icon) # create the main window From 05d38f583f8743ba8ecb88aad5bc9154ce14c9fe Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 8 Jul 2015 14:57:22 +0200 Subject: [PATCH 0485/1144] corrected names for the output format to write event information on disk --- pylot/core/util/defaults.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index df4fe5f7..1fde1c3a 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -13,4 +13,6 @@ FILTERDEFAULTS = {'P': {'filtertype': None, 'order': 4, 'freq': [.5, 5]}} -OUTPUTFORMATS = {'QuakeML':'QUAKEML', 'VelEst':'VELEST'} +OUTPUTFORMATS = {'QuakeML':'QUAKEML', + 'VelEst':'CNV', + 'NonLinLoc':'NLLOC_OBS'} From ec9840839ef5e6f4d666db6d3bd519f9767da75f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 8 Jul 2015 14:58:42 +0200 Subject: [PATCH 0486/1144] preserve the occurrence of the authority id in the filenames for write support of event information --- pylot/core/util/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 7b409351..9fddb15b 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -36,7 +36,7 @@ def runProgram(cmd, parameter=None): def fnConstructor(s): if type(s) is str: - s = s.split('/')[-1] + s = s.split(':')[-1] else: s = getHash(UTCDateTime()) From 0236a19db1d2fb1f1940437e9a4e0ef73cfb3fa7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 8 Jul 2015 15:00:51 +0200 Subject: [PATCH 0487/1144] [bugfix] the type of obspy.core.event.Event objects resource_id attribute is of type obspy.core.event.ResourceID but must be of type str; simple conversion fixed this --- pylot/core/read/data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index dd54f51b..bcf841d7 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -78,9 +78,9 @@ class Data(object): if fnout is None: ID = self.getID() # handle forbidden filenames especially on windows systems - fnout = fnConstructor(ID) + fnout = fnConstructor(str(ID)) else: - fnout = fnConstructor(fnout) + fnout = fnConstructor(str(fnout)) evtformat = evtformat.upper().strip() From 591e5a71106765c54b60e2c28e4757ba5ce9502a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 8 Jul 2015 15:12:35 +0200 Subject: [PATCH 0488/1144] standard output format should be QuakeML for saving the event relevant data, moreover the other formats should only be written temporarily for processing purposes --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 048e08dc..0449c3fd 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -372,7 +372,7 @@ class MainWindow(QMainWindow): def saveData(self): settings = QSettings() - exform = settings.value('data/exportFormat', 'None') + exform = settings.value('data/exportFormat', 'QUAKEML') try: self.data.exportEvent(self.fname, exform) except FormatError: From 1bee360bbb0ffd4805d70f34b633e104231a3fc6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 8 Jul 2015 15:14:05 +0200 Subject: [PATCH 0489/1144] use get methods rather than directly access attributes --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 0449c3fd..0d84fbd1 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -381,7 +381,7 @@ class MainWindow(QMainWindow): print 'warning: {0}'.format(e) fname = QFileDialog.getSaveFileName(self, 'Save event') fname = fname[0] - self.data.exportEvent(fname, exform) + self.getData().exportEvent(fname, exform) return True def getComponent(self): From dde7d73d2a072d872eba75b5405e54fc3ea2789c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 9 Jul 2015 09:17:10 +0200 Subject: [PATCH 0490/1144] Improved text output on stdout for some control routines. --- pylot/core/pick/utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index a97057d4..5a4303bd 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -490,6 +490,7 @@ def wadaticheck(pickdic, dttolerance, iplot): checkedSPtimes = [] # calculate deviations from Wadati regression ii = 0 + ibad = 0 for key in pickdic: if pickdic[key].has_key('SPt'): wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) @@ -500,6 +501,7 @@ def wadaticheck(pickdic, dttolerance, iplot): # (not used anymore) marker = 'badWadatiCheck' pickdic[key]['S']['weight'] = 9 + ibad += 1 else: marker = 'goodWadatiCheck' checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) @@ -519,6 +521,7 @@ def wadaticheck(pickdic, dttolerance, iplot): # calculate vp/vs ratio after check cvpvsr = p2[0] + 1 print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr + print 'wadatacheck: Skipped %d S pick(s).' % ibad else: print '###############################################' print 'wadatacheck: Not enough checked S-P times available!' @@ -679,7 +682,7 @@ def checkPonsets(pickdic, dttolerance, iplot): # these picks did not pass jackknife test badjk = np.where(PHI_pseudo > 2 * xjack) badjkstations = np.array(stations)[badjk] - print 'checkPonsets: %d picks did not pass jackknife test!' % len(badjkstations) + print 'checkPonsets: %d pick(s) did not pass jackknife test!' % len(badjkstations) # calculate median from these picks pmedian = np.median(np.array(Ppicks)[ij]) @@ -691,8 +694,8 @@ def checkPonsets(pickdic, dttolerance, iplot): goodstations = np.array(stations)[igood] badstations = np.array(stations)[ibad] - print 'checkPonsets: %d picks deviate too much from median!' % len(ibad) - print 'checkPonsets: Skipped %d P onsets out of %d' % (len(badstations) \ + print 'checkPonsets: %d pick(s) deviate too much from median!' % len(ibad) + print 'checkPonsets: Skipped %d P pick(s) out of %d' % (len(badstations) \ + len(badjkstations), len(stations)) goodmarker = 'goodPonsetcheck' From 120f2743d205777fad3b0a0c5beb5e4bcabac69d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 9 Jul 2015 11:34:41 +0200 Subject: [PATCH 0491/1144] preparing call to autoPyLoT from QtPyLoT --- icons/sync.png | Bin 0 -> 4266 bytes pylot/core/util/thread.py | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100755 icons/sync.png create mode 100644 pylot/core/util/thread.py diff --git a/icons/sync.png b/icons/sync.png new file mode 100755 index 0000000000000000000000000000000000000000..fa9ea46d0f8b53030dcef46cd0687903a765845b GIT binary patch literal 4266 zcmeI0X;c$g7RM`u074KVtDsmW;t>_H2}lS+WDzhyL^g~M0iMyaU@LM7G?l|08woi<76=$ngDMTrNT0%1OjLr5eot% z1WYaz12H+7d)go>09f|&ViVv52A#qd3A~t-8ZWs(Y|I7#YM@-qWT!(g$bz^$VF0%O z+D$CT;{;&iy%_|CI2cOfg=a~iZCO#V?5uRQ9|s$_!ICPc7!3pv%mn2EzEDb$2Vkds zDaLs6F&+y}N#OJV>~vEJjA$@eB!R$nUPMnef$RgW^YJ42`uLIvYe9xc2!=r{kmv`J z2n1h{M4}MMlyziq=E82U1gR2EDkUZ)Y^I`d7JyBIVKD`dm&s&aGP0LQ!o?H){QU3) z5}rizG%7r$nL?N;_Y_KTlNQq+A&`_U;fZ0MvFM~HlO@^<2Vk+rb#TUePdO1@Z!{esG($8r^=?rKia=dZbm_ooyrZm%;pHnCdrX(FQ z0&lGP^>vNoN}QbLKMBJRWMzU|_`D2A0>&CQnIn=((|J?Hf3t{(SleJGJk{Oj7Fc`{ z*VqJy$A%#eEMkjNQz6KU15J?tsT?+Cx}QlfGiYdhczQNXiqD(mLE$rn+yJcHlLMtP z_wr%vhX1EiDt?kNpEpxwYW8axD*ks(OiO2Nk1=QZnQ_~VA07VlS2xq-EM9*^IK}0U zFrMu>Ut~6oIpcX;b0V58n8!7n#+>myt~n9S7R=+CO=He@9@m_RW()ooF3aD}t&q@o zc9I!Sr4gsr^#A}WIUEAL{B8w`O+pL(4VnG){&VlcNbC3iu`2MdE?=(R7hbjh0H zcFpaDbqk-9^ah0@$bM(>kQM_fojs%ucS6Pfzhv!GdX1{9o{wF-Dc*cG5w-kk&|!7; zR*!zgMAA2)BypD%oX4uANJswL&q@mMh*lB_K5V_wNIn zZancVSbyMFX}_UoWrOmI(gDTMoKM4}_`Es3Tx?)J#VX7%I8N@}Gn4TK3+=jFbT5;07df@?Fmhe|#+ z(Qpd$lDvP3#9w0F_0UZmN<4*Q?wWtD?{&v!LfIWGqi}fSoV$Aj;)xK5b?oo8x!8I31Hg?D< zt>I>}!6NPmn6Fo<`m5pQl-HH(RPQ(3A%iB!$fA@8KcfA^!Tw)kAU(~sxxuNP( zQ4gpoJ~6Z`w#pzMb4ou3v`vf^`fCPWBE3B^dFETX8|?gXkw3R+1aCgvIr}1?^z@pU1P$czOZt=zr}wbc>h zblh$HxaG$FXSY8s+;F(4hgS49)W(fkn?^f+a5v`Szwc2FFz6YVI~Z@{4qBt{E-HDk z+2_c}jm{_LpWeIu1BeDz0rtR?CA3}51 zaWy%0fBQ!HAe+lEMUM8psQ-!;{k%%>+M~ir8W*Z>tX}3Y!9{Vb!GG08Zro+3b}eyv zb@jY0W{)tBn>gINI^~2Z@)af9%3JaFrw`=ZY7_8v)J0v)br)L2xvNdHuO8%zhn^GFIs9HUjH?4Q6qvgzC-m}buVej$Kp)D5u3y_5_cfXzddk_&C K6>@H4it4}N8IhC# literal 0 HcmV?d00001 diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py new file mode 100644 index 00000000..4fdc4943 --- /dev/null +++ b/pylot/core/util/thread.py @@ -0,0 +1,19 @@ +import sys +from PySide.QtCore import QThread, Signal + +class WorkerThread(QThread): + message = Signal(str) + + def __init__(self, func, data, param): + super(WorkerThread, self).__init__() + self.func = func + self.data = data + self.param = param + + def run(self): + sys.stdout = self + + self.func(self.data, self.param) + + def write(self, text): + self.message.emit(text) From 398a25f902a600342a120b172918cddbbace57a1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 9 Jul 2015 11:37:03 +0200 Subject: [PATCH 0492/1144] non-working commit of autoPyLoT functionality in overview window --- QtPyLoT.py | 38 +++++++++++++++++++++++++---- icons.qrc | 1 + icons_rc.py | 8 +++--- pylot/core/pick/utils.py | 51 ++++++++++++++++++++++++++------------- pylot/core/util/thread.py | 10 ++++++-- 5 files changed, 80 insertions(+), 28 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 0d84fbd1..8ffe3cef 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -34,15 +34,16 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen, \ - QActionGroup + QActionGroup, QListWidget import numpy as np from obspy.core import UTCDateTime -from pylot.core.read import Data, FilterOptions +from pylot.core.read import Data, FilterOptions, AutoPickParameter from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ checkurl, FormatError, FilterOptionsDialog, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ DatastructureError, createAction, getLogin, createCreationInfo, PickDlg +from pylot.core.util.thread import WorkerThread from pylot.core.util.structure import DATASTRUCTURE import icons_rc @@ -147,11 +148,13 @@ class MainWindow(QMainWindow): filter_icon = QIcon() filter_icon.addPixmap(QPixmap(':/icons/filter.png')) z_icon = QIcon() - z_icon.addPixmap((QPixmap(':/icons/key_Z.png'))) + z_icon.addPixmap(QPixmap(':/icons/key_Z.png')) n_icon = QIcon() - n_icon.addPixmap((QPixmap(':/icons/key_N.png'))) + n_icon.addPixmap(QPixmap(':/icons/key_N.png')) e_icon = QIcon() - e_icon.addPixmap((QPixmap(':/icons/key_E.png'))) + e_icon.addPixmap(QPixmap(':/icons/key_E.png')) + auto_icon = QIcon() + auto_icon.addPixmap(QPixmap(':/icons/sync.png')) newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, @@ -261,6 +264,17 @@ class MainWindow(QMainWindow): componentToolBar.setObjectName("PhaseTools") self.addActions(componentToolBar, componentActions) + auto_pick = self.createAction(parent=self, text='autoPick', + slot=self.autoPick, shortcut='Alt+Ctrl+A', + icon=auto_icon, tip='Automatically pick' + ' the entire dataset' + ' displayed!', + checkable=False) + + autoPickToolBar = self.addToolBar("autoPyLoT") + autoPickActions = (auto_pick,) + self.addActions(autoPickToolBar, autoPickActions) + # pickToolBar = self.addToolBar("PickTools") # pickToolActions = (selectStation, ) # pickToolBar.setObjectName("PickTools") @@ -570,6 +584,20 @@ class MainWindow(QMainWindow): else: self.updateStatus('picks discarded ({0})'.format(station)) + def autoPick(self): + list = QListWidget() + autopick_parameter = AutoPickParameter('autoPyLoT_local.in') + list.addItem(str(autopick_parameter)) + + # Create the worker thread and run it + self.thread = WorkerThread(parent=self, + func=autopickevent, + data=self.getData().getWFData(), + param=autopick_parameter) + self.thread.message.connect(list.addItem) + self.thread.start() + + def addPicks(self, station, picks): stat_picks = self.getPicksOnStation(station) if not stat_picks: diff --git a/icons.qrc b/icons.qrc index de3a672a..442d4f29 100644 --- a/icons.qrc +++ b/icons.qrc @@ -15,6 +15,7 @@ icons/key_W.png icons/key_Z.png icons/filter.png + icons/sync.png icons/zoom_in.png icons/zoom_out.png splash/splash.png diff --git a/icons_rc.py b/icons_rc.py index b87c5eca..33603df2 100644 --- a/icons_rc.py +++ b/icons_rc.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Di. Juli 7 11:48:47 2015 +# Created: Do. Juli 9 09:55:07 2015 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x9e\x04\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x80\x00\x00\x01\x00\x08\x06\x00\x00\x00#\x95\xc7f\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x17\x12\x00\x00\x17\x12\x01g\x9f\xd2R\x00\x00\x00\x07tIME\x07\xdf\x07\x07\x090\x11\xbal\xcd}\x00\x00 \x00IDATx\xda\xec}w\x94\x5c\xe5y\xfe3w\xee\xf4\x99\x9d\xb2\xb3;\xdb\x9bV\xbb*+\xd1-\x0c\xa2\x83\xb0\x0d\x1cSbL\xe28v0v|lp\x0eq\x09\x04\x88c\x1cl\x9c\xe08\x80K\x82c\xe3\xb8!sL\x5c\xe4P\x02\x18\x04\x085TWm{/\xb3\xd3{\xbf\xf3\xfbC\xbf\xf7\xf3\x9d\xbb\xb3\xb3\xbb\xd2\xae\xb4\xda\xfd\x9est\xa4\x1d\xcd\xcc\xce|\xed}\xbe\xe7m\xaa|>\x9f\x07\x07\x07\x07\x07\x07\xc79\x8c|>\x8fT*\x85\xed\xdb\xb7#\x95J!\x97\xcb\xe1\xb6\xdbn\x83$I\x10\x04\x81\x0f\x10\x07\x87\x02|Wppppp\x9c\xf3P\xa9T\xc8\xe7\xf3\xc8\xe7\xf3P\xa9T\xc8\xe5r|P888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0\x0e-\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0* \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqppp\x02\xc8\xc1\xc1\xc1\xc1\xc1I \x07\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqpp\x02\xc8\xc1\xc1\xc1\xc1\xc1\xc1\x09 \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x9c\x00rppppp,%P!h\x0e\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\xe3\x9c\x07w\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xc1\x01\x95J\xc5\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\xe5\x0aI\x92\xb8\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7J\x05W\xfe888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83c\xb9A\xe9\xfe\x95$\x89\x0f\x0a\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\xcb\x9d\x00\xcaI\x1f\x8f\x07\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x10@\xe5\xcf<\x16\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\x02\xb9\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a \x7fr\xc5\x8f\x13@\x0e\x0eN\x0098888V\x08\x09\xe4\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\xfe\xe4\xa4\x8fg\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x08 A\xa5Rq\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95F\x029\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x91?N\x00988\x01\xe4\xe0\xe0\xe0\xe0X!\x04\x90\xc7\x00rpp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x09 \x95\x82\xe1\x0a \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0cR\x9e+\x80\x1c\x1c\x9c\x00rpppp\xac(p\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15F\xfc\xf2\x12'\x80\x1c\x1c\x9c\x00rpppp\xacH\x22\xc8\xc1\xc1\xc1\x09 \x07\x07\x07\x07\x07'\x80\x1c\x1c\x9c\x00\xf2!\xe0\xe0\xe0\xe0\xe0Xn\xe0\x9d@888\x01\xe4\xe0\xe0\xe0\xe0X\x81\xe0\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x86\x5c.\xc7\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eN\x0098888\x96-x+8\x0e\x0eN\x0098888V\x18x\x0c \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0c\x5c\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x85\x81+\x80\x1c\x1c\x9c\x00rpppp\xac \xf0:\x80\x1c\x1c\x9c\x00rpppp\xac@p\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a\x03\x8f\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\x13@\x0e\x0e\x0eN\x0098888\x963\xb8\x0b\x98\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x04\x95J\x85L&\xc3\x07\x82\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95\x04\xde\x0b\x98\x83cf\x88\x8b\xf5\xc6$\xbds\x09~\xf1o\xb9\xf2\xbf\xf3\xf9<\xfb7\x07\xc7b\xedm\xf9:+\xb5\xc7\xe9y\xf4\xb7r\xbdrpp\xfc\x09\x14\xb3\xc8K\xd8,\xdf\xb3S~n\xe6\xf3y\xa8\xd5\xeai\xf6\xfc\x9c$\x80\xf9|\x1e\xd9l\x16*\x95\x0a\x92$A\x92\xa4i\xc6\x82c\xe1\x17\x92\xdc\xb0\x0a\x82P\xf0X\xb1\xe7pp\x9c*\xe9\xa3}M\xc6\xaa\xd4\xfe\xce\xe7\xf3\xc8\xe5r\x10\x04\x81\xad?A\x10\xa0V\xab\xa1R\xa9\x0a\x0e>\x0e\x8e\x85\x14\x1e\x94\x84j\xa9\x93>I\x92\x98ZI{\x8a\xdb\xcd\xe5\xb76%I\x82J\xa5*8\x17\xe9L\x14\x04\x81\xfd9\xe7\x08`6\x9bE6\x9b\x85$I\xc8d2H\xa5R\xecg2\x14\x1c\x0b\x07Z\xb8$IB:\x9d\x86$Il\x7fg\xb3Y\xf6\xff\xc5@\x97\xc1|>\x0f\xadV\x0b\xbd^\x0f\xbd^\x0f\xadV\x0b\x00\x9c\x04r,\x0a\x01\xa4\xbf\x97\xba\x0b8\x9b\xcd\x02\x002\x99\x0c\xb2\xd9,2\x99\x0c\xd2\xe94r\xb9\x5c\xc1e\x8bcy\x81\xe6W\xa3\xd1@\x14E\x98\xcdfh\xb5Zh\xb5\xda3J\xfc\xc5\x85\xfa2\xf4'\x1e\x8f#\x1c\x0e#\x1c\x0e#\x16\x8b!\x99L\x22\x97\xcbq\x02\xb8\xc0 \x05EN\xfch1i\xb5Zh4\x1ah4\x1ah\xb5Z\xf6\x98(\x8aP\xab\xd5\x10E\x11*\x95\x0a\x1a\x8d\xa6@1\xe4\xe0(\xa5R\xa4\xd3i\xa4\xd3i\xb6\xbf#\x91\x08\x92\xc9$\xbb\xe8\x15\x83Z\xadfF\xd8h4\xc2n\xb7\xa3\xbc\xbc\x1cv\xbb\x1d\x1a\x8d\x86\x0f,\xc7\xa2\x13\xac\x5c.\xb7\xa4/\x1a\x99L\x06\x89D\x02\x89D\x02\xa1P\x08\xe1p\x18\x89D\x02\x99L\x86\x13\xc0eL\x00\xb3\xd9,DQ\x84\xd5jEyy9\x9cN',\x16\x0b4\x1a\xcd\x19[\xaf\xe2B~\xa1X,\x86`0\x88\xee\xeenx\xbd^\xf6E\xe4j\x15\xc7\xc2\x19\xe4h4\xca\x16\x92\x5ce\x15E\x11\x06\x83\x01&\x93\x09v\xbb\x1dF\xa3\x11F\xa3\x11z\xbd\x1e:\x9d\x0ez\xbd\x9e\x91C\xadV\x0bA\x10\x18)\xe4\xf3\xc4QLU\xc9\xe5rH\xa7\xd3\x08\x06\x83\xe8\xeb\xeb\xc3\xe8\xe8(\xbbP\x90\xb2\x5c\x0c\xf1x\x1c{\xf7\xeeE,\x16Css3\xd6\xaf_\x8f\x96\x96\x16\x08\x82\x00\xbb\xdd^\xf2\xb5\x1c\x1c\xa7z9\x96\x9f\x93K\x95\x00\x92r\x9eN\xa7\x11\x8dF1>>\x8e\xae\xae.\xe6\xa5!\xdb\xc9\xb1\xfc\xce\xd2\xfd\xfb\xf7cxx\x18v\xbb\x1d\xabW\xaf\xc6\xfa\xf5\xeb!\x08\x02\x13e\xce\x19\x02H_(\x9b\xcd\x22\x16\x8badd\x04\x95\x95\x95\xb8\xfa\xea\xab\x0bT*\x8e\x85_Hr\xf7\x1b\x11\xc0L&\x83\x89\x89\x09\xc4\xe3qLNN\xe2\xd8\xb1c\xc8f\xb30\x1a\x8dp:\x9d\xb0\xdb\xed0\x18\x0c\xd0\xeb\xf50\x99L0\x9b\xcd0\x18\x0c\xd0\xe9t\xd0h4|\xce8f\xdc\xe3\xf1x\x1c\x13\x13\x13p8\x1c\xd8\xb4i\x13[+\xa5\x0e\xabl6\x8b\x1bo\xbc\x11\xdb\xb7o\xc7\x0f~\xf0\x03\x08\x82\x00\xadV\x0b\x9b\xcd\x06\x8b\xc5\xc2\x14i\x0e\x8e\x85Z\xab\xca\x8b\xf2RU\xd1\xc8\x0d\x98L&\x11\x0c\x06\x11\x0e\x87q\xe7\x9dw2e\x88\x0b'\xcbw\x8dn\xd9\xb2\x05G\x8e\x1c\xc1\xd3O?\x8d#G\x8e \x16\x8b\xa1\xac\xac\x0cF\xa3\x11:\x9d\xee\x8c\xb9\x81O\xfb\xe4\xa5\xa0\xc6t:\x8dx<\x8e\xa9\xa9)\xe8\xf5z\x98\xcdf\xee\xf6]dP\x1c\x95r\x9c+++\x0b~\x0e\x06\x83p\xbb\xdd\xd8\xbf\x7f?\xba\xba\xba \x08\x02\x0c\x06\x03l6\x1b\x1c\x0e\x07\xacV+\xca\xca\xca`6\x9ba4\x1a\xd9\xe2#U\x90\x83\x1fX\x14\xd7\xeb\xf3\xf9PYY\x09\x8b\xc52\xeb\xfe\xa6\xb8\xbf\xae\xae.<\xf7\xdcs\xe8\xe9\xe9\xc1\xaaU\xabP__\x8fh4\x0a\xbb\xdd\xce\xd60\x07\xc7b`\xa9\xc7\xd1e\xb3Y$\x93I\x84\xc3a\xf8\xfd~\x18\x0c\x06fS9\x96'T*\x15\xa6\xa6\xa6\xf0\x9b\xdf\xfc\x06===\x98\x9a\x9a\x82\xdb\xedF0\x18\x84\xcb\xe5bb\xce9A\x00\x05A@:\x9dF2\x99D,\x16C\x22\x91\x80N\xa7c_\x94\xe3\xcc,\xa8b\xb7`\xca$\xb3\xd9l\xb0\xd9lhooG>\x9fGgg'zzz\xb0o\xdf>\x08\x82\x80\xd6\xd6VTTT\xc0\xe9t\xc2\xe1p\xb0\x9b\x08\xbd7w\xd3\xadl\xe4r9d2\x19\xc4\xe3q\xc4b1\xa4\xd3\xe9Y\xf7w>\x9f\x87 \x08\x18\x1d\x1d\xc57\xbf\xf9M\x9c8q\x02\x1e\x8f\x07\x91H\x04\xf1x\x1c\x99L\xa6d\xf2\x08\x07\xc7\xa9^V\x94?/U!\x82\x12&\x13\x89\x04\xdb[\xb4\xaf\xf8y\xbb<\xd7\xa5J\xa5B8\x1c\xc6/~\xf1\x0b\xec\xdc\xb9\x13###,|.\x1e\x8f\xb3\x98\xd53\x85\x05q\x01\x93\x8b\x88\xbe\xc4\x5c\x82\xbb\xb9:\xb8p\xe4Oy[\x98\xa9\xd6\x1a=o\xe3\xc6\x8d\xd8\xb8q#6o\xde\x8c\xb7\xdez\x0b/\xbe\xf8\x22\xea\xea\xea\xd0\xda\xda\x0a\x97\xcb\x85\xaa\xaa*8\x9dN\xe6\x1e\xa6\x0cb\xee\x16^\xb9kL\x92$\xc4\xe3qD\xa3\xd1Y\x89\x1f\x91\xbf}\xfb\xf6\xe1\xa7?\xfd)\x0e\x1e<\x08\xaf\xd7\x8bL&\xc3\xfePl\x167t\x1c\x8bmx\x97\xaa\xad!\x17o&\x93A \x10(i7\x17\xab\xae\xee|\xf6\xdfB\xfe\xee\x95\xb4\xef\xe5\x84\xde\xeb\xf5\xe2\xfb\xdf\xff>\xde|\xf3M\xf4\xf7\xf7#\x16\x8bA\xadV\xb3\x90\xae3\x9d0\xbb \x04\x90\x0cD4\x1a\xc5\xd8\xd8\x18\xbc^/_\x00gx\x81\x9d\xca\xf3\x5c.\x17\xee\xb8\xe3\x0e\xdcz\xeb\xad\xf8\xe7\x7f\xfeg\xbc\xf2\xca+hkkCss3\x9a\x9b\x9bQUU\x05\x87\xc3\x01\xb3\xd9\xcc\xe6\x9a\x97\xedX\xb9F4\x99Lbjj\x0aUUU%\xd7X<\x1e\xc7[o\xbd\x85\x17^x\x01\x87\x0f\x1f\x86\xdf\xefG<\x1e\x9fv\x18\xf23\x80c\xb1\x94\x16\xc2Rv\x01+\xe3\xe7\xa7\xa6\xa6f=\xbb\x17\xf2\xbb\x9c\xa9\xb1Q\xce\x89\xbc4\xd4\xd9\xba\xcc\x9e\xa95\xa1R\xa9\x90L&\x11\x8dF\xd1\xdd\xdd\x8d\xd7_\x7f\x1dG\x8f\x1eEWW\x17b\xb1\x18KP\x22q\xe5L\x8b,\xe2B.\xa6\x5c.\x87H$R\x92\x00F\x22\x11\xe4\xf3yLNN2\xe6;\xd3\xfbQ\x8d\xa4RFi)\xdd\xeeN\xf5\xf3\x9c\xcakT*\x15R\xa9\x14\xaa\xaa\xaa\x10\x0e\x87a\xb5Z!I\x12\xca\xca\xca\x90\xcb\xe5`6\x9b\xa1R\xa9\xa0\xd5jg,\xd2+7\xc4_\xfb\xda\xd7\xb0{\xf7n\xfc\xe4'?A\x7f\x7f?.\xb8\xe0\x02\xb4\xb6\xb6\xa2\xbe\xbe\x1e.\x97\x0b6\x9b\x8d\x05\xebs\x12\xb8\xf2@\xfb;\x1e\x8f\xc3\xe7\xf3\x15}N.\x97\xc3\xc0\xc0\x00^y\xe5\x15\xec\xd9\xb3\x07'N\x9c\xc0\xc8\xc8\x08b\xb1\x18;p)\xcb\x8d\x07\xb8s\xccv\x86.\xc4\x85s\xa9\x87\x19\x90\x1b8\x1e\x8fc||\xbc\xe8\xf9\x1c\x8b\xc50<<\x0c\xb7\xdb\x8d\xde\xde\xde\x19\xc9\xd3\x5c\xec\x88\xdc\x15y*\x9f\xf5T^W,\xc9\xebTm\xe5b\xb8H\xe7\xf3YJ=\xaf\x98\xc7M\x10\x04D\xa3Q\x04\x02\x01\x8c\x8f\x8fcdd\x04\x83\x83\x83\x88F\xa3,\x0c\x86\x88\xdf\xd9h\xda .\xd4\x00\x92\x01\x88\xc7\xe3,FH\x8e\xd1\xd1Q\xf4\xf7\xf7\xa3\xbb\xbb\x1b\xf1x\x1c\x1e\x8f\xa7(\xdb\xa5\x85\x9fJ\xa5\x90N\xa7Y\x11c\xe5\x00+[K-$\xb9:]\xe26\xd7\x00\xce\xf9\xc8\xfa\xb4\xf9\xe4\xef-\xaf\x01HcAn[\x8b\xc5\x82\xf2\xf2r\x18\x8dF\x94\x95\x95\xa1\xac\xac\x0cv\xbb\x1dv\xbb\x1d:\x9d\x8e\x1d\xac\xf4\x1e\xd9l\x16\x9b6m\xc2\xea\xd5\xab\xf1\xf4\xd3O\xe3\xc5\x17_\xc4\xfb\xdf\xff~\x04\x83A\xa4\xd3id\xb3Y\xf6ZN\x02W.\xb2\xd9,\x82\xc1`\x01\xe9K\xa7\xd3\x98\x9a\x9a\xc2\xfe\xfd\xfb\xb1\x7f\xff~\xf4\xf6\xf6bhh\x08n\xb7\x1b\x89D\xa2\xc0\xad!\xdf\xb3<\x0c\x84C\xde\x05\x83J\x9f(\xcf\xceP(\x04\xb7\xdb\x8d@ P\xd0UF\xaf\xd7\xa3\xbd\xbd\xbd\x80`\xccG\x01<\x1d2\xb4P \xfb\x96\xc9d\x0ab\x00\x01\xc0\xef\xf7\xa3\xbf\xbf\x1f;v\xec@\x7f\x7f?\xc2\xe10\x82\xc1`A\xfd\xd6S\xb5u\x0bE\x8a\xe5cX\xec\xf7SR\x8b\xb2\xc5\x9d\xb2\x1d\x9arLf\xfa^D\x98\x8b\xa9\x8a\xa5~.5N\x0b\xd5v\xaf\xd8{\xe4r9$\x12\x09V?\xd5\xe7\xf31\x8e$O\xf6\xa0j\x0ag\xfaL\x5c\xd0\xfa\x0b\xc4\xce'&&\xd8\x97\x0f\x06\x83\xe8\xe9\xe9\xc1\xc1\x83\x071::\x0a\x9f\xcf\x87d2\x09\xaf\xd7[@\xee\x94\x93F\x93\x12\x8f\xc7\xd9\x82 \xb9\x5c.\x87'\x93\xc9\x05'p\x8bI\x0eO\xf5\x90\xa0X<\xe5\xe3t\xf8i4\x1a\xe8t:\x18\x0c\x06$\x12\x098\x1c\x0eH\x92\x04\xb3\xd9\x0c\x93\xc9\xc4J\xc0TVV\xc2f\xb3\xc1n\xb7\xa3\xa2\xa2\x02&\x93\x09\x06\x83\x01\x82 \x97\xcb!\x95J\xe1\xc6\x1bo\x84\xc5b\xc1\x0b/\xbcPP\xec7\x97\xcb\xc1\xe1p\xc0h4\xb2R\x05\x1c+\x03tI\xc8f\xb3\xf0z\xbdH&\x93\xd0\xe9tH&\x93\xe8\xec\xec\xc4\xee\xdd\xbb\xd1\xdb\xdb\x8b\x91\x91\x11LNN\xc2\xe7\xf3!\x12\x89\xb0RE\xb4v\xc9\xc0s\xf2\xc7\x89\x1f)$\xa2(B\x14Ex<\x1e\xbc\xfd\xf6\xdbx\xeb\xad\xb7\xf0\xf6\xdboc``\xa0 ^T\xbefr\xb9\x1cZ[[\xf1\xd6[o\xc1\xe9t\xce\xe8:+\xe5j\xccI9\xa8p\xf6[\x12\x92]\x8bF\xa3\xe8\xec\xec\x84 \x08\x18\x1f\x1fGoo/\x06\x06\x06\xd0\xdd\xdd\x8d@ \x80d2\x89D\x22\x01\xe0d\xf1\xe8\xb3I\x5c\xf3\xf9<#\xecr\xb5\x8bHL>\x9f\x87\xc9d\x9aF\xc0\x95s\xa1\xd5j\x0b\xc6\x9fBMJ\xa9k\xc5\xbc\x834\x1er\x01\xe9L\x9d1\xf4{\x8b\xfd>\xba\xacd\xb3YV\xec;\x95JM[\xcf\xf2\x8bM\xb1\xb8\xfe%O\x00I\xae\xcf\xe7\xf3\xac\x04L:\x9df$.\x9dN#\x16\x8b\xc1\xe7\xf3\xb1\xfaG\xca~\x87$\x8bR\xab\xa9L&\x03\x8dF\xc3\xda\xe3H\x92\x04A\x10\x90J\xa5\xd8k\xa8v\xddb\xa8t\xa7\xb3\x80\x16r\xf2h,\x09T`\x9bz\x0aR\xc9\x16Q\x14!I\x12L&\x13<\x1e\x0f4\x1a\x0d\xab+\xd4\xd3\xd3\x03\x87\xc3\x01\xbd^\x8f\xf2\xf2r\xd4\xd7\xd7\xc3j\xb5\xa2\xb6\xb6\x16N\xa7\x13\xa2(\xb2[h:\x9d\x86F\xa3\xc1\xc5\x17_\x8c\xbd{\xf7\x16\x14\x9c&\xc5P\xa7\xd3-\xf9\x0a\xfb\x1c\x0b{\xc0\xc9\x0f)\xba|%\x12\x09$\x93I\xa6\x12+[X)\x0fF\xa5r\xcf\x13\x8bV\x06\x94\xe7m,\x16\x83\xc7\xe3\xc1;\xef\xbc\x83_\xfd\xeaWx\xe9\xa5\x97\x0a\xd6\x09u6\x92\xaf!\xe5:\x9a\xcb\x99-\xef\xb1+\x7fN\x22\x91\xc0\x1f\xff\xf8G\xd4\xd4\xd4\xa0\xa3\xa3cV;\xb2\xd8\xe3\x92\xcb\xe5\x10\x8dF\xf1\xf2\xcb/#\x16\x8b!\x93\xc9\xa0\xaf\xaf\x0f\x1e\x8f\x07^\xaf\x97u\x06\xc9d2KfNi\xaf\x13\x91#\x11\x81:R%\x12\x09&\x14P\xe7)R\x04\xe9\x5c\xa1\xccW\xf9x(\xc3\x96H\x80\x90\x17\xa4\xa7\x0eC\xc4\x17\xe8w\x93\xa7\xecL\xf7\xd4-\xc5\x15\xe8<$\x1e\x93\xc9d\x0a\xfa>\xcb/\xc6g\x1a\x0bF\x00\xc9(\x00@\x7f\x7f?\xf6\xec\xd9\x03I\x92\xe0\xf3\xf9\xd0\xdf\xdf\x8f\xfe\xfe~LNN\x22\x1c\x0e#\x9b\xcd\xb2\xa0pb\xef\xc4\xe0\x05A`\x841\x97\xcb\xb1Vr\xc4\xa2\xcf\x04\xab_\xca\xea\x04\xa9n\xb4\x80\xa8`(\x19g\xb5Z\x8dX,\x06\x95J\xc5\x5c\xbd\x1a\x8d\x06\x16\x8b\x05\x81@\x00eee\x98\x9c\x9cD__\x1f\xecv;\x1c\x0e\x07\x0c\x06\x03V\xaf^\xcdnc\xe1p\x18\xa2(B\xaf\xd7\xa3\xb5\xb5\x15G\x8f\x1ee\xad\xfeh\xa1\x92;\x98\xc7r\xad\x1c\xd0\x9as\xbb\xdd8r\xe4\x08\x04A\xc0\xd4\xd4\x14\x06\x07\x071<<\x8c\xf1\xf1q\xf8|>D\xa3\xd1\x02\xd5X\x0ey\xb8\xc2R\xdfk\x1c\x0b\xb3fH-\x02\x80\xd7_\x7f\x1d\xdb\xb6m\xc3\xee\xdd\xbb\xb1k\xd7\xae\x82K-\x11\x05\xf9e\xa2\x14\xc6\xc6\xc6fue\x16S\xa0T*\x15N\x9c8\x81t:\x8d\xf1\xf1q477\xc3n\xb7\x9fUrL\xf5\x00\xdf|\xf3M\x18\x0c\x06\xa6\xa2\xc7b1\xa4R)\xa4R\xa9\xb3\x9681\x1b\x04A`\xaa\x1d\xa9\x80\x94\xd8\xa0\xd5j\x19\x09$\xe2M\x82\x05\x915\xa5\xfd \x92K]R\xe89\xd4\x8aR\x9e<#\x9fc\xb9r\xb6XY\xd3\xf3\x15|\xe8\xf7\xcb\x9b5\x14\x9b\xc7\xb3U\xfaG\x5c\x8cA\x10E\x91)D\xd4aB\xde\xda\x86\x9e\xa7V\xab\x11\x8f\xc7\xd9\xe4\x91\x9f\x0f\xe5\xe5\xe5\xf0\xf9|0\x9b\xcd\xa8\xa8\xa8\x00\x00x<\x1ev\x135\x9b\xcd\xe8\xec\xecd]\x1bh.m6\x1b\xdb\xcc\x1c+\x07t\xa9 %\x98\x0es\xba\x8c\xc8\xd7\xecL5*9\x96\xbf\xe2\x97\xcdf\x99\x8a\xf7\xf4\xd3O\xe3{\xdf\xfb\x1e\x06\x07\x07\x91J\xa5\xd8\xda\xa0\xb3C\xaenY\xadV\x5cs\xcd5hkkCCC\x03\xfbc\xb5Z\x0b\xde_\xa3\xd1\xa0\xa2\xa2\xa2\xc0\x16\xccu}%\x93I\xf6\xbaR\xc9\x86gb\x9c\x88\x10\xe4r9\xf4\xf5\xf5!\x99L\x22\x95J!\x99L2\xb5\x88D\x10\xb9z\xb6\x14m\x14\x8d)\x9d\x11\xe9t\x1a:\x9d\x0e\xd9l\x96\xf5\xa8'\x8f\x16u\xbc(f\xd3\x88\xf0\xd2\xbf3\x99\x0c\xb2\xd9,\xe3\x04t\xb1P\xd6\xbc\x95\x7f\x9e\xa5r\xd6(\x95k%9%\xe1\xe6L\xb9}\x17\x8d\x00\xcewP\x22\x91\x08\x93ri\xc1\xd3m\x87\xfc\xe4rW\x92\xfc\xef3Y,q\xa9\x93Be\xf6\x10-(\x22\xe3Z\xad\x96\xf5\x07\xa6q\xa6\x9f\xa9\x83\x8b\xc9d\x82\xcb\xe5B(\x14b\xad\xe2\xd4j5\x8cF#\xdb|\x07\x0f\x1edq\x86\xd4K\x98nv\x5c\x05\xe4\x06\x9f\x83C~&\x85\xc3a<\xf5\xd4Sx\xf4\xd1G\xd9Y\xa44\x86z\xbd\x1e\x95\x95\x95\xb8\xf3\xce;q\xc3\x0d7\xe0\xf2\xcb/\x9f\xd6\x1d\x86\xc8Q\xb15\xb6\x10Y\xc2\x14^s6\xce1y\x22\x9f \x08\x18\x1b\x1b\x83Z\xadf\x1e0y\xa8\xd4R\xdc\xefJ;$'\xaa\xe4\x0e&\xb2N\xa4\x8cD\x84L&3\xcdU+O\xf2 \xc50\x9b\xcd2AH9&\xa7\x9b\x88y6\xc6l\xa6X\xfe\xb3\xa1\x02\x8a\x8b\xb5\xf9U*\x15\xeb\x0c\x92L&\xd9d\xc9\xcbI\xe8t:\xa4\xd3\xe9\x82\xe0H\x8au(\x15GT,\x93h\xa5\xa9\x7f\xf2\x9b\xa0|\x1c\xe4A\xa7\x14\x17\x91\xc9d \x8a\x22\x0b\xdc\xcff\xb3\xcc}\xeb\xf5z\xa1\xd7\xeb\xd9\x1cP\x1b\xbfd2\x09\x8dF\x83p8\xcc$\xfd`0\x88}\xfb\xf6\x15\xf4\x10\xa6x\x0d\xde6n\xf9\x13f\xf2\x18\x8b\x5c.\xc7\x08!\x05\xd2\x92{\x97\xdc\xec\x14\x9bID\x906\xb3Z\xadF&\x93\x81Z\xad\xc6\xd0\xd0\x10\x8e\x1e=\xcaJ\xcb\x18\x8dFF\xfe\xb8+\x98+\x7f\x1c+\x0ft~h\xb5Z<\xf2\xc8#\xf8\xe1\x0f\x7f\x08\xb7\xdb\x0d\x00,\x06L\x92$\xacY\xb3\x06\x8f<\xf2\x086o\xde\x8c\x86\x86\x86\xa2kh\xb1\xcf\x10eAe\x95J\x05)/\xcd\xba\xc6\xe5\xee\xec\x85T\xff\xc8\xcbb\xb5ZY\xf8\x8d\xd2\xce\xcd&\x06\x9cm\x05\xb0X\xfb=\xa5r)\x0f\xfb\x22\xc5\x8b\xc2Fh.\x94\xe4\x8f\x04!\xb2M\xc5\xde\x93\x88\xe5\xb9$>(\xe7\xd4`0\xc0f\xb3\xc1h4N\xcb\x8a>\xa7\x08\xa0\xbc\xae\x8d\xdb\xedFUU\x15\xe2\xf18K@ \xd7$\x11\x0br\xf5\xc6b\xb1\x82x\x07\xb9\xaa\xa0\xcc>,\xa64\xae$\x05p\xa6\x1aF\xc5T\x18\xf9\xd8\x90\x22H\xb7(\x9dN\xc7\xe4v\x8b\xc5\x02\x95J\x85H$\x02\xbd^\xcfn\xc5\x92$\xb1\x22\xbe\xf2\xf2\x1d\x9d\x9d\x9dp:\x9d\xacw0\x91@\xee\x0a\xe6d\x90c\xe5\x81\x5c\x97\xd7]w\x1d\x06\x06\x06\x0a\x5cz\xe9t\x1a\x97]v\x19\xbe\xff\xfd\xef\xe3\xbc\xf3\xce\x9b\xb1T\xc6\x99\x5c\x83rwr>\x9fG^*}\xc9>r\xe4\x08\xc6\xc6\xc6\xb0a\xc3\x06\xd4\xd6\xd6.\xd89G\x99\xb1F\xa3\x11.\x97\x0b\xf5\xf5\xf5\x05\x04j&\x22\xb8\x14I\x8c2\x16Oi\x93\xc8\x0bEv%\x95J\xb1\xd2f\xa4\xd6\x91:H\xc9\x1f\x14\x06&O\x08*V6\xee\x5c%\x7f\x04\xb5Z\x0d\x97\xcb\x05\x8b\xc5rn\x13@\x22\x81\x82 \xa0\xa1\xa1\x81\x05\xd7\xca\xbf4e\xf1\xa4R)\xa6\x14\x92\x8f_\x1e\xf3\x07\x00z\xbd\x1e\xf5\xf5\xf5X\xb5j\x15+[B\x81\xe6\xf2E\xb7\x9cI\x87|<\xe8\xdf\xd9l\x16\xa1P\x88e[\xd2f\x19\x1d\x1dE4\x1aE8\x1cf\x8f\xd1\xa6Q\xb6\xdf\xa1C\x862\xb8r\xb9\x1cL&\x13{\x1e5)'\xd7\xb1^\xafg\x85J\x83\xc1 \x0e\x1f>\x8c\xea\xeajTWW\xc3b\xb1\xb0\x98A^\x16fe+A\x8be\xd89\x96\x16\xc8\xdd\x9bJ\xa5\xf0\x9d\xef|\x07\x0f>\xf8 \x04A`e\xbb\x00\xe0\xaa\xab\xae\xc2?\xfc\xc3?`\xcb\x96-,\xd6\xeel\x94[Q\xaeKe\xd8B)\x050\x99Lb||\x9c\x950\xab\xad\xad]P2 \x08\x02\xccf3jkkq\xe9\xa5\x97\xe2\xa7?\xfd)\xc6\xc7\xc7\x0bB\xa1\xe4b\xc7R =r\xd7\xab<\xb35\x93\xc9 \x99Lbxx\x18\x03\x03\x03\x18\x19\x19A$\x12a\x17\x82L&\xc3\x1a\x09P9\x18\x22\x85\xc0\xc9Lp*\x1cO|\x80\x94?9\xf9\xd3h4\x8c\x17\xd4\xd5\xd5\xa1\xbc\xbc\x9c%\xa5\x15k\x16q\xb6/\xc5\xca\x905\xb9\xdb\xdfn\xb7\xa3\xaa\xaa\x0av\xbb\x1dV\xab\x95y\xde\xcei\x17\xb0\xb2\xb1\xb1\xcdf\xc3\xc0\xc0\x00#\x17\x82 0\xb7/\xc9\xbdJ\xe5\xcf\xe5ra\xf3\xe6\xcdX\xb7n\x1d\x9cN'\x1b\x1cr7\xcao\x99+A\x01T\xc6\xaa\xd0x\x11y\xa6\x04\x9ah4\x0a\xb7\xdb\x8d`0\xc8Z\xcf\xf4\xf5\xf5\x15,D\xb9\x9cN\x8a \x118yk\x1aJ\xceQ\xab\xd5,k\x8e2\x81\xfb\xfa\xfa\xd0\xd5\xd5\x85\x9a\x9a\x1a8\x9dN\x94\x95\x95\xb1ZP\xdc\xf0\xaf\x1c\x92W\x8a\xf0qEpy\x93\xbf\xa3G\x8f\xe2\xe3\x1f\xff8\x0e\x1c8p\xd2\x98\x88\x22\xd2\xe94\xd6\xacY\x83'\x9f|\x127\xdcpCA\xf8\x892\xb9c1\x8cm1\xa3\xaf\xfcY^/\x0e\xc0\x8c\x0a\xa0\xbc$\x8d \x08\xec\x02\xbc\x90g\xbbZ\xadf\x09xZ\xad\x16eee\xa8\xac\xacd\xd51\x8ay\xc0\x96\xc2\xbeR\xc6\xda\x11YK\xa7\xd3X\xb7n\x1d&''\xd1\xdd\xdd\x8d\xfd\xfb\xf7\xa3\xbf\xbf\x9f}\x07\xaa\xe8\x91\xcdf\x99`@\xff&\xce@\xb1\x7fD\x80\xe5j\xa2\xcdf\xc3\xd5W_\x8d\x8e\x8e\x0eTVV\xc2j\xb5\xb2J#d\xb7\x96\xd2\x19\xa4\x8c\xcf\x97\xab~T-\xc5j\xb5\x16\x10\xc0sZ\x01\x94\x7fqb\xf8\xb4\x89\x08\xf2\xac\x1e\xba9\xc8\x0d\x8b\xddn\xc7\x07>\xf0\x01\xac[\xb7\x0e---p\xb9\x5c\x05\x04C\x9e\xf6\x7f\xb6\xea\xe7\x9c\xe9\x03\x97\xc6\x94\xfe-'p\x14\xcfG1\x13\xd1h\x14\xd1h\x14\xc1`\x10>\x9f\x0f\x13\x13\x138|\xf80:;;Y\x11m\xda\xb4\x82 \xb0\xc2\x9a\xc9d\xb2\xe0\xa6I%d\xe8\x00\x94\xbb\x81s\xb9\x1c\x0e\x1f>\xcc\xfa\x05;\x9dN\xe6\x0a\xe6*\xe0\xf2\xc5\xd9l\xe4\xceq\xf6A1\xc1/\xbf\xfc2\xfe\xec\xcf\xfe\x8c\x91\x22:\xe7\xef\xbf\xff~<\xf1\xc4\x13\x05\x86\xf8L\x9f\x07\xb3%&)\xeb\xc9\x96Z\xcbD\x16\x17\x83\x00\xd2\xb8Q\xd7\x0c\xadV\x0b\xab\xd5\xcaD\x11%i\x90\x17d_*kAN\x02\xc9\xae\x93\x08A\x84&\x9f\xcfctt\x94\xa9w\xe4\xe6\xa4\xf8P\xbd^\xcf:w\x10\x1f\x90\x8b\x14\xf4\xddM&\x13n\xbf\xfdv\xb4\xb7\xb7\xa3\xb9\xb9\x99\xb9M\xa9\xde\xadR%]*c\xa4\xbc\x84\xd0\xe5\x83l%%\x80P\x99\x9c39\xbf\x8b\x1aqK\x0bV\xde\xdaE\xb9`\xc8%,\x1f\xa4\xcd\x9b7c\xcd\x9a5X\xbf~=\x93x\x0d\x06\x03\x0b\x10V\xca\xe0\xcb\xd5\x18\xcd\x14`+\x1f/\xda(tsR\x12A\x9f\xcf\x07\x8f\xc7\x83\xa6\xa6&\x9cw\xdeyx\xe7\x9dw\xd0\xd7\xd7\xc7Z\xea\x91\xbb\x86\x12v\xe8\xbd\x89\x94S\x89\x04y\x0b>\xfa{jj\x0a===hllDuu5/\x0e\xcd\xc1\xb1\x8c\x89\x1f\x19\xd7\x1f\xfc\xe0\x07\xf8\xdc\xe7>W@\xec\x9a\x9b\x9b\xf1\xb3\x9f\xfd\x0c\x9b6mb\xc1\xfcg\x03\x92$\xa1\xaf\xaf\x0f555\x05IjJ\xdbS\xaa=Y\xb1\xf3V\x10\x04\xd6\x86m!]\x8bt\xd1&\x85\x94b\xb3g\x22\x0f\xf2\xdf\x7f\xb6\xec\x9e\xd2\xcd*\xff\x9b*|P\xd7)I\x92\x10\x0a\x85\xe0\xf5z\x0b\x08\x9a^\xaf\x07\x00\x18\x8d\xc6\x82\xf6v\xca,_\xb9\xbd\xdb\xb2e\x0bZZZ\xb0f\xcd\x1a455\xb1\xdfA\xea\xdfR\xb49J\x05P\xe9\x0a&[)o\xe8p&!.\xf6\xa1Q,iA\x1e\xd8)\x7f\x8e$I\xa8\xae\xae\xc6\xda\xb5k\xd1\xd8\xd8\x88\xba\xba:TUU\xc1b\xb1\xb0\xf6g\xf2\xc5\xb6\x12\xdaH\xcd\xa5\x06\x94\xfc@\xa3MDD\xb0\xba\xba\x1a\xc1`\x10\x1e\x8f\x07UUU\xa8\xae\xae\xc6\xae]\xbb\xb0w\xef^\x84B!6\x1f\xf4|Z\x94\xb4A\xe5E*S\xa9T\x81\x0b>\x16\x8b\xa1\xbf\xbf\x1f\xa3\xa3\xa3hjj\x82\xcb\xe5\x82\xc9d\xe2\xb1\x80+\x84\x10p\xac\x1c\x90\xd1\xfa\xd4\xa7>\x85\x1f\xff\xf8\xc7\xac\x96[.\x97\xc3=\xf7\xdc\x83g\x9ey\x86]H\xcf\x06\xf9#\x92v\xe4\xc8\x11<\xf0\xc0\x03\xa8\xab\xabCmm-V\xb5\xb4\xe0\xaf?\xf5)\x5c\x7f\xddu\x8c\xfc\xc9E\x07\x22-\xb3\xa9\x89\xe4)Y\xac\xb1%5H\xab\xd5\x16\xcdt]\xaa(\x16_Iud\xa9\x8e\xdf\xf8\xf88\xea\xea\xea055\x05\xa3\xd1XP9\x82TARe\x95\x8d\x1f\x08\xabW\xafFSS\x13\x9a\x9b\x9b\xd1\xd8\xd8\x88\xca\xcaJ\xa6\xfe)\xc3\xc1\xce\x15^\xa0$\x83g\x03\xe2\x99\xfe\xc2\xa4\x08*\xb3Wi\x00\xd6\xae]\x0b\xbb\xdd\x8e\xca\xcaJ\xd8l6\x98\xcd\xe6\x82\xfa8+\xf5\xe0-\x059\xd9\xd2j\xb5\x8c\x10f\xb3YX,\x16X\xadV\xd8l6\x94\x97\x97\xc3f\xb3A\xab\xd5\xa2\xbc\xbc\x1c\xff\xf7\x7f\xffW@\x02\xd3\xe94+\xd0I2}>\x9f/\x88\xd1\xa4\xc3\x8a\xc8\xe0\xe0\xe0 \xc6\xc7\xc7\xe1v\xbbY\xa2\x8e\x9c\xacs,_,\x85:[\x1cgf\x9eU*\x15\xee\xba\xeb.<\xff\xfc\xf3\xec\x02\x98L&\xf1\xcc3\xcf\xe0\xd3\x9f\xfe\xf4\x9f\x0c\xcaY*\x05E\xc6\xf4\x8f\x7f\xfc#\x00`tt\x14\xa3\xa3\xa3\xd8\xbd{7~\xfb\xbb\xdf\xa1\xa9\xa9\x09\x9f\xfb\xdc\xe7\xf0\xf9\xcf\x7f\x9e\x95\xb5\x9a\xcbE\x86\xce\xd23q\x9e\x9d\x8bY\xad\xcaq\xa1\xd86\xe0d(\x97\xcb\xe5b\x7f\x02\x81\x00\xb3\xe7\x0e\x87\x83%0R\xbc#\x91\xf1b\xed\xd2jjj\xe0r\xb9PUU\x05\x87\xc3\x01\x8b\xc5\x02\x83\xc1pN\xf3\x82\xa50\xcf\xe2\x99>H\xe80Q\xa6\xe2\x13(\xde\xcfl6\xc3d2\x9d\xb5&\xdd\xe7\xf2\xa2\x22\x05\x8e\xba\x80h4\x1a\xe8\xf5z\x18\x8dF\xe8\xf5z\xd6\xbe+\x9f\xcf\xe3\xa5\x97^b}\x99\x95-\x87\xe4\xe4\x9c$jy\xcb\x1aA\x10\x10\x0e\x87\xe1v\xbb\xe1\xf1x\x10\x0a\x85PQQ\xc1:\x85ppp\x9c\xdb\xa0K\xfa\xddw\xdf\xcd\xc8\x1f\xa9,o\xbf\xfd66o\xde\xccz\xbd.\x05\xec\xd8\xb1\xa3\xa0\xf6,p\xb2\xf2DOO\x0f\xee\xbd\xf7^\xdc\x7f\xff\xfdx\xf8\xe1\x87\xb1v\xedZ\xe8t\xbaY\x0b\x06\xcf\xd4\xb7\xb5\x94\x9a\xc3m\xd0I{a2\x99`\xb1X`\xb3\xd9\xd8\xdfv\xbb\x9d\xf5]\xa6\xb2dF\xa3\x91\xcd\x13\xcd\x87\xbc\xc4\x9bV\xab\x85\xc3\xe1`\xbc@\xa7\xd3\xb1\xe2\xe2\x1cK\x90\x00\xca'Q\xf98\xc5\xac\xc9\xff\xc8AA\xa2D\xfcVB\x92\xc7b\x93A\xea\xd8!\xef\x0dL\x99\xd8\xf1x\x1c\xaf\xbe\xfa*#\x7f\xca\xc0^j\xe5C\x89 D\x02\xe57\xfe\xa3G\x8f\xe2\xfc\xf3\xcfG0\x18D\x22\x91@YY\x19\x0b\x9c\xe6X\xfe\x04a>\x8fs\x9c{g\xc8_\xfe\xe5_b\xeb\xd6\xad,h\xbf\xac\xac\x0c\xef\xbe\xfb.\xd6\xad[\xc7\x12\xfdf[\x1f\x8b}\x86Sm\xd9\xef~\xf7\xbbx\xf1\xc5\x17\xd1\xdf\xdf\x8f\xde\xde^\x1c9r\x84\x91A\xbd^\x8fl6\x8b\xaf~\xf5\xab0\x9b\xcd\xb8\xe9\xa6\x9bp\xfb\xed\xb7\xb3\xb8\xbbb%D\x94\xa5\xc9\x94\xe7\x1a\xfdL\xf1\xd4\xfc\xe2\xfb\xa7\xf9 \x976u9\xb1\xdb\xed\xa8\xad\xad\x85\xc1``%\xc6\xa8\x12\x88<\xa9\xb1Xm[\x12/\xc8\x8eq^\xb0\x04\x09\xa0r\x03\x15K`\xa0\x85A\x05\xa1\x95\xc5\x9c\xc9}H$\x83O\xf2\xc2\x80\xb2\x8d\xe8\xdf\xf9|\x1e\xf1x\x1c\xe1p\x18\x93\x93\x938t\xe8\x10\x80\xc2\x12\x09\xf2lk\x22{\xf1x\xbc\xe0\xff\x04A\xc0\xe4\xe4$B\xa1\x10\x82\xc1 \xe2\xf18#\xf9\x9c\x00.op\x17\xf0\xf2\xc7'>\xf1\x09l\xdd\xba\x95\x15\xe9\xd5\xe9t8t\xe8\x10\x1a\x1b\x1bg\x8d\xb7\x92\xb7\x06;S\xeb\xb1\xaa\xaa\x0a\x97]v\x19\xae\xb9\xe6\x1aD\x22\x11\x04\x83A\x04\x02\x01\xfc\xe2\x17\xbf\xc0\x81\x03\x07\xd8%8\x1a\x8d\xe2W\xbf\xfa\x15^{\xed5<\xf0\xc0\x03\xf8\xd2\x97\xbe\xc4\xea\x9d\xceDdU*\x15|>\x1f\x92\xc9$\x02\x81\x00\x9a\x9b\x9ba\xb1X\xe0\xf1x\xf0\xf5\xaf\x7f\x1dO=\xf5\xd4\x92RC\x97\x0a\x11$\x8f\x13\x95\xbb\xa1\xf8Q\xaf\xd7\x8bL&\x03\xb7\xdb\xcdb3\x8b\xad\x17y\x1b9\xce\x07\x16x~\x16\xe3\xc6(\x872\xd1CN\x0e\x8b\xc9\xebt\xa8\xcc\xd4\xfd\x83\xe3\xf4H\xa0V\xab\x85^\xafGEE\x05\x1a\x1b\x1b\xb1j\xd5*\xb4\xb7\xb7\xa3\xbc\xbc\xbc\xb06\xd6\xff\x9f\x17\xbau\xa9\xd5j\xe4r\xb9\xa2\x1d?(\xf3.\x12\x890\x02\xc8\x15\xa0\xe5\x0f>\xc7\xcb{^\xbf\xfb\xdd\xef\xe2\xa7?\xfd);\xc7\x8dF#zzzPSS3\xa7\xcb\xb9 \x08x\xf5\xd5W\xb1k\xd7\xae3\xb6^\xa8\xb2D\x22\x91\x80Z\xad\x86\xd3\xe9\xc4\x1dw\xdc\x81\xfd\xfb\xf7\xe3\xbd\xf7\xde\xc3\xc6\x8d\x1bY\xfc\x1fp\xb2\xa8\xfd\x97\xbf\xfcel\xd9\xb2\xa5\xa4\x1d#\xbb\xb4o\xdf>\x1c?~\x1c}}}\x18\x1e\x1e\xc6\x81\x03\x07\xd0\xd6\xd6\x86\xef}\xef{x\xf2\xc9'9\xf9S\xcc?\x09=\x82 \xa0\xae\xae\x0e\xabV\xadB[[\x1b\xf3\xf0%\x12\x09X,\x16&P\xc8\xcb\xc5\x15\xe3\x16\xe4\xd5\xe2\xbc`\x89\x12\xc0\xb9lt\xb9\xebW9\x91\x9c\xf8-.\xa8\xf8\xa4\xd9lf$\xb0\xb9\xb9\x19\xf5\xf5\xf5Lu\xa59\xd0\xeb\xf5H&\x93\xac\x04\x0c\x91\xbfb\xca\xad\xc7\xe3A8\x1cF4\x1ae\xdd]8\x96\x0ff\xda\x8fs\xc9R\xe78w@1\xc0\xaf\xbf\xfe:\xee\xbb\xef>f\xbc\x8dF#v\xef\xde\x8d\xda\xda\xda\x92nN\xf9Zx\xea\xa9\xa7\xb0e\xcb\x16\x5cv\xd9ep\xbb\xddg\xa5t\x09)J\xd9l\x16\x17]t\x11\xf6\xed\xdb\x87\xe7\x9e{\x0e\xeb\xd7\xafg\xdfW\x14E\xbc\xfa\xea\xabhoo\xc7\xdbo\xbf\x0d\x00\x05$Q\x0e*\xb1%I\x12|~\x1fn\xbc\xf1F\x04\x83A\xe4\xf3y|\xe5+_\xc1\xce\x9d;\xf9\x22\x92\xd9y9\x014\x99L\xa8\xa9\xa9\x81\xd1hD}}=DQdmH\x95\xf6\xbf\xd8:\x91W\x0b\xe1g\xce\x12$\x80r\xf5h.\x13T\xcc\xa8PM;>\xc1\x8b\x07J\x10\xb1Z\xadp:\x9d\xa8\xad\xadEcccAp-\xb9J,\x16\x0b\x8cF#s\xf5\xd0\x86Vf\x7fMLL \x1e\x8f#\x91H\xb02\x0b|\x0e\x97\x8f\x224\x9f\xb9\xe4\xf3~n\x9f\x0d]]]\xb8\xf9\xe6\x9b\x99\xfbN\x92$\xfc\xcf\xff\xfc\x0f\xd6\xacY3\xeb\xdcR\xec\xf0\x87?\xfca\xfc\xed\xdf\xfe-T*\x15t:\x1d.\xbd\xf4R$\x12\x89E\xbf\xd8\x17\xf3(E\xa3\xd1\x82f\x04W\x5cq\x05\xbe\xf1\x8do\xe0\x8b_\xfc\x22\x8b\x0b\x14E\x11\xc3\xc3\xc3\xd8\xb2e\x0b\xfe\xe5_\xfe\x05\x1a\x8dfZ\xfc\x9f\xfc;\xaa\xd5j\xd8]\xe1/\x0f\x00\x00 \x00IDAT\xac6<\xf5\xd4S\xec2\x9cN\xa7q\xd3M7!\x1c\x0e\x17\xd4\xbe]\xa9{\x83\x92<\x88\xd4\x95\x97\x97\xa3\xa6\xa6\x06\xf9|\x1e\xc1`\xb0\xa0\x86\x9f\x92\x03\xcc\xa4\xc0r,a\x02(\xdf \xf2\x94\xf6\x996\xd1L\x87\x01/$\xbc\xf8j\x8e(\x8a\xd0\xeb\xf5(//\x87\xcb\xe5Buu5\x1c\x0e\x07{\xdcl6\x17\xa4\xe6\xd3\xebf:\xc0\xd2\xe94\xc2\xe10\xe2\xf18S\x009\x11X>\xeb\x85\xf6%\xc7\xf2\xc7G>\xf2\x11\x96\x91\x99H$\xf0\xd4SO\xe1\xc6\x1bo,j\x98\x8b\x91\xad\xcd\x9b7\xe3\xf7\xbf\xff=#\x94\xa9T\x0a\x7f\xfd\xd7\x7f\x0d\x83\xc1\xb0\xe8g\x82\xf2\xfd%I\xc2\xe0\xe0 \xdez\xeb-\xec\xdd\xbb\x97\xd53\x15\x04\x01W]u\x15~\xf2\x93\x9f\xa0\xbd\xbd\x9dy9R\xa9\x14\xfe\xfe\xef\xff\x1e\x0f=\xf4\x10\x8b{\x9ciO\xa4\xd3i\xdcu\xd7]\xb8\xef\xbe\xfb\x98\xda\x15\x08\x04p\xe5\x95W2b\xa9\xfc,+)n\x96\xba\xc6\xd0w\x0e\x85B\x08\x85B\xa8\xaa\xaaBCC\x03\xcb\xfe-v\xc9,\x96@\xca\xed\xc99B\x00\xe5\x13z*\x0b\xbeTaN\x8e\x85\xbb\x9dQ\x86\xb0\xddnGEE\x05\xacV+\xabF/\x8a\x22l6\x1bk\xe7C\xedvH\xceW\xcek<\x1e\x87\xcf\xe7\x9b\xd6\xf6o\xb6\xf5!/\x05\xa4|Ly\xa0\xf3\x03\xe0\xd4\xf7a\xb11\x9ei\xbc\x8b\xa9(3\xdd\xcc9\x96\xcf:\x01\x80\xc7\x1e{\x0c\x9d\x9d\x9d\xcc\x13\xf3\xc9O~\x12\xf7\xddw\x1f\x0b\xd0/\xf5Z\xaf\xd7\x8b\xcb.\xbb\x0c;w\xeed\xcf\xb5Z\xadx\xeb\xad\xb7\xf0\x8f\xff\xf8\x8f\x05\xdd\x84\xce\x04\xf9\x93?\x9e\xc9dX\xa9*\xf9z\x06\x80\x17^x\x01\x0f?\xfcpA\xd7\x8fo|\xe3\x1b\xf8\xf2\x97\xbf\xccb\x9fg\xda\x0f\x99L\x06O>\xf9$\xb6l\xd9\x82\x5c.\x07\x8dF\x83C\x87\x0e\xe1[\xdf\xfa\x16\x0b\x99\x01N\xba\x8e\xfb\xfa\xfap\xe8\xd0!\xd6\xf9b%\xd8\x18\xea\xf3\xabR\xa9PSS\x83\xb6\xb66\x96\x8c8\x1fn\xc0\x15\xc0s\x8c\x00\x16c\xf1\x9c\xc9/-UG\x1e\x0fh\xb3\xd9PQQ\x81|>\xcfZ(\xe9\xf5z\x16#Cs\xa9\x8c\x13\x94oPR\xff(\xad\x9f68\x15\xa5\xa6^\x91\xd4\x09F\xfe\x98\xfcg\x0a\xe4\xa6\xee$\xd45F\xdeA\x86\xa3\xf8!)o\xcc>\xdb\x18\xd3\xf8R_n\xf98+\xc30\x94\x8a\xfdL$\x91\xe3\xdcD&\x93A__\x1f\x1e~\xf8a\xf6X{{;\xfe\xf3?\xff\x93\xd5b+u\x96D\xa3Q\x5c}\xf5\xd5\xe8\xec\xecd\xc5\xe8\xdb\xda\xdap\xf4\xe8Q\x5cq\xc5\x15\xec\x1c9\x955M\xeb9\x9dN\x97\xdc\xfbs\x89S\x0d\x06\x83\xcc;A\xeei\xbf\xdf\x8f\xaf\x7f\xfd\xeb\xf8\xcdo~\x03\x00\xac+\xc7\x13O<\x81G\x1ey\x04v\xbb\xbd\xe8\xa5\x96\x08_:\x9d\xc6\xb6m\xdbPSS\x83L&\x03A\x10\xf0\xe8\xa3\x8f\xa2\xab\xab\x8b}\xa6@ \x80\xae\xae.\x0c\x0f\x0f#\x14\x0a\xad(;#o\x137\xd7\x02\xdc\xa5\xe6\x90c\xe1pFR\x96x\xb9\x88\xa5M\x02\x0d\x06\x03,\x16\x0b\xccf3\xacV+\xacV+\xeb\xb3\x98N\xa7\xe1\xf3\xf9\x10\x8b\xc5\xd8\x0d\xac\xd8A\x9b\xcb\xe5\x90L&\x19\xf9\x937\xf3&2A\x07@\xa9,\xf0b\x87\x87<\xfbK\xde\xa1\x84g\xdc\x15\xee1\x1ag\x22x\xf2\xdaZ\xb3\x1d\xac\xf22L\x1a\x8d\x06:\x9d\x8e\x198\xf9\xf3h.\xe4e~\xf2\xf9\xfcY\xeb\xfd\xca\xb10\xe4O\xab\xd5\xe2\xe6\x9bofk@\x92$|\xf7\xbb\xdf-I\xfc\x08\xa9T\x0a\x1f\xf8\xc0\x07p\xec\xd81\xd62\xed\xa2\x8b.\xc2\xae]\xbb\xd8\x1e\x9do'\x0d\xba@\x16\x8b7>U\x15\x10\x00\xa2\xd1\xe84%\x89>\xe3\xad\xb7\xde\x8a\x83\x07\x0f\xe2\xfc\xf3\xcfg$\xf6\xbf\xff\xfb\xbf\x99\x0b[\xa9\xdc\xd1\xfe\xa21\xfa\xe1\x0f\x7f\x88\x9bn\xba\x09\x92$!\x1e\x8f\xe3\xcb_\xfe2~\xfd\xeb_\xb3\xf2Y\xa4bq{\xc89\xc1\x8a\x22\x80\x1cK\x9b\x00\xeat:\x98L&\x98\xcdf\x88\xa2\x08\xbb\xdd^P\x0f\x8bj8\xe5r9v\x80)\x89C.\x97C,\x16c7u\xf9\xe3\x92$\xb1\xe4\x10\xca\xa2\xa3\xdb|\xa9\x9b\x9d\xbc96e\x22\xd2\xe7\xa4 m\xder\xeeO*\x09\x91py2\x0e\x11\xee\x99\x0e\x5c*\x08\x9e\xc9d\x0a\xaa\xf7\x93\x0a,\x8f\xff$\xe2\xa7\xd7\xeb\x0b\x94`y\xed\xb3\xd9\xdc4\xdc\x03\xb0\xf4\xd6\x8eF\xa3\xc1\x13O<\x81\x13'N@\xab\xd5\x22\x9dN\xe3\x9e{\xee\xc1\xf5\xd7__r\x8f\xd1\xff\xdds\xcf=\xd8\xb1c\x073\xec\x97\x5cr\x09\xf6\xec\xd9\xc3\x8a\xfd\x9e\x0ab\xb1\x18\x02\x81\x00\x8e\x1c9\x82\x1d;v`\xdf\xbe}\xe8\xea\xea\xc2\xc7?\xfeq<\xfa\xe8\xa3\xa7\xdcy#\x93\xc9L\xfb>\xf2\xb3j\xfd\xfa\xf5\xe8\xea\xeaB{{;[\xaf[\xb7nE}}=\xae\xbc\xf2\xca\x0276\x9d\x83\xf4\xf3\x87>\xf4!\xdcz\xeb\xad\xf8\xedo\x7f\x0b\xb5Z\x8dm\xdb\xb6\xe1\xd5W_\xc5\x87>\xf4!\xd6\xee\x8c\x8aK\xaf4;\xc3\xc3GV \x01\xe4\xedq\x96>\x04A`\xad\xe2L&\x13$IbE^EQD(\x14B.\x97c\x07\x98\xbc\xf2\xbfrn\xe5\xaa\x13\x1d\x90*\x95\x0a\xc9d\x12\xd1h\x14\xe1p\x18^\xaf\x17>\x9f\x0f\xa1P\x08\xc9drNY\x85\x00\xa0\xd3\xe9X\xb2\x0a\xf5\x89\xd6\xe9t+~\x8d\xc9\xdd\xbe\xd1h\x14\x81@\x00~\xbf\x1f\x13\x13\x13\xac3K)\xd0\xeb\xf2\xf9<\xccf3\xea\xea\xeaP[[\xcb\x02\xb8\xa9\xd9\xba\x9c\xf4'\x93I\xb6\x0e(K4\x95J1e\x96\xe3\xdc2\xce\xb1X\x0c\x8f?\xfe8#\x7f\x8d\x8d\x8d\xf8\xe1\x0f\x7fX\xb202\x91\xfe\xa7\x9f~\x1a?\xff\xf9\xcfY\xb0\x7fKK\x0b^{\xed5\xe4\xf3y\x18\x0c\x869}\x06\xf9\x05b\xe7\xce\x9d\xd8\xb6m\x1b^{\xed5\xec\xdd\xbbw\xdas\x87\x87\x87g,!6\x177a)\x22B%c\xda\xda\xda\xf0\xea\xab\xaf\xe2\x86\x1bn`\x17\x96'\x9f|\x12k\xd7\xae\x85\xd5je\x04R\xe9\xc1\x08\x06\x83\xb8\xfb\xee\xbb\xf1\xca+\xaf0\x97\xf1_\xfd\xd5_\xc1\xe7\xf3!\x1a\x8d\xb2D\x11^\x22\x8bcE\x10@~\xdb_\xfa\x87\xbf\xbc\xca:\xb9=jjj`\xb7\xdb\x11\x0c\x06\x11\x8b\xc5\x98\xb2D\x07d\xb1\x04\x0e:\x10)\xbe\x86\x0eiR\x04\x03\x81\x00\x04A\x80V\xab\x85\xc1`\x80\xc3\xe1`\x8d\xc1\x8b)\x0c\xa4j\xa5\xd3itww#\x14\x0a\xe1\xbd\xf7\xdeCkk+#\x1f\xd4\xd2n\xa5\x13\xc0l6\x8bD\x22\x01\x9f\xcf\x87\xb1\xb11\x0c\x0f\x0f\xa3\xbe\xbe\x9e5M'\xb7y1\x8c\x8f\x8f\xa3\xb7\xb7\x17{\xf6\xecA<\x1e\x87V\xab\xc5\x95W^\x09\xa3\xd1\x08\xb3\xd9\x5c\xe0\x02T\xb6f\xcc\xe7\xf3\x88D\x22\x10E\x11f\xb3\x99\xb9\x9e\xe7c\x909\xce>\xbe\xf5\xado\xc1\xe7\xf3\xb1y}\xf6\xd9ggu\xeb\x8b\xa2\x88]\xbbv\xe1\x0b_\xf8\x02\xdb\x8b\x00\xb0g\xcf\x1e\x94\x95\x95\xcd\xf9w\xa7\xd3ih\xb5Z\xec\xda\xb5\x0b\xf7\xde{/\xba\xbb\xbb\x11\x89D\x0a.\xa7\x94l\x01\x00###L\xad>\x15\xcc\xd4\x83^N\x02\x01\xe0\xfa\xeb\xaf\xc73\xcf<\x83\xcf|\xe63,\xce\xef\xa1\x87\x1e\xc2\x8f\x7f\xfcc\xc4\xe3q\x88\xa2X\xe0\xc1\xc8\xe5r\xd8\xb9s'4\x1a\x0d>\xfb\xd9\xcf\xe2;\xdf\xf9\x0e\xb4Z-\xfc~?\xfe\xf5_\xff\x15\xb7\xdcr\x0b\xc6\xc7\xc7\x91\xcf\xe7\xe1\xf1xPYY\x09\xa3\xd1\xb8b\xce..\x04-s\x02\xa8\xec!;\xdbm\x8bci\x90\x07r\xb3R\xdd?\x8dF\x83\xf7\xbd\xef}\xe8\xea\xeab\xaa[(\x14\x82\xc3\xe1\xc0\xc8\xc8H\xc1\x5c\x17K\x04\x90\xf7\xfe\xa4\xf5\x90N\xa7\x91L&\xd1\xdd\xdd\x8d\x8f}\xecc\xf3\xfe\x9c\x17^x!\xa2\xd1(\xee\xb9\xe7\x1e\xa6J\x10y\x5c\xc9\xbd7i\xbc\xd3\xe94\xa2\xd1(\xbc^/\xba\xbb\xbbq\xf3\xcd7\xa3\xae\xaenN\xef\xb1f\xcd\x1a\xc4b1x\xbd^tvv\xc2\xe7\xf3\xe1\xf2\xcb/G8\x1cFee%\xfb=\xf2\xf9T\x1e\xec\x82 \x14\x0a\xf1,\xbds\x0c\x92$abb\x02?\xfe\xf1\x8f\xd9<_{\xed\xb5\xb8\xf4\xd2Kg5\xda\x99L\x06w\xddu\x17DQ\x84$IH&\x93\xd8\xbbw/l6\xdb\xbcT\xf9h4\x8a\xbf\xf8\x8b\xbf\xc0+\xaf\xbc2MA\xa6z\xa5uuu\xd8\xbcy3\x1a\x1b\x1bq\xe1\x85\x17\xb2\x8b\xdfL{\xa2\xd4\xef\x9ek\x11\xe1\x5c.\x87O\x7f\xfa\xd3x\xf1\xc5\x17\x99Kwrr\x12\xff\xfe\xef\xff\x8e\xaf|\xe5+\x88D\x22\x05\x1e\x91\x91\x91\x11\xd6&\xf3\xba\xeb\xae\xc3\xd6\xad[111\x01Q\x14\xf1\xf8\xe3\x8f\xe3\xa2\x8b.b\x97\xb0\x89\x89\x09\xf8|>\xb4\xb5\xb5a\xd5\xaaU\xcb\xde\x8b\xc1\xfb\x85\xaf\x10\x05P\xbe\x88\x95\xea\x1f\x9f\xec\xa5y+#\xb7\x04\x05\x5c\x9b\xcdf\xb4\xb5\xb5\xa1\xae\xae\x0e\xdd\xdd\xdd\x88\xc7\xe3\xd0\xeb\xf5\xd3\x0e\xdc\x992\xbc\x8b)r\x94!\xec\xf3\xf9\xf0\xf6\xdbo\xe3\x8a+\xae\x98\xf3\xe5\x80n\xd7\xdb\xb6m\x83\xdf\xefG4\x1a\x85\xc3\xe1@EE\x05\xca\xca\xcaX\xab\xba\x95z\xd1\xc8f\xb3H\xa5R\x08\x04\x02\x18\x1c\x1c\x84\xddnG]]\x1d+\xbfP\x8a\xf8'\x93I\xfc\xe4'?\xc1\xb3\xcf>\x8b\xde\xde^\x04\x02\x01\x5cs\xcd5\xacN\x9a\xbc\xdb\x8b8\x96>\xf9S\xa9Tx\xe1\x85\x17066\xc6\xce\x80\xcf\x7f\xfe\xf3sr\xdd>\xfe\xf8\xe3\x18\x1a\x1ab??\xf6\xd8c\xb8\xf8\xe2\x8bY\x9c\xf0l{Z\xa5R\xe1\xd9g\x9f\xc5\xa7?\xfdi\xe4r9\x18\x0c\x06\x16\xae\xd0\xda\xda\x8a\x1bn\xb8\x017\xddt\x13\xae\xbf\xfe\xfa9'\x18\xcd\xc5\xc6\x90\x87b\xb6\xe7\xaa\xd5j\xc4b1|\xeb[\xdf\xc2\xde\xbd{166\x06A\x10\xf0\xc6\x1bo\xe0\xc6\x1boD{{{\xc1\xfb\xd0\x05\x88\x92`>\xf3\x99\xcf\xe0k_\xfb\x1a\xb2\xd9,\xfc~?\x9e}\xf6Y|\xecc\x1fC*\x95\x82J\xa5bY\xd7\xabV\xad\xe2\x8b\x91\xe3\xac\xe1\x8c\x97\x81\xe1\xd9?K\xebfFA\xc9j\xb5\x1a\xe9t\x9ae\xc0\xe9t:\xe6\xa6\xb0Z\xad\x88\xc7\xe3\xecfL%E\x94\x07)\x95O(X`\xff_]\xa4\xf7\xdf\xb6m\x1b\xb2\xd9\xec\xb4l8\xb9{Q\x99iz\xf0\xe0A\xec\xd8\xb1\x03^\xaf\x17\xbd\xbd\xbd\x18\x1c\x1c\xc4\xf8\xf88\x02\x81\x00\x8bG[\x89\xa0\xb9\x88D\x22\xf0x<\xf0x<\xb8\xe5\x96[\x0aJ\xf7(\xdd\xb64']]]x\xe0\x81\x07\xf0o\xff\xf6o8~\xfc8\xc2\xe10\x0c\x06\x03\x9a\x9b\x9ba\xb5Za4\x1a\xa7\xc5z*/x\x84p8<\xef\x0b^\xb1\xf7\xe48s\xc8d2P\xa9Tx\xe4\x91G\xd8\xfa\xb8\xf0\xc2\x0bq\xfb\xed\xb7\x97\x9c\xcbl6\x8b\xf1\xf1q<\xfa\xe8\xa3\xec\xb1\xf7\xbd\xef}x\xf0\xc1\x07Y\xdch)P7\x90?\xff\xf3?\xc7\xddw\xdf\x8d\x5c.\x07\xadV\x8bD\x22\x81\xc6\xc6F<\xf7\xdcs\xd8\xb1c\x07\xbe\xff\xfd\xef\xe3\xa6\x9bnb1\xa6\xf2\x18\xd4\xd3!\x81\xa9T\x8a%\xab\xcd\x16\xa2\xa4\xd3\xe9\x90\xc9d\xf0\x85/|\x81\x85\xc7\xa4\xd3i\xfc\xf2\x97\xbfD2\x99d\x09V\xf2\xefE\xfb\xeb\xe2\x8b/\xc6\xa6M\x9b\xd8{\xed\xd8\xb1\x03~\xbf\xbf\xa0\xd6 \x9d\xa9\x00\x10\x89D\xd0\xdf\xdf\x0f\xaf\xd7\xcb\xc5\x12\x8e\xe5A\x00y\x0c\xd0\xd2W\x00Ia\xa3@\xfe\x8d\x1b7\x02\x00\x0c\x06\x03k\x17\xe7\xf5z\x91\xcdf\xa1\xd7\xeb\x0b\x5c\xbcJ\x90\x0b\x99\x0a\x7f\xca{\x0aS\xad\xc1\x81\x81\x01\xfc\xd3?\xfd\x13N\x9c81k\xdfgA\x10p\xe4\xc8\x11\xec\xde\xbd\x1b]]]\x88D\x22\xf0\xfb\xfd\xe8\xe9\xe9\xc1\xd8\xd8\x18|>\x1f\xe2\xf1x\xc1A\xbc\x92\xc8;eW\x87\xc3a\x0c\x0f\x0fc\xc3\x86\x0d\x05m\xfb\x8a\xcd\xb7\xdf\xef\xc7\xcf\x7f\xfes<\xf8\xe0\x83\xf8\xedo\x7f\x8b\xd1\xd1Qf\x10\x9b\x9a\x9a\xd0\xd4\xd4\x04\xbb\xdd\x0e\x93\xc9\xc4\xe6\xb2\xd8\xd8\xcac\xfd\x94*!\xc7\xd2_;:\x9d\x0e\xbf\xff\xfd\xef\x11\x0e\x87\xa1\xd3\xe9\x90\xcdf\xf1\xd5\xaf~uV\xf5L\x14E|\xf1\x8b_d\xff\xd6h4x\xec\xb1\xc7\xe6\xd4\xbf]\x92$\x18\x0c\x06\x9c\x7f\xfe\xf9\xd8\xbau+{~:\x9d\xc6\xc3\x0f?\x8c\x81\x81\x01|\xf4\xa3\x1fe\xa1\x07\xf2s`.=\xe2\xe7\x12s.I\x12FGG\xe7\x1c\x9f.I\x12:::p\xf5\xd5W\xb3\xdf\xf1\xde{\xef\xa1\xab\xabkZ\xc2\x9b\x1ceee\xf8\xc8G>\xc2>\xfb\xc0\xc0\x00\xc6\xc6\xc6\x8a\xc6\x1f\xe6\xf3y\x0c\x0c\x0c\xe0\xc4\x89\x13\xe8\xef\xefG:\x9d\xe6\x8b\x94\xe3\x8c\x80\x97\x81Y\xe1\x86\x80n\xd7\xa4&Q\x00\xb7$I\xa8\xa8\xa8@6\x9b\x85\xd9l\x86Z\xadF&\x93\x99V\xdbO~\x90\xe9\xf5z\x88\xa2\xc8\x12\x07\xa8\xcc\x8cV\xab\x85\xc5bAee%\x9a\x9b\x9b\xf1\xc6\x1bo`\xff\xfe\xfdhoo\xc7\xb5\xd7^\x8b\x86\x86\x06TUU\xc1h4\x22\x9dN#\x9dN#\x91H`hh\x08\xef\xbd\xf7\x1e\x8e\x1f?\x8e@ \x80x<\x8et:\x8d\xfe\xfe~\x8c\x8c\x8c\xa0\xa9\xa9\x09\x15\x15\x15\xac|\xcdJ\xaa\x0bH\xf3E\xf1{\xa9T\x0a\x1b6l(J\xce\xf3\xf9<\xbc^/\x8e\x1d;\x86\xe7\x9f\x7f\x1e{\xf6\xec\xc1\xd0\xd0\x10\x22\x91\x08S\x82DQ\xc4E\x17]\x84\xea\xeaj\xb8\x5c.F\x00\xe5FWi\xe4\x94\xe5,\xb8\x9awn\x80\xc2>\x1ey\xe4\x11\xb6\xaf\xadV+n\xb9\xe5\x16\x96\x94Q\x0cj\xb5\x1a\x13\x13\x13\xd8\xbau+\xf4z=\x92\xc9$n\xbb\xed\xb6Y\xcb\xc5\xc8\x09\xe4e\x97]\x86\xc3\x87\x0fC\x14Ed\xb3Y\xb4\xb4\xb4\xe0\x97\xbf\xfc%6m\xda\x84t:}Z1\xbdsq\xeb\x0e\x0e\x0e\xe2\xfe\xfb\xefGMM\x0d\xbe\xf9\xcdo\x96\xfc}*\x95\x8a\x85X\xdc}\xf7\xdd\xd8\xb1c\x07+L\xff\xcc3\xcf\xe0G?\xfa\xd1\x8c%]\xe2\xf18>\xfa\xd1\x8f\xe2\xf1\xc7\x1fG(\x14\x82$I\xf8\xfd\xef\x7f\x8f\x07\x1ex\x80e\xcc\xcb?w8\x1cF6\x9bE0\x18D2\x99\x5c\xb6u5\x8b\x85\x85\xf1K\xe32!\x80|\x22\xcf]\x02H\x99u\xf2\xfaW\xf9|\x1eF\xa3\x91\xd5\x0a\xa3\x8e\x11\xf2b\xce\xf4>\x1a\x8d\x06\x16\x8b\xa5\xa0e\x1c\xbd\x8fF\xa3AYY\x19\xaa\xab\xab\xd1\xdc\xdc\x8c\x9e\x9e\x1e\x1c\x8f\x91\x91\x11\x84B!LNN\x22\x99L\xb2\xcf499\x89\xbe\xbe>444\xc0\xe5r\xc1f\xb3\x15(\x8f+\xc1\x80Sfv(\x14\xc2\xd4\xd4\x14\xaa\xaa\xaa\xa6)'4?\x07\x0f\x1e\xc4K/\xbd\x84\x1d;v\xa0\xaf\xaf\x0f^\xaf\x17\x91H\xa4\xa0\xd9}[[\x1b:::P]]\x0d\x9b\xcd\x06\x83\xc1\xc0\xc63\x97\xcbM\xeb\xef]l\xbf\xf3\xf0\x8es\x03j\xb5\x1a\xfb\xf7\xef\xc7\xe1\xc3\x87\xa1\xd3\xe9\x90J\xa5\xf0\xf8\xe3\x8f\x9f4\x083\x5c\xa2(\x1c\xe3S\x9f\xfaTA\xf8\xc6\xcf~\xf63d2\x99\x92D\x8aH\xe5\xad\xb7\xde\x8a}\xfb\xf61bu\xfd\xf5\xd7\xe3\xb9\xe7\x9e\x83\xd3\xe9D6\x9b\x9dS\xd1\xe9\xd3\xd93^\xaf\x17\xf7\xde{/\xd2\xe94zzz\xf0\xd8c\x8f\x15t>Q\x92\xd5\xae\xae.LLL\x00\x00\xca\xcb\xcbq\xe7\x9dw\xe2\xd9g\x9f\x85V\xab\xc5\xe0\xe0 v\xef\xde\x8d\xcb/\xbf|\xc6\xd7\xd7\xd5\xd5\xe1\xd6[o\xc5\x7f\xfd\xd7\x7fA\x14E\xbc\xf3\xce;\x88D\x22\xd0j\xb5E\xdb/R\x9f\xe2\x85\xaa\x13\xc8\xcb\xb0q\xcc\x86\x05s\x01\x173\x06|\xf1-}\x15I\xde\xb5C\xa3\xd1\xa0\xad\xad\x8d\x110jh\x1e\x8f\xc7Y\xbc\x9d\xb2\x06\x96<\x19\xc0`0L\xeb\x19L\x06\xc7h4\xc2\xe1p\xa0\xbe\xbe\x1e\xad\xad\xad\xb0\xd9l\x98\x9a\x9a\xc2\xf0\xf00\x0e\x1e<\x887\xdex\x03\xbbw\xef\xc6\x9e={\xb0\x7f\xff~\xec\xdc\xb9\x13\x03\x03\x03\x08\x87\xc3\x88\xc5b\x05\xbf+\x9dN\xa3\xb7\xb7\x17\xa3\xa3\xa3p\xbb\xdd\x08\x85B\xb3\xb6\x89Z\x8e\xea\x1f\xd5\xfd\xf3z\xbd\xb8\xe2\x8a+\x8a\x1a\xde]\xbbv\xe1\xe7?\xff9\xdex\xe3\x0d\xf4\xf6\xf6\xc2\xe3\xf1 \x12\x89\xb09'R}\xfd\xf5\xd7\xc3\xe9t\xa2\xb2\xb2\x92\x95\x8eQ\xba\x92\x95Y\xfe3\xa9&\x1cK\x1f?\xfb\xd9\xcf\x00\x9c\x8c\x89\xd3\xe9t\xb8\xe3\x8e;X\x1b\xb3\xa2\x86B\x10p\xfc\xf8ql\xdf\xbe\x9d\xc5\xf3\xde{\xef\xbd0\x99L%\xe7\x9cb\xfc\xbe\xf4\xa5/\xe1\xa5\x97^b\xcf\xbd\xf2\xca+\xf1\xd2K/\xc1\xe9t\x96$\x9e\x0b)BX\xadV\xac_\xbf\x9e\xfd|\xe0\xc0\x01<\xff\xfc\xf3E_322\x82\xbe\xbe>6&\xa1P\x08w\xdcq\x07\x8b\x0bT\xab\xd5\xf8\x8f\xff\xf8\x0fx\xbd^$\x12\x09\xa6\xa4\xcb\xf7\xc1\xa4{\x12\xdf\xf8\xc67\x0a\xbe\xdfK/\xbd\xc4.\xd5r[\xa9\x8c\x87>\x1d\xc4\xe3qvy^\x0a\xe0g\xc2\x0a \x80\xf31\x12\xa5\x16\x87\xbc\x84\x0c\xc7\xe2\xaa\x7fD\x12R\xa9\x14\xfc~?\xee\xb8\xe3\x0eF\xd8\x08\xe1p\x98\xa9x\x94\xe1V\xac\x9f\xa3N\xa7\x83\xddng\xc9\x03r\x02(W\x01].\x17\x9a\x9b\x9b\xd1\xde\xde\xce\x8a\x10\x87\xc3a\xf8\xfd~x\xbd^\x8c\x8f\x8fcbb\x02\xb1X\x8c\xd5\x0fL&\x93\xec}(\xeehhh\x08\xc3\xc3\xc3\x18\x1d\x1den\x13e\xff\xda\xe5:oT\x1f1\x1c\x0ec||\x1c\xd5\xd5\xd5(//\x9f\xb6gz{{\xf1\xe2\x8b/\xe2\xe0\xc1\x83\x18\x19\x19\x81\xc7\xe3A\x22\x91(p\xdfK\x92\x84\x0b/\xbc\x10UUU\xa8\xaa\xaabsH\xf3]l\xaf\x16#\x86\x1c\xe7\x0e$I\xc2\x9e={\xd8\xcf\x1f\xfe\xf0\x87a\xb3\xd9fu\xe1n\xdd\xba\x95\xc5\xdc\x02\xc0\xfd\xf7\xdf_\xb2\xc7/%\x85l\xdf\xbe\x1d\xdf\xfe\xf6\xb7Y\x0cq[[\x1b\xb6o\xdf~F3\xc7\xa9\x06\xe9\x83\x0f>\x88\xd6\xd6Vv\xce\xfd\xeaW\xbf\xc2\xb1c\xc7\xa6\xadc\xea\x19L \xd2{\xf7\xddw\xb3\xe7MMM\xe1\xd7\xbf\xfe5\x0e\x1c8P\x90\xd0A\xc8\xa43\xa8\xa8\xa8\xc0\xa6M\x9b\xd8\xe5\xf9\xcd7\xdf\x84\xc1`(8\x7f\x95\x04\xf0t\xf7S\x7f\x7f?\xf6\xee\xdd\x8b\xce\xce\xce%aG\xb9\xabw\x05\x11@9\xa1S.>\xf9\x02\x98)\xb0w\xa6zc\x1c\x0bo\x04\x88H\x84B!\xc4\xe3q\xd4\xd7\xd7\x17(z\xc1`\x10\xe9t\x1a\xc1`\x10\xe1p\x98e\xff\xca\xfb\xce\x12\x0c\x06\x03*++\xa1\xd5j\xa7\xa9G\xd4\xc7\xd7h4\xa2\xbc\xbc\x1c\xf5\xf5\xf5hllDKK\x0b\xe2\xf18#\x81\xe1p\x18\xd1h\x94\x156\xa6vfT\x80\x9a\x0aVS\xfb\xb2c\xc7\x8eatt\x14\xe3\xe3\xe3\x88D\x22\xec0]\x09\xf3\x16\x8dFY\xe1\xe7\x0f~\xf0\x83\x05\xbdy\x01`ll\x0c\xbf\xfb\xdd\xef\xb0\x7f\xff~\xf8\xfd~6>\xcan-j\xb5\x1a\x17_|1jkk\x0b\xd4?\xb9\xd2[\xea`\xa7B\xd4\x1c\xe7\x0e\x8e\x1f?\xceH\x0f\x00\x5c}\xf5\xd5E\x09\xbf\x1c\x89D\x02\xaf\xbf\xfe:\xfb\xf9\xb6\xdbnCccc\xc9\xd7\xa8T*V\xe7O\xab\xd5\x22\x99L\xc2j\xb5\xe2\xddw\xdfea\x05\x8bE6f\xfa?\xa3\xd1\x88\x87\x1ez\x88\x95.\x12E\x11_\xfa\xd2\x97\x90\xcf\xe7\x91\xc9d\xe0\xf7\xfb155\x85X,V4\xeb\xfd\xe2\x8b/\x86^\xafg!\x18;v\xec@8\x1c\x9e\x96\xb8!\xb7a\x9f\xf8\xc4'\xd8\x99:66\x86\x91\x91\x11F\x9a\xc9s1W\x028[\xa2\x15\x95\x9d\x11E\x11\xd1h\x14\xd1htI]^g\xeb\xfd\xce\xb1\x0c\x14\xc0\xb9L\xba\xbc\x91|\xb1\xd7s\x17\xf2\xe2\x92\x08\xca\xfa\x0d\x87\xc3\x98\x9a\x9aBEE\x05\xaa\xab\xab\x0b6\x22\x95|\xa0\x96mD\xb0\x8a\x19\xfc\x8a\x8a\x0a\x18\x8dFV\x97\x8f\x12\x08\xe4\xf3-\x8a\x22\xca\xca\xcaPUU\x85\x86\x86\x06\xacZ\xb5\x0aF\xa3\x11\x99L\x86\xf5\x08\xa6^\xb6Tf\x81\x0eF\xeaTB\x17\x07\x9dN\x87\xc1\xc1A\x0c\x0d\x0da||\x9c\xb9b\x96\xb3\x0aH\xea\x1f\xc5\xfe\x0d\x0f\x0fc\xe3\xc6\x8d0\x99L\xd3\x9e\xb3\x7f\xff~\xf4\xf7\xf7#\x14\x0a1\x82\x9f\xcdf\xa7\x8dOGG\x07\x9a\x9a\x9a\x98\xfaG\x89<\xc5\x0c\xd9L\xfbu\xa6va\x1cK\x13\x87\x0f\x1fF0\x18\x84J\xa5\x82\xcdfc\x85\x9fK\xad\xbb\x9e\x9e\x1e\xd6\xef\x17\x00n\xbc\xf1\xc69\xc5\xdc>\xf1\xc4\x13\x18\x1f\x1fg\x04\xe9\x97\xbf\xfc%\x1c\x0e\xc7Y\xeb\xe0\x93\xc9\x9cT\xe5\x1e~\xf8a\xe6\xb6\x0d\x87\xc3\xb8\xef\xbe\xfb\xa0\xd1h\xb0w\xef^\xbc\xf7\xde{\xacs\x91\x92\xa0\x94\x97\x97\xe3\x82\x0b.`\x8f\x1dD\x22\x11f\x8c\xe4c\xe4\xf7\xfbq\xf8\xf0aLLL \x10\x080w\xba\xb2\x0b\x82N\xa7\xc3\xc6\x8d\x1bQ]]\x8d\x8a\x8a\x0a\x98L&F\xb2g2\xce\xc5B6\xf8E\xed\xdc\x00\xed\xa7W^y\x85\xcdeMMM\x01\xa1)\xb6\xe6T*\x15\xdez\xeb-\x00'c\xd9\xca\xcb\xcb\xf1\xa1\x0f}h\xd6\xdf511\x81\xef}\xef{\xec\xb1\xbb\xee\xba\x8b\xbdn\xb1\xd4\xbf\xb9\xf4\x16O\xa7\xd3\xe8\xe8\xe8\xc0u\xd7]\xc7\xe2\xf9\x9e\x7f\xfey\xbc\xfa\xea\xab\xac\x9da1\x12\xa6R\xa9\xa0\xd3\xe9p\xd1E\x17\xb1\xc7\xfa\xfb\xfb\xe1\xf7\xfbg\xfc<*\x95\x0a\x06\x83\x01UUUl\xff\x1d\x04\x83A\xdcv\xdbm,\xf1C\x8eP(\x84\xb1\xb11\x0c\x0e\x0e\x22\x1c\x0e#\x91H\xb0\xd8\x18\xb9\x92D\xf3q\xfe\xf9\xe7\xc3b\xb1\xc0f\xb317m\xa9\x182\xa5\x0aX[[\x8b\xe6\xe6f\xf4\xf6\xf6B\x14EV\x9a\x86\xe6]\x14E\xe6N\xa1\x9e\x9c\xf4\xb7(\x8a\x08\x87\xc3\xe8\xef\xefg\xad\xcc\x9cN'+G\xb3\x1c\x08\x09\x19\x85x<\x8e`0\x88\xc1\xc1A\x96Y(\x1f\xe7\xc9\xc9Itww#\x16\x8b\xb1\xb8\xa0T*UP\xb3\x91\x08\xde\xe5\x97_\x8e\x8a\x8a\x0a8\x9dN\x94\x95\x95\xb1q.\xa6\xd8\x92\xa23\x9b\xd2R,\xa4\x83c\xe9\xac!A\x10\x0a2y\x01`\xdd\xbau3v\xf1\xa0\xd7\xec\xdc\xb9\xb3\xe0\xf1\x8b.\xbah\xc6\xe2\xcf\xb4\xc6\x9ey\xe6\x19\x00'=:\xf5\xf5\xf5X\xb7n]\xc9.#\xb3|\xf8\xb90\x89y\xbfm*\x95\xc2e\x97]\x06\x8b\xc5\x82H$\x02\x00\xd8\xb9s'ZZZ\x98\x9d*v~\x85\xc3a\xdc\x7f\xff\xfd\xd0j\xb5\x88\xc7\xe3,\xfe\xae\x94\x22J\x9d\x8e(<\xc5\xe3\xf1\xa0\xbd\xbd\x9d\xb9h\xe5\xe5\xd3\x02\x81\x00\xd4j5\x12\x89\x04\xecv;\x9cN';\xc7i\x8c}>\x1f***\x10\x89DX8\xd5\xd8\xd8\xd8\xb4\xb9\xa40\xaa\x85\xdc\x97\xd4\x13Y\xa7\xd3!\x97\xcbadd\x04\x83\x83\x83\x00\x00\x8b\xc5\x82\xb6\xb66\xbe\xe18\x01\x9cy\xf1\x10\x01\xd4h4\xc8\xe5r0\x18\x0c,K\xd4\xedvcdd\x04\xfd\xfd\xfdp8\x1c,\xa1@\xd9\xe0\x9e\x03\xd3T\x1d\xfa\x9b\x5c\xa0\x99L\x86e\xd5\x86\xc3ax\xbd^\xf4\xf4\xf4\xc0n\xb7\xe3\xf6\xdboGUU\xd5\xb4\xb9\xc9d2\xd8\xb1c\x07v\xef\xde\x8d@ \xc0\x0a1\xa7R\xa9\x82Vp\xf4\xfb\xd7\xad[WP@X\x19\xffW\x0cD\xe4\xacV+\xaa\xab\xab\xd1\xd0\xd0\x80\xd5\xabWchh\x88\xa9}Db\xe5\xb1\x80\x14\x9fCa\x04\xf4\x1d\x0d\x06\x03:;;\xd1\xde\xde\x8e\x91\x91\x11\xe6\xd2\x9c\xcbg9W\xd4?r\xfb\x8c\x8c\x8c\xa0\xbe\xbe\x1eN\xa7\xb3\xe0`\x97$\x09\xfb\xf6\xedCww7+\xfbB\xea\x822\xcbp\xed\xda\xb5hjjBuu5\xecv;t:]\xc9\xd6Ss\xddo\xbc\x17\xf0\xd2?+\x0e\x1e<\xc8\xc8\x0f\xc5\xa5\xcd6g\x87\x0e\x1d\x02p2\xc1\xa0\xae\xaenN\x8a\xce\x1f\xfe\xf0\x07\xd6e\xe4o\xfe\xe6o\x0aT\xc7y\xe3\x14\xd6\xdf|\x5c\xc1\x9b7o\xc6\xb6m\xdb\x00\x00o\xbf\xfd6>\xf9\xc9O\xb2\xf2S3\xbd&\x1e\x8f\xb3R1\xb3\x91\xabL&\x83\xea\xeaj\x98\xcdf\x84\xc3a\xd6\xfb\xf7\xf2\xcb/G4\x1a-\x88\xe3U\xa9T\x18\x19\x19\xc1\xf0\xf00\xb2\xd9,\x9cN'6m\xda\xc4\xcef\x82\xd7\xebE\x7f\x7f?\xe2\xf18\xb3\x89>\x9fo\xda\xe7!\xaf\xcdlc_\xac\xc7{\xb1\xc7(\x16qdd\x04\xeb\xd6\xadCYY\x19\x02\x81\x00\xfb\x0cn\xb7\x9b\xd5YT\x8eY\xb19\xe1\xe7\xc5\x0a#\x80\xe4\x02\xa4\xd6?z\xbd\x1e\xa1P\x08\xa2(\xb2\x1e\x89\xc7\x8f\x1fg\xeeD\xca\x94\xa2 \xf5\xb3U>`)\x1e\xe8\xf2\x929D\xfa\xc8\xddKeU\xa2\xd1(\x82\xc1 <\x1e\x0fB\xa1\x10.\xbf\xfcr\x5ct\xd1EE\x8b\xf9\x0a\x82\x80\xbe\xbe>\xbc\xf3\xce;\x18\x1b\x1bC0\x18D4\x1ae\x99eJW\x82 \x08\xd8\xb0a\x03\xaa\xaa\xaa\xe0p8X\x06\xb0\xb2\x06\xa0r\x93\xd3!e4\x1aQQQ\x81\x86\x86\x06455\xa1\xae\xae\x0e\x03\x03\x03\x05\x87\x18)}r\x12(\xbfDP}\xb1L&\x83\xc3\x87\x0f\xa3\xa9\xa9\x09\xf5\xf5\xf5p8\x1c\xcc\xady.\xabR\xf2\xd8?\xaaQv\xfb\xed\xb7O\xcb\x8e\x9e\x9c\x9c\xc4\xd1\xa3G\xe1\xf1x\x98\xfb\xb7X\xe6\xaf(\x8a\xe8\xe8\xe8@MM\x0d*++QVV\x06\x9dN\xc7\x0e\xf0\xd3q\x17\x15\xdb\x97|\xaf.-\x02\xd8\xdf\xdf\xcf\xce\x0cy\xdb\xc7R\xaf9|\xf80\xbb\x84o\xdc\xb8\xb1\xe4kT*\x15\xc6\xc7\xc7Y\xf7\x1e\x00\xb8\xf9\xe6\x9b\xd9\xc5n\xde\x18\x0d\x03\xbf\xef\x06D\x01@\x91\xdfI\x0f]\xdb\x044\x9b\xe7E(T*\x15b\xb1\x18\xae\xb9\xe6\x1al\xdb\xb6\x0d:\x9d\x0e\xe3\xe3\xe3s\xda\x03\xf3\x11\x22\xb2\xd9,\xacVkA\xb9$\x9f\xcf\x07\x00\xac\x16k\xb1q\xa7\x18\xe7t:=\xed\x1c\xcdd2\x18\x19\x19\x99V[\xb7\x18\xf9\x94\xf2\x12\x90/>|\xf3\xdd\xbb\xb1X\x0c\x83\x83\x83\x88\xc7\xe3\xe8\xee\xeeFmm-s\xfb\x92::\x97\xb9^\x8c\xe4\x0f^?x\x09\x11@r\xdb\xc9'\x99\x8c~,\x16c\x1d\x1d\xa2\xd1(\xecv{A?Z\x8dF\x83\x13'N0\x22\x13\x8f\xc7QSS\xc32\xaf\xe6\xbb\x01\x97\x0b\x94\x07\x13);D\xfc\xa8TK,\x16+\x88\xa3\xd4h4hnn\xc6\x95W^9-XY\xbe\xd9\x8f\x1f?\x8em\xdb\xb6\xb1\xae\x11\x81@\xa0@IR\x06+\xd7\xd5\xd5a\xed\xda\xb5,\x8e\x8c\xdc\xbf\xc5H\xbf\x12\x94\xf9Ku\x01\xa90\xf4\xd8\xd8\xd84\xd7\xb5\xc1`(\xd8\xe0\x82 \xb0\x18R*\xe3@1<\xc3\xc3\xc3hhh`I)\xa2(\x9e\xb3\x8d\xd5\xe5\xb1\x7f\x91H\x04\xe3\xe3\xe3\xacc\x87RQ\xa7\xde\xca\xa4\xda\x16+\xfa\x0c\x00UUUX\xbf~=\x5c.\x17S\xffHe?\x9dCw9\x97\xdfYN\x04p``\x00Z\xad\x16\xe9t\x1a\x8d\x8d\x8dszMgg'\x8b\xdd\xa6Vj\xa5\xd6Kgg'K\xe42\x18\x0c(//?\xf5\x0f\x9e\xce\x01\x13Q@3\x03\x01\x14p\x92\xdc$\xb2\xa7\xe4\x06N\xa7\xd3\xd8\xb0aC\xc1c===hnn>\xed\xf5L\xaf\xa7s\xd3f\xb3\xb1\xc7\x02\x81@\xc1\xe5n&2\x94\xcb\xe5\x10\x8b\xc5X\x17\x95R\xf6`\xa6\xef'\xc3E\x87\xb0\x00\x00 \x00IDATe%\xe4uy\xa80s\xa2\xcf\xc0\xc0\x00\xb4:-\xaa\xab\xaaY\xd8\xc7\xc4\xc4\x04&''\xb1f\xcd\x1aX\xadVF\x5c\x93\xc9$k\x8fG\xb1\x88d\x8f\xb3\xd9,\x92\xc9$\xccf\xf3\xbc/\x80s\x1doyMZ\xb2gT\xab\x96B\x7ff\xb2\x9f\xc41\x96k\xac2\xc5\x89\xce\xf5\xb2%\x9e\xce\x22\x97\xdfJ\xe4\x7fK\x92\x84\xf2\xf2r\x8c\x8d\x8dM{>%#\x10,\x16\x0b3\xea:\x9d\x0e\xe5\xe5\xe50\x9b\xcdp:\x9d\x08\x87\xc3x\xf3\xcd7\x11\x08\x04P[[\x0b\xb3\xd9\xcc\x12\x00V2\x01\xa4\x80`\x22eJ\xa5\xc7\xe1p\xa0\xac\xac\x0c\xcd\xcd\xcd\xa8\xaa\xaaBmm-s\x9f*\x95#\x8a\x19\xe9\xed\xed\xc5\xb6m\xdbp\xe4\xc8\x11\x8c\x8c\x8c\xc0\xef\xf7#\x1a\x8d\xb2\xd2\x02\xf2\xa4\x0a*\x7f\xf0\xfe\xf7\xbf\x1f\x0d\x0d\x0d\xa8\xac\xac\x84\xd9l.(\xcf2\x97uE\xae\xdc\xf2\xf2r\xd4\xd5\xd5\xa1\xad\xad\x0dG\x8f\x1e\x85\xdf\xefg\xc9&\xa4l\xca\xe3\xcb\xe41\xa4\xa4&\x93[\xe6\xe8\xd1\xa3hhh@MM\x0dS\x8d\x89 \x9e\x8b\xf3M\xfb\xc5\xef\xf7#\x10\x08\xe0\xca+\xaf,\xd8\xdc*\x95\x0a^\xaf\x17\x9d\x9d\x9d\x98\x9a\x9ab\x074\x112\xf9\x9cK\x92\x84M\x9b6\xb1\xba\x7ff\xb3\xb9@\x95P\x92E\xa5\xda\x5c\xaa\xec\x07\xc5\xf0\xce\xe7f?\xdb\xfbr,<\x01\x9c\x9a\x9ab\x17\xaa\xe6\xe6\xe6\x92\xc6\x97\x1e\x0b\x87\xc3,\xde\xaf\xb6\xb6\xb6`\xee\x8a\xd9\x84\xd1\xd1QFz\xea\xeb\xeb\x0b.p\xf32\xf6*\x15TR\xfed\x0c\xa0<\xd9\xa9\xe0C\xaaNK\x05\x92$\x09f\xb3\x19f\xb3\x99\xd5\xcc\x1b\x1e\x1eF[[\x1b\x8bC\x9e\x8f=\xd4\xeb\xf5\xd3\xceK\xba\xa8;\x9d\xcei\x040\x91H`rr\xb2d\xdd\x5cj\xc7y*H\xa5R\xc8J\xd9\x821\x97'z\x09\x82\x80\x13'N\xa0\xab\xab\x0bZ\xad\x16\xa3#\xa30\x9b\xcd\x88F\xa3\xec\x1cN&\x93\xb8\xe4\x92K`4\x1a\xa7\x95\x95\x917\x05\xa0\xc7\xe3\x898\xca\xca\xca\x98m\x9a\xc9\xfd{\xaa\x9c\x83\x0a\xff\x93\xfd\xa0nV\xd9l\x16\xb5\xb5\xb5X\xbf~=\xcb\x19\xa0\xf5@\x9d\xa3B\xa1\x10\xf4z=.\xb8\xe0\x82\x82\x18\xd6\xb9\xac\x9bd2\xc9<\x5ct\xf1\x96$\x09\xb1X\x0c\x16\x8b\xa5\xe0}\x8a\xbd\x9f\xfc\xff\xe8\xbbP\x85\x0d\x9b\xcd6\xe3\xe5z\xa6\xcf\xa6t\xd1\x07\x02\x01\xf4\xf4\xf4@\x92$\xd4\xd4\xd4\xa0\xa1\xa1\x81\xbdvrr\x92\x09it\xe6\xe7\xf3y\x88\xa7K\xa2\xe4\x85\x9ciQi\xb5ZLNN\xa2\xa2\xa2\x02\xa9T\xaa\x80\x14P\xe0\xbe|Ah\xb5ZVC\xce\xe1p`\xcd\x9a5\xb8\xfd\xf6\xdbQYY\xc9\xcaY\xe4\xf3y\x84B!\x04\x83\xc1eY\xe6c\xae\xaa*\x15T\xb6\xdb\xed\x8c\x04\x91+\xddf\xb3\xc1d21uL9\xb7\xca\xf8\x0e\xbaA\xed\xde\xbd\x1b/\xbe\xf8\x22\xba\xbb\xbbY/\xdeh4\xca\x02\x9c)+\x8d\x16S.\x97\xc3\x86\x0d\x1bp\xc9%\x970\xf7/\x91\xf3\xf9\xb8[)\x09\xc8f\xb3\xb1X\xc0\xf3\xce;\x0f/\xbf\xfc2\x04A@*\x95b\xea\x94\xbc\x9c\x01\xb9\x82\xa9\x86\x17e\x03\xe7r9\x0c\x0e\x0ebpp\x10\xf5\xf5\xf5\xac\xbb\xc5\xe9(\x5cg\x1b\xe9t\x1a\x91H\x04>\x9f\x0f:\x9d\x0ek\xd7\xae\x9dv@P1l\x8f\xc7\xc3\x92?h\x8f\xc8\xc3\x04jjjX\xe1g\x87\xc31\xad\xed\xdbL\x87\x8d\xb2m\xa3\x92(\xca]\xf4\xb3\x19w\xba\xec\xc9K@q\xb7\xcd\xe2\x83\x92/.\xbc\xf0Bx\xbd^\x0c\x0f\x0f3\x028\xd3\xe5\xa8X\x92\x07)A\xa5\x14\x06\xb7\xdb\xcd.\xab.\x97\x0bf\xb3y\xda\xf3\xe7<\xe7\x1a\x0d \x08\x80\xa0\xa6E\xaf\xf8\xfb\xf4\xc6\x85\xca\xd4\xd4\xd4\xd4\xa0\xbb\xbb\x1b\xc0\xc9,\xddS\x09\x1bQ\xab\xd5\xa8\xa8\xa8\xc0\xd4\xd4T\x81}\xa2\xcb\xac\x5c\x09\x0d\x04\x02\x8c\xa0\x95\xb2e\x92$\xc1\xef\xf7\x17\x9d\x8b\xb9z\x10\x90GQ[\x90\xcf\xe7Y\xb9/\xba\xd4{<\x1ex<\x9e\x82\xe7\x06\x83A\x9c8q\x02k\xd7\xae\x85\xdb\xed\x9e\x95\x90\x0c\x0f\x0d#\x93>\x19\x92\x13\x08\x04\x10\x8dF\x17d\x8f\xabT*\x04\x83A\xb8\xddnv\x86\xc8\xc7\x98\xe2'\xa9\xbe%\xd9\xb8\x9e\x9e\x1e\x8c\x8e\x8e\x16(\xad\xe1p\x18v\xbb\x1d\xa2(\xa2\xb5\xb5\x15&\x93\x09\xbd\xbd\xbd\xe8\xe9\xe9\x81\xd1h\xc4\x05\x17\x5c\x80\xb2\xb22\xf6\x9d\x8e\x1f?\x8e\xc1\xc1A\xf6\xfc\x96\x96\x16\xf4\xf4\xf4```\x00\xd9l\x16:\x9d\x0e\x97^z)L&\x13\xb3\x95\x9d\x9d\x9d\x98\x98\x98\x80\xc1`@{{;\x5c.W\xc1\xda\x9f\x9c\x9c\xc4\x81\x03\x07X7\xa5\xf5\xeb\xd7#\x9b\xcd\xc2\xeb\xf52QDN`'''\x11\x89D \x8a\x22\xf3p\x112\x99\x0c:;;\xd9\xba\x9a\x9a\x9a\x82N\xa7\x83\xcb\xe5BWW\x17\xba\xbb\xbb\x91\xcf\xe71::\x8a\x0b/\xbc\x10f\xb3\xf9$\x97\x90W\x15\x9f\xaf\x14K\x03\x9f\xc9d\xe0v\xbb\x11\x0c\x06\x99\xf4O\x8bK\xe96\xa4/\x1b\x8f\xc7\x19Y$\x03m2\x99\x98q\x22bSYY\x89\x9a\x9a\x1a~\x82\xcfC!\x9cOl\xca\xeb\xaf\xbf\x8e\xed\xdb\xb7\xb3\x96jtS\xa0^\xbc\xf2\xac_\x9a?\x83\xc1\x80K/\xbd\x94\xd5\xa4\xa26l\xf3%\x80\xf4\x19\x92\xc9$\xa2\xd1(\x04A`\x89!\x1e\x8f\x07\x82 0\xb5\xd8h4N\xcb2%7\xb2N\xa7c\xaen\xbf\xdf\x8f\xee\xeenX\xadVh4\x1a\x84\xc3a\xd8l\xb6\x19\x13BJ\x8dU\xa9[9\xfb7\xf2,\xbeF\xe9bQ\x12ny(\x84\xf2&^p0#\x8f\x5c\xf6O1\x9d~\xbf\x1fCCC\xac\xa5\x94\xfc\xb9\xd1h\x14\x87\x0e\x1d\xc2\xe4\xe4$\x02\x81\x00\x8b\x87T\x96\xeb\xc9\xe7\xf3\xd8\xb8q#r\xb9\x1c\x22\x91\x08\x9bgRm\xe5\xdd\x08\xe4\xddz\xe8p\x09\x85B\x98\x9c\x9c,X\x17\xc5\xc2\x8f<\xf20\x8c%Q;\xe0A^#\x14\x84~X\xad\xd6\xe9$\xf4\x14\xd5\xe4\x5c.\x07\xbb\xdd\xce~\x8e\xc5b\xa7D\x00\xc9\x86)\xd73\xd9C\xa7\xd3\xc9\x88\x22e\x1d\xcf\xe5\xf3Ro\xf4\xf9|7\xaa\xd9(I\x12\xeb\x92\x14\x0e\x87Q^^\x0e\xadV\x8b\x9e\x9e\x1e\x0c\x0f\x0f\xcf\xd9;2<<\x8c\x91\x91\x919y\xde\xc6\xc7\xc7\xd9s\xb3\xd9,+F_\xcc^\x95R\xb8\x8a)p\xa4.[,\x16\x88\xa2\x88L&\x03\x87\xc3\x81\x96\x96\x16D\x22\x11\xf4\xf5\xf5\xb1\x8b\xb0\xd9d\xc6\xbb;\xdf\x85\xd7sR-t8\x1c\xec\xfc\xa2$\x1eI\x92011\x01\x8b\xc5\x02\x8f\xc7\x03\xb5Z\x8dP(\x84\xae\xae.ttt\xc0`0\xa0\xaf\xaf\x0f'N\x9c`cz\xf8\xf0atvv\x16x\xd3\xb2\xd9,v\xee\xdc\x89\xf6\xf6v\xf6\x9a\x89\x89\x09\xa8\xd5j\xa4R)\x1c8p\x00\xe7\x9dw\x1etz\x1db\xd1\x18&''\x0bzBG\xa3Q\xd6jQ\xadV\xa3\xaf\xaf\x0fn\xb7\x1b\xf5\xf5\xf5\x08\x87\xc38z\xf4h\x81\x08\xd2\xd9\xd9\x09\xb3\xd9\x8c\xc6\xc6F\xb8\x5c.\x0c\x0e\x0e\xb2\x5c\x0a\xaa\xa4\xf1\xce;\xef\xa0\xba\xba\x1a~\xbf\x9f\xed\xdf@ \x80\xbe\xbe>\xb4\xb4\xb4`jj\x0a\xe2{\xef\xbdw\xda\x12:\x19\x15\xb7\xdb] \x9b\x0b\x82\x80d29\xad\x03\x01\xb9\xecHB\xcdf\xb3L\xb1\x92+\x18\xf2N\x10\xa7BN\x973\x8a\xcdQ\xa9\x22\xbe\xf2\xc5\x9aH$\xd0\xdd\xdd\x8d\xd7^{\x0d]]]\xac\xf4\x8e\xcf\xe7C<\x1eg\xca\x1f\x91\x08e\xa0\xf1%\x97\x5c\xc2\xfaL\xba\xddnD\xa3\xd1\x02\xf7\xef|n\xfatK\x8dF\xa3H&\x93\x10E\x11\xb5\xb5\xb5\x98\x9c\x9cd\xe4\x93.\x15\xa4\x80\x12\x11$\x05\x93n\x83d\xb0\x8e\x1d;\x06\x9b\xcd\xc6n\x8c\x0e\x87\x03:\x9d\xee\xd4\xb3\x10K\xcd\xc3I\xe6\x87\x93\xb6?\xbf\xa0sKk?\x12\x89\xc0`0`\xcd\x9a5\xd3J:\xec\xdb\xb7\x0f\x03\x03\x03\xacc\x0b\x91?e1Y\xa7\xd3\x09\x8b\xc5\x82d2\x09\x9f\xcfW\x90}/\xff]\xc5\xe6-\x97\xcb!\x1e\x8f\xc3\xe3\xf1 \x91H\x14\xb8W(\x0bq\xae\xc9#\xd1h\x14\x1e\x8f\x07###\x88\xc5b\xacn\xe4B\xee\x83\xd39\xcf\x94\xef9/\x17 \xad\x85\x05\xfc\xfc\xf3\xfd\x0c\xb3\xc1\xe3\xf1\xb0\xf3\xa0T\xc1v\xb9\xaa%_GtY\xcbKy\xa8\x04\xd5\x8cdj\xcd\x9a5\xc8d2\xa8\xab\xabC2\x99\x9cV\x1ajN\xdfI\xa5\x82\x86B]\x00H\xb9?\x15\xa0\xcff\xb3\xa8\xac\xac<\xf9\x19\x16`\xdb\xc9\x09\xca\x5c\xd6\xa3<\xfb^\xfe\x98^\xaf\x9f\xf6\x7fd\xcb\xe4*\xde|\xe64\x9dN\xb3\xec\xe1\xb9\x90\x7f\xbd^\x0f\xab\xd5\x0a\xaf\xd7\x0bI\x92p\xe2\xc4\x09v\x8eR\xa1\xfeD\x22\xc1\xe2\x0a\xe5\x7f\x94\x97V\xf2\x10\xcc\xa7&/}\x7f:\xbf)1\xb1\x14\xf1\x9fK\x0b\xbfP(\x84d2\x09\xa7\xd3\x89\xd6\xd6V8\x1c\x0e\xc4b1\xb8\x5c.V\xcagdd\x04\x89D\x02CCC'\xd5\x5c\x9f\x1fz\xbd\x1eMMMhnn\x86\xd7\xeb\xc5\x91#G\x90H$\x98-\xa1\x84I\xb5Z\x8dL&\x03A\x10\xe0\xf3\xf90>>\x0e\xa3\xd1\x88\xbe\xbe>\x08\x82\x00\x83\xc1\xc0B\x8dr\xb9\x1c4\x1a\x0d\xca\xcb\xcbQQQ\x81\xf1\xf1q\xf8\xfd~\xec\xdb\xb7\x8fy\xc9\x8cF#\xf4z=\xc2\x91\x931\xd9\x9d\x9d\x9d\x90$\x09\xf1x\x9c\x952\xa3P\x1c\x9f\xcf\x87h4\xca\x08\x5c,\x16\xc3\xe8\xe8(\xc6\xc7\xc7Y\xc2lee%\x9cN'2\x99\x0c\xb3\xd7\xc7\x8f\x1f\xc7\xe8\xe8(\xa2\xd1(\xab\xefZQQ\xc1jwNMM1\xd2N\x89\xb6\x14\xe2\x15\x0a\x85 RP\xef)\x1dx*\x15rR\x0e\xb9l\x0eSSS\xac\xce\xd3L\x86DyP\x90A\xa6\xbaB\xe1p\x18Z\xad\x16\x1e\x8f\x07\xef\xbd\xf7\x1e\x1c\x0e\x07\x93T\xe9\xbd\xb8\xbbhn\xd2?\xfd-w\xdd\x05\x83Atuu\xe1\xd0\xa1Cl\xe1P\xcd\xc5X,\xc6b\xfe\xe4\x0a\x92R\xb5\xda\xbcy3n\xb8\xe1\x06\xac^\xbd\x1a\x8d\x8d\x8dp:\x9d\xd0\xeb\xf5,\x93t\x0eV\xb2P9P\x01\xd9\xcc\xc9\x1b\xae\xdb\xed\x86\xd3\xe9\x84(\x8a\x98\x9a\x9a\xc2\xc8\xc8\x08ssS\xbc#\x1d\xa2D\x00I)\xa6D\x0f\x8aY\x09\x04\x020\x9b\xcd\xac\xc4\x0c}Nf\xb4\xf2s$\x0bE\x94\xbd\x85\x9c\xa7bcF\xb13\xd1h\x94\xd5c\xbc\xed\xb6\xdb\xa6\xed\xa7P(\x84\xc3\x87\x0fchh\x08\xe1p\x98\xa9\x7fr\x97=\xe1\xfd\xef\x7f?.\xbd\xf4Rttt\xa0\xb1\xb1\x91%\xed(U\x19e\x8c(\x19 \x9f\xcf\x87\xee\xeen\x8c\x8d\x8d!\x14\x0a\xb1\xf7&W\xc2\x5c\xe1r\xb9\xd0\xde\xde\x8eK.\xb9\x04\xd5\xd5\xd5\xc5\x95\x9c%~\xd1Z\xca\x9f\xf5t\x09\xafr\x1d\x96\x95\x95\xb1\x90\x0b\x9a\xbf\xab\xae\xbaj\xc6:y\xc0\xc9\xba\xa0\x0f?\xfc0S\x92\xf5z\xfd\xa9\x8d\xa3J\x05\xa1?\x08\xf1\xf8\x11\xe4E\x81\xb9C#\x91\xc8\x9fTh\xb5l\xfd\x9e\xa2@@FY\xeeY\x98M5\xa63'\x10\x08L\xebU\xae\x0c\x85 \xc1CIv\xe6J\xfe\xe9RN\xaf\xb5\xdb\xed\xac\xf4J1\x98L&TTT0\xe5\x87~\x9fF\xa3a\x15\x1d\xe4kE\xadV\xa3\xb1\xb1\x11\x91H\x84\x95\xb6\xa1\xef\xb2f\xcd\x1a$\x93I\x1c:th\xd6\xd0\x0e\xf9\x19B\xef-\x0f\xe1QzB\x8a\x89\x143\x8d7u\xaf\xaa\xac\xacD}}=\xda\xdb\xdb\xa7\x9d\x1b&\x93\x09eeeH$\x12\x18\x1c\x1cd\x8aYMM\x0d\xd6\xacY\x03\x95J\x85\xba\xba:H\x92\xc4H]:\x9df\xe4O\x14E\xb8\x5c.VP\xbb\xab\xab\x8b\x11D\x97\xcb\x85\xf5\xeb\xd7C\x14E\xd6q\x8a\xcahi\xb5Z\xd4\xd7\xd7chh\x08\x13\x13\x13\xc8\xe5r\xb0Z\xadhii\x81\xc9d\xc2\xe0\xd0 \xfaz\xfb\x18\xc1s\xb9\x5c(++\x83\xd3\xe9Dyy94\x1a\x0d+\xd7F\xbd\xee\xa9\xd4N*\x95\x82^\xafG\xc7\x86\x0eT\xb9\xaa\x98\xfdkhh`\xe1N\xe1p\x18\x82 \xa0\xbc\xbc\x1ck\xd7\xaee\xe3B\xed\x01\x05A\xc0\xaaU\xabP^^\x8e\xa3G\x8f\x22\x1a\x8d\x22\x14\x0a\x9d\xf4\x04\x9cn\xd1F2V\x1a\x8d\x06n\xb7{\xc6x\xab\x99\xfa\x1b\x92\x8cK\xb7\x8c`0\x88\xc9\xc9I\xe6\xdaknnF{{;/0{\x0a\x86@\x92$x\xbd^\x8c\x8d\x8d\xe1\xd8\xb1c\x18\x18\x18`\x01\xb4>\x9f\x0fSSS\x88\xc5bl\xf1Q\xf0\xb2\x92@\xd0|vtt\xe0\xe6\x9bo\xc6\xea\xd5\xab\xd1\xd2\xd2\x02\x97\xcb\x05\xab\xd5zJ\xae_\xe5:\xa0x\xbfx<\x0e\x9f\xcf\x87\x81\x81\x01\x16B@EG)\xf1\x85bH)\x03\x8cjJR'\x0a\xb5Z\x8d\x9e\x9e\x1el\xdc\xb8\x91\xb5\x86\xd3h40\x99Lls\xcc\xd9 -\xa0\xb27\xd3<\xc9\x7f&E\x9dZ\xbey\xbd^\xb8\x5c.vp\x91\xbb\x96b\xffN\x9c8\x81P(\x84P(TP\xaeG\xfe\xbe\xb5\xb5\xb5\xe8\xe8\xe8@ss3\xea\xea\xeaX\xd2\x8e\xbc\xf3\x87\xf2\x10\x97\x7f&*(-/\xf2]\xcc}=\x97\x84\x0e\x0a\xdc\xa6\xc3\x9a\x5c9\xcbI\x89_*\x17\xc0\xd3\xfd\x0e\xa4\x8a\xe9\xf5z\xf6oR\xa3g*\x1e=\xd3\xef?\xe5q2\xa4N\xc6\x01jN\xfe.\xaa\x22!wK\x9f\xcew'\x8c\x8d\x8d\xb1KeEE\xc5\xac1\xe6r\xaf\x83\xfc;\x16ss+\x09\xe0|\xc7C\x1e\xca!I\x12\xaa\xab\xabYu\x8cb\xef\xa3\xd3\xe9`\xb7\xdba0\x18\xa6%\x90\x14K\xc8\xb0Z\xad\xd8\xb0a\x03+\x1bF\xcf\xd7\xe9tLY\x93\x87m\xd1z\x90\x8f\x11\x85\xd8P\x22\x0d\xbd\xbe\xa6\xa6\xa6\xe0s\x9e\xca\xfcP\xfc\x7f*\x95BYY\x19\xea\xea\xeaf\xf4\x80\x99\xcdf\x16\xa7Hjnuuu\xc1\xf3\xeb\xeb\xeba\xb5Z\x99+\xfe\xe8\xd1\xa3H\xa7\xd3hmmEkk+K\xaa#\x95\xb0\xa9\xa9\x09\xabV\xadb\x99\xcd---\xd3~\xb7V\xab\xc5\xea\xd5\xabQSS\x83L&Sp\xae556\xc1l2#\x10\x08\xc0d2\xa1\xaa\xaa\xaa \xf9\x0e\x00\x0c\x06\x03K\x96\x02N\x16hw8\x1c\x88\xc7\xe3\xb0X,'\xd5n\xd9w\xd0\xeb\xf5X\xb5j\x15\xacV+&&&\xa0\xd5j\xd1\xd0\xd0\xc0~gee%6n\xdc\x08\xaf\xd7\x0b\x8b\xc5\x82\xda\xdaZh\xb5Zd2\x19\x9c8q\x82er\x8b\x0b\x91\xea\xae\xcc\xe6\x9bO:7\x19t*2,\x08\x02\xdcn7#$\xc7\x8e\x1d\xc3\xf8\xf88+5B\x81\xeb\x92$\xcd\xe8v\x5c\x8c\x83\xf3l\xba\x9e\x95\xf5\x9f\x8cF#\x8b\x9d\x8b\xc7\xe30\x99L\xcc\xfd\x97H$X\xaf\xd8\xbe\xbe>\x04\x02\x01&gS-\xb9P(\xc4\xdc\xbc\x941ZL\xf5\x93+m\xe7\x9f\x7f>\xee\xbc\xf3N444\xa0\xa1\xa1\x01\xe5\xe5\xe5\x8cP\x9d\xae\x11$y\xdah4\xb2\x1b^[[\x1b\xba\xba\xbaX&\x9a\x5c\xa2\x97\xc7\xa8\xd1\xcd\x8d\x94\x86L&\xc3n\xcd\xc7\x8e\x1dCSS\x13jkkY]@r\x19\xcf\xc3\xaf\xb7h\x0a`1C@.\xf1D\x22\x81@ \x00\xaf\xd7\x8b\xff\xd7\xde\x99G\xc7]\x97\xfb\xff=\xfb\xbeg\xb6\xccdk\xd2\xa4{KiYZ\x0ae\x97\x02r\xa5\x02z\x11\x11\x5c\xa8\x82\x0a\xcaQ\xef\xfdq\xc5\x8b\xc7\x85\xe3\x82\x22\x02\xea\x11\xf1\x82\xe0\x02\x17/(\xdb\xbd*{i\x11*]\xd26M\x97\xecK\x93\xc9Lf\xdf\x7f\x7f\xc0\xf3\xf1;\x93I\x9a\xb4\x93t\x92<\xafs\xe64\xcd2\x99\xcc\xf7\xf3\xfd|\xde\x9f\xe7\xf3<\xefg\xfd\xfa\xf5cDR4\x1aE[[\x1b\xfa\xfa\xfaD\xee\x1f\x89?\xba\x86R\xe1N\xe6\xd1&\x93I\xf4\xe2\x96\x1e5O\xf4\x9a\xc6\xfb\xbe\xf9\x18e\x9bO\xa2\x94\x9e\xc7\xe9t\xa2\xbb\xbb\x1b\xc0{\x89\xeb3~=\xb3y@\x9e\x07d\x80R&\x87\x1c2\xe42YdSi@\xa5\xfeg\x95\xf0qN\xd1\xc9d\x12\xa3\xa3\xa3\xc2\xb8\xda\xe7\xf3\xa1\xaa\xaa\x0a}}}\xe3\xce\xfb\xc5E\x08\xf4\xb9RF\xca'*\x00\x8b\x85\x86\xd3\xe9D,\x16\xc3\x91#G\xa0V\xaba\xb5ZEZ\x07U\xe3Ses0\x18,x\x9d4g\x93\xe0\x90\xcb\xe5\xa2\x1bT\xb1\x08!\xf4z=\xccf\xb38\xa1kiiA\xfb\x81v\xc4\xe2\xff\x8c\x9a\x9a\xcdf\x18\x0c\x86\x02oB\xeabB\xa7\x0d':6\xb4Z-jjj\x84\x10+\x85\xcb\xe5\x12\x913\xaa\xbc.\xae\xb0\x95\xc9d\xa2\x98\xc9`4\x88\xe0\x82\xddn\x87\x5c.\x87\xd3\xe9\xc4\xa9\xa7\x9e\x8aX,\x06\x95J%r\xc8\x8b\xafk)aM\xaf\xad\xb8\xc8\xb5\xba\xba\xba\xa0\x96\xe1X\x91z\xb9\x5c.*\xee'\xdaL\x93\xa3Cq~\xbc\x5c.\x87\xdf\xef\x87\xd7\xeb-\xb8\xfe\xd5\xd5\xd5\xd0\xe9t\x22\xe0\xa2,\xc7\xe2}\x22\x17\x96r\x13h\x80P\xdfYz\xc8\xe5rtww\x8b\x81^UU\x85T*\x05\x85B\x01\x9b\xcdV\xf6\xc8`q\xa9\xf6T\x05\xe0t\x08E\xa9\xb0\xa6|,\xda\x0d%\x12\x09$\x12\x09\x1c=zT\x88\xc2@ \xc4!\xe5\xec\xd0\xcf\x90Ap:\x9d\x16\xc7\x85R\xe1Wl\x1a\x9c\xc9d\xb0n\xdd:\x5cv\xd9eX\xb0`\x01\x1a\x1b\x1bE\x05R\xb9\x8c\xb9\xe9\x18B\xa3\xd1\xc0b\xb1\xc0\xe7\xf3\xa1\xb6\xb6\x16---x\xe3\x8d7Dd\x8f\xc6\x09]#\xda\x11K\x13\x99)\x12\x98\xc9dp\xe8\xd0!\x1c:t\x08~\xbf\x1fN\xa7S\xb4<\xab\xd4h2\xbd\xff\xe4\xfbw\xf4\xe8Q1\xe1\x16\x8f\xcfC\x87\x0ea\xfb\xf6\xed\x08\x04\x02\xc2\xf7\x8f\xc4\xb1t\x822\x9b\xcdX\xb3f\x0d\xdcn\xb7H\xa9(\xe7{p\xa2\xe3\x9d\xc5_\xe5\xb3l\xd92\x1c>|X\x1c+\xcd\xe8\x86X\xad\x00\xbcFa\x04-3\x00\xe9L\x10\xb9l\x0ei\x87\x06:\x8b\xfe}u\xa0\x9c\xb2\xa5\x10u$\xda\xb3gO\xc1\xe7\xcf<\xf3L477#\x18\x0c\x8e\xdb\xdb\xb6T\xbb\xcb\xe2B\x17:^\xa4 \x07\x15~\xe4r9q\xb4>U\x9f<\x8dF\x03\x83\xc1\x80\x85\x0b\x17\xc2h4\xc2d2\xc1b\xb1`\xfb\xf6\xed\xe28Z\xab\xd5\x8a#j\xe9\xdaa\xb3\xd9\xb0j\xd5\xaa)\x1b\xe4\xd3\x111u&\xa9\xaf\xaf\x7f/H\x13\x8f\x89\xb5\xd2\xe9t\xc2\xe5r\x89\x9c\xb5|>/,\xde\xa4]\x9cJ5\x05\x98\xec\xdf\xeev\xbbE\xd7\x94\xf1\xae\xb3\xc3\xe1@KK\x0b\x0e\x1e<(\xe6\xce\x89\xda\x0f\xca \x83\xddn\x1f\xf3\x9a\xacVkIk\x96\x89^\xf3Tr\x5cO\xf4\xeb\xc5\xe3n\xa2kW\xfc\x7f*F\x02\xcal\x04]\x5c\xb41\x99\xc4pir1\x0d^24\x8eD\x220\x99L\xa2\xa4\x99\xbe\xa6R\xa9`4\x1a\x0b\x0c \xcb-\xb4*\x19\xba\x91\xc8u]zdC\xc7\xb8\x94\xd7Bm\xc4\xa4Q>\x12H\xd2hQ\xf1\xce\x86\x8eW\xaf\xb8\xe2\x0a\xac[\xb7\x0e\x8d\x8d\x8d\xa2}\x98\xd4\xf3\xaf\x5c\x0b8M\xc6\x06\x83A\x94\xc0755a\xef\xde\xbd\xc2\xe3\x89Z\xdb\x19\x8dF!\x0a\xe9\xef&s[\xaap\xa4\xe3\xef\xc3\x87\x0f\xa3\xae\xae\x0e~\xbf_\xb4\x16\x9cr\x14p\x86\xa0\xe2\x8dD\x22\x81P(\x84\x81\x81\x01\xacZ\xb5j\xcc\x0d\x9eN\xa7\xf1\xf2\xcb/chh\x08###\x22\xf2K\xc7\xc7\xd2\xe3\xa2u\xeb\xd6\x89]\xa2\xd9l.HR?Q\x91Wj\x93\xc4\x82n\xeeE\x12\xd7\xae]+:e\xbc\xf5\xd6[\x93\x9e\xdb\xcbB\x8d\x19\xb8e-\xbd \xa4GF\xd0\xb7#\x8bx\x22\x01\xcb\xcaf\x98\xab\xdf\x8f\x92\xc8e\x80\xe4\xf8q\xb2\x0b'\xd9`\xd1}e\xb7\xdb\xe1\xf5z\xa1V\xab'\xb4^1\x18\x0c0\x1a\x8d\x18\x1c\x1c\x14\xe3\x9e\x12\xf8\x09\xca\xd1\xa2H\xd4\xc0\xc0\x80\xf8ZUU\xd5q\xcdA4\xe7\x1a\x0c\x86\x82\x96k\x14\xed\xa3\xaf\xc9d2a\x15&\x15\x80\x14\xf9\x9a*\x1e\x8f\xa7\xc0\xc6\xc4f\xb3\x89\x82\x22\x99L\x06\xbd^\x0f\xab\xd5\x0a\xbb\xdd\x8e\xfe\xfe~\xe8t:8\x9dND\x22\x91\x82\xf9\xedx\x03+\x1e\x8f\x07\x0e\x87\xe3\x98\xf3\x8bB\xa1\xc0\x82\x05\x0b\x84\xc5\x11\xcfG\xc7\xb8\x07\xa6S\xa0LdGPj'\x90\xcb\xe5D\xb5\x93B\xa1\x10\xc9\xfc4\xf0\xa5\x09\xeat|\x5c*\x17\xe3d\x89\xb2\x99BZ>/\x15qd\x15@\xd1=iO`\x8a\x1eJm]J\x09?\xfa\xbc\xcdf\xc3\x87>\xf4!,^\xbc\x18\x0b\x16,(0|\xa6\x1dd\xb9o.2R\xb5\xd9l\xf0z\xbd\xa8\xa9\xa9\xc1\xe2\xc5\x8b\xf1\xf2\xcb/\x8b\x1cQJ\x98\xd5\xeb\xf5\x22e@\xab\xd5\x22\x99LB\xa5R\x89\xf4\x00\xaa:;p\xe0\x00\x9a\x9b\x9b\xd1\xdb\xdb+\xfa\x15Wb{8\xba\x06\xc9d\x12\x91H\x04CCCH$\x12X\xb3fM\x81\xb0\x92\xc9d\xd8\xbf\x7f?\xda\xdb\xdb\x11\x08\x04D\x82xq\xbf_\x00\xb0Z\xadX\xb5j\x15\x9cNgEF\xff\x98\xd9!\x00\xd7\xacY#\x16W\xdaLN5o\xf3\xb87\x07y\x14\x1c\xed\xcae\xef\x07\x19\xb2\xef\x15\x1fJ\xbf6U\xc3a\x12\x80/\xbf\xfc\xb28\xed8\xed\xb4\xd3\x0a\x1c(J\xfd\x1dF\xa3\x11\x8f\xff\xf6ql\xdf\xb6\x1d+V\xac@ss3\x1a\x1a\x1aD\xb4\xab\xb8\x15&\xbdw\x03\x03\x03\xe25R$k\xaa\x11@i\xe4Q\xfa\x9e\xfa|>\x04\x02\x01q2F\x22\xd3h4\x0a[\x1b\x83\xc1P\xb6\xb1Q\xaa;\x13\x00\xb4\xb4\xb4 \x99L\x8a<\xbbR\x1e\x80S\x9d7hS?U\xfd\xc1\xe2\xef$\x09\xc0\xa9F\xd3\xc8\xe8\x90\x84\x0a\x09\x1c\x8aZ\x91\xe8!\xf1\x22\xcdq:\x9e\xc5l\xb6-\x5c\xd2\x1b^\xba\xd0\x17w\x06\xa1\xef\xa1(\x90T\xec\x15G\xfa\x8a\xf3Q\xa8\xfd\x8fV\xab\xc5\xda\xb5kq\xce9\xe7\xa0\xa1\xa1\x01\xb5\xb5\xb5\xf0\xf9|\xb0\xd9l\xa2sD\xa9\xe3\x8frE\x01U*\x15t:\x9d\xc8\x05\x5c\xb0`\x01\xf6\xef\xdf\x8f\xa1\xa1!\xf1\x1a\xa9j\x9c*\xces\xb9\x9c8\xfaU\xab\xd5\x05\xbd(\xfb\xfa\xfap\xf0\xe0A\xd4\xd4\xd4\xc0\xe7\xf3\x15\xb4\xac\xab$\x11H\x1b\x1a\xca\xe1\xff\x22\xf6\xb4\xee\xc1\xeb\xaf\xbf\x8e\x05\x0b\x16\xe0G?\xfa\xd1\x98(`\xf1<\x1b\x08\x04\xc4\xef^\xb0`AAq\xcdT#\x80\xc5\xe2\x91\x0a\xf2\xe8$\x05x/wo\xcd\x9a5\xd8\xb9s'\xacV+\x1a\x1b\x1b\xcb\xb2\x0e\x01\xff\xec\xe0\x05@x3\x02\xef\xe5\xfd\x9ds\xce9\x00PP\x10R<&\xa6{\xdc\xb2\xf8\xab \x01x\xac\x9b\x92\xbaM\xd0\xc7\x94\x04O\xc7\x95\xc59j\xf3I\xdd\x97\xaa\xb4,\xf5\xb5R\x1f\x97\x12\xe1\xa5\xaa\xd6H|\xacX\xb1\x02\x1b6l@SS\x13jkk\xe1\xf5zE\xc9\xbaN\xa7\x13\xc7\x87\xd3\xf9\xdeS. u\x07\xa9\xa9\xa9Acc\xa3HrN&\x93B\x80\xd2$-\xf5\xb2\xa3\x9e\xa5\x1a\x8dF\x08\xc3\xdd\xbbw\xa3\xb9\xb9\x19\xfd\xfd\xfd\x22\x8aYi\xb9\x80T\xedL&\xcdj\xb5\x1a+W\xae\x1c\xb3h\xbe\xf3\xce;x\xfb\xed\xb7E\xcf_\xa9\xf1\xb3\xf4\xda\xba\x5c.\xb4\xb4\xb4\x08\xf1\xae\xd7\xebO\xb8Z{*\x0b\x043w\xe6\x1f\xb3\xd9\x8c\xfa\xfaz\x1c9r\x042\x99\x0c\x7f\xf8\xc3\x1f\xb0y\xf3\xe6I\xfd,\xdd\x9fO<\xf1\x04>\xfc\xe1\x0f\x9f\xf0\xdc=\x9e\x00:\x9e\x0d\x88F\xa3\xc1\x8b/\xbe(\xd6\x1e\xb7\xdb-\x92\xe6\xc7\x13\x11r\xb9\x1c\xfd\xfd\xfd\xe8\xe9\xed\x11Q\xc3\xb3\xce:K8ZH+\xe4\xa5~\xb8\xc3\xc3\xc3\x05\x82h\xd1\xa2E\xa2\xe5\x1a\xcd{\xe4\x91;\xd9\x08`1\xc5\x95\xa5\xc0{\xa7\x00\xeb\xd6\xad+{\xb1\xa4\xd4\xdaG\xa5R\x8d\xe9\xb7~\xac*j\xde,\xceq\x01(\xbd\xd0\xe3%G\x92\xc5\x04E\xfeb\xb1\x98\xc8c\xa3\xe8\x9f4\xf27\xdf\x0c\xa1K\xd9s\x90`\x9b(\x97b\xa2\xf7\xa9\xf8\x98D\xadV\xa3\xa1\xa1\x01\xe7\x9cs\x0e\x1a\x1b\x1b\xe1t:\xe1\xf3\xf9\xe0r\xb9`\xb7\xdb\xa1\xd7\xebE\x83\xed\x99h\xdbE\xbbh\x83\xc1\x00\xb7\xdb]\x10\x05\xa4\x8a6\x8a\x02R\xc4O.\x97\x17t\xa6\xa0\xb6p\xf4\xbd###\xd8\xbf\x7f\xbf\xa8\x086\x9b\xcd\x22\x87\xb1\x12D \x8dq\x8a\xfeuwwc\xed\xda\xb5b\xf1\xa0\xeb\x18\x8b\xc5\xb0s\xe7ND\x22\x911\x95\xbf\xc5cb\xf1\xe2\xc5\xa8\xad\xadEUU\x15,\x16\xcb\xb4\x19a3s\x1b\xda|\x5cv\xd9e\xb8\xef\xbe\xfb\xa0T*\xf1\xc6\x1bo\x1cS\xf0\xd3\xa6\xf2\xe7?\xff9\xbe\xf4\xa5/!\x9f\xcfc\xfd\xfa\xf5\xa2\xe2\xf4D^\xcfD\xfd\xc6\xa7\xb2.\xf4\xf4\xf4`\xfb\xf6\xed\xe2\xff\xeb\xd7\xaf/\xa8\xf8,57(\x95J\xec\xda\xb5K\xa4]\x00\xc0\xd2\xa5K\x0b\xc4Y\xa9\xea\xe0\x9e\x9e\x9e\xf7\x0c\xb4\xdf\x7f}\xabV\xad\x12\x96id\xca\xadR\xa9\x84?\xdbd\x04\xf0T\xafa9Q\xa9T0\x99L\x18\x1e\x1e\x86V\xab-Y=<\xde\x5cW\xaa\xb9\x03o\x1e\xe7\xa8\x00\x9c\xe8\xe6\xa4.!T\xb0@\xc9\xec\xa9TJ,\xf2\xc5\x1d\x0d\xe6\xd3\xce\xa1\xd8\x5c\xb3\xd4\xc7\xe3\x09;i\x84Oz\xf3\xd1\xee\xcd\xe5r\xa1\xb1\xb1\x11+V\xac@CC\x03\xecv;\x9cN'\xdcn\xb78\xee\xd5\xe9t\xc2\x98r&#<\xd4#\xd8b\xb1\xa0\xba\xba\x1a\x0d\x0d\x0dhhh\xc0\xbb\xef\xbe+\xf2\xfb\xd2\xe94t:\x9d8\xf2\x966\xe8V\xa9T\xe2\x98\x98\xc4\xe1\xfe\xfd\xfb\xb1h\xd1\x22\xf8\xfd~8\x1c\x0e\x98\xcd\xe6\x8a\xc9\x05\xa4<\xcdp8\xfc^{\x1e\xa5\x12---cr}\xde}\xf7]\x1c:t\x08}}}\xa2\xf2\x97\x22\xe4\xd2\xcd\x82\xd1h\xc4\x9a5k\xe0\xf5zQUUU\xf6\xdc?\xde\xcd\xcf\x1fh\xc3y\xc9%\x97\xe0\xbe\xfb\xeeC6\x9b\xc5\xf0\xf00^y\xe5\x15\x9c}\xf6\xd9\x13.\xf4\x1d\x1d\x1d\xb8\xe5\x96[\xc4\xbdy\xd7]w\xe1\x81\x07\x1e(\xcb\x9c(\x9d\xcf\x8a=\xe6\x8e5O\xd1\x91\xf43\xcf<#\x04\x97Z\xad\xc6y\xe7\x9dW \xe4J=\x8fL&\xc3[o\xbd%:c\xd8\xedv\xd1\xe3\x976\x94\xc5\xf7\x99\x5c.Goo\xaf\x10\x80J\xa5\x12\xd5\xd5\xd50\x9b\xcd\xe8\xe8\xe8\x80B\xa1\x10\xc5\x19\xc1`\xf0\x98bn\xaa\x86\xdf\xd3\x91\xab\xadT*\xd1\xdc\xdc\x8c\xe1\xe1aaf<\x99y\x82\xe7\x8ay*\x00\x8b/<\xe5\xb2\xa5\xd3i\x11\x05\x0c\x87\xc3\xa2B\xb5T\xb1\xc2|c\xb2;\x5c\xe9Q\x83t\xd2\x96\xe6\x0b\xaa\xd5j\xd4\xd7\xd7\xa3\xa9\xa9I\xf4\x0f\xa4\xbe\xcbv\xbb\x1dV\xabU\x1c\x15R\x0b\x9b\x89&\xc2\xe9^t\xc8\xcb\xca\xe3\xf1\xc0\xef\xf7c\xc9\x92%8p\xe0\x80\xb0\x06J\xa7\xd3\x88\xc5b\xd0j\xb5\x05\x93\x0fM\xe6\xd9lV\xfcK\x15\xc1\xed\xed\xed\x22\xa7\x91\xda\xc3\x9d\xec\x5c\xc0\xe2\xe8\xdf\xe0\xe0 \x9a\x9a\x9a\xc6x]\xa5R)\xbc\xf3\xce;\xa2Y{<\x1eG\x22\x91(\xc8\xfb\xa3\xeb\xb4|\xf9r\xd4\xd5\xd5\xc1\xe9t\xc2b\xb1@\xab\xd5\x96=\xfa'mIW\xca\xfb\x8c\x99;\x9b\xd0|>\x8f\xe5\xcb\x97\xc3\xeb\xf5\xa2\xaf\xaf\x0f\xb1X\x0cO?\xfd\xf4\x84\x02P.\x97\xa3\xa9\xa9\x09_\xf8\xc2\x17p\xef\xbd\xf7\x02\x00\x1e|\xf0A|\xeaS\x9f\xc2\xca\x95+\xcb2\x1eK\x1d3N6\xbf\xec\xd0\xa1C\xf8\xe5/\x7f)\x22j\xa7\x9f~:\x96/_\x8e\xc1\xc1A1\xef\x15\xcf\x0b\xf9|\x1e\x81@\x00;w\xee\x14\x9f[\xbat)<\x1e\x8f(H,NI!\xa1\xd6\xd3\xd3#^oMM\x0d\xdcn\xb7\xe88\x22\x97\xcb\xe1v\xbb\x91\xcdf'\xf4Y,\xd5\xa5\xe7d\xe2\xf1x\xe0\xf1x\xf8&a\x018y!C\xc2$\x91H\x88\x9eyT\xf9H\xbet\xd2\x82\x06fr\x93\xb4T\xa8)\x14\x0a\xd1\x9e\xa6\xaa\xaa\x0a---\xc2\x10\x93|\xa3l6\x9b\xf8\xbf\xc9d\x12\xa2\x8f\xec\x0fNv\x22-\x19;\x9b\xcdfTWW\xc3\xe7\xf3a\xe1\xc2\x85x\xe7\x9dw\x0a\x8e\x81\xa5cJj\x05Cm\x8f\xe8H&\x16\x8b\xa1\xbd\xbd\x1dMMM\xf0\xfb\xfd\x222v\xb2\xa3\x80\xb4\x09\xa2c\xddH$\x823\xce8\xa3\xe0~\x91\xc9dhmmE__\x1fz{{\x11\x0e\x87E\xf4\x5c\xbaQ\xa2\x8a\xf8\x0d\x1b6\xc0\xe1p\x88#\xfcJ\xf6>df\xc7\xfcRSS\x83\xb3\xcf>\x1b\xbf\xfb\xdd\xef\x00\x00/\xbc\xf0\x02\xee\xb8\xe3\x0e\x98\xcd\xe6\x09{\x80\xdfv\xdbmx\xec\xb1\xc7022\x82l6\x8b\x0f\x7f\xf8\xc38p\xe0@A\xa1\xd6\x89\xdc;\xe3E\xc2&\xaa\x08V(\x14\xb8\xe3\x8e;DG\xa1l6\x8b/\x7f\xf9\xcb\x18\x1d\x1d-\x88\xb2\x95\xca1\x1c\x18\x18\xc0\xe1\xc3\x87\xc5\xd7\xd7\xae]+r\x01\xa5U\xc0\xd2S\x9aT*\x85\xce\xceN\xf1<>\x9f\x0fn\xb7[XY\xa9\xd5j\x98\xcdf\xe1i:\x91\x88\xe5\xfb\x98\xa9x\x01(5\xb4\xa5\x85J\x0ay\xfeQ'\x0aJd\xa7\x9c\xbfb\x8cF#\xf4z\xbdh\xc6,\xbd\xc1\xe8f\x9f1_\xaacL\x94\xd3\xf5\x9c\xc5\xff\xd2b\xafV\xaba\xb1XD\x95'\xb5\xca!\xaf>\x12vz\xbd\x1eF\xa3\x11\x16\x8bE\x1c\xef\xd2\xd7(\xff\xa4\x12\x84\x9ft\xa2#\xbfG\xb7\xdb-\x8c\xa1)\x0aH\xa6\xd7\xe4AE\x16\x15\xf4\xbeP$\x90\x0a$\xb4Z-:;;\xd1\xd5\xd5\x85\xbe\xbe>\xd4\xd6\xd6\xc2j\xb5\x8a\xca\xd8\x93\xf17\xd315\xf9\xfeuuua\xf1\xe2\xc5\xa2I;]\x8bl6\x8b\xed\xdb\xb7\xa3\xb3\xb3S$\x93K;\xb7H\xef\x81s\xcf=\x17UUU\xa2\xf2w:\xa2\x7f\xcc\xfc\xe4\x8a+\xae\xc0\x13O<\x01\x00\xd8\xbd{7\xb6n\xdd\x8a\x8b.\xbah\x5c\x11\x96\xcf\xe7Q__\x8fo}\xeb[\xb8\xe9\xa6\x9b\xa0\xd5jq\xe4\xc8\x11|\xf0\x83\x1f\xc4\xb3\xcf>{B\x9bC\x12n\xa5N\x96(\xa7\x8e\x9c$\xa4si2\x99\xc47\xbf\xf9MaO\x92\xcb\xe5\xf0\x95\xaf|\x05Z\xad\x16\x89DB<\x7f\xb1\xd8\xcaf\xb3\xd0\xe9tB\x00+\x14\x0a\xe8t:\x9cw\xdeyH&\x93\xef}\xbfB>&\x07\x90\xd6\xc1\xbd{\xf7\x8a\xcf577\x0b\xf3\xfaK/\xbd\x14\x89d\x02&\xa3\x09\xd1h\x14\x0a\x85\x02\xd9lv\xdc\xf9h.\x09\xc0\xf9\x96\xd3?g\x05`\xb1MI>\x9f\x177\x98\xd4z\x82nX\xda\x9d\xd1\xf1/U\xfe\x16[\x95\xb8\x5c.\xac\x5c\xb9\x12\xd5\xd5\xd5\x22\x99]\x9a\xa0+\xad\x08\x9e\xe8\xa6\x99\xe9\x08\xdct\xdc$RGu\x12\xc1\xd4\x06\x8d\x1eT\x8dE\xff\xeat:!\x02\xe9c\xfa:y\xe6I\x05S\xa5\xa1P(\xa0\xd7\xeba\xb3\xd9\xe0\xf3\xf9\xe0\xf3\xf9\xd0\xd2\xd2\x82m\xdb\xb6\x89\xd7Lm|T*\x95h)(\xad\x10V\xa9T\x05\x1b\x8b={\xf6\x08c\xe8\xaa\xaa*\xe8t:\xd1\xc7r\xa6\xa1\x08\x1e\xf9\xfe\x85B!\x9cw\xdeyc*\x7f[[[\xd1\xd9\xd9\x89\xbe\xbe>D\xa3Q\x912Q\x5c\x1ce4\x1a\xb1b\xc5\x0ax\xbd^\xd1\xaaO\xa3\xd1L\xeb}q\xacj?fv,\xc4\x93\x19#\x1f\xfd\xe8G\xf1\xa5/}I\xb4g\xdc\xb2e\x0b:::D\xd5}\xa9y1\x93\xc9\xe03\x9f\xf9\x0c\xb6n\xdd\x8a\x87\x1f~\x18j\xb5\x1a\xcf=\xf7\x1cn\xb8\xe1\x06\xfc\xeaW\xbf\x9a\xb25\x8cT`\x8d'\x1ah\xdd\xc9\xe7\xf3H&\x93\x05\x7f[$\x12AGG\x87\xc8\x0d>\xf7\xdcs\xb1a\xc3\x06qZ \x15\x80\xd2M\xb7L&CGG\x07\xb6o\xdf.\xe6\x94k\xae\xb9\x06N\xa7S\xf4\x11V\xc8\x15\xc2\xa7P\x1a\x08\x19\x1a\x1aBww\xb7\xd8\x88m\xda\xb4I\xbc\x1e\x9a\x8b)7\xf0X\xf3\xf7\xa2\xfd=\x00\x00 \x00IDAT\xf0\x5c\x12J\x5c\xf81\xc7\x22\x80\xd2\x1085D\xa6\xddQ\xf1\xe0%\xdb\x8b\xe2\xfe\xa5\xf4=K\x96,\xc1Yg\x9d\x85\xfa\xfazQ\x98@\xc7\x93\xf31\x0c^\x9coE\xb9&\xb4\xd3%\x11#\x8d\xe6\xd1C*\x14\xe9{\xe89*\xf9&$qj4\x1a\xe1r\xb9\xd0\xd0\xd0\x80\xce\xceN\x1c|\x18g\x9ey\xe6\x98\xdc\xa3d2\x89\xdd\xbbw\xe3\xc0\x81\x03\x88F\xa3B\x00\x16wq\x01\x80SN9\x05>\x9f\x0f\x1e\x8f\x07V\xabU\xb4\xeac\x98\x13]\x88I$~\xe3\x1b\xdf\xc0\x96-[\xa0\xd1h\xd0\xd9\xd9\x89\xff\xfe\xef\xff\xc6\x95W^9\xae\x88\xa4{\xf3\xfe\xfb\xef\xc7\xae]\xbb\xf0\xf6\xdbo\x03\x00\x1e~\xf8a\xc8\xe5r\xfc\xe2\x17\xbf\x98\xb2\x08\x9c(\x02H\x7f\x8f^\xafG<\x1e\x1f\x93\x9f\xeap8\xf0\xf3\x9f\xff\x1c\xb7\xddv\x1b\xb4Z->\xfb\xd9\xcf\x8a\xcaT\xe9\xf3K\xdf\x97\x5c.\x07\x9b\xcd\x86;\xef\xbc\xb3`\xa3\xff\xe3\x1f\xff\x18\x7f\xfb\xdb\xdf\x0aOgd\x85\x01\x11\xb5Z\x8d\xd7^{M<\xaf\x5c.\xc7\xe5\x97_>\xee|\xa7\xd1hD$r\xbc\xf5u\xael:\xb8(d\x8e\x08\xc0R\x17\xd1n\xb7\x0b\x13g\xe9\xf7Q\x7fZi\xd4\xaf8\xfaWWW\x87\xf3\xcf?\x1f\x8b\x16-B}}=<\x1e\x0f\xf4z\xbd\x88fHo\x84\xc9V~\xcd\x85\x1bF*\x02\xa5\x0f\x12\x83R\xab\x96b\xaf<\xcaw\x99m\x93\x085.\xb7Z\xad\xf0x<\xa8\xaf\xaf\x87\xdf\xefG0\x18\x14\xd5\xb3\x14\xfd\x94&]K{\x03\x03\x10f\xc9\x0a\x85\x02\xbbv\xed\x12\xc50\xe4\x96?\xd3Q@\xca\xfd\x0b\x87\xc3\x18\x1a\x1aB.\x97\xc3\xb2e\xcb\x0a\x22\xe42\x99\x0c\x87\x0f\x1f\xc6\x8e\x1d;\x10\x0c\x06122\x22<3\xa9\xdf&a4\x1a\xb1t\xe9R\x11\xfd#\xdf?6Ee&\x8a\xf8\xb5\xb7\xb7\xe3\xd0\xa1C8\xf7\xdcs'\xec\xeeC\xc7\xa97\xddt\x13\xbe\xfa\xd5\xafbtt\x14j\xb5\x1a[\xb6l\xc1\x95W^9\xe1\x18\xa3\xcd\xe7\xd3O?\x8d\x0d\x1b6\xe0\xd0\xa1CP\xab\xd5x\xe8\xa1\x87p\xf0\xe0A<\xfe\xf8\xe3\xf0z\xbd\x93\x9a\xcb\xa5\x1b\xa4\x89\xc4\x03m\xfe\x8a\x85i.\x97\x83\xc1`\xc0\xb6m\xdb\xd0\xd6\xd6\x86D\x22!\xee%:U\xa2y\xc0j\xb3bxx\x18.\x97\x0b\xaf\xbe\xfa*v\xef\xde-:\x12\xdd~\xfb\xed\xc2\xfa\xa4\xb8\x08O\xda\xf7V\xa3\xd1\xe0/\x7f\xf9\x8b0\xd5^\xb7n\x9d(N+e\x17c2\x99D.\xa2\xc1`\x80\x5c.\x17=\x84I 2LEF\x00\xa5\x13\x0cE(\x8aof:\x9e\x8cD\x22\x05v/4\xc0\x95J%\xce>\xfbl466\x0a\xdb\x0e\xab\xd5Z\x90\xcb4\xdfv\x0d\xc5\x13\xccx\xbbA\xe9\xce\xb5\x94Y\xeal\x14\x024&\xccf\xb3\xe8\x0e\xb2h\xd1\x22\x1c\x9f\x18\x1fmmmH\xa7\xd3\xa2\x18\x86\xf2\xff\xc8\x08\x9a\xc6\x1e\x1d}\xd3\x04\x1a\x8f\xc7\xd1\xde\xde\x8e\xc6\xc6Faz\xad\xd7\xeb\xcbR\x998\x19\xa4\xbe\x7f###\x88\xc5b8\xe5\x94S\xc6\x5c\xdf\x9e\x9e\x1e\xbc\xf9\xe6\x9b\x08\x85B\x08\x85B\xc2$]\x9a\xfbJ\x1ekg\x9f}6\x1c\x0e\x87\xb8_\xa6\xbb\xeb\x874RY\x09\x05X\xcc\xd4\xb0X,x\xec\xb1\xc7\x84\xe0{\xe0\x81\x07p\xf7\xddwOx\x1cK\xd7\xfb\xd2K/\xc5\x05\x17\x5c\x80\xff\xfb\xbf\xff\x03\x00\xdc{\xef\xbd\xb8\xfe\xfa\xeb\xb1l\xd9\xb2\x09E[>\x9f\x87\xcf\xe7\xc3\xce\x9d;q\xd9e\x97\xe1\xe5\x97_\x86\x5c.\xc7\xd0\xd0\x10>\xfd\xe9O\xe3\x87?\xfc!\x1e~\xf8a\xac]\xbb\xb6 \x80P<\x8e\xa5\x11\xb6\xf1\x8e\x80K\xad\x17t2@\x1e\xa0\xa5\x02\x14\x14\x01\xa4\x8a\x5c\xbb\xdd\x8e\xfb\xef\xbf\x1f;v\xec\x10\xdf\xf3\xaf\xff\xfa\xafp\xbb\xddc\xde'\xe9\x06<\x9f\xcf\xc3b\xb1\xe0\xbf\xfe\xeb\xbf\x0a:|\x5c{\xed\xb5\x22-\xa5\xd4f\xd7b\xb1\x88cs\xa7\xd3YpoI\xd3w\xe6\x0a\xbcy<\xc9\x01\x96\x99\xbe\xd8T\xf9H\x85\x1b\xc5a\xffE\x8b\x16\xc1l6\xc3j\xb5\x8aH\x86\xb4R\x93\x1f\xf3\xefHO\x1a\x05\xa4N%\xcb\x96-\x13\x13<\xd9\x0aIm\x84h\xb2\x97\x16\xc5\x90(R\xab\xd5\xa2\x22\xb8\xb7\xb7\x17\xc3\xc3\xc3\xa2\x0b\xcdtOH\x94\xea\x90H$D\xdb\xb7\xea\xeajX\xad\xd61\x11\xb5m\xdb\xb6\xa1\xb7\xb7W\x1c\xfd\xc6b1Q8\x22\x15\x93k\xd6\xac\x11\xb9\x7f\x16\x8b\x05:\x9dnZ+\x9b+\xa5\xea\x9e9\xfe\x0d\x88\xdf\xef\xc7\xc6\x8d\x1b\x91N\xa7\xa1R\xa9\xf0\xbd\xef}\xef\x98Q5\xba\x17\xd5j5\xfe\xe3?\xfeC\x88,*\x88\xa0\xfb\xefX\x9bV\xadV\x8b\x97^z\x09\x9f\xfb\xdc\xe7\x0a*n\x0f\x1c8\x80\xd3N;\x0d\xcb\x97/\xc7\x1f\xff\xf8G\xf4\xf4\xf4\x8c\xc9\x87S*\x95\xa2\x8bOq\x0el\xf1\xeb,\xfe\xdd\xe4*`\xb3\xd9D\xbe\x1ed\xe3\xdf\xa72\x99\x0c;w\xee\xc4-\xb7\xdc\x22\xee\xa7\xea\xeaj\x5c~\xf9\xe5\x13\xfe\x9d\xb4\xd9\xe3LD\xffJEP\x98\xd9\xb1\xa1\xca\xe7\xf3\xf8\xc1\x0f~P\x10Q{\xf0\xc1\x07E\xca\xc4\xb1\xae\xff\xd9g\x9f\x8dO\x7f\xfa\xd3\xc2\x7fs\xef\xde\xbd\xf8\xc4'>!*\xf1\x8fu/g\xb3Y\xfc\xf4\xa7?\xc53\xcf<\x83u\xeb\xd6\x89\xa8\xb8N\xa7\xc3\xee\xdd\xbb\xb1y\xf3f477\xe3\xd2K/\xc5-\xb7\xdc\x82{\xef\xbd\x17\xbf\xfd\xedo\xf1\xdcs\xcf\xe1\xa5\x97^\xc2\xc8\xc8\xc81\xffF\xe9\xd8\xac\xad\xad\xc5\xc6\x8d\x1bq\xd6Yg\xc1\xe1p\xfcs3\x8d\xd2\xfd\x84\x95J%\xc2\xe1\xb0\xc8\x8d\xa4\xf7\xe8\x96[n\x81\xc9d\x9a\x94H\xfe\xdb\xdf\xfe&6\x96\x00p\xc3\x0d7\x1c\xb3\xe0E\xa1P`\xd1\xa2EX\xb6l\x19\x00\x14\xa4\xa6\xd0I\xc7l\xe5D{73\x15.\x00'\xb3\x18H\x8f\x8dJ-\xb6dUB\xbbK\x1e$\x0cM\xaa\xd4\x7f\xd2\xe5r\xa1\xb6\xb6\x16\x0b\x16,\x80\xc9d\x12\xd5\xe4\xe4+Y||D\x132E\x12\xc9^f\xef\xde\xbd\xe8\xed\xedEww\xb7\xc8\xb1\x9b\xce(`q\xee\xdf\xe1\xc3\x87q\xca)\xa7\x88\x02\x1d\xe9&\xe9\xed\xb7\xdfF[[\x1b\x82\xc1`A\xee\x9f\xf4\xbe\xc9\xe7\xf3\x22\xfa\xe7\xf5za\xb5ZO\xda\xa6\x89\xf3\x01g\xd7B\x9cN\xa7\xb1z\xf5j,\x5c\xb8\x10\x89D\x02J\xa5\x12\xdf\xf9\xcew&5\x8f\xd3\xe9\xcd\xfd\xf7\xdf\x8fe\xcb\x96!\x95JA\xa9T\xe2\xf1\xc7\x1f\xc7]w\xdd5)\x11I\x82\xea\xe2\x8b/\xc6_\xff\xfaW<\xf5\xd4Sp\xbb\xdd\x88\xc7\xe3P*\x95\xc2\xd4\xfd\xa5\x97^\xc2O\x7f\xfaS|\xf1\x8b_\xc4\xb5\xd7^\x8b\xcd\x9b7c\xcb\x96-hkk\x9b\xd0\xec\xb9\x18\xaa\x8a\xa7\x1c;\x00%\xc5\x1f\x09\xacd2\x89K.\xb9\x04###\xc2\xcc\xf9\xa6\x9bn\xc2\xca\x95+\x85\xdf\xe8D\x1c=z\x14/\xbf\xfc\xb2\xf8\xffE\x17]\x84\x85\x0b\x17Njc&\xcd\xdd\xa5>\xec\xf4\xda\xf4z\xfd\xac\x1dw<7\xccq\x01H\x15\xbd\xc7\xf2\xe5\xa3\x5c\xacR-\xa4\xa4\xed\xccJ5\xd6f\xe6\xb7\x08\x94\xe6\x02\xd6\xd5\xd5\xa1\xae\xaeN\x1c=I\x8bB\xa4\x91*z\xd01\xb0\xd4S\xf1\x9dw\xdeAOO\x0f\x06\x06\x06D\x7f\xdd\xe9\x8a\x02f\xb3Yd2\x19D\x22\x11\x11\xd9;\xed\xb4\xd3\x0a\xda\xaa\x01@WW\x17\xb6n\xdd*,b\x8a}\xff\x08\x9dNWP\xf9KQ\xf3\x99\xcc\x95e\xe17;!\x91v\xd7]w\x89\xb1\xd9\xd9\xd9\x89\xfb\xee\xbboR?O\xc6\xeb/\xbd\xf4R\x81\x0d\xd3\x9dw\xde\x89G\x1f}tR\xe2LZ\xa8u\xf9\xe5\x97\xa3\xbf\xbf\x1f\xcf?\xff\xf8\xa0\x98\x7f\xa4\xc7\xcb\xd2\x00\x85\xf4\xf7=\xf8\xe0\x83\xc2\xc7\xb4\xa1\xa1\x01\x9f\xf9\xccg\x8e\xeb:iuZ,jY\x84E\x8b\xdf\xb3D\x9b\x8dE \xc5\xc6\xd8\xcc\x1c\x15\x80\x93\xbd\xc0\xc5\xe2\xb08\x12\xc8\xdee\xccD\x22P\xab\xd5\xc2\xe1p\xa0\xa6\xa6\x06MMM\xa8\xae\xae\x16\xbez$\x04\xa9ZV\xba\x18\xa8\xd5j\xf1\xf3tL\x9aN\xa7\xd1\xda\xda\x8a\xae\xae.\x0c\x0e\x0e\x22\x1c\x0e\x1f3\x99\xfdx\x05 \xf5\xbc\x0e\x04\x02\x88F\xa3X\xbe|\xb9xm\xe4C\xb6\x7f\xff~\x1c8p\x00\x83\x83\x83\x08\x04\x02\xc2\xa7Lj\x94N\x0b\xcb\x19g\x9c\x01\xaf\xd7+r\xfff:\xfa\xc7\x85 \xb3_\x04\x9aL&l\xd9\xb2E\x88\xb0\xd6\xd6V<\xf5\xd4SSZ\xac/\xbc\xf0B<\xf2\xc8#B@\x01\xc0\xd5W_\x8d\x1f\xfd\xe8G\xc2~i2\xaf\x85\xbaw\xe4r98\x1c\x0e\x5c{\xed\xb5\xb8\xe7\x9e{\xf0\xe7?\xff\x19\x7f\xfd\xeb_\xf1\xdak\xaf\xe1\xb5\xd7^\xc33\xcf<\x83\xe6\xe6f\xa4\xd3\xe9I\x09\xc0\xe2\xc0\xc4x\xe2D&\x93\xe1\x8e;\xee\xc0}\xf7\xdd'N\xa9\xf4z=^\x7f\xfdux\xbd\xde1\xebSq\x852}|\xcf=\xf7\x08G\x81t:\x8d[o\xbd\xf5\xb87L2\xc8\xe0v\xbb\xd1\xd2\xd2\x02\x8b\xc52'\xc6\xdcd\x8d\xc7\x99Y&\x00'\xbb\xf8\x14W\xfe\x16G\x0a\xa7#\x02\xc3\xcc\x0d(\x11\x9ar\x01\xfd~?\x96/_.\xc4H,\x16\x13\xc7D\xa5\xa2\x80d\x8cM\x1fS\xc1\xc5\xe1\xc3\x87\xd1\xd5\xd5\x85\x91\x91\x91\x92\xfd\xab\xcb\x11\xfdK\xa5R\x18\x1d\x1d\xc5\xe0\xe0\xa08\xc6.\xe6\x9dw\xde\xc1\xf0\xf00\x06\x06\x06\x90L&\x0b\xbc2\xa5G_\x8d\x8d\x8dhnn\x86\xd3\xe9\x14\x95\xbf3\x19\xfdc\xe6\x0e\x9f\xfd\xecg\xe1\xf7\xfbE\xf5\xea\xf5\xd7_?\xa5{ \x97\xcb\xe1c\x1f\xfb\x18\xee\xb9\xe7\x1e\xc4\xe3qqzs\xdbm\xb7\xe1\x9b\xdf\xfc\xe6\x94\xac\x95J\xd9\xbe\x00\x80\xc9d\x82\xc7\xe3A]]\x1djkkE\x04p\xa2\xe78\x96\x07\xaa\xd4P:\x91H\xe0\xdf\xfe\xed\xdf\xb0u\xebV\xf17%\x12\x09\xec\xdf\xbf\x1fUUU\xc8d2c\x8c\xf8\xa5\xcfA\xddG\x8e\x1c9\x82{\xef\xbdW\xe40644\xe0\x92K.\x19\xf75\xcc\xb7\x0d\x07\xb5\x80e\xe6\xa0\x00\x9cj\xa8\xb7\x94\xad\x09G\xff\x98cA\x15\xc1v\xbb\x1d\xd5\xd5\xd5\x22\x12H\x96\x11\xa9T\x0a\xf1x\xbc\xa0\x070\x8d+*\x02!k!\xa5R\x89\xd1\xd1Q!\x00)\x0a(=J>Q\xa4\x95\xbf\xa1P\x08}}}\xd8\xb0a\xc3\x98{\xa1\xbd\xbd\x1d\x07\x0f\x1eDWW\x17\x82\xc1\xa0\xa8 \x94F\xff\xe8\xde\xa0j\xc6\xaa\xaa*q$\xc6>\x99\xcc\xf1\x8cM\x00\xf8\xc9O~\x22<5\xe5r9.\xb9\xe4\x12\xe1\x957\x99\x8d\x7f*\x95\xc2\xad\xb7\xde\x8a\x9f\xfe\xf4\xa7b\xbc*\x14\x0a|\xfd\xeb_\xc7\xd5W_\x8d\xde\xde\xde\xb2Dv\xa4y\xe1\x13\xdd\x9fr\xb9\x1cN\xa7SD\x09\xc7\xab@\xd5h4hmm\xc5\x96-[\xb0o\xdf>\xf1\xdcUUUhkk\x83\xd7\xeb\x1d\xb3NI\xc5\xa5\xf4y\xe5r9n\xb8\xe1\x06\x91\x8f\x0c\x00\x9f\xff\xfc\xe7\x11\x8f\xc7y\xa0\x15m\x18\x8aE!3\x07\x04`\xa9\x0b9\xd5s\x7f\xce\x13`&\xb3\xe0h4\x1aX,\x16x\xbd^\xd4\xd6\xd6b\xd1\xa2E\xc2\x0c\x9a,%\xa4\xc7\xc0R\xe3b\xaa2\xa7#aZ\x04\xba\xba\xba\xd0\xd9\xd9\x89@ \x80X,V\x96#N:\x9a\xa6\xe8_ww7\xbc^/\xbc^\xef\x98\xe7omm\x15\xbe\x84\xd1hTD\xff\xa4\x0b\x1d\xb5\x8c\xab\xaf\xaf\x87\xd7\xeb\x15\xb9\x7f\x95d\x974\xd5\xfb\x9d9\xb9Q\x99t:\x8d\x0f~\xf0\x83\xf8\xe8G?\x8aT*\x05\x85B\x81W_}\x15?\xfb\xd9\xcf&\xbd\xa9\xa0\x88\xdc\xe7>\xf79<\xf5\xd4Sb\xa3\xa6R\xa9\xf0\x87?\xfc\x01\xeb\xd7\xaf\xc7\xd0\xd0\xd0\x09\x8fQi\xca\xc4D\xbd\x80\x95J%\xea\xeb\xeb\xb1r\xe5J,]\xba\x146\x9bM|\x9d\x0c\xe35\x1a\x0d\x1ex\xe0\x01\xfc\xbf\xff\xf7\xff\x10\x0a\x85D\x8b\xb6\xfa\xfaz\xfc\xe3\x1f\xff\xc0\xc2\x85\x0b\x0b|\x0a\xa5\x05\x8a\xd2\x8fi\x1c?\xf4\xd0Cx\xe9\xa5\x97\xc4\xef\xd9\xb4i\x13\x1a\x1b\x1bgu\xe5n\xb9\x84^\xa9{\x9d\xd7\xfa9&\x00y\x82gfj\xd1\xa2\x8a\xe0\xaa\xaa*\xf8|>\xf8\xfd~444 \x95J\x15\xe4\x06\xa5R)1aK;\x04\xd0q\x17=\x12\x89\x04\xf6\xed\xdb'*\x82\xc3\xe1pY*\x82\xa9\xb0#\x16\x8baxx\x18\xbd\xbd\xbd8\xff\xfc\xf3\x0bz;\x03@OO\x0fv\xef\xde\x8d\xee\xeen\xf1\xbb\xa5\xa6\xcfR\xdf\xbfSN9\xa5 \xf7O\xab\xd5VD\xf4O\xfa^M\xf6\xbe\xe7h\xff\xc9\x87\xf2\xf4\xee\xbd\xf7^\xb8\xddnqDw\xe7\x9dw\xe2\xc8\x91#S\xda\x98\x01\xc0\xbf\xfc\xcb\xbf`\xcf\x9e=\xa2u\x1c\xf0\x9e\x0dJUUUY\xd6\x83\xc9\xf4\x02\xa6\xfb\xba\xb9\xb9\x19MMM\xa2\xea\x99ZF\xbe\xf2\xca+X\xb0`\x01\x9e|\xf2I1\x0e\xa9\x08\x85\x22\x7f\xc5\xbfS\x1a\x01\x94\x1e\xfd\xa6\xd3iD\xa3Q\xfc\xfb\xbf\xff\xbb\xf8\xba\xd3\xe9\xc4u\xd7]\x87\x5c.7'r\xf7\xca\xad\x07\x8a\xfdL\x999,\x00y\x92g\xca\x0d\xb5E2\x1a\x8dp\xbb\xdd\xa8\xaf\xafGCC\x03\xf4z=\xb2\xd9,\x92\xc9$\x92\xc9$r\xb9\x5c\x81Y4\x89@\x95JU\x109S\xa9ThkkCWW\x17\xfa\xfa\xfaDE\xf0\x89\x1c\x03\x93\xf8K&\x93\x08\x85B\xe8\xea\xeaBSS\x13\xecv\xfb\x98\x1c\xc5]\xbbv\xa1\xbd\xbd\x1d\xa3\xa3\xa3\x08\x87\xc3\x22\xff\xaf\xb8\xd2\xb1\xb1\xb1QTC\xda\xedv\xe1\x13V\xe9\x13~\xf1\xff)\xcf\xb7\x9c\xb9\x96\xcc\xf1\xa3T*QUU%z\xfa\xcad2\x0c\x0c\x0c\xe0\xfa\xeb\xaf/\xb0V\x9a\xec5onnF \x10\xc0\x86\x0d\x1b\xb0j\xd5*\xfc\xecg?C2\x99,\xcbZ0\x99\xcdN\xa9~\xc1\xf9|\x1e;w\xee\xc4\xe5\x97_\x8es\xce9\x07\xa3\xa3\xa3\x05\x91\xbc\x8f}\xeccx\xe4\x91GJ\xaeY\xb4i,6l\xa7t\x92\xdbo\xbf]\xf4\x1c\xcf\xe5r\xf8\xcew\xbe\x03\xaf\xd7\x0b\xbd^?\xa6\xc77\xc3\x81\xa2y#\x00\x19f: \xd1\xa6\xd7\xebEEpcc#\x5c.\x970L\x96V\xf3f\xb3\xd91\x91\x03\xa91\xb4L&C2\x99\xc4\x9e={\xd0\xd9\xd9\x89\xc1\xc1AD\x22\x91\x82<\xc2\xe3\x15\x80\x14\xfd\x0b\x87\xc38\xe5\x94S\xc6$\xba\x0f\x0c\x0c\xe0\xad\xb7\xde\xc2\xe0\xe0\xa0(B\xa1\xdf+\xcd\xfdS*\x95X\xb5j\x15jjj\xe0t:a0\x18\x0a|\x0dgj\x92&\x9fO\xfa\xdcd+1\xa5\xcf#=Zc*\x87\x8f}\xecc\xb8\xf4\xd2K\x85\x98y\xe5\x95Wp\xc3\x0d7L\xaa\xc3\x87\xf4\xba\xd3&\xeb\x85\x17^\xc0\xd6\xad[\x91\xcb\xe5\xa0\xd1h\xca\x22\x0a\xc6\x1b\xebTAL) 4\xbe\xe2\xf18\x1ey\xe4\x11\x5cq\xc5\x158\xf5\xd4S\xf1\xa7?\xfd\x09j\xb5Z\xe4\xd7.[\xb6\x0cw\xddu\x17\xae\xbb\xee:\xc4b1Q\x8d\x5c,\x00\x8b#\x80\xd4g\xfc\xdb\xdf\xfe6\xfa\xfa\xfa\xa0P(\x90N\xa7q\xc3\x0d7\xe0\x93\x9f\xfc$\x1a\x164\xe0\xb4\xd3N\x83V\xab\xe5\x81\xc5T\xe6\xc6\x8f\xdf\x02f\xd6\xee^\xde\xf7\xf5\xb3X,p\xbb\xdd\xa8\xa9\xa9\xc1\xa2E\x8b\xd0\xdb\xdb+\xaa\xf6\x92\xc9$\xf4z\xbd\x10-R?@Z\xa8(\x7f)\x93\xc9\xe0\xc8\x91#8|\xf80jkk\xe1r\xb9`2\x99\xc4\xf7L\x05\x8a8R\xf4\x8f*\x7fkjj\x0a\x16,\x99L\x86\xdd\xbbw\xa3\xa3\xa3\x03###\xe2\xf8\x97\x8e\xb1\xa5\xa2\xcb\xeb\xf5b\xc5\x8a\x15p\xb9\x5c\xb0Z\xad0\x18\x0c\xd3\xde\xf6\xedD\x16\xec\xf1\x16\xeab\x01\xc9T\x16\x7f\xfa\xd3\x9f\xe0\xf3\xf9088\x08\x85B\x81G\x1f}\x14\x8d\x8d\x8d\xf8\xc67\xbe1\xe5\x13\x9d\x135.\xa6\xfb\xa08\xe7N\xea\xfbIb/\x97\xcbA\xa9T\xc2`0\xc0b\xb1\xe0\x8d7\xde\xc0\xfd\xf7\xdf\x8f\xe7\x9f\x7f\x1e\x81@@\x8c5\x95J%\x8a4\xbe\xfc\xe5/\xe3\x8c3\xce\x80N\xa7\x13]H\xc6\x9bkJmZ\x1e{\xec1l\xdb\xb6M\xbc\xd63\xce8\x03\x0f<\xf0\x002\x99\x0cjkjy0\x1dC\xcc3'y\x0d\x9d\xc9\x8b\xcf\x83\x80)'\xd2(\xa0\xddn\x87\xdf\xefGcc#\xdcn\xb7\x88\x04\xe4r9Q\xd5+\xed\x17L\x22\x90\x1e\x9434::\x8a\xf6\xf6vtuuahh\xe8\xb8*\x82i\x9cSnP(\x14\xc2\xd0\xd0\x10V\xaf^=\xe6\xf5\xf7\xf7\xf7\xe3\xad\xb7\xdeB__\x1f\x82\xc1 \xb2\xd9\xac(^\xa1\xc8#5\xbe_\xb7n\x1d\x5c.\x17\x9cNg\xc5W\xfeR5i\xb1\x10\xa4\xbcF\x8e\xfcU.\xc9d\x12;v\xec\x10\xd7K&\x93\xe1[\xdf\xfa\x16\x9ex\xe2\x89\x19\xa9\xda\xa4\xe7\xdf\xb6m\x1b\xda\xda\xda\x10\x0a\x85\x10\x89D\x90H$\x84):U\xda\xc6\xe3qD\x22\x11\x8c\x8c\x8c\xe0\x9dw\xde\xc1SO=\x85\xaf}\xedk\xb0\xd9lX\xbf~=~\xf3\x9b\xdf`tt\xb4\xe0\xf9\xb5Z-n\xbe\xf9f\xf4\xf7\xf7\xe3\xa2\x8b.\x82B\xa1\x10=\xeaK\x8dY\xfa\xbf\xf4s*\x95\x0a\x7f\xfe\xf3\x9f\xf1\xa3\x1f\xfdH|\xde\xedv\xe3\xf1\xc7\x1f\x17Ef\x0cS\xe9\xcc\xd8(\xa5\x89\x84\xcdc\x99\xb2\xee`\xde\xaf\x08\xb6Z\xad\xf0z\xbd\xf0\xfb\xfdX\xb2d\x09zzz\xc4\x91\x0c\x1d\xf7\x90\x8b>E\x14\xb2\xd9\xac\x88\x06\x90\xe5E.\x97C{{;\x16-Z\x84\x9a\x9a\x1a\xb8\xddn\x98\xcd\xe61\x1d\x00hL\xd3sI\x05\x0dE\xee\x92\xc9$\xc2\xe10\x06\x06\x06\xa0\xd5j\xd1\xd4\xd44&\x82\xb2o\xdf>\xb4\xb5\xb5!\x12\x89 \x16\x8b\x89~\xc4\xd2$\xe9|>\x0f\xbf\xdf\x8f\xa5K\x97\x8a\xe8\x9f^\xaf\xaf\x88\xca\xdf\xf1\xaa0\xd5j5\x22\x91\xc8\x18\xa1G\x02\x90\xc49o\x0a+\x0f\x8dF\x03\x97\xcb\x85g\x9f}\x16\x17]t\x91\xe8\xdet\xf5\xd5W\xe3\xc9'\x9f\xc4\x87>\xf4!\xa4\xd3\xe9i\xebJ\x91\xcdf\xa1T*q\xcd5\xd7\xa0\xb3\xb3\x13\x00\xe0\xf1xPUU%r^GGGE\x8e\xef\xe8\xe8(\x8e\x1e=:\xe6o {\x1a\x8a\x10\xaeX\xb1\x02\x9b6m\xc2\xed\xb7\xdf\x0e\x87\xc3\x81\xe1\xe1a\xb1.QZ\xc2x\x1b\x13\x12\x80\xb9\x5c\x0ef\xb3\x19\x8f=\xf6\x18>\xfe\xf1\x8f\x8b{0\x95J\xe1\xe1\x87\x1fF}}=\x0f\xa0In\xde\x999.\x00yrgfb\x22!_@\x87\xc3\x01\x9f\xcf\x87\x86\x86\x06\xd4\xd6\xd6\xa2\xa3\xa3\x03\x0a\x85B\x08\xbcd2Y\xd0-\x83\x8eu(\xe7\x87\x04a(\x14B[[\x1b\xfc~?jjj`\xb5Z\xa1\xd5jE\x94\x90\xc6\xb64*X,\x00\xd3\xe94\xe2\xf18\x82\xc1 \x8e\x1c9\x82\xab\xaf\xbez\xcc\xc47<<\x8c\xad[\xb7bdd\x04\xc3\xc3\xc3\xa2\x0d]q\xee_>\x9f\xc7\xc6\x8d\x1b\xe1p8\xe0r\xb9`6\x9b\xc5\xeb\xa94\xb2\xd9,\xd4j5\xc2\xe1\xb0\xe8\x11K\x11L\x8a.\xa5\xd3\xe9\x13\xca\xaddf\x86\x0b/\xbc\x10w\xdf}7\xbe\xfa\xd5\xafB\xa3\xd1 \x99L\xe2\xaa\xab\xae\xc2\xa3\x8f>\x8a\x8f|\xe4#\xc8f\xb3\xd32\x06i\xdd\x18\x18\x18\x80\xd9lF4\x1aE\x7f\x7f?\xfa\xfb\xfb\xc7_\xc8\xde\xcf\xe5\xa5\xfb2\x99L\x8a\xcfo\xd9\xb2\x05\x9f\xf8\xc4'\xb0p\xe1B\xd8\xedv\xf1;hSW,\xf2&\x12,\x1a\x8d\x06\xbf\xf9\xcdo\xf0\xdd\xef~W|.\x95J\xe1\xf6\xdbo\xc7\x07>\xf0\x01.z<\x0em\xc0\xef\xd9\x1c\x13\x80\x5c\xe2\xcd\xcc$r\xb9\x1cj\xb5Z\xf8\x02RAHOO\x8f8*\xa2c^\xda\xf1\x17\xb7s\xa2\xa4u\x1a\xbf\xfb\xf6\xedCSS\x13:;;\x85\xd92\xe5\xdbI\xab\x00\x8b'/:\xbaM$\x12\x08\x06\x83\xe8\xee\xee\x86\xdb\xed\x86\xcf\xe7C.\x97+Xp:::p\xe8\xd0!\x04\x02\x01\xc4\xe3\xf11]?\xe8\xf9\x1a\x1a\x1aPWW\x87\xea\xea\xea\x93\xd2\xf3w\xbc\xfb\xbb\x18\x85B\x01\x93\xc9\x84\x91\x91\x11h4\x1ad2\x99\x82\xe35\xe0\xbdN-\xd4\xadE\x1a\x05\xe4\x05\xa0\xf2\xc8f\xb3\xf8\xcaW\xbe\x82X,\x86\xff\xfc\xcf\xff\x14\xd7\xe8\xba\xeb\xae\x83J\xa5\xc2\xe6\xcd\x9b\xa7\xe5\xf7\xaaT*Q\x80\xa5T*\x8f\xb9\x8e\xd0}H\xe3\xd2\xeb\xf5\xe2\x9ak\xae\xc1\x8d7\xde\x88%K\x96\x881{\xac\xce R\x93\xe9b\xb1\x22\x93\xc9`6\x9b\xf1\xf4\xd3O\xe3\xee\xbb\xef.\xd8\xe8}\xf1\x8b_\xc4E\x17]\xc4\x91\xad2\xcc!\xcc\x1c\x10\x80\x0c3\xd3\x02\x90\xfav\x92/`CC\x03\xf6\xed\xdb\x87\xbe\xbe>(\x95J\x11\x05\x94\xb6\x80#\xd1\xa2\xd1h\x84A4M\xe2\xa9T\x0a{\xf6\xec\x81\xcf\xe7\x83\xd3\xe9\x84\xd1h\x84Z\xad.\x10_$\x06\xa5\x93>\xf5!\x8eD\x22\x18\x1a\x1aB__\x1f\xae\xb8\xe2\x8a\x82D\xf6|>\x8fh4\x8a\xbf\xfc\xe5/\xa2\xf2\x97\x16<:V\x96.z\xcb\x97/GMM\x0d\xaa\xaa\xaaD\xf4o\xa6s\xe8\xc6\xeb\xa4 \xfd\x1aE[\x13\x89\x84\xe8\xb2\x22\xed\x05+\x93\xc9\xd0\xdd\xdd\x8dP($:\x9dX,\x16\x16\x80\x15\x0a\xf9c~\xe3\x1b\xdf\x80\x5c.\xc7\x9dw\xde)6P\x1f\xfe\xf0\x87\xf1\xc3\x1f\xfe\x10\xb7\xddv\xdb\x98\xd6\x9e\xe5\xc0h4\x22\x91H\xa0\xad\xad\x0dmmm8t\xe8\x10\xc2\xe10\xa2\xd1(\x86\x87\x87\xd1\xd9\xd9\x09\x9b\xcd\x86\xc5\x8b\x17\xc3\xe5ra\xc1\x82\x05\xa8\xaf\xaf\xc7\xc8\xc8\x08\x14\x0a\x05\x96.]\x0a\xa3\xd1\x88d2)\xa2\x83\xa5\xe6\x8d\x89\xc6x\xf1\xe7\x9f\x7f\xfey\xdc}\xf7\xdd\x22\xbf/\x93\xc9\xe0\x9e{\xee\xc1\xcd7\xdf\xcc\xe3\xf78`#\xe8y&\x00\xf9b3\xd36\x90\xdf\xaf\xd4\xb5X,\xa8\xae\xaeFCC\x03\x1a\x1b\x1bq\xf4\xe8Q\xd1\x98=\x93\xc9@\xaf\xd7\x8b\x89\x87\xf2\x00)\x82(\xb5\x83\x91\xc9d\xe8\xe9\xe9Akk+l6\x1b\x8cF#T*\x15\x9cN\xa78>\x92&\xc4S\x04\x22\x99L\x8a~\xbf]]]\xf0\xfb\xfd\xa8\xab\xab\x1b\x13M\xd8\xb9s'\xda\xda\xda\x84=\x0cY\xd6\x14\xe7\xc5\xb9\xddn\xac\x5c\xb9rL\x0e\xd4\xc9\x16\x80\xd2\x1d\x0c\xa3\xd1\x88\xcd\x9b7\xc3\xeb\xf5\x8eY$v\xef\xde\x8dW^y\x05\x07\x0e\x1c\xc0\xc8\xc8H\x81\xaf\x99\xb4\xf27\x97\xcb\xe1\x82\x0b.\x80\xddn\x87\xdb\xed\x86\xc5b\xa9\xa8\xe8_q\x15\x9f\xf4c\xb5Z-\x8cv\xe5r9\x0c\x06C\x81\xa7\xa1B\xa1\xc0\xe0\xe0 ^~\xf9e\xa4R)\xc4b1\xd4\xd5\xd5\x89\xc8\xeaLu6\x99/\xd0F&\x9f\xcfC\xa7\xd3\xc1h4\xc2h4\x8a\xb6\x89\x93\x8d\x04R\x84|\xe3\xc6\x8d\xd8\xb5k\x176m\xda\x84\xfd\xfb\xf7C\xab\xd5\x22\x9dN\xe3\xdak\xaf\xc5#\x8f<\x82\x9f\xfc\xe4'hjj\x12\xc2\xbf\xdc\x22\x89\xee\xdf1'J8~1A\xa6\xd2V\xab\x15o\xbf\xfd6n\xbd\xf5V\xbc\xf6\xdak\x05\x91\xeds\xce9GX\xbd\xb0\xf8cX\x00\x1ec\x91\xe0I\x9c\x99i\xe4r9\xb4Z\xad\xc8\x05\xf4\xfb\xfdX\xb4h\x11^}\xf5U$\x93I1iS^ y\x04\xd2\xcfR\x84M\xa3\xd1\xc0h4\xc2\xe7\xf3\xc1\xe3\xf1\xe0\x82\x0b.\xc0\x1bo\xbc\x81w\xdf}\x17\x89D\x02\xdd\xdd\xdd0\x18\x0cP(\x14H&\x93\x88F\xa3\xd0h48\xf3\xcc3\xb1|\xf9rq\x9c)]$R\xa9\x14v\xef\xde\x8d\xa3G\x8fb``@T\xfe\x16\x1f\xff\xe6\xf3y\xb8\xddn477\xa3\xba\xba\x1av\xbb\x1d:\x9dND\x1cO6\xd2\xfe\xc3\xc5]=\xe8\x08\x8e\x84\x9e4:H\xef\xb7\xc3\xe1@SS\x13\x94J%\xb6m\xdb\x86\xde\xde^,^\xbc\x18\x1e\x8fG\x1c\xad\xf3\xdcQ\x1e\xa1Dc0\x91H \x9b\xcd\xc2b\xb1\xc0\xe3\xf1\xa0\xba\xba\x1a2\x99\x0cz\xbd~JG\xee\xf4\xbd\x0b\x16,\xc0\xbb\xef\xbe\x8b\x9bn\xba\x09\xbf\xfe\xf5\xaf\xc5xx\xfe\xf9\xe7\xb1l\xd92\xdc|\xf3\xcd\xf8\xc1\x0f~ \x04h9\x8f\xf5e\xf2\x7f\x0a\xc0rT\x93\xe6r9\xe1\x12p\xe3\x8d7\xe2\xb7\xbf\xfd-\xe2\xf1\xb8x\x0fS\xa9\x14\xbe\xf0\x85/\xe0\xee\xbb\xef\x86V\xab-(\xe8b\x8e/0T\xfc13\xc7\x04`qd\x80afJ\x00R;\xa8\xaa\xaa*\xd4\xd4\xd4\xa0\xbe\xbe\x1emmm\x18\x1c\x1cD&\x93A\x22\x91\x00\xf0^Q\x02\xe5\x0dIs\x7f\x8cF\xa3\x10\x91\x16\x8b\x05MMMX\xb6l\x19\xd6\xacY\x03\x00\x18\x1c\x1cD2\x99DWW\x17\xd2\xe94\xccf3|>\x1f\x5c.\xd7\xb8\x13[6\x9b\xc5\x1f\xfe\xf0\x07\xbc\xfa\xea\xab8|\xf8\xb0\xb0\xa7!3\xdb\xe2\xe2\x8f\xb5k\xd7\xc2\xeb\xf5\xc2\xe5r\xc1b\xb1\x9c\x94\xca\xdf\xf1\x88\xc7\xe3\xa2x\xa6\xb8C\x8aF\xa3\x11\x06\xd0\x14\x0d\xccf\xb3\xd0\xe9t\xd0\xeb\xf50\x99L\xf0z\xbdhhh\xc0\xb9\xe7\x9e\x8b\xef}\xef{\xd8\xb3g\x0f\x8cF#\x02\x81\x00\x82\xc1 R\xa9\xd4\x94:\xaf0\x13\xcf\xbf===\xd8\xbbw/\x12\x89\x04t:\x1dZZZD\xd5<\x09\x9f\xe3\x19[r\xb9\x1c\x0f?\xfc06o\xde\x8cO}\xeaS\x18\x1c\x1c\x14\xe3\xff\x87?\xfc!~\xfc\xe3\x1f\xe3\x17\xbf\xf8\x05\xae\xbc\xf2JX,\x96\xf2\x09@\xc8D\xb1\xd0\x89\xe4\x95+\x95J(\x95J\xc4\xe3q\xbc\xf0\xc2\x0b\xf8\xf9\xcf\x7f.>O\xb8\x5c.\xfc\xfa\xd7\xbf\xc6\x05\x17\x5c \xc6$oN\xca?FY+\xb0\x00d\x98\xb2 \xcd\x05\xf4z\xbd\xa8\xaf\xafG}}\xbd\xa8\x08\xa6\x02\x8fT*U\x90\x0cO\x22\x8c\xaay\xa5\xf9m\xa9TJ\x1cu\x91\xd0\x93\xf6\xf5\xa5\x9f\x97V\x12RD,\x99L\xe2\xcd7\xdf\xc4\xae]\xbb088\x88H$\x82`0X\xb2\xf8\x03\x00\x9cN'\x16/^\x0c\xaf\xd7[q\xd1\xbf\xe2\x05Pz\x0cN\xef\x81\xb4\xad\x16u\x8c A\xa8\xd3\xe9\xa0\xd5jQ]]\x8d\xc6\xc6F\xa4\xd3iQ\xe5)\xad\x96f\xca\x0f\xb5s\xeb\xee\xee\x86\xcf\xe7\x83\xcdf\x83\xc1`8\xee\xe7#\x8b\x9fG\xa8\x91U\x00\x00\x1daIDAT\xcb/\xbf\x1c\xbbv\xed\xc2\xd7\xbe\xf65\xfc\xeaW\xbf\x12\xddwr\xb9\x1cn\xbc\xf1F<\xf7\xdcs\xf8\xfd\xef\x7f/\xa2\xee\xe5\x18\x7f\xc5\xde\x7f\x93]o\xa4'S\xad\xad\xadx\xec\xb1\xc7\xf0?\xff\xf3?\x18\x1a\x1a\x12v1d\x22}\xf3\xcd7\xe3\xfb\xdf\xff\xbe\xb8\xef\xa7\xab\xf3\xc9|\x15}\xac\x15* `2\x9dj\xbed\xa2.\xc3L\xf7\xa0~?\xbf\xcfl6\xc3\xe5r\x89c`\xb3\xd9,\xc4])\xcf=\x8aj%\x12\x09\xa4\xd3i\x84\xc3a\x0c\x0e\x0e\xe2\xc8\x91#hoo?\xe6\x22 5\x89\x96V\xc8\xbe\xf2\xca+x\xf6\xd9g\xd1\xde\xde\x8e\xfe\xfe~Q\xf8!\xb5O\xa1\x85I.\x97c\xc9\x92%\xa8\xab\xab\x83\xc7\xe3)\x88\xfeUZ\xe4\x81Z\xbeI\x0bSH\xe4Q!\x00\xe5VJ;\x81\x00\xefuA\x19\x1a\x1a\x82B\xa1\x18\x131\xa5k\xc2\x8f\xe3\x7f\xd0\xfbH\xe9\x0d\xdf\xf9\xcew\xf0\xc4\x13O\xa0\xab\xab\x0b\xb1XL\xe4\x9fR\xf4\xf9\xb8#\x08\xef\x1b5\xbb\x5c.<\xf4\xd0C\xd8\xbe};N?\xfdtQu\x0f\x00\x9f\xfd\xecg\x91\xcb\xe5\xcav\x0c,\x15\x80S][d2\x19^x\xe1\x05l\xdc\xb8\x11\x17^x!~\xf9\xcb_\x8aqH\xe3\xf3\xfc\xf3\xcf\xc7\x8e\x1d;p\xdf}\xf7\x89#_\x16\x7f\xe5\xd3\x08\x13\x8d7\xd6\x0a\xb38\x02X\xaep.\x87\xd8\x99rD\x01\xa5\xb9\x80\xb5\xb5\xb5hll\xc4;\xef\xbc\x03\x00\xa2\xe8\x22\x99L\x8a\xcaS\xa9\xb1s2\x99D*\x95B8\x1cF0\x18\xc4\xdbo\xbf\x0d\x97\xcb\x05\xb7\xdb=\xa9\xc5@.\x97c\xdf\xbe}\xd8\xb9s'^}\xf5U\xf4\xf7\xf7#\x18\x0c\x22\x12\x89\x08\xb3d\xa9\x08\xa5\xdfKy\x84.\x97\x0b6\x9b\xad\xa2*\x7fK\xdd\xef\x91HDD\x82\xa4\x22Pj\xa7Cy\x82$\x88\xbb\xba\xba`\xb5Z\xf1\xd2K/\xe1\xe2\x8b/\x86\xdb\xed\xe6\xfb\x7f\x9a\x04z<\x1e\xc7\xa3\x8f>\x8a'\x9f|\x12\x9d\x9d\x9d\x88\xc5bH$\x12H$\x12\x22\x1a^\x8e\xdfC\xd7}\xf5\xea\xd5x\xf3\xcd7\xf1\xfc\xf3\xcf\xe3\xe3\x1f\xff8\xd6\xacY\x833\xcf<\xb3\xac\xd7\x956Jt\x0fOE<<\xf1\xc4\x13\xb8\xea\xaa\xabJ\xf6\xa8\xf6\xfb\xfdx\xec\xb1\xc7\xb0a\xc3\x06\xf1\xdc\x5c\x90\x84\xb2\x8d\x0f\x16ys\x5c\x00N$\x0a\xa7\xb2\xcb\xe4\x1b\x8e)\xc7\xa4\xa3P(`6\x9bEw\x90%K\x96`\xdf\xbe}\x22\xc7,\x99L\x8a\x9d?\x1dO\xa5R)\xc8d2\xe8t:D\x22\x11a\x1f\x93L&\xf1\xe8\xa3\x8f\xe2\xd4SO\xc5\x8a\x15+\xa0\xd5ja6\x9b\x0b\xc6j6\x9bE(\x14B__\x1f\xda\xda\xda\xb0o\xdf>\xb4\xb6\xb6\x22\x12\x89`xx\x18\xa1P\x08\xb1XlL\xcf_\x22\x97\xcba\xfd\xfa\xf5p\xbb\xddp:\x9d\xa2\xebG\xa5\x0a@z\x9fI\xc8J\xefy\x8a\xa6\xd2\x91\x1fE^3\x99\x0cl6\x1bzzz\x10\x89D\x10\x8dF\xb1z\xf5j\xb4\xb4\xb4\xc0h4\x0a!\xce\x9c\xf8\xbc{\xe8\xd0!\xfc\xf1\x8f\x7f\xc43\xcf<\x83\xfe\xfe~\x91r maX\xceD|\xba\xe7R\xa9\x14>\xf0\x81\x0f`pp\x10\xdd\xdd\xdd\xd0j\xb5e\x1fs\xd2H\xfbT\xb8\xf8\xe2\x8b\xb1d\xc9\x12\xb4\xb6\xb6\xbe\xb7\x00*\x95X\xb2d\x096n\xdc\x88\x8f|\xe4#X\xb3f\x0d\x1f\xf7\xce\x80\x1e`\xe6\xa8\x00\x94\xde\x98\x94\x0fT\x5c\xe1\xc8032\xb8\x95Jh4\x1a8\x1c\x0e\xf8|>\xf8|>,_\xbe\x1c\xaf\xbf\xfe\xbaXD\xa8\xe2T\xaf\xd7\x8b\xf1I\xc6\xc5\xe9tZ\x88\xbfp8\x0c\x97\xcb\x85@ \x807\xdf|\x13~\xbf\x1f\x1e\x8fGT\x11SqIGG\x07\xfa\xfb\xfb\x0b\x04_8\x1c\x16\xc7\xceT\xf8!\xb5\xe6\xa0{\xc2d2a\xf5\xea\xd5p\xbb\xddp8\x1c\xc2\x1c\xb9\xd2\xc4\xdfxG\x8e\xd2\xaf\x93\xc8\xc8d2B\xd4\xc5b1h4\x1a\x04\x02\x01\xa4\xd3i\x04\x02\x01$\x93Itww\xc3\xe1p\xa0\xaa\xaa\x0a\xb5\xb5\xb5\xd0\xe9t\xc2\xf2\xc6h4B\xa7\xd3\x9d\x94\x85\xa7\x9c\xe2\x88\x9e\xebx\xaf\xe5d_\x7f2\x99D(\x14\xc2\xc1\x83\x07\xf1\xc6\x1bo`\xc7\x8e\x1dhooG(\x14\x12Q-i\xc7\x95\xe9\x18[j\xb5Z<\xb7\xdf\xef\x9f\x96MG\xa9\x8e1\x93y\x8fL&\x13\xd6\xad[\x87\xbe\xbe>|\xees\x9f\x13\xf3\x82Z\xadF$\x12A&\x93)\xbb`e\xfey}H\x0b\xb0G\xf0\x1c\x15\x80\xd2\x5c&\xbaY\xa5\xf9K\x1c\xddcf2:\xa5P(`4\x1a\x0b*\x82\xf7\xee\xdd\x8b\xd1\xd1QQ\xdc\xa1R\xa9D\xa7\x0a\xeap\x00\xbc\x17\xd1\xa3j]\x83\xc1\x80\x9e\x9e\x1etww\xc3\xe9t\xa2\xbd\xbd]\xfc\x0e\xb2\x8c\x19\x1c\x1cD.\x97C<\x1eG\x22\x91@4\x1aE4\x1a\x15Gm\xf1x\x5c\x88?\xa9\xef\x1fAG\xbf\x1e\x8f\x07V\xabU\x1cMWb\xee\x9f\xf4\xbe\x9e\xa8\xa7*E\x03\xb3\xd9,4\x1a\x8d\xb0\xcbI\xa7\xd3\xd0\xe9t\x18\x1a\x1a\x12\x1dN\xc8\x9f.\x16\x8b\x8d1\xd0\x9e\xca\x22_\x1cY\xad\xa4H\xc7tZ_P46\x12\x89 \x10\x08```\x00CCC\x22\xed\x80~7\xe5eR\x9e\xe6t\xbd\x96\xe9B*`\x8f\xe7\xfd\xbc\xf7\xde{\xf1\xb3\x9f\xfd\x0c\xb1X\x0c[\xb7n\x15\xc7\xe2F\xa3\x91\xd7\xa7i\x9e7\x8aSm\x989\x22\x00\xa5\x8b\xc2\xf0\xf00Z[[\x91\xc9d\x90N\xa7\xd1\xd1\xd1\x81\xd1\xd1Q\x84\xc3a$\x12\x09\x11\x0d\x91\xee\x06fb\xe2`\xe6\xe1\x00\x7f?\x0ah\xb7\xdb\xe1\xf5zQSS\x83\xc6\xc6F\xbc\xf5\xd6[\xa2Wm\x22\x91\x80\xc1`\x10BE\xea\x0bFQl\x12\x88z\xbd\x1e\xa1PHT\x19\x93P\xa4\xde\xb7\x99LF\x88<\xe9d\x97H$D\xe5\xb14\xfaG\xd8l6\xb4\xb4\xb4\xc0\xe7\xf3\x89\xca_\x8dFS1\xc7\xa1R\x93\xeaP(\x84}\xfb\xf6A\xa1P \x18\x0c\x8a\xc2\x96p8,\x22\xa7T|@\xa2'\x97\xcb!\x1a\x8d\x0a[\x18\x00\xc2k-\x14\x0a\x89B\x97t:\x0d\x83\xc1\x80\xae\xae\xaec\xce\x09\xb3\xcdG\xecD\xc5\xd6\xb1~>\x9b\xcd\x22\x95J!\x93\xc9 \x12\x89 \x16\x8batt\xb4\xc0V\x87\x22\x80\xd2\x0d\xfal\x9bs\xc7+\x02\x99lT\x89\xa2\xf6\xf4^p$jz\xaf\x15\xcdu}}}hmm\xc5\xc8\xc8\x08\x06\x06\x060<<\x8c\x91\x91\x91\x82\xd3\x96\xe2\x8d1\xfd\xcb}\xc2+\x5c\x00J/\x18%\xcf\xd3b\x11\x0e\x87\x0b\x92\xe0\xc73\xc0\x95F\x10\x19\xa6\x1c(\x14\x0a\x11\x05\xf4x<\xa8\xaf\xaf\xc7\xc2\x85\x0bq\xe0\xc0\x011Fe2\x99\xc8\x01\xa4\x1e\xb6T!IQ\x12\x95J\x05\x99L\x86H$\x02\x8dF\x83d2\x89`0\x88|>\x0f\xbd^/\x8e8\xa59\xaft\xdcKQ\xb0x<.\x04e\xf1D\xb6h\xd1\x22,X\xb0\x00.\x97\x0bV\xabU\x1c\x81V\xd2\xbd@\x133\xe5:\xaaT*\x84\xc3a\x0c\x0d\x0d\xa1\xbf\xbf_\xe4\xf4\xd1\x91x\xf1u\x00 r,\xa5\x0b\x84B\xa1@(\x14\x12\xdf\x17\x08\x04Dk\xbc\xa9F\x99\xa6k\x81\x98-\x0b\x0fy3R\x11\x13\xe5]J\xe7U\xe9b:[\xf3-O$\x02Hs\x02\xa5'\xb0\xb0\x98\xfey\x83\xdc\x15\x82\xc1 b\xb1\x18B\xa1\x10zzz\x10\x8dF1::\x8aX,&r\x89KE\xf0\xf9\xfa\xcc\x82\x08 M2\xc3\xc3\xc3\xd8\xb7o\x1f\x86\x87\x87E\x97\x04Z\x1c\xa4\x11\x02i\x04\x90&'\xee\x02\xc0\x94\x1b\x95J\x05\x8dF\x03\x9b\xcd\x06\xbf\xdf\x8f\xfa\xfaz\xd4\xd5\xd5a\xf7\xee\xddb\x13B\xd1\xbbX,V\x10!\xa1\xc8\x1e\x99\xe5*\x95J$\x93I\xa8T*\xf1 \xe3c\xe9B\x22\xb5\x96\xa1^\xc1\xd9lv\x8c\xfd\x0c\x15\x9c\xac]\xbb\x16n\xb7\x1bUUU\xd0\xeb\xf5\xa2%Z\xa5-\xba\x00\x10\x0e\x87\xd1\xd6\xd6&&n\x9a\xc4\xa3\xd1\xa8\x10\xbc\xc5\x02\xf0X\xd1\xbcb\x11CsA%L\xfc\xb3i\xf1\xa1\xb1%\x1dk\xd2\xb6\x9c\xd2\x08\xdal\xed\xc4p\x22>\x80\xe3\x8d5f\xfa\x85z \x10\xc0\x9e={\xd0\xdd\xdd-\xf4\xc0\xf0\xf0\xb0\x08\x08\xd1\xc6\xb1x\xbc\xd2ub\x11X\xc1\x02\x90\x8eyd2\x19FGG\x11\x0c\x06E\xd4\x83\x16\x06\xca\x85\x92\x1e\xb5\x95\xf2\x0ed\x01\xc8\x94;*\xa2R\xa9`4\x1a\xe1p8P[[\x8b\x95+Wb\xef\xde\xbdb\xa1$\x1b\x93\xe2\xb1'\x97\xcbE\xb50\xf5\x1a&\x93[\xaa\x10\xd6\xe9t\xc8d2\x05\x9b\x17\xb2\x92\xa1EY:\xfe\xa5c=\x9f\xcfc\xc9\x92%\xa8\xaf\xaf\x87\xcb\xe5\x82\xd9l\x86N\xa7\xab\xc8\x8d\x10\xbdn\xca1\xa3\xa3\xf1H$\x22\x8e\xb8\x8b\x0b[&\xbb8\x17\xe7\x0d\xce\xf5]\xfft\xe7\xdeI7\xd7\xf4\xb1TXOV\x8cW\xe2\xfb&\xed\x05|\x22\x22\xb6\xf8~\xe7ugz6$t\x8d\xfa\xfa\xfa\xd0\xdf\xdf\x8fd2)R\xc2h\xfe\x95\xce\x1d\xc5\x91\x7fi\xee S\xa1\x02\x90\x16K\x85B\x81H$\x82P($\xaa\x1e\xa59O\xc5\xea\x9e&)\xba\xa19A\x94\x99\x96\x81\xaeTB\xa7\xd3\x89\x5c@\x9f\xcf\x87U\xabV\xe1\xf5\xd7_\x1f\xd3\xcd\xa2xA\x90V\x0c\xd3\x18\xd6j\xb5H\xa5R\xa2\xa0A\xa5R\x15$\xd9\xd3\xc4GB\x90\xa2\x80\xd2\x85\x99\xee\x97\xf5\xeb\xd7\xc3\xe9t\x8a\xca_i\xff\xdcJ\x8b\xba(\x95JD\xa3Q\xd1U\x85v\xee\xe3\x1d\xdd0'O(\x8d'\xa4\xa9\x05\xdal\x8e*\x8d\x97\x03x\xbc\x82\x99\x05\xe0\xf4\x5c'\x9a\xc7\xe4r9\x22\x91HA\x0a\x98t\xde(\xbe~\xc5u\x01\xe5\xec%\xcdL\x83\x00\xa4\xe31\xbd^\x8fL&\x83\x91\x91\x91\x82\x04\xd0\xc9F\x05t:\x9d\xc8}\xe2\x1b\x92)\xe7\xf8\xa4\xee \x1e\x8f\x07uuu\xc2\x0f,\x18\x0cNj\xc1\x91\xf6\xec\xcd\xe7\xf30\x18\x0cB\x10R\xa7\x03\xfa\x7f\x22\x91\x80Z\xad\x169qT\x85Y\xdc\x0c\xfd\xcc3\xcfDuu5\xdcn\xb7\xe8\xfaA\xf9\x86\x95\x04\xfd}\x1a\x8d\x06\xf9|^\xe4?J\xff~\xa6\xb2\x04\xe0xc\xf9D\xfa\xffV\x8a\xb0(\xb7@\xa19\x82)\xffuR\xa9T\xd0\xeb\xf5H$\x12\x18\x1e\x1e\x16\xf3E\xa9`\xd0\xb1\xc6,U\xb03\x15$\x00I\xfc\xe9t:\x98\xcdf\x11\x15)\x95\x13u,\xccf\xb3h/\xc5\x17\x9a)'\x0a\x85\x02:\x9d\x0eV\xab\x15~\xbf\x1f\xb5\xb5\xb58\xe3\x8c3\xf0\xdcs\xcf\x8d\x9b\x08^*\xd2@\xc9\xe3\xd4\xf3T:V\xa9\xe2W\xa7\xd3\x09\xc3gz\x14\x1f\x8f\xeat:,_\xbe\x1c\x1e\x8f\x07UUU0\x1a\x8d\x15\x99\xfbG\x93\xb8F\xa3\x81\xd5j\x85V\xab\x15\xc7\xe2'\x9a\x83\xc5\xcc\xdcbLcY\xab\xd5Vt\x87\x99\xa9\xfc-'*\x109\x028\xbd\xd7I\xadVC\xa7\xd3\xc1b\xb1@\xa5R\x89\x22\xb0RQ\xbf\xf1\xae\x8b\x5c./\xf0\x06\xadDk\xacy+\x00)<\xabV\xaba2\x99\xe0\xf3\xf9D\x94`*\x0b\x02\x0d\x88\xaa\xaa*\x98L&\xa1\xf8yQa\xca\x05mT,\x16\x0b<\x1e\x0f\x9a\x9a\x9a\x10\x8b\xc5\xf0\x8f\x7f\xfc\x03}}}\xc7\x8c\xa4H\xad4\xc80Zz\xc4!\x15v\xe4\xf5G\xd1\xbfb\xdb\x17\x00X\xbat)\xea\xeb\xeb\x85\xef\x9f4\xfa]i\xc2\x99L\x99\xa9HE\xfa~0\x95\x8f4\xb7\xcal6\x8b\xcd\xc6l=Z\x1b/8 \xb5\x0d\x99\xaa\x00daQ\xfe\xeb#\x97\xcb\x85\x03\x834Mf2\xe3\x95H\xa5R\xb0Z\xad\x228\xc4\xd7\xa9\x82\x04 \xf5d\xd4\xe9t\x22\xc1~\xfd\xfa\xf5\x22G\xaa\xb8\x92\x87vo\xd2\xfc*\x95J\x05\xadV\x0b\x93\xc9\x04\xaf\xd7\x0b\x87\xc3!\x12\xe1\xf9\xec\x9f)\xe7\x8e\x94z\x04\xdb\xedv,X\xb0\x00\x00p\xd5UWa\xc7\x8e\x1d\xc2\x8bJ\x9a\xabJ\x95\x94\xa9TJT\xaee\xb3Ya\x19\xa3\xd5j\xa1\xd1h\xc4\xf8\xa6/\x04`:\x9d\xc6e\x97]\x86\xd3O?\x1d\xa3\xa3\xa3\xa20N\x1a\x18\xa2\xebI\xa2P\xa3\xd1\xc0`0\xc0n\xb7\x8f\x19\xb3\x9c\x1eVA\x02\x90&\x18\xab\xd5*\x12\xc5\xddn7\x12\x89\x84\x88\x10\x94*\xb9\x97VB\xaa\xd5j\x18\x0c\x06\x98L&q<\xc1\xa1^f:&&:\x8a\xa0vkf\xb3\x19\xe1p\x18\xb1XlL\xa1\x07\xf0\x9e\x17\xe0\xc8\xc8\x88hS\x16\x08\x04\xa0\xd7\xebQ[[+\xbe/\x9dNC\xaf\xd7\x17\xb4{\x93\x8e\x7fi\xe5/\x80\x82\xca_\x8a\xfeU\xea\xa4F\xef\x99\xd1h\x04\x00h\xb5Z8\x9dN\xd1\xd5G:\x91K==\xf9\xde\x9dY\x8a#`\xd2\xe3PZP\xb5Zm\xc1\x11\xf0l\xcd\x03,\xfe\x9b\x8b\xff\xfe\xc9\xfeM\x1c\x01\x9c>\xc8D\x9f\x8e\x80u:\x1d\x5c.\x17\x12\x89\x84h\xb7Y\xea:HuA\xf1\x98\xa5\x9ck\xa6\x82\x04 -\x10\x00`\xb1X\xa0\xd7\xeb\x0b\x8e\x87\x8e\xe58O\x17\x9bz\xb1\xd2\x83U>3]\x82\x86\x04\x17%\x18\xdbl\xb6\x92-\xda\xf2\xf9<\x22\x91\x08\x06\x07\x07\x11\x89Dp\xf0\xe0A\xa8\xd5j,^\xbcX\x08\xc8@ \x04$\xfd,\x89\xc0\xe2\xa3\xdf\x5c.\x87e\xcb\x96\xa1\xa9\xa9\x09n\xb7[\xecj+}!\xa6|\x5c\x12\x0e\x16\x8b\xa5\xe0\xfe\xe6hJ\xe5\x08\xa2R\xd11\x8a\xe2\xd2f[\xadV\xcf\xfaH\x0a\xb9L\x9c\xe8s\x14\x7f\xccc\xb7\xbc\x22\x10\x80\xf07\x95\xea\x82\xf1\xdeo\xe9\xc6E:fU*\xd5\xac\xdf\xb0\xccI\x01(\x15\x81$\xe4t:\xdd\xa4\x0bA\xa4G\x15\xec\x03\xc8\xcc\x04\x14\xb5V(\x14\xc8f\xb3\xd0\xeb\xf5\x05bFZ\xf0A\xad\xca\xe8hw\xf9\xf2\xe5\xb8\xfe\xfa\xeb\x11\x0e\x87q\xe4\xc8\x118\x1c\x0e\x1cW\xc5\x95\xbfk\xd7\xae\x85\xd7\xeb\x85\xd3\xe9\x84\xc1`\x98\x15\xb9\x7f\x0cS\x89\xd0=u\x22-\x08K9S0\xcc\xbc\xba\x8f\xf8-`\xe6;\x94\xaf\x07@\x18\xe3\xe6\xf3y$\x12\x09D\x22\x11\x0c\x0f\x0fC\xa3\xd1\xa0\xa5\xa5\xa5`\xb1\x88\xc7\xe3hkkC$\x12A,\x16C4\x1a\x15\xfe\x7f\xd2\xe8_>\x9fGuu5V\xacX\x01\xa7\xd39k*\x7f\x19\xa6R!\xcf\xccr\x1e\x01\xf3\xbd\xc8\xb0\x00d\x98y\xb8\x98\x90\xcd\x00\x1d\x07e\xb3Y\xe1\xff\xd7\xd5\xd5\x85\xb3\xce:k\xcc\xcfuvvb``\x00\xc1`\x10\x91H\xa4\xe0\xf8Wj\x7f\x94\xcf\xe7q\xe1\x85\x17\xc2n\xb7\xc3\xe9t\xce\x9a\xca_\x86\xa9\xd8\x85\xeb}\xe1&\xad\xd6\x97\x1e\xe5N\x16\x8e\x002,\x00\x19f\x1eS\xdc\xc1\x82L\x9cC\xa1\x10zzz`4\x1a\xd1\xd4\xd44\xe6\xb8\xa9\xad\xad\x0d}}}\x08\x87\xc3\xa2\x8b\x08\x89?\x22\x97\xcba\xe1\xc2\x85\xa8\xa9\xa9\x81\xd7\xeb\x85\xcdf\x83N\xa7\xab\xd8\xb6o\x0c3[6mr\xb9\x1c\xf1x\x5c\x08\xc0P($\x22\xf9\x93\x15\x91l\x04\xcd\xccg8\x07\x90a$bM*\xfe\x06\x07\x07\xd1\xd5\xd5\x85\xeb\xae\xbb\x0e\xb9\x5c\xae`\xb1\xe8\xed\xedEkk+\xba\xba\xbaD\xf4Oj\xfd\x22=\x8eZ\xb3f\x8d\xc8\xfd3\x99L\xa2\xcb\x0d\xc30'\xb6i\x8bF\xa3\xd8\xb3g\x0f\xd2\xe94\x06\x06\x06\xa6l\x0c\xcdG\xc0\x0c\x0b@\x86\x99\xc7\x90`\x93\x8a\xbf\xfe\xfe~\x1c\xe8gs\xb9\x1c\xd2\xe94\x12\x89\x04FGGq\xf4\xe8Q\x1c\x9f\xc7\xf0\xf00\x06\x07\x07100\x80\x03\x07\x0e\xe0\xc0\x81\x03\xe8\xec\xec\x14\x85&\xc1`P\x08?\x12\x7f\xd2\xc2\x0f\x85B\x81L&\x83\x8d\x1b7b\xc9\x92%\xa8\xa9\xa9\x81\xc3\xe1\x80\xc1`8\xa6Pe\x18\xe6\xf8\xe6\x84\xf16qS\xb9\xdf\x5c.\x17\x16/^\x0c\xadV\x0b\x8f\xd7\xc3o,\xc3\x02\x90a*\x09\x8a\xe6e\xb3YD\xa3Q\x0c\x0d\x0d!\x9dN\x17\xe4\x00\x92\xa9\xeb\xd0\xd0\x10\xe2\xf18\x86\x87\x87\x11\x0e\x87E\xa4 \x9f\xcfC\xa9T\xa2\xbf\xbf\x1f\x81@\x00n\xb7\x1b\xfd\xfd\xfd\x88\xc7\xe3\x08\x85B\x88\xc7\xe3\xe8\xed\xedE$\x12A\x7f\x7f?\xa2\xd1(b\xb1\x18\xc2\xe1\xb0\xb0\x9e\x91\xb6{\xa3\xe7\x94\xc9d\xc8f\xb3X\xbat)\xce>\xfbl\xf8\xfd~x\xbd^\x98L&\xf6\xfdc\x98\x93(\x10'\xb5\x00*\x95X\xb8p!\xbfa\x0c\x0b@\x86\xa9\xe4\xc9\x9c\xaam\xa9\x02W&\x93\xc1h4\xe2\xe8\xd1\xa3\xe8\xee\xee\xc6\x8b/\xbe\x88\xa3G\x8fb``\x00\x89DB\xe4\x0a\xd2sd\xb3Y\xb8\x5c.(\x95J\x8c\x8c\x8c \x91H@\xa1P\x08\x91\x17\x89D\x90\xcdf\x91J\xa5\x84\xc7 \x09?i\xab7\x12\x7fd\xf8\x5cUU\x85\x8b.\xba\x08\x0b\x17.Dmm\xad\xc8\xfd\xd3h4,\xfe\x18\xa6\x82\x05\xa0t#\xc70,\x00\x19\xa6\xc2\x90\x8a\xae\x5c.\x87@ \x80\xbf\xfd\xedop:\x9d\xc8\xe5r\xe8\xec\xecDww7\xf6\xee\xdd\x8b`0\x88t:\x8dT*U\x10\xa9\xcbd2\x00\x80\xde\xde^$\x12\x09qTL\xad\xe0\xf2\xf9\xe8\xf5zTUUA.\x97\xc3l6#\x1c\x0e\x0b\xc3\xe7l6[\xb0\x00\x94\x8a\x00\xd01\xb1\xb4\xb8C\xa1P\x88|\x22\x8dF#\xa2{Z\xadV|\xacR\xa9\xa0\xd1h\x0a*|9\xc2\xc00'\x17\xa3\xd1\x08\x83\xc1\x80\xba\xba:ttt\x08\xafO\x99\x9c\xab\x80\x19\x86\x05 3\xa7v\xfcj\xb5\x1af\xb3\x19\x1a\x8d\x06V\xabUt\xfc\x18\xaf\x95S\xa9*`\xea\xe0AB\x8e\xa2\x80$\x04\xe9\xb8Y\xadV\x8bJC\x85B\xc1G\xbd\x0cS\x81P\xe4_Z\x11,\xcb\xf3}\xca0,\x00\x999\x83L&\x83Z\xad\x16\x22M\xa7\xd3\x89N\x1d\xc5\x22o2\xcf%E*\xf0H\x0c\x96:6f\x18\xa6\xf2\xe6\x851~\x9f\xb2\xf7\x1f\x0c\xc3\xb0\x00d\xe6\xcedO\x11\xba\xe2N\x1f\xe5x\xeeR\xe2\x90a\x98\xcaG\x9a\x8f+\x93\xc9 \x97q~.\xc3\xb0\x00d\xe6\xfc\x84\xcf0\x0c\xc30\xcc\x14\xd7Q~\x0b\x18\x86a\x98\xd9\x0cG\xee\x19\x86\x05 \xc30\x0c3\x8f\x05 \xe7\xee2\x0c\x0b@\x86a\x18f\x1e\x8bA\x86aX\x002\x0c\xc30\xf3D\xf4\xb1\x08d\x18\x16\x80\x0c\xc30\xcc<\x13\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb8\xf8\xe3\x1c@\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe62\xd2N \x1c\x01d\x18\x16\x80\x0c\xc30\xcc<\x11\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb1\x18dA\xc80,\x00\x19\x86a\x98\xb9\xbe\x90qkH\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe60\xc560\x1c\x11d\x18\x16\x80\x0c\xc30\xcc<\x15\x83\x0c\xc3\xb0\x00d\x18\x86aX\xf41\x0c\xc3\x02\x90a\x18\x86a\x01\xc80,\x00\x19\x86a\x18\x86a\x18\x16\x80\x0c\xc30\x0cS\x99P\xe4\x8f#\x80\x0c\xc3\x02\x90a\x18\x86a\x18\x86a\x01\xc80\x0c\xc3\xcce8\x02\xc80,\x00\x19\x86a\x98\xf9\xb6\x90\xc9\xe5\xc8\xe7\xf3\xfcF0\x0c\x0b@\x86a\x18\x86a\x18\x86\x05 \xc30\x0c3'\xe1#`\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe6*2\x99\x8c#\x80\x0c\xc3\x02\x90a\x18\x86\x99\x8f\x22\x90a\x18\x16\x80\x0c\xc30\xcc<\x16\x83,\x08\x19\x86\x05 \xc30\x0c3\x0fD\x1f\xa1P(\xf8\x0da\x18\x16\x80\x0c\xc30\xcc\xbcZ\xd4\xe4\xbc\xac1\x0c\x0b@\x86a\x18f\xce#\x8d\x00\xf2\xf1/\xc3\xb0\x00d\x18\x86a\xe6\x99\x00\xe4\x08 \xc3\xb0\x00d\x18\x86aX\x002\x0c\xc3\x02\x90a\x18\x86\x99{\x0a\xb0\xb4\x18d\x18\x86\x05 \xc30\x0c3g\xf5\x1f\xe7\x002\x0c\x0b@\x86a\x18f~\x09@.\x02a\x18\x16\x80\x0c\xc30\xcc\xfc\x15\x80\x9c\x03\xc80,\x00\x19\x86a\x98y,\x06\x19\x86a\x01\xc80\x0c\xc3\xcc\x87EM\xc6\xcb\x1a\xc3\xb0\x00d\x18\x86a\xe6<\x05Q?^\xd5\x18\x86\x05 \xc30\x0c3\xbf\x04 G\x00\x19\x86\x05 \xc30\x0c3\x1f\x162\xb9\x1c\xf9|\x9e\xdf\x08\x86a\x01\xc80\x0c\xc3\xccGdr.\x02a\x18\x16\x80\x0c\xc30\xcc\xdc\x16|2Y\x81\xf5\x8bB\xae\xe07\x85aX\x002\x0c\xc30\xf3A\x042\x0c\xc3\x02\x90a\x18\x86\x99\xaf\x8b\x1a\x1bA3\x0c\x0b@\x86a\x18f~\xc1\xd1@\x86a\x01\xc80\x0c\xc3\xcc#\xf2\xf9<\x0b@\x86a\x01\xc80\x0c\xc3\xcc7X\x002\x0c\x0b@\x86a\x18\x86\x05 \xc30%\xf8\xff\x82\xe5f\x8a\x9e\x05\x9f\x09\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xa4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xab\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" -qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x06\x07\xa7(\x98\x00s\x00p\x00l\x00a\x00s\x00h\x00\x0a\x08\x94\x19\x07\x00s\x00p\x00l\x00a\x00s\x00h\x00.\x00p\x00n\x00g\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" -qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x16\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x11\x00\x00\x00\x05\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xac\x00\x00\x00\x00\x00\x01\x00\x00\xe0E\x00\x00\x01\xbe\x00\x00\x00\x00\x00\x01\x00\x01\xed\xa1\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x01\xf6c\x00\x00\x01l\x00\x00\x00\x00\x00\x01\x00\x01\xa8\x10\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x01\x00\x01\xc3\xbf\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x00\xcd\xe5\x00\x00\x01$\x00\x00\x00\x00\x00\x01\x00\x01x\x10\x00\x00\x00J\x00\x00\x00\x00\x00\x01\x00\x00\x9e\x08\x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x00\xad\xe2\x00\x00\x00z\x00\x00\x00\x00\x00\x01\x00\x00\xbd\x8a\x00\x00\x00\xc4\x00\x00\x00\x00\x00\x01\x00\x019\x87\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x01Im\x00\x00\x00\xf4\x00\x00\x00\x00\x00\x01\x00\x01YO\x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x01hm\x00\x00\x01<\x00\x00\x00\x00\x00\x01\x00\x01\x87\x8a\x00\x00\x01T\x00\x00\x00\x00\x00\x01\x00\x01\x97{\x00\x00\x01\xa6\x00\x00\x00\x00\x00\x01\x00\x01\xdd\xde\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x17\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x12C" +qt_resource_data = "\x00\x00\x9e\x04\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x80\x00\x00\x01\x00\x08\x06\x00\x00\x00#\x95\xc7f\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x17\x12\x00\x00\x17\x12\x01g\x9f\xd2R\x00\x00\x00\x07tIME\x07\xdf\x07\x07\x090\x11\xbal\xcd}\x00\x00 \x00IDATx\xda\xec}w\x94\x5c\xe5y\xfe3w\xee\xf4\x99\x9d\xb2\xb3;\xdb\x9bV\xbb*+\xd1-\x0c\xa2\x83\xb0\x0d\x1cSbL\xe28v0v|lp\x0eq\x09\x04\x88c\x1cl\x9c\xe08\x80K\x82c\xe3\xb8!sL\x5c\xe4P\x02\x18\x04\x085TWm{/\xb3\xd3{\xbf\xf3\xfbC\xbf\xf7\xf3\x9d\xbb\xb3\xb3\xbb\xd2\xae\xb4\xda\xfd\x9est\xa4\x1d\xcd\xcc\xce|\xed}\xbe\xe7m\xaa|>\x9f\x07\x07\x07\x07\x07\x07\xc79\x8c|>\x8fT*\x85\xed\xdb\xb7#\x95J!\x97\xcb\xe1\xb6\xdbn\x83$I\x10\x04\x81\x0f\x10\x07\x87\x02|Wppppp\x9c\xf3P\xa9T\xc8\xe7\xf3\xc8\xe7\xf3P\xa9T\xc8\xe5r|P888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0\x0e-\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0* \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqppp\x02\xc8\xc1\xc1\xc1\xc1\xc1I \x07\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqpp\x02\xc8\xc1\xc1\xc1\xc1\xc1\xc1\x09 \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x9c\x00rppppp,%P!h\x0e\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\xe3\x9c\x07w\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xc1\x01\x95J\xc5\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\xe5\x0aI\x92\xb8\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7J\x05W\xfe888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83c\xb9A\xe9\xfe\x95$\x89\x0f\x0a\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\xcb\x9d\x00\xcaI\x1f\x8f\x07\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x10@\xe5\xcf<\x16\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\x02\xb9\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a \x7fr\xc5\x8f\x13@\x0e\x0eN\x0098888V\x08\x09\xe4\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\xfe\xe4\xa4\x8fg\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x08 A\xa5Rq\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95F\x029\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x91?N\x00988\x01\xe4\xe0\xe0\xe0\xe0X!\x04\x90\xc7\x00rpp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x09 \x95\x82\xe1\x0a \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0cR\x9e+\x80\x1c\x1c\x9c\x00rpppp\xac(p\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15F\xfc\xf2\x12'\x80\x1c\x1c\x9c\x00rpppp\xacH\x22\xc8\xc1\xc1\xc1\x09 \x07\x07\x07\x07\x07'\x80\x1c\x1c\x9c\x00\xf2!\xe0\xe0\xe0\xe0\xe0Xn\xe0\x9d@888\x01\xe4\xe0\xe0\xe0\xe0X\x81\xe0\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x86\x5c.\xc7\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eN\x0098888\x96-x+8\x0e\x0eN\x0098888V\x18x\x0c \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0c\x5c\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x85\x81+\x80\x1c\x1c\x9c\x00rpppp\xac \xf0:\x80\x1c\x1c\x9c\x00rpppp\xac@p\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a\x03\x8f\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\x13@\x0e\x0e\x0eN\x0098888\x963\xb8\x0b\x98\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x04\x95J\x85L&\xc3\x07\x82\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95\x04\xde\x0b\x98\x83cf\x88\x8b\xf5\xc6$\xbds\x09~\xf1o\xb9\xf2\xbf\xf3\xf9<\xfb7\x07\xc7b\xedm\xf9:+\xb5\xc7\xe9y\xf4\xb7r\xbdrpp\xfc\x09\x14\xb3\xc8K\xd8,\xdf\xb3S~n\xe6\xf3y\xa8\xd5\xeai\xf6\xfc\x9c$\x80\xf9|\x1e\xd9l\x16*\x95\x0a\x92$A\x92\xa4i\xc6\x82c\xe1\x17\x92\xdc\xb0\x0a\x82P\xf0X\xb1\xe7pp\x9c*\xe9\xa3}M\xc6\xaa\xd4\xfe\xce\xe7\xf3\xc8\xe5r\x10\x04\x81\xad?A\x10\xa0V\xab\xa1R\xa9\x0a\x0e>\x0e\x8e\x85\x14\x1e\x94\x84j\xa9\x93>I\x92\x98ZI{\x8a\xdb\xcd\xe5\xb76%I\x82J\xa5*8\x17\xe9L\x14\x04\x81\xfd9\xe7\x08`6\x9bE6\x9b\x85$I\xc8d2H\xa5R\xecg2\x14\x1c\x0b\x07Z\xb8$IB:\x9d\x86$Il\x7fg\xb3Y\xf6\xff\xc5@\x97\xc1|>\x0f\xadV\x0b\xbd^\x0f\xbd^\x0f\xadV\x0b\x00\x9c\x04r,\x0a\x01\xa4\xbf\x97\xba\x0b8\x9b\xcd\x02\x002\x99\x0c\xb2\xd9,2\x99\x0c\xd2\xe94r\xb9\x5c\xc1e\x8bcy\x81\xe6W\xa3\xd1@\x14E\x98\xcdfh\xb5Zh\xb5\xda3J\xfc\xc5\x85\xfa2\xf4'\x1e\x8f#\x1c\x0e#\x1c\x0e#\x16\x8b!\x99L\x22\x97\xcbq\x02\xb8\xc0 \x05EN\xfch1i\xb5Zh4\x1ah4\x1ah\xb5Z\xf6\x98(\x8aP\xab\xd5\x10E\x11*\x95\x0a\x1a\x8d\xa6@1\xe4\xe0(\xa5R\xa4\xd3i\xa4\xd3i\xb6\xbf#\x91\x08\x92\xc9$\xbb\xe8\x15\x83Z\xadfF\xd8h4\xc2n\xb7\xa3\xbc\xbc\x1cv\xbb\x1d\x1a\x8d\x86\x0f,\xc7\xa2\x13\xac\x5c.\xb7\xa4/\x1a\x99L\x06\x89D\x02\x89D\x02\xa1P\x08\xe1p\x18\x89D\x02\x99L\x86\x13\xc0eL\x00\xb3\xd9,DQ\x84\xd5jEyy9\x9cN',\x16\x0b4\x1a\xcd\x19[\xaf\xe2B~\xa1X,\x86`0\x88\xee\xeenx\xbd^\xf6E\xe4j\x15\xc7\xc2\x19\xe4h4\xca\x16\x92\x5ce\x15E\x11\x06\x83\x01&\x93\x09v\xbb\x1dF\xa3\x11F\xa3\x11z\xbd\x1e:\x9d\x0ez\xbd\x9e\x91C\xadV\x0bA\x10\x18)\xe4\xf3\xc4QLU\xc9\xe5rH\xa7\xd3\x08\x06\x83\xe8\xeb\xeb\xc3\xe8\xe8(\xbbP\x90\xb2\x5c\x0c\xf1x\x1c{\xf7\xeeE,\x16Css3\xd6\xaf_\x8f\x96\x96\x16\x08\x82\x00\xbb\xdd^\xf2\xb5\x1c\x1c\xa7z9\x96\x9f\x93K\x95\x00\x92r\x9eN\xa7\x11\x8dF1>>\x8e\xae\xae.\xe6\xa5!\xdb\xc9\xb1\xfc\xce\xd2\xfd\xfb\xf7cxx\x18v\xbb\x1d\xabW\xaf\xc6\xfa\xf5\xeb!\x08\x02\x13e\xce\x19\x02H_(\x9b\xcd\x22\x16\x8badd\x04\x95\x95\x95\xb8\xfa\xea\xab\x0bT*\x8e\x85_Hr\xf7\x1b\x11\xc0L&\x83\x89\x89\x09\xc4\xe3qLNN\xe2\xd8\xb1c\xc8f\xb30\x1a\x8dp:\x9d\xb0\xdb\xed0\x18\x0c\xd0\xeb\xf50\x99L0\x9b\xcd0\x18\x0c\xd0\xe9t\xd0h4|\xce8f\xdc\xe3\xf1x\x1c\x13\x13\x13p8\x1c\xd8\xb4i\x13[+\xa5\x0e\xabl6\x8b\x1bo\xbc\x11\xdb\xb7o\xc7\x0f~\xf0\x03\x08\x82\x00\xadV\x0b\x9b\xcd\x06\x8b\xc5\xc2\x14i\x0e\x8e\x85Z\xab\xca\x8b\xf2RU\xd1\xc8\x0d\x98L&\x11\x0c\x06\x11\x0e\x87q\xe7\x9dw2e\x88\x0b'\xcbw\x8dn\xd9\xb2\x05G\x8e\x1c\xc1\xd3O?\x8d#G\x8e \x16\x8b\xa1\xac\xac\x0cF\xa3\x11:\x9d\xee\x8c\xb9\x81O\xfb\xe4\xa5\xa0\xc6t:\x8dx<\x8e\xa9\xa9)\xe8\xf5z\x98\xcdf\xee\xf6]dP\x1c\x95r\x9c+++\x0b~\x0e\x06\x83p\xbb\xdd\xd8\xbf\x7f?\xba\xba\xba \x08\x02\x0c\x06\x03l6\x1b\x1c\x0e\x07\xacV+\xca\xca\xca`6\x9ba4\x1a\xd9\xe2#U\x90\x83\x1fX\x14\xd7\xeb\xf3\xf9PYY\x09\x8b\xc52\xeb\xfe\xa6\xb8\xbf\xae\xae.<\xf7\xdcs\xe8\xe9\xe9\xc1\xaaU\xabP__\x8fh4\x0a\xbb\xdd\xce\xd60\x07\xc7b`\xa9\xc7\xd1e\xb3Y$\x93I\x84\xc3a\xf8\xfd~\x18\x0c\x06fS9\x96'T*\x15\xa6\xa6\xa6\xf0\x9b\xdf\xfc\x06===\x98\x9a\x9a\x82\xdb\xedF0\x18\x84\xcb\xe5bb\xce9A\x00\x05A@:\x9dF2\x99D,\x16C\x22\x91\x80N\xa7c_\x94\xe3\xcc,\xa8b\xb7`\xca$\xb3\xd9l\xb0\xd9lhooG>\x9fGgg'zzz\xb0o\xdf>\x08\x82\x80\xd6\xd6VTTT\xc0\xe9t\xc2\xe1p\xb0\x9b\x08\xbd7w\xd3\xadl\xe4r9d2\x19\xc4\xe3q\xc4b1\xa4\xd3\xe9Y\xf7w>\x9f\x87 \x08\x18\x1d\x1d\xc57\xbf\xf9M\x9c8q\x02\x1e\x8f\x07\x91H\x04\xf1x\x1c\x99L\xa6d\xf2\x08\x07\xc7\xa9^V\x94?/U!\x82\x12&\x13\x89\x04\xdb[\xb4\xaf\xf8y\xbb<\xd7\xa5J\xa5B8\x1c\xc6/~\xf1\x0b\xec\xdc\xb9\x13###,|.\x1e\x8f\xb3\x98\xd53\x85\x05q\x01\x93\x8b\x88\xbe\xc4\x5c\x82\xbb\xb9:\xb8p\xe4Oy[\x98\xa9\xd6\x1a=o\xe3\xc6\x8d\xd8\xb8q#6o\xde\x8c\xb7\xdez\x0b/\xbe\xf8\x22\xea\xea\xea\xd0\xda\xda\x0a\x97\xcb\x85\xaa\xaa*8\x9dN\xe6\x1e\xa6\x0cb\xee\x16^\xb9kL\x92$\xc4\xe3qD\xa3\xd1Y\x89\x1f\x91\xbf}\xfb\xf6\xe1\xa7?\xfd)\x0e\x1e<\x08\xaf\xd7\x8bL&\xc3\xfePl\x167t\x1c\x8bmx\x97\xaa\xad!\x17o&\x93A \x10(i7\x17\xab\xae\xee|\xf6\xdfB\xfe\xee\x95\xb4\xef\xe5\x84\xde\xeb\xf5\xe2\xfb\xdf\xff>\xde|\xf3M\xf4\xf7\xf7#\x16\x8bA\xadV\xb3\x90\xae3\x9d0\xbb \x04\x90\x0cD4\x1a\xc5\xd8\xd8\x18\xbc^/_\x00gx\x81\x9d\xca\xf3\x5c.\x17\xee\xb8\xe3\x0e\xdcz\xeb\xad\xf8\xe7\x7f\xfeg\xbc\xf2\xca+hkkCss3\x9a\x9b\x9bQUU\x05\x87\xc3\x01\xb3\xd9\xcc\xe6\x9a\x97\xedX\xb9F4\x99Lbjj\x0aUUU%\xd7X<\x1e\xc7[o\xbd\x85\x17^x\x01\x87\x0f\x1f\x86\xdf\xefG<\x1e\x9fv\x18\xf23\x80c\xb1\x94\x16\xc2Rv\x01+\xe3\xe7\xa7\xa6\xa6f=\xbb\x17\xf2\xbb\x9c\xa9\xb1Q\xce\x89\xbc4\xd4\xd9\xba\xcc\x9e\xa95\xa1R\xa9\x90L&\x11\x8dF\xd1\xdd\xdd\x8d\xd7_\x7f\x1dG\x8f\x1eEWW\x17b\xb1\x18KP\x22q\xe5L\x8b,\xe2B.\xa6\x5c.\x87H$R\x92\x00F\x22\x11\xe4\xf3yLNN2\xe6;\xd3\xfbQ\x8d\xa4RFi)\xdd\xeeN\xf5\xf3\x9c\xcakT*\x15R\xa9\x14\xaa\xaa\xaa\x10\x0e\x87a\xb5Z!I\x12\xca\xca\xca\x90\xcb\xe5`6\x9b\xa1R\xa9\xa0\xd5jg,\xd2+7\xc4_\xfb\xda\xd7\xb0{\xf7n\xfc\xe4'?A\x7f\x7f?.\xb8\xe0\x02\xb4\xb6\xb6\xa2\xbe\xbe\x1e.\x97\x0b6\x9b\x8d\x05\xebs\x12\xb8\xf2@\xfb;\x1e\x8f\xc3\xe7\xf3\x15}N.\x97\xc3\xc0\xc0\x00^y\xe5\x15\xec\xd9\xb3\x07'N\x9c\xc0\xc8\xc8\x08b\xb1\x18;p)\xcb\x8d\x07\xb8s\xccv\x86.\xc4\x85s\xa9\x87\x19\x90\x1b8\x1e\x8fc||\xbc\xe8\xf9\x1c\x8b\xc50<<\x0c\xb7\xdb\x8d\xde\xde\xde\x19\xc9\xd3\x5c\xec\x88\xdc\x15y*\x9f\xf5T^W,\xc9\xebTm\xe5b\xb8H\xe7\xf3YJ=\xaf\x98\xc7M\x10\x04D\xa3Q\x04\x02\x01\x8c\x8f\x8fcdd\x04\x83\x83\x83\x88F\xa3,\x0c\x86\x88\xdf\xd9h\xda .\xd4\x00\x92\x01\x88\xc7\xe3,FH\x8e\xd1\xd1Q\xf4\xf7\xf7\xa3\xbb\xbb\x1b\xf1x\x1c\x1e\x8f\xa7(\xdb\xa5\x85\x9fJ\xa5\x90N\xa7Y\x11c\xe5\x00+[K-$\xb9:]\xe26\xd7\x00\xce\xf9\xc8\xfa\xb4\xf9\xe4\xef-\xaf\x01HcAn[\x8b\xc5\x82\xf2\xf2r\x18\x8dF\x94\x95\x95\xa1\xac\xac\x0cv\xbb\x1dv\xbb\x1d:\x9d\x8e\x1d\xac\xf4\x1e\xd9l\x16\x9b6m\xc2\xea\xd5\xab\xf1\xf4\xd3O\xe3\xc5\x17_\xc4\xfb\xdf\xff~\x04\x83A\xa4\xd3id\xb3Y\xf6ZN\x02W.\xb2\xd9,\x82\xc1`\x01\xe9K\xa7\xd3\x98\x9a\x9a\xc2\xfe\xfd\xfb\xb1\x7f\xff~\xf4\xf6\xf6bhh\x08n\xb7\x1b\x89D\xa2\xc0\xad!\xdf\xb3<\x0c\x84C\xde\x05\x83J\x9f(\xcf\xceP(\x04\xb7\xdb\x8d@ P\xd0UF\xaf\xd7\xa3\xbd\xbd\xbd\x80`\xccG\x01<\x1d2\xb4P \xfb\x96\xc9d\x0ab\x00\x01\xc0\xef\xf7\xa3\xbf\xbf\x1f;v\xec@\x7f\x7f?\xc2\xe10\x82\xc1`A\xfd\xd6S\xb5u\x0bE\x8a\xe5cX\xec\xf7SR\x8b\xb2\xc5\x9d\xb2\x1d\x9arLf\xfa^D\x98\x8b\xa9\x8a\xa5~.5N\x0b\xd5v\xaf\xd8{\xe4r9$\x12\x09V?\xd5\xe7\xf31\x8e$O\xf6\xa0j\x0ag\xfaL\x5c\xd0\xfa\x0b\xc4\xce'&&\xd8\x97\x0f\x06\x83\xe8\xe9\xe9\xc1\xc1\x83\x071::\x0a\x9f\xcf\x87d2\x09\xaf\xd7[@\xee\x94\x93F\x93\x12\x8f\xc7\xd9\x82 \xb9\x5c.\x87'\x93\xc9\x05'p\x8bI\x0eO\xf5\x90\xa0X<\xe5\xe3t\xf8i4\x1a\xe8t:\x18\x0c\x06$\x12\x098\x1c\x0eH\x92\x04\xb3\xd9\x0c\x93\xc9\xc4J\xc0TVV\xc2f\xb3\xc1n\xb7\xa3\xa2\xa2\x02&\x93\x09\x06\x83\x01\x82 \x97\xcb!\x95J\xe1\xc6\x1bo\x84\xc5b\xc1\x0b/\xbcPP\xec7\x97\xcb\xc1\xe1p\xc0h4\xb2R\x05\x1c+\x03tI\xc8f\xb3\xf0z\xbdH&\x93\xd0\xe9tH&\x93\xe8\xec\xec\xc4\xee\xdd\xbb\xd1\xdb\xdb\x8b\x91\x91\x11LNN\xc2\xe7\xf3!\x12\x89\xb0RE\xb4v\xc9\xc0s\xf2\xc7\x89\x1f)$\xa2(B\x14Ex<\x1e\xbc\xfd\xf6\xdbx\xeb\xad\xb7\xf0\xf6\xdboc``\xa0 ^T\xbefr\xb9\x1cZ[[\xf1\xd6[o\xc1\xe9t\xce\xe8:+\xe5j\xccI9\xa8p\xf6[\x12\x92]\x8bF\xa3\xe8\xec\xec\x84 \x08\x18\x1f\x1fGoo/\x06\x06\x06\xd0\xdd\xdd\x8d@ \x80d2\x89D\x22\x01\xe0d\xf1\xe8\xb3I\x5c\xf3\xf9<#\xecr\xb5\x8bHL>\x9f\x87\xc9d\x9aF\xc0\x95s\xa1\xd5j\x0b\xc6\x9fBMJ\xa9k\xc5\xbc\x834\x1er\x01\xe9L\x9d1\xf4{\x8b\xfd>\xba\xacd\xb3YV\xec;\x95JM[\xcf\xf2\x8bM\xb1\xb8\xfe%O\x00I\xae\xcf\xe7\xf3\xac\x04L:\x9df$.\x9dN#\x16\x8b\xc1\xe7\xf3\xb1\xfaG\xca~\x87$\x8bR\xab\xa9L&\x03\x8dF\xc3\xda\xe3H\x92\x04A\x10\x90J\xa5\xd8k\xa8v\xddb\xa8t\xa7\xb3\x80\x16r\xf2h,\x09T`\x9bz\x0aR\xc9\x16Q\x14!I\x12L&\x13<\x1e\x0f4\x1a\x0d\xab+\xd4\xd3\xd3\x03\x87\xc3\x01\xbd^\x8f\xf2\xf2r\xd4\xd7\xd7\xc3j\xb5\xa2\xb6\xb6\x16N\xa7\x13\xa2(\xb2[h:\x9d\x86F\xa3\xc1\xc5\x17_\x8c\xbd{\xf7\x16\x14\x9c&\xc5P\xa7\xd3-\xf9\x0a\xfb\x1c\x0b{\xc0\xc9\x0f)\xba|%\x12\x09$\x93I\xa6\x12+[X)\x0fF\xa5r\xcf\x13\x8bV\x06\x94\xe7m,\x16\x83\xc7\xe3\xc1;\xef\xbc\x83_\xfd\xeaWx\xe9\xa5\x97\x0a\xd6\x09u6\x92\xaf!\xe5:\x9a\xcb\x99-\xef\xb1+\x7fN\x22\x91\xc0\x1f\xff\xf8G\xd4\xd4\xd4\xa0\xa3\xa3cV;\xb2\xd8\xe3\x92\xcb\xe5\x10\x8dF\xf1\xf2\xcb/#\x16\x8b!\x93\xc9\xa0\xaf\xaf\x0f\x1e\x8f\x07^\xaf\x97u\x06\xc9d2KfNi\xaf\x13\x91#\x11\x81:R%\x12\x09&\x14P\xe7)R\x04\xe9\x5c\xa1\xccW\xf9x(\xc3\x96H\x80\x90\x17\xa4\xa7\x0eC\xc4\x17\xe8w\x93\xa7\xecL\xf7\xd4-\xc5\x15\xe8<$\x1e\x93\xc9d\x0a\xfa>\xcb/\xc6g\x1a\x0bF\x00\xc9(\x00@\x7f\x7f?\xf6\xec\xd9\x03I\x92\xe0\xf3\xf9\xd0\xdf\xdf\x8f\xfe\xfe~LNN\x22\x1c\x0e#\x9b\xcd\xb2\xa0pb\xef\xc4\xe0\x05A`\x841\x97\xcb\xb1Vr\xc4\xa2\xcf\x04\xab_\xca\xea\x04\xa9n\xb4\x80\xa8`(\x19g\xb5Z\x8dX,\x06\x95J\xc5\x5c\xbd\x1a\x8d\x06\x16\x8b\x05\x81@\x00eee\x98\x9c\x9cD__\x1f\xecv;\x1c\x0e\x07\x0c\x06\x03V\xaf^\xcdnc\xe1p\x18\xa2(B\xaf\xd7\xa3\xb5\xb5\x15G\x8f\x1ee\xad\xfeh\xa1\x92;\x98\xc7r\xad\x1c\xd0\x9as\xbb\xdd8r\xe4\x08\x04A\xc0\xd4\xd4\x14\x06\x07\x071<<\x8c\xf1\xf1q\xf8|>D\xa3\xd1\x02\xd5X\x0ey\xb8\xc2R\xdfk\x1c\x0b\xb3fH-\x02\x80\xd7_\x7f\x1d\xdb\xb6m\xc3\xee\xdd\xbb\xb1k\xd7\xae\x82K-\x11\x05\xf9e\xa2\x14\xc6\xc6\xc6fue\x16S\xa0T*\x15N\x9c8\x81t:\x8d\xf1\xf1q477\xc3n\xb7\x9fUrL\xf5\x00\xdf|\xf3M\x18\x0c\x06\xa6\xa2\xc7b1\xa4R)\xa4R\xa9\xb3\x9681\x1b\x04A`\xaa\x1d\xa9\x80\x94\xd8\xa0\xd5j\x19\x09$\xe2M\x82\x05\x915\xa5\xfd \x92K]R\xe89\xd4\x8aR\x9e<#\x9fc\xb9r\xb6XY\xd3\xf3\x15|\xe8\xf7\xcb\x9b5\x14\x9b\xc7\xb3U\xfaG\x5c\x8cA\x10E\x91)D\xd4aB\xde\xda\x86\x9e\xa7V\xab\x11\x8f\xc7\xd9\xe4\x91\x9f\x0f\xe5\xe5\xe5\xf0\xf9|0\x9b\xcd\xa8\xa8\xa8\x00\x00x<\x1ev\x135\x9b\xcd\xe8\xec\xecd]\x1bh.m6\x1b\xdb\xcc\x1c+\x07t\xa9 %\x98\x0es\xba\x8c\xc8\xd7\xecL5*9\x96\xbf\xe2\x97\xcdf\x99\x8a\xf7\xf4\xd3O\xe3{\xdf\xfb\x1e\x06\x07\x07\x91J\xa5\xd8\xda\xa0\xb3C\xaenY\xadV\x5cs\xcd5hkkCCC\x03\xfbc\xb5Z\x0b\xde_\xa3\xd1\xa0\xa2\xa2\xa2\xc0\x16\xccu}%\x93I\xf6\xbaR\xc9\x86gb\x9c\x88\x10\xe4r9\xf4\xf5\xf5!\x99L\x22\x95J!\x99L2\xb5\x88D\x10\xb9z\xb6\x14m\x14\x8d)\x9d\x11\xe9t\x1a:\x9d\x0e\xd9l\x96\xf5\xa8'\x8f\x16u\xbc(f\xd3\x88\xf0\xd2\xbf3\x99\x0c\xb2\xd9,\xe3\x04t\xb1P\xd6\xbc\x95\x7f\x9e\xa5r\xd6(\x95k%9%\xe1\xe6L\xb9}\x17\x8d\x00\xcewP\x22\x91\x08\x93ri\xc1\xd3m\x87\xfc\xe4rW\x92\xfc\xef3Y,q\xa9\x93Be\xf6\x10-(\x22\xe3Z\xad\x96\xf5\x07\xa6q\xa6\x9f\xa9\x83\x8b\xc9d\x82\xcb\xe5B(\x14b\xad\xe2\xd4j5\x8cF#\xdb|\x07\x0f\x1edq\x86\xd4K\x98nv\x5c\x05\xe4\x06\x9f\x83C~&\x85\xc3a<\xf5\xd4Sx\xf4\xd1G\xd9Y\xa44\x86z\xbd\x1e\x95\x95\x95\xb8\xf3\xce;q\xc3\x0d7\xe0\xf2\xcb/\x9f\xd6\x1d\x86\xc8Q\xb15\xb6\x10Y\xc2\x14^s6\xce1y\x22\x9f \x08\x18\x1b\x1b\x83Z\xadf\x1e0y\xa8\xd4R\xdc\xefJ;$'\xaa\xe4\x0e&\xb2N\xa4\x8cD\x84L&3\xcdU+O\xf2 \xc50\x9b\xcd2AH9&\xa7\x9b\x88y6\xc6l\xa6X\xfe\xb3\xa1\x02\x8a\x8b\xb5\xf9U*\x15\xeb\x0c\x92L&\xd9d\xc9\xcbI\xe8t:\xa4\xd3\xe9\x82\xe0H\x8au(\x15GT,\x93h\xa5\xa9\x7f\xf2\x9b\xa0|\x1c\xe4A\xa7\x14\x17\x91\xc9d \x8a\x22\x0b\xdc\xcff\xb3\xcc}\xeb\xf5z\xa1\xd7\xeb\xd9\x1cP\x1b\xbfd2\x09\x8dF\x83p8\xcc$\xfd`0\x88}\xfb\xf6\x15\xf4\x10\xa6x\x0d\xde6n\xf9\x13f\xf2\x18\x8b\x5c.\xc7\x08!\x05\xd2\x92{\x97\xdc\xec\x14\x9bID\x906\xb3Z\xadF&\x93\x81Z\xad\xc6\xd0\xd0\x10\x8e\x1e=\xcaJ\xcb\x18\x8dFF\xfe\xb8+\x98+\x7f\x1c+\x0ft~h\xb5Z<\xf2\xc8#\xf8\xe1\x0f\x7f\x08\xb7\xdb\x0d\x00,\x06L\x92$\xacY\xb3\x06\x8f<\xf2\x086o\xde\x8c\x86\x86\x86\xa2kh\xb1\xcf\x10eAe\x95J\x05)/\xcd\xba\xc6\xe5\xee\xec\x85T\xff\xc8\xcbb\xb5ZY\xf8\x8d\xd2\xce\xcd&\x06\x9cm\x05\xb0X\xfb=\xa5r)\x0f\xfb\x22\xc5\x8b\xc2Fh.\x94\xe4\x8f\x04!\xb2M\xc5\xde\x93\x88\xe5\xb9$>(\xe7\xd4`0\xc0f\xb3\xc1h4N\xcb\x8a>\xa7\x08\xa0\xbc\xae\x8d\xdb\xedFUU\x15\xe2\xf18K@ \xd7$\x11\x0br\xf5\xc6b\xb1\x82x\x07\xb9\xaa\xa0\xcc>,\xa64\xae$\x05p\xa6\x1aF\xc5T\x18\xf9\xd8\x90\x22H\xb7(\x9dN\xc7\xe4v\x8b\xc5\x02\x95J\x85H$\x02\xbd^\xcfn\xc5\x92$\xb1\x22\xbe\xf2\xf2\x1d\x9d\x9d\x9dp:\x9d\xacw0\x91@\xee\x0a\xe6d\x90c\xe5\x81\x5c\x97\xd7]w\x1d\x06\x06\x06\x0a\x5cz\xe9t\x1a\x97]v\x19\xbe\xff\xfd\xef\xe3\xbc\xf3\xce\x9b\xb1T\xc6\x99\x5c\x83rwr>\x9fG^*}\xc9>r\xe4\x08\xc6\xc6\xc6\xb0a\xc3\x06\xd4\xd6\xd6.\xd89G\x99\xb1F\xa3\x11.\x97\x0b\xf5\xf5\xf5\x05\x04j&\x22\xb8\x14I\x8c2\x16Oi\x93\xc8\x0bEv%\x95J\xb1\xd2f\xa4\xd6\x91:H\xc9\x1f\x14\x06&O\x08*V6\xee\x5c%\x7f\x04\xb5Z\x0d\x97\xcb\x05\x8b\xc5rn\x13@\x22\x81\x82 \xa0\xa1\xa1\x81\x05\xd7\xca\xbf4e\xf1\xa4R)\xa6\x14\x92\x8f_\x1e\xf3\x07\x00z\xbd\x1e\xf5\xf5\xf5X\xb5j\x15+[B\x81\xe6\xf2E\xb7\x9cI\x87|<\xe8\xdf\xd9l\x16\xa1P\x88e[\xd2f\x19\x1d\x1dE4\x1aE8\x1cf\x8f\xd1\xa6Q\xb6\xdf\xa1C\x862\xb8r\xb9\x1cL&\x13{\x1e5)'\xd7\xb1^\xafg\x85J\x83\xc1 \x0e\x1f>\x8c\xea\xeajTWW\xc3b\xb1\xb0\x98A^\x16fe+A\x8be\xd89\x96\x16\xc8\xdd\x9bJ\xa5\xf0\x9d\xef|\x07\x0f>\xf8 \x04A`e\xbb\x00\xe0\xaa\xab\xae\xc2?\xfc\xc3?`\xcb\x96-,\xd6\xeel\x94[Q\xaeKe\xd8B)\x050\x99Lb||\x9c\x950\xab\xad\xad]P2 \x08\x02\xccf3jkkq\xe9\xa5\x97\xe2\xa7?\xfd)\xc6\xc7\xc7\x0bB\xa1\xe4b\xc7R =r\xd7\xab<\xb35\x93\xc9 \x99Lbxx\x18\x03\x03\x03\x18\x19\x19A$\x12a\x17\x82L&\xc3\x1a\x09P9\x18\x22\x85\xc0\xc9Lp*\x1cO|\x80\x94?9\xf9\xd3h4\x8c\x17\xd4\xd5\xd5\xa1\xbc\xbc\x9c%\xa5\x15k\x16q\xb6/\xc5\xca\x905\xb9\xdb\xdfn\xb7\xa3\xaa\xaa\x0av\xbb\x1dV\xab\x95y\xde\xcei\x17\xb0\xb2\xb1\xb1\xcdf\xc3\xc0\xc0\x00#\x17\x82 0\xb7/\xc9\xbdJ\xe5\xcf\xe5ra\xf3\xe6\xcdX\xb7n\x1d\x9cN'\x1b\x1cr7\xcao\x99+A\x01T\xc6\xaa\xd0x\x11y\xa6\x04\x9ah4\x0a\xb7\xdb\x8d`0\xc8Z\xcf\xf4\xf5\xf5\x15,D\xb9\x9cN\x8a \x118yk\x1aJ\xceQ\xab\xd5,k\x8e2\x81\xfb\xfa\xfa\xd0\xd5\xd5\x85\x9a\x9a\x1a8\x9dN\x94\x95\x95\xb1ZP\xdc\xf0\xaf\x1c\x92W\x8a\xf0qEpy\x93\xbf\xa3G\x8f\xe2\xe3\x1f\xff8\x0e\x1c8p\xd2\x98\x88\x22\xd2\xe94\xd6\xacY\x83'\x9f|\x127\xdcpCA\xf8\x892\xb9c1\x8cm1\xa3\xaf\xfcY^/\x0e\xc0\x8c\x0a\xa0\xbc$\x8d \x08\xec\x02\xbc\x90g\xbbZ\xadf\x09xZ\xad\x16eee\xa8\xac\xacd\xd51\x8ay\xc0\x96\xc2\xbeR\xc6\xda\x11YK\xa7\xd3X\xb7n\x1d&''\xd1\xdd\xdd\x8d\xfd\xfb\xf7\xa3\xbf\xbf\x9f}\x07\xaa\xe8\x91\xcdf\x99`@\xff&\xce@\xb1\x7fD\x80\xe5j\xa2\xcdf\xc3\xd5W_\x8d\x8e\x8e\x0eTVV\xc2j\xb5\xb2J#d\xb7\x96\xd2\x19\xa4\x8c\xcf\x97\xab~T-\xc5j\xb5\x16\x10\xc0sZ\x01\x94\x7fqb\xf8\xb4\x89\x08\xf2\xac\x1e\xba9\xc8\x0d\x8b\xddn\xc7\x07>\xf0\x01\xac[\xb7\x0e---p\xb9\x5c\x05\x04C\x9e\xf6\x7f\xb6\xea\xe7\x9c\xe9\x03\x97\xc6\x94\xfe-'p\x14\xcfG1\x13\xd1h\x14\xd1h\x14\xc1`\x10>\x9f\x0f\x13\x13\x138|\xf80:;;Y\x11m\xda\xb4\x82 \xb0\xc2\x9a\xc9d\xb2\xe0\xa6I%d\xe8\x00\x94\xbb\x81s\xb9\x1c\x0e\x1f>\xcc\xfa\x05;\x9dN\xe6\x0a\xe6*\xe0\xf2\xc5\xd9l\xe4\xceq\xf6A1\xc1/\xbf\xfc2\xfe\xec\xcf\xfe\x8c\x91\x22:\xe7\xef\xbf\xff~<\xf1\xc4\x13\x05\x86\xf8L\x9f\x07\xb3%&)\xeb\xc9\x96Z\xcbD\x16\x17\x83\x00\xd2\xb8Q\xd7\x0c\xadV\x0b\xab\xd5\xcaD\x11%i\x90\x17d_*kAN\x02\xc9\xae\x93\x08A\x84&\x9f\xcfctt\x94\xa9w\xe4\xe6\xa4\xf8P\xbd^\xcf:w\x10\x1f\x90\x8b\x14\xf4\xddM&\x13n\xbf\xfdv\xb4\xb7\xb7\xa3\xb9\xb9\x99\xb9M\xa9\xde\xadR%]*c\xa4\xbc\x84\xd0\xe5\x83l%%\x80P\x99\x9c39\xbf\x8b\x1aqK\x0bV\xde\xdaE\xb9`\xc8%,\x1f\xa4\xcd\x9b7c\xcd\x9a5X\xbf~=\x93x\x0d\x06\x03\x0b\x10V\xca\xe0\xcb\xd5\x18\xcd\x14`+\x1f/\xda(tsR\x12A\x9f\xcf\x07\x8f\xc7\x83\xa6\xa6&\x9cw\xdeyx\xe7\x9dw\xd0\xd7\xd7\xc7Z\xea\x91\xbb\x86\x12v\xe8\xbd\x89\x94S\x89\x04y\x0b>\xfa{jj\x0a===hllDuu5/\x0e\xcd\xc1\xb1\x8c\x89\x1f\x19\xd7\x1f\xfc\xe0\x07\xf8\xdc\xe7>W@\xec\x9a\x9b\x9b\xf1\xb3\x9f\xfd\x0c\x9b6mb\xc1\xfcg\x03\x92$\xa1\xaf\xaf\x0f555\x05IjJ\xdbS\xaa=Y\xb1\xf3V\x10\x04\xd6\x86m!]\x8bt\xd1&\x85\x94b\xb3g\x22\x0f\xf2\xdf\x7f\xb6\xec\x9e\xd2\xcd*\xff\x9b*|P\xd7)I\x92\x10\x0a\x85\xe0\xf5z\x0b\x08\x9a^\xaf\x07\x00\x18\x8d\xc6\x82\xf6v\xca,_\xb9\xbd\xdb\xb2e\x0bZZZ\xb0f\xcd\x1a455\xb1\xdfA\xea\xdfR\xb49J\x05P\xe9\x0a&[)o\xe8p&!.\xf6\xa1Q,iA\x1e\xd8)\x7f\x8e$I\xa8\xae\xae\xc6\xda\xb5k\xd1\xd8\xd8\x88\xba\xba:TUU\xc1b\xb1\xb0\xf6g\xf2\xc5\xb6\x12\xdaH\xcd\xa5\x06\x94\xfc@\xa3MDD\xb0\xba\xba\x1a\xc1`\x10\x1e\x8f\x07UUU\xa8\xae\xae\xc6\xae]\xbb\xb0w\xef^\x84B!6\x1f\xf4|Z\x94\xb4A\xe5E*S\xa9T\x81\x0b>\x16\x8b\xa1\xbf\xbf\x1f\xa3\xa3\xa3hjj\x82\xcb\xe5\x82\xc9d\xe2\xb1\x80+\x84\x10p\xac\x1c\x90\xd1\xfa\xd4\xa7>\x85\x1f\xff\xf8\xc7\xac\x96[.\x97\xc3=\xf7\xdc\x83g\x9ey\x86]H\xcf\x06\xf9#\x92v\xe4\xc8\x11<\xf0\xc0\x03\xa8\xab\xabCmm-V\xb5\xb4\xe0\xaf?\xf5)\x5c\x7f\xddu\x8c\xfc\xc9E\x07\x22-\xb3\xa9\x89\xe4)Y\xac\xb1%5H\xab\xd5\x16\xcdt]\xaa(\x16_Iud\xa9\x8e\xdf\xf8\xf88\xea\xea\xea055\x05\xa3\xd1XP9\x82TARe\x95\x8d\x1f\x08\xabW\xafFSS\x13\x9a\x9b\x9b\xd1\xd8\xd8\x88\xca\xcaJ\xa6\xfe)\xc3\xc1\xce\x15^\xa0$\x83g\x03\xe2\x99\xfe\xc2\xa4\x08*\xb3Wi\x00\xd6\xae]\x0b\xbb\xdd\x8e\xca\xcaJ\xd8l6\x98\xcd\xe6\x82\xfa8+\xf5\xe0-\x059\xd9\xd2j\xb5\x8c\x10f\xb3YX,\x16X\xadV\xd8l6\x94\x97\x97\xc3f\xb3A\xab\xd5\xa2\xbc\xbc\x1c\xff\xf7\x7f\xffW@\x02\xd3\xe94+\xd0I2}>\x9f/\x88\xd1\xa4\xc3\x8a\xc8\xe0\xe0\xe0 \xc6\xc7\xc7\xe1v\xbbY\xa2\x8e\x9c\xacs,_,\x85:[\x1cgf\x9eU*\x15\xee\xba\xeb.<\xff\xfc\xf3\xec\x02\x98L&\xf1\xcc3\xcf\xe0\xd3\x9f\xfe\xf4\x9f\x0c\xcaY*\x05E\xc6\xf4\x8f\x7f\xfc#\x00`tt\x14\xa3\xa3\xa3\xd8\xbd{7~\xfb\xbb\xdf\xa1\xa9\xa9\x09\x9f\xfb\xdc\xe7\xf0\xf9\xcf\x7f\x9e\x95\xb5\x9a\xcbE\x86\xce\xd23q\x9e\x9d\x8bY\xad\xcaq\xa1\xd86\xe0d(\x97\xcb\xe5b\x7f\x02\x81\x00\xb3\xe7\x0e\x87\x83%0R\xbc#\x91\xf1b\xed\xd2jjj\xe0r\xb9PUU\x05\x87\xc3\x01\x8b\xc5\x02\x83\xc1pN\xf3\x82\xa50\xcf\xe2\x99>H\xe80Q\xa6\xe2\x13(\xde\xcfl6\xc3d2\x9d\xb5&\xdd\xe7\xf2\xa2\x22\x05\x8e\xba\x80h4\x1a\xe8\xf5z\x18\x8dF\xe8\xf5z\xd6\xbe+\x9f\xcf\xe3\xa5\x97^b}\x99\x95-\x87\xe4\xe4\x9c$jy\xcb\x1aA\x10\x10\x0e\x87\xe1v\xbb\xe1\xf1x\x10\x0a\x85PQQ\xc1:\x85ppp\x9c\xdb\xa0K\xfa\xddw\xdf\xcd\xc8\x1f\xa9,o\xbf\xfd66o\xde\xccz\xbd.\x05\xec\xd8\xb1\xa3\xa0\xf6,p\xb2\xf2DOO\x0f\xee\xbd\xf7^\xdc\x7f\xff\xfdx\xf8\xe1\x87\xb1v\xedZ\xe8t\xbaY\x0b\x06\xcf\xd4\xb7\xb5\x94\x9a\xc3m\xd0I{a2\x99`\xb1X`\xb3\xd9\xd8\xdfv\xbb\x9d\xf5]\xa6\xb2dF\xa3\x91\xcd\x13\xcd\x87\xbc\xc4\x9bV\xab\x85\xc3\xe1`\xbc@\xa7\xd3\xb1\xe2\xe2\x1cK\x90\x00\xca'Q\xf98\xc5\xac\xc9\xff\xc8AA\xa2D\xfcVB\x92\xc7b\x93A\xea\xd8!\xef\x0dL\x99\xd8\xf1x\x1c\xaf\xbe\xfa*#\x7f\xca\xc0^j\xe5C\x89 D\x02\xe57\xfe\xa3G\x8f\xe2\xfc\xf3\xcfG0\x18D\x22\x91@YY\x19\x0b\x9c\xe6X\xfe\x04a>\x8fs\x9c{g\xc8_\xfe\xe5_b\xeb\xd6\xad,h\xbf\xac\xac\x0c\xef\xbe\xfb.\xd6\xad[\xc7\x12\xfdf[\x1f\x8b}\x86Sm\xd9\xef~\xf7\xbbx\xf1\xc5\x17\xd1\xdf\xdf\x8f\xde\xde^\x1c9r\x84\x91A\xbd^\x8fl6\x8b\xaf~\xf5\xab0\x9b\xcd\xb8\xe9\xa6\x9bp\xfb\xed\xb7\xb3\xb8\xbbb%D\x94\xa5\xc9\x94\xe7\x1a\xfdL\xf1\xd4\xfc\xe2\xfb\xa7\xf9 \x976u9\xb1\xdb\xed\xa8\xad\xad\x85\xc1``%\xc6\xa8\x12\x88<\xa9\xb1Xm[\x12/\xc8\x8eq^\xb0\x04\x09\xa0r\x03\x15K`\xa0\x85A\x05\xa1\x95\xc5\x9c\xc9}H$\x83O\xf2\xc2\x80\xb2\x8d\xe8\xdf\xf9|\x1e\xf1x\x1c\xe1p\x18\x93\x93\x938t\xe8\x10\x80\xc2\x12\x09\xf2lk\x22{\xf1x\xbc\xe0\xff\x04A\xc0\xe4\xe4$B\xa1\x10\x82\xc1 \xe2\xf18#\xf9\x9c\x00.op\x17\xf0\xf2\xc7'>\xf1\x09l\xdd\xba\x95\x15\xe9\xd5\xe9t8t\xe8\x10\x1a\x1b\x1bg\x8d\xb7\x92\xb7\x06;S\xeb\xb1\xaa\xaa\x0a\x97]v\x19\xae\xb9\xe6\x1aD\x22\x11\x04\x83A\x04\x02\x01\xfc\xe2\x17\xbf\xc0\x81\x03\x07\xd8%8\x1a\x8d\xe2W\xbf\xfa\x15^{\xed5<\xf0\xc0\x03\xf8\xd2\x97\xbe\xc4\xea\x9d\xceDdU*\x15|>\x1f\x92\xc9$\x02\x81\x00\x9a\x9b\x9ba\xb1X\xe0\xf1x\xf0\xf5\xaf\x7f\x1dO=\xf5\xd4\x92RC\x97\x0a\x11$\x8f\x13\x95\xbb\xa1\xf8Q\xaf\xd7\x8bL&\x03\xb7\xdb\xcdb3\x8b\xad\x17y\x1b9\xce\x07\x16x~\x16\xe3\xc6(\x872\xd1CN\x0e\x8b\xc9\xebt\xa8\xcc\xd4\xfd\x83\xe3\xf4H\xa0V\xab\x85^\xafGEE\x05\x1a\x1b\x1b\xb1j\xd5*\xb4\xb7\xb7\xa3\xbc\xbc\xbc\xb06\xd6\xff\x9f\x17\xbau\xa9\xd5j\xe4r\xb9\xa2\x1d?(\xf3.\x12\x890\x02\xc8\x15\xa0\xe5\x0f>\xc7\xcb{^\xbf\xfb\xdd\xef\xe2\xa7?\xfd);\xc7\x8dF#zzzPSS3\xa7\xcb\xb9 \x08x\xf5\xd5W\xb1k\xd7\xae3\xb6^\xa8\xb2D\x22\x91\x80Z\xad\x86\xd3\xe9\xc4\x1dw\xdc\x81\xfd\xfb\xf7\xe3\xbd\xf7\xde\xc3\xc6\x8d\x1bY\xfc\x1fp\xb2\xa8\xfd\x97\xbf\xfcel\xd9\xb2\xa5\xa4\x1d#\xbb\xb4o\xdf>\x1c?~\x1c}}}\x18\x1e\x1e\xc6\x81\x03\x07\xd0\xd6\xd6\x86\xef}\xef{x\xf2\xc9'9\xf9S\xcc?\x09=\x82 \xa0\xae\xae\x0e\xabV\xadB[[\x1b\xf3\xf0%\x12\x09X,\x16&P\xc8\xcb\xc5\x15\xe3\x16\xe4\xd5\xe2\xbc`\x89\x12\xc0\xb9lt\xb9\xebW9\x91\x9c\xf8-.\xa8\xf8\xa4\xd9lf$\xb0\xb9\xb9\x19\xf5\xf5\xf5Lu\xa59\xd0\xeb\xf5H&\x93\xac\x04\x0c\x91\xbfb\xca\xad\xc7\xe3A8\x1cF4\x1ae\xdd]8\x96\x0ff\xda\x8fs\xc9R\xe78w@1\xc0\xaf\xbf\xfe:\xee\xbb\xef>f\xbc\x8dF#v\xef\xde\x8d\xda\xda\xda\x92nN\xf9Zx\xea\xa9\xa7\xb0e\xcb\x16\x5cv\xd9ep\xbb\xddg\xa5t\x09)J\xd9l\x16\x17]t\x11\xf6\xed\xdb\x87\xe7\x9e{\x0e\xeb\xd7\xafg\xdfW\x14E\xbc\xfa\xea\xabhoo\xc7\xdbo\xbf\x0d\x00\x05$Q\x0e*\xb1%I\x12|~\x1fn\xbc\xf1F\x04\x83A\xe4\xf3y|\xe5+_\xc1\xce\x9d;\xf9\x22\x92\xd9y9\x014\x99L\xa8\xa9\xa9\x81\xd1hD}}=DQdmH\x95\xf6\xbf\xd8:\x91W\x0b\xe1g\xce\x12$\x80r\xf5h.\x13T\xcc\xa8PM;>\xc1\x8b\x07J\x10\xb1Z\xadp:\x9d\xa8\xad\xadEcccAp-\xb9J,\x16\x0b\x8cF#s\xf5\xd0\x86Vf\x7fMLL \x1e\x8f#\x91H\xb02\x0b|\x0e\x97\x8f\x224\x9f\xb9\xe4\xf3~n\x9f\x0d]]]\xb8\xf9\xe6\x9b\x99\xfbN\x92$\xfc\xcf\xff\xfc\x0f\xd6\xacY3\xeb\xdcR\xec\xf0\x87?\xfca\xfc\xed\xdf\xfe-T*\x15t:\x1d.\xbd\xf4R$\x12\x89E\xbf\xd8\x17\xf3(E\xa3\xd1\x82f\x04W\x5cq\x05\xbe\xf1\x8do\xe0\x8b_\xfc\x22\x8b\x0b\x14E\x11\xc3\xc3\xc3\xd8\xb2e\x0b\xfe\xe5_\xfe\x05\x1a\x8dfZ\xfc\x9f\xfc;\xaa\xd5j\xd8]\xe1/\x0f\x00\x00 \x00IDAT\xac6<\xf5\xd4S\xec2\x9cN\xa7q\xd3M7!\x1c\x0e\x17\xd4\xbe]\xa9{\x83\x92<\x88\xd4\x95\x97\x97\xa3\xa6\xa6\x06\xf9|\x1e\xc1`\xb0\xa0\x86\x9f\x92\x03\xcc\xa4\xc0r,a\x02(\xdf \xf2\x94\xf6\x996\xd1L\x87\x01/$\xbc\xf8j\x8e(\x8a\xd0\xeb\xf5(//\x87\xcb\xe5Buu5\x1c\x0e\x07{\xdcl6\x17\xa4\xe6\xd3\xebf:\xc0\xd2\xe94\xc2\xe10\xe2\xf18S\x009\x11X>\xeb\x85\xf6%\xc7\xf2\xc7G>\xf2\x11\x96\x91\x99H$\xf0\xd4SO\xe1\xc6\x1bo,j\x98\x8b\x91\xad\xcd\x9b7\xe3\xf7\xbf\xff=#\x94\xa9T\x0a\x7f\xfd\xd7\x7f\x0d\x83\xc1\xb0\xe8g\x82\xf2\xfd%I\xc2\xe0\xe0 \xdez\xeb-\xec\xdd\xbb\x97\xd53\x15\x04\x01W]u\x15~\xf2\x93\x9f\xa0\xbd\xbd\x9dy9R\xa9\x14\xfe\xfe\xef\xff\x1e\x0f=\xf4\x10\x8b{\x9ciO\xa4\xd3i\xdcu\xd7]\xb8\xef\xbe\xfb\x98\xda\x15\x08\x04p\xe5\x95W2b\xa9\xfc,+)n\x96\xba\xc6\xd0w\x0e\x85B\x08\x85B\xa8\xaa\xaaBCC\x03\xcb\xfe-v\xc9,\x96@\xca\xed\xc99B\x00\xe5\x13z*\x0b\xbeTaN\x8e\x85\xbb\x9dQ\x86\xb0\xddnGEE\x05\xacV+\xabF/\x8a\x22l6\x1bk\xe7C\xedvH\xceW\xcek<\x1e\x87\xcf\xe7\x9b\xd6\xf6o\xb6\xf5!/\x05\xa4|Ly\xa0\xf3\x03\xe0\xd4\xf7a\xb11\x9ei\xbc\x8b\xa9(3\xdd\xcc9\x96\xcf:\x01\x80\xc7\x1e{\x0c\x9d\x9d\x9d\xcc\x13\xf3\xc9O~\x12\xf7\xddw\x1f\x0b\xd0/\xf5Z\xaf\xd7\x8b\xcb.\xbb\x0c;w\xeed\xcf\xb5Z\xadx\xeb\xad\xb7\xf0\x8f\xff\xf8\x8f\x05\xdd\x84\xce\x04\xf9\x93?\x9e\xc9dX\xa9*\xf9z\x06\x80\x17^x\x01\x0f?\xfcpA\xd7\x8fo|\xe3\x1b\xf8\xf2\x97\xbf\xccb\x9fg\xda\x0f\x99L\x06O>\xf9$\xb6l\xd9\x82\x5c.\x07\x8dF\x83C\x87\x0e\xe1[\xdf\xfa\x16\x0b\x99\x01N\xba\x8e\xfb\xfa\xfap\xe8\xd0!\xd6\xf9b%\xd8\x18\xea\xf3\xabR\xa9PSS\x83\xb6\xb66\x96\x8c8\x1fn\xc0\x15\xc0s\x8c\x00\x16c\xf1\x9c\xc9/-UG\x1e\x0fh\xb3\xd9PQQ\x81|>\xcfZ(\xe9\xf5z\x16#Cs\xa9\x8c\x13\x94oPR\xff(\xad\x9f68\x15\xa5\xa6^\x91\xd4\x09F\xfe\x98\xfcg\x0a\xe4\xa6\xee$\xd45F\xdeA\x86\xa3\xf8!)o\xcc>\xdb\x18\xd3\xf8R_n\xf98+\xc30\x94\x8a\xfdL$\x91\xe3\xdcD&\x93A__\x1f\x1e~\xf8a\xf6X{{;\xfe\xf3?\xff\x93\xd5b+u\x96D\xa3Q\x5c}\xf5\xd5\xe8\xec\xecd\xc5\xe8\xdb\xda\xdap\xf4\xe8Q\x5cq\xc5\x15\xec\x1c9\x955M\xeb9\x9dN\x97\xdc\xfbs\x89S\x0d\x06\x83\xcc;A\xeei\xbf\xdf\x8f\xaf\x7f\xfd\xeb\xf8\xcdo~\x03\x00\xac+\xc7\x13O<\x81G\x1ey\x04v\xbb\xbd\xe8\xa5\x96\x08_:\x9d\xc6\xb6m\xdbPSS\x83L&\x03A\x10\xf0\xe8\xa3\x8f\xa2\xab\xab\x8b}\xa6@ \x80\xae\xae.\x0c\x0f\x0f#\x14\x0a\xad(;#o\x137\xd7\x02\xdc\xa5\xe6\x90c\xe1pFR\x96x\xb9\x88\xa5M\x02\x0d\x06\x03,\x16\x0b\xccf3\xacV+\xacV+\xeb\xb3\x98N\xa7\xe1\xf3\xf9\x10\x8b\xc5\xd8\x0d\xac\xd8A\x9b\xcb\xe5\x90L&\x19\xf9\x937\xf3&2A\x07@\xa9,\xf0b\x87\x87<\xfbK\xde\xa1\x84g\xdc\x15\xee1\x1ag\x22x\xf2\xdaZ\xb3\x1d\xac\xf22L\x1a\x8d\x06:\x9d\x8e\x198\xf9\xf3h.\xe4e~\xf2\xf9\xfcY\xeb\xfd\xca\xb10\xe4O\xab\xd5\xe2\xe6\x9bofk@\x92$|\xf7\xbb\xdf-I\xfc\x08\xa9T\x0a\x1f\xf8\xc0\x07p\xec\xd81\xd62\xed\xa2\x8b.\xc2\xae]\xbb\xd8\x1e\x9do'\x0d\xba@\x16\x8b7>U\x15\x10\x00\xa2\xd1\xe84%\x89>\xe3\xad\xb7\xde\x8a\x83\x07\x0f\xe2\xfc\xf3\xcfg$\xf6\xbf\xff\xfb\xbf\x99\x0b[\xa9\xdc\xd1\xfe\xa21\xfa\xe1\x0f\x7f\x88\x9bn\xba\x09\x92$!\x1e\x8f\xe3\xcb_\xfe2~\xfd\xeb_\xb3\xf2Y\xa4bq{\xc89\xc1\x8a\x22\x80\x1cK\x9b\x00\xeat:\x98L&\x98\xcdf\x88\xa2\x08\xbb\xdd^P\x0f\x8bj8\xe5r9v\x80)\x89C.\x97C,\x16c7u\xf9\xe3\x92$\xb1\xe4\x10\xca\xa2\xa3\xdb|\xa9\x9b\x9d\xbc96e\x22\xd2\xe7\xa4 m\xder\xeeO*\x09\x91py2\x0e\x11\xee\x99\x0e\x5c*\x08\x9e\xc9d\x0a\xaa\xf7\x93\x0a,\x8f\xff$\xe2\xa7\xd7\xeb\x0b\x94`y\xed\xb3\xd9\xdc4\xdc\x03\xb0\xf4\xd6\x8eF\xa3\xc1\x13O<\x81\x13'N@\xab\xd5\x22\x9dN\xe3\x9e{\xee\xc1\xf5\xd7__r\x8f\xd1\xff\xdds\xcf=\xd8\xb1c\x073\xec\x97\x5cr\x09\xf6\xec\xd9\xc3\x8a\xfd\x9e\x0ab\xb1\x18\x02\x81\x00\x8e\x1c9\x82\x1d;v`\xdf\xbe}\xe8\xea\xea\xc2\xc7?\xfeq<\xfa\xe8\xa3\xa7\xdcy#\x93\xc9L\xfb>\xf2\xb3j\xfd\xfa\xf5\xe8\xea\xeaB{{;[\xaf[\xb7nE}}=\xae\xbc\xf2\xca\x0276\x9d\x83\xf4\xf3\x87>\xf4!\xdcz\xeb\xad\xf8\xedo\x7f\x0b\xb5Z\x8dm\xdb\xb6\xe1\xd5W_\xc5\x87>\xf4!\xd6\xee\x8c\x8aK\xaf4;\xc3\xc3GV \x01\xe4\xedq\x96>\x04A`\xad\xe2L&\x13$IbE^EQD(\x14B.\x97c\x07\x98\xbc\xf2\xbfrn\xe5\xaa\x13\x1d\x90*\x95\x0a\xc9d\x12\xd1h\x14\xe1p\x18^\xaf\x17>\x9f\x0f\xa1P\x08\xc9drNY\x85\x00\xa0\xd3\xe9X\xb2\x0a\xf5\x89\xd6\xe9t+~\x8d\xc9\xdd\xbe\xd1h\x14\x81@\x00~\xbf\x1f\x13\x13\x13\xac3K)\xd0\xeb\xf2\xf9<\xccf3\xea\xea\xeaP[[\xcb\x02\xb8\xa9\xd9\xba\x9c\xf4'\x93I\xb6\x0e(K4\x95J1e\x96\xe3\xdc2\xce\xb1X\x0c\x8f?\xfe8#\x7f\x8d\x8d\x8d\xf8\xe1\x0f\x7fX\xb202\x91\xfe\xa7\x9f~\x1a?\xff\xf9\xcfY\xb0\x7fKK\x0b^{\xed5\xe4\xf3y\x18\x0c\x869}\x06\xf9\x05b\xe7\xce\x9d\xd8\xb6m\x1b^{\xed5\xec\xdd\xbbw\xdas\x87\x87\x87g,!6\x177a)\x22B%c\xda\xda\xda\xf0\xea\xab\xaf\xe2\x86\x1bn`\x17\x96'\x9f|\x12k\xd7\xae\x85\xd5je\x04R\xe9\xc1\x08\x06\x83\xb8\xfb\xee\xbb\xf1\xca+\xaf0\x97\xf1_\xfd\xd5_\xc1\xe7\xf3!\x1a\x8d\xb2D\x11^\x22\x8bcE\x10@~\xdb_\xfa\x87\xbf\xbc\xca:\xb9=jjj`\xb7\xdb\x11\x0c\x06\x11\x8b\xc5\x98\xb2D\x07d\xb1\x04\x0e:\x10)\xbe\x86\x0eiR\x04\x03\x81\x00\x04A\x80V\xab\x85\xc1`\x80\xc3\xe1`\x8d\xc1\x8b)\x0c\xa4j\xa5\xd3itww#\x14\x0a\xe1\xbd\xf7\xdeCkk+#\x1f\xd4\xd2n\xa5\x13\xc0l6\x8bD\x22\x01\x9f\xcf\x87\xb1\xb11\x0c\x0f\x0f\xa3\xbe\xbe\x9e5M'\xb7y1\x8c\x8f\x8f\xa3\xb7\xb7\x17{\xf6\xecA<\x1e\x87V\xab\xc5\x95W^\x09\xa3\xd1\x08\xb3\xd9\x5c\xe0\x02T\xb6f\xcc\xe7\xf3\x88D\x22\x10E\x11f\xb3\x99\xb9\x9e\xe7c\x909\xce>\xbe\xf5\xado\xc1\xe7\xf3\xb1y}\xf6\xd9ggu\xeb\x8b\xa2\x88]\xbbv\xe1\x0b_\xf8\x02\xdb\x8b\x00\xb0g\xcf\x1e\x94\x95\x95\xcd\xf9w\xa7\xd3ih\xb5Z\xec\xda\xb5\x0b\xf7\xde{/\xba\xbb\xbb\x11\x89D\x0a.\xa7\x94l\x01\x00###L\xad>\x15\xcc\xd4\x83^N\x02\x01\xe0\xfa\xeb\xaf\xc73\xcf<\x83\xcf|\xe63,\xce\xef\xa1\x87\x1e\xc2\x8f\x7f\xfcc\xc4\xe3q\x88\xa2X\xe0\xc1\xc8\xe5r\xd8\xb9s'4\x1a\x0d>\xfb\xd9\xcf\xe2;\xdf\xf9\x0e\xb4Z-\xfc~?\xfe\xf5_\xff\x15\xb7\xdcr\x0b\xc6\xc7\xc7\x91\xcf\xe7\xe1\xf1xPYY\x09\xa3\xd1\xb8b\xce..\x04-s\x02\xa8\xec!;\xdbm\x8bci\x90\x07r\xb3R\xdd?\x8dF\x83\xf7\xbd\xef}\xe8\xea\xeab\xaa[(\x14\x82\xc3\xe1\xc0\xc8\xc8H\xc1\x5c\x17K\x04\x90\xf7\xfe\xa4\xf5\x90N\xa7\x91L&\xd1\xdd\xdd\x8d\x8f}\xecc\xf3\xfe\x9c\x17^x!\xa2\xd1(\xee\xb9\xe7\x1e\xa6J\x10y\x5c\xc9\xbd7i\xbc\xd3\xe94\xa2\xd1(\xbc^/\xba\xbb\xbbq\xf3\xcd7\xa3\xae\xaenN\xef\xb1f\xcd\x1a\xc4b1x\xbd^tvv\xc2\xe7\xf3\xe1\xf2\xcb/G8\x1cFee%\xfb=\xf2\xf9T\x1e\xec\x82 \x14\x0a\xf1,\xbds\x0c\x92$abb\x02?\xfe\xf1\x8f\xd9<_{\xed\xb5\xb8\xf4\xd2Kg5\xda\x99L\x06w\xddu\x17DQ\x84$IH&\x93\xd8\xbbw/l6\xdb\xbcT\xf9h4\x8a\xbf\xf8\x8b\xbf\xc0+\xaf\xbc2MA\xa6z\xa5uuu\xd8\xbcy3\x1a\x1b\x1bq\xe1\x85\x17\xb2\x8b\xdfL{\xa2\xd4\xef\x9ek\x11\xe1\x5c.\x87O\x7f\xfa\xd3x\xf1\xc5\x17\x99Kwrr\x12\xff\xfe\xef\xff\x8e\xaf|\xe5+\x88D\x22\x05\x1e\x91\x91\x91\x11\xd6&\xf3\xba\xeb\xae\xc3\xd6\xad[111\x01Q\x14\xf1\xf8\xe3\x8f\xe3\xa2\x8b.b\x97\xb0\x89\x89\x09\xf8|>\xb4\xb5\xb5a\xd5\xaaU\xcb\xde\x8b\xc1\xfb\x85\xaf\x10\x05P\xbe\x88\x95\xea\x1f\x9f\xec\xa5y+#\xb7\x04\x05\x5c\x9b\xcdf\xb4\xb5\xb5\xa1\xae\xae\x0e\xdd\xdd\xdd\x88\xc7\xe3\xd0\xeb\xf5\xd3\x0e\xdc\x992\xbc\x8b)r\x94!\xec\xf3\xf9\xf0\xf6\xdbo\xe3\x8a+\xae\x98\xf3\xe5\x80n\xd7\xdb\xb6m\x83\xdf\xefG4\x1a\x85\xc3\xe1@EE\x05\xca\xca\xcaX\xab\xba\x95z\xd1\xc8f\xb3H\xa5R\x08\x04\x02\x18\x1c\x1c\x84\xddnG]]\x1d+\xbfP\x8a\xf8'\x93I\xfc\xe4'?\xc1\xb3\xcf>\x8b\xde\xde^\x04\x02\x01\x5cs\xcd5\xacN\x9a\xbc\xdb\x8b8\x96>\xf9S\xa9Tx\xe1\x85\x17066\xc6\xce\x80\xcf\x7f\xfe\xf3sr\xdd>\xfe\xf8\xe3\x18\x1a\x1ab??\xf6\xd8c\xb8\xf8\xe2\x8bY\x9c\xf0l{Z\xa5R\xe1\xd9g\x9f\xc5\xa7?\xfdi\xe4r9\x18\x0c\x06\x16\xae\xd0\xda\xda\x8a\x1bn\xb8\x017\xddt\x13\xae\xbf\xfe\xfa9'\x18\xcd\xc5\xc6\x90\x87b\xb6\xe7\xaa\xd5j\xc4b1|\xeb[\xdf\xc2\xde\xbd{166\x06A\x10\xf0\xc6\x1bo\xe0\xc6\x1boD{{{\xc1\xfb\xd0\x05\x88\x92`>\xf3\x99\xcf\xe0k_\xfb\x1a\xb2\xd9,\xfc~?\x9e}\xf6Y|\xecc\x1fC*\x95\x82J\xa5bY\xd7\xabV\xad\xe2\x8b\x91\xe3\xac\xe1\x8c\x97\x81\xe1\xd9?K\xebfFA\xc9j\xb5\x1a\xe9t\x9ae\xc0\xe9t:\xe6\xa6\xb0Z\xad\x88\xc7\xe3\xecfL%E\x94\x07)\x95O(X`\xff_]\xa4\xf7\xdf\xb6m\x1b\xb2\xd9\xec\xb4l8\xb9{Q\x99iz\xf0\xe0A\xec\xd8\xb1\x03^\xaf\x17\xbd\xbd\xbd\x18\x1c\x1c\xc4\xf8\xf88\x02\x81\x00\x8bG[\x89\xa0\xb9\x88D\x22\xf0x<\xf0x<\xb8\xe5\x96[\x0aJ\xf7(\xdd\xb64']]]x\xe0\x81\x07\xf0o\xff\xf6o8~\xfc8\xc2\xe10\x0c\x06\x03\x9a\x9b\x9ba\xb5Za4\x1a\xa7\xc5z*/x\x84p8<\xef\x0b^\xb1\xf7\xe48s\xc8d2P\xa9Tx\xe4\x91G\xd8\xfa\xb8\xf0\xc2\x0bq\xfb\xed\xb7\x97\x9c\xcbl6\x8b\xf1\xf1q<\xfa\xe8\xa3\xec\xb1\xf7\xbd\xef}x\xf0\xc1\x07Y\xdch)P7\x90?\xff\xf3?\xc7\xddw\xdf\x8d\x5c.\x07\xadV\x8bD\x22\x81\xc6\xc6F<\xf7\xdcs\xd8\xb1c\x07\xbe\xff\xfd\xef\xe3\xa6\x9bnb1\xa6\xf2\x18\xd4\xd3!\x81\xa9T\x8a%\xab\xcd\x16\xa2\xa4\xd3\xe9\x90\xc9d\xf0\x85/|\x81\x85\xc7\xa4\xd3i\xfc\xf2\x97\xbfD2\x99d\x09V\xf2\xefE\xfb\xeb\xe2\x8b/\xc6\xa6M\x9b\xd8{\xed\xd8\xb1\x03~\xbf\xbf\xa0\xd6 \x9d\xa9\x00\x10\x89D\xd0\xdf\xdf\x0f\xaf\xd7\xcb\xc5\x12\x8e\xe5A\x00y\x0c\xd0\xd2W\x00Ia\xa3@\xfe\x8d\x1b7\x02\x00\x0c\x06\x03k\x17\xe7\xf5z\x91\xcdf\xa1\xd7\xeb\x0b\x5c\xbcJ\x90\x0b\x99\x0a\x7f\xca{\x0aS\xad\xc1\x81\x81\x01\xfc\xd3?\xfd\x13N\x9c81k\xdfgA\x10p\xe4\xc8\x11\xec\xde\xbd\x1b]]]\x88D\x22\xf0\xfb\xfd\xe8\xe9\xe9\xc1\xd8\xd8\x18|>\x1f\xe2\xf1x\xc1A\xbc\x92\xc8;eW\x87\xc3a\x0c\x0f\x0fc\xc3\x86\x0d\x05m\xfb\x8a\xcd\xb7\xdf\xef\xc7\xcf\x7f\xfes<\xf8\xe0\x83\xf8\xedo\x7f\x8b\xd1\xd1Qf\x10\x9b\x9a\x9a\xd0\xd4\xd4\x04\xbb\xdd\x0e\x93\xc9\xc4\xe6\xb2\xd8\xd8\xcac\xfd\x94*!\xc7\xd2_;:\x9d\x0e\xbf\xff\xfd\xef\x11\x0e\x87\xa1\xd3\xe9\x90\xcdf\xf1\xd5\xaf~uV\xf5L\x14E|\xf1\x8b_d\xff\xd6h4x\xec\xb1\xc7\xe6\xd4\xbf]\x92$\x18\x0c\x06\x9c\x7f\xfe\xf9\xd8\xbau+{~:\x9d\xc6\xc3\x0f?\x8c\x81\x81\x01|\xf4\xa3\x1fe\xa1\x07\xf2s`.=\xe2\xe7\x12s.I\x12FGG\xe7\x1c\x9f.I\x12:::p\xf5\xd5W\xb3\xdf\xf1\xde{\xef\xa1\xab\xabkZ\xc2\x9b\x1ceee\xf8\xc8G>\xc2>\xfb\xc0\xc0\x00\xc6\xc6\xc6\x8a\xc6\x1f\xe6\xf3y\x0c\x0c\x0c\xe0\xc4\x89\x13\xe8\xef\xefG:\x9d\xe6\x8b\x94\xe3\x8c\x80\x97\x81Y\xe1\x86\x80n\xd7\xa4&Q\x00\xb7$I\xa8\xa8\xa8@6\x9b\x85\xd9l\x86Z\xadF&\x93\x99V\xdbO~\x90\xe9\xf5z\x88\xa2\xc8\x12\x07\xa8\xcc\x8cV\xab\x85\xc5bAee%\x9a\x9b\x9b\xf1\xc6\x1bo`\xff\xfe\xfdhoo\xc7\xb5\xd7^\x8b\x86\x86\x06TUU\xc1h4\x22\x9dN#\x9dN#\x91H`hh\x08\xef\xbd\xf7\x1e\x8e\x1f?\x8e@ \x80x<\x8et:\x8d\xfe\xfe~\x8c\x8c\x8c\xa0\xa9\xa9\x09\x15\x15\x15\xac|\xcdJ\xaa\x0bH\xf3E\xf1{\xa9T\x0a\x1b6l(J\xce\xf3\xf9<\xbc^/\x8e\x1d;\x86\xe7\x9f\x7f\x1e{\xf6\xec\xc1\xd0\xd0\x10\x22\x91\x08S\x82DQ\xc4E\x17]\x84\xea\xeaj\xb8\x5c.F\x00\xe5FWi\xe4\x94\xe5,\xb8\x9awn\x80\xc2>\x1ey\xe4\x11\xb6\xaf\xadV+n\xb9\xe5\x16\x96\x94Q\x0cj\xb5\x1a\x13\x13\x13\xd8\xbau+\xf4z=\x92\xc9$n\xbb\xed\xb6Y\xcb\xc5\xc8\x09\xe4e\x97]\x86\xc3\x87\x0fC\x14Ed\xb3Y\xb4\xb4\xb4\xe0\x97\xbf\xfc%6m\xda\x84t:}Z1\xbdsq\xeb\x0e\x0e\x0e\xe2\xfe\xfb\xefGMM\x0d\xbe\xf9\xcdo\x96\xfc}*\x95\x8a\x85X\xdc}\xf7\xdd\xd8\xb1c\x07+L\xff\xcc3\xcf\xe0G?\xfa\xd1\x8c%]\xe2\xf18>\xfa\xd1\x8f\xe2\xf1\xc7\x1fG(\x14\x82$I\xf8\xfd\xef\x7f\x8f\x07\x1ex\x80e\xcc\xcb?w8\x1cF6\x9bE0\x18D2\x99\x5c\xb6u5\x8b\x85\x85\xf1K\xe32!\x80|\x22\xcf]\x02H\x99u\xf2\xfaW\xf9|\x1eF\xa3\x91\xd5\x0a\xa3\x8e\x11\xf2b\xce\xf4>\x1a\x8d\x06\x16\x8b\xa5\xa0e\x1c\xbd\x8fF\xa3AYY\x19\xaa\xab\xab\xd1\xdc\xdc\x8c\x9e\x9e\x1e\x1c\x8f\x91\x91\x11\x84B!LNN\x22\x99L\xb2\xcf499\x89\xbe\xbe>444\xc0\xe5r\xc1f\xb3\x15(\x8f+\xc1\x80Sfv(\x14\xc2\xd4\xd4\x14\xaa\xaa\xaa\xa6)'4?\x07\x0f\x1e\xc4K/\xbd\x84\x1d;v\xa0\xaf\xaf\x0f^\xaf\x17\x91H\xa4\xa0\xd9}[[\x1b:::P]]\x0d\x9b\xcd\x06\x83\xc1\xc0\xc63\x97\xcbM\xeb\xef]l\xbf\xf3\xf0\x8es\x03j\xb5\x1a\xfb\xf7\xef\xc7\xe1\xc3\x87\xa1\xd3\xe9\x90J\xa5\xf0\xf8\xe3\x8f\x9f4\x083\x5c\xa2(\x1c\xe3S\x9f\xfaTA\xf8\xc6\xcf~\xf63d2\x99\x92D\x8aH\xe5\xad\xb7\xde\x8a}\xfb\xf61bu\xfd\xf5\xd7\xe3\xb9\xe7\x9e\x83\xd3\xe9D6\x9b\x9dS\xd1\xe9\xd3\xd93^\xaf\x17\xf7\xde{/\xd2\xe94zzz\xf0\xd8c\x8f\x15t>Q\x92\xd5\xae\xae.LLL\x00\x00\xca\xcb\xcbq\xe7\x9dw\xe2\xd9g\x9f\x85V\xab\xc5\xe0\xe0 v\xef\xde\x8d\xcb/\xbf|\xc6\xd7\xd7\xd5\xd5\xe1\xd6[o\xc5\x7f\xfd\xd7\x7fA\x14E\xbc\xf3\xce;\x88D\x22\xd0j\xb5E\xdb/R\x9f\xe2\x85\xaa\x13\xc8\xcb\xb0q\xcc\x86\x05s\x01\x173\x06|\xf1-}\x15I\xde\xb5C\xa3\xd1\xa0\xad\xad\x8d\x110jh\x1e\x8f\xc7Y\xbc\x9d\xb2\x06\x96<\x19\xc0`0L\xeb\x19L\x06\xc7h4\xc2\xe1p\xa0\xbe\xbe\x1e\xad\xad\xad\xb0\xd9l\x98\x9a\x9a\xc2\xf0\xf00\x0e\x1e<\x887\xdex\x03\xbbw\xef\xc6\x9e={\xb0\x7f\xff~\xec\xdc\xb9\x13\x03\x03\x03\x08\x87\xc3\x88\xc5b\x05\xbf+\x9dN\xa3\xb7\xb7\x17\xa3\xa3\xa3p\xbb\xdd\x08\x85B\xb3\xb6\x89Z\x8e\xea\x1f\xd5\xfd\xf3z\xbd\xb8\xe2\x8a+\x8a\x1a\xde]\xbbv\xe1\xe7?\xff9\xdex\xe3\x0d\xf4\xf6\xf6\xc2\xe3\xf1 \x12\x89\xb09'R}\xfd\xf5\xd7\xc3\xe9t\xa2\xb2\xb2\x92\x95\x8eQ\xba\x92\x95Y\xfe3\xa9&\x1cK\x1f?\xfb\xd9\xcf\x00\x9c\x8c\x89\xd3\xe9t\xb8\xe3\x8e;X\x1b\xb3\xa2\x86B\x10p\xfc\xf8ql\xdf\xbe\x9d\xc5\xf3\xde{\xef\xbd0\x99L%\xe7\x9cb\xfc\xbe\xf4\xa5/\xe1\xa5\x97^b\xcf\xbd\xf2\xca+\xf1\xd2K/\xc1\xe9t\x96$\x9e\x0b)BX\xadV\xac_\xbf\x9e\xfd|\xe0\xc0\x01<\xff\xfc\xf3E_322\x82\xbe\xbe>6&\xa1P\x08w\xdcq\x07\x8b\x0bT\xab\xd5\xf8\x8f\xff\xf8\x0fx\xbd^$\x12\x09\xa6\xa4\xcb\xf7\xc1\xa4{\x12\xdf\xf8\xc67\x0a\xbe\xdfK/\xbd\xc4.\xd5r[\xa9\x8c\x87>\x1d\xc4\xe3qvy^\x0a\xe0g\xc2\x0a \x80\xf31\x12\xa5\x16\x87\xbc\x84\x0c\xc7\xe2\xaa\x7fD\x12R\xa9\x14\xfc~?\xee\xb8\xe3\x0eF\xd8\x08\xe1p\x98\xa9x\x94\xe1V\xac\x9f\xa3N\xa7\x83\xddng\xc9\x03r\x02(W\x01].\x17\x9a\x9b\x9b\xd1\xde\xde\xce\x8a\x10\x87\xc3a\xf8\xfd~x\xbd^\x8c\x8f\x8fcbb\x02\xb1X\x8c\xd5\x0fL&\x93\xec}(\xeehhh\x08\xc3\xc3\xc3\x18\x1d\x1den\x13e\xff\xda\xe5:oT\x1f1\x1c\x0ec||\x1c\xd5\xd5\xd5(//\x9f\xb6gz{{\xf1\xe2\x8b/\xe2\xe0\xc1\x83\x18\x19\x19\x81\xc7\xe3A\x22\x91(p\xdfK\x92\x84\x0b/\xbc\x10UUU\xa8\xaa\xaabsH\xf3]l\xaf\x16#\x86\x1c\xe7\x0e$I\xc2\x9e={\xd8\xcf\x1f\xfe\xf0\x87a\xb3\xd9fu\xe1n\xdd\xba\x95\xc5\xdc\x02\xc0\xfd\xf7\xdf_\xb2\xc7/%\x85l\xdf\xbe\x1d\xdf\xfe\xf6\xb7Y\x0cq[[\x1b\xb6o\xdf~F3\xc7\xa9\x06\xe9\x83\x0f>\x88\xd6\xd6Vv\xce\xfd\xeaW\xbf\xc2\xb1c\xc7\xa6\xadc\xea\x19L \xd2{\xf7\xddw\xb3\xe7MMM\xe1\xd7\xbf\xfe5\x0e\x1c8P\x90\xd0A\xc8\xa43\xa8\xa8\xa8\xc0\xa6M\x9b\xd8\xe5\xf9\xcd7\xdf\x84\xc1`(8\x7f\x95\x04\xf0t\xf7S\x7f\x7f?\xf6\xee\xdd\x8b\xce\xce\xce%aG\xb9\xabw\x05\x11@9\xa1S.>\xf9\x02\x98)\xb0w\xa6zc\x1c\x0bo\x04\x88H\x84B!\xc4\xe3q\xd4\xd7\xd7\x17(z\xc1`\x10\xe9t\x1a\xc1`\x10\xe1p\x98e\xff\xca\xfb\xce\x12\x0c\x06\x03*++\xa1\xd5j\xa7\xa9G\xd4\xc7\xd7h4\xa2\xbc\xbc\x1c\xf5\xf5\xf5hllDKK\x0b\xe2\xf18#\x81\xe1p\x18\xd1h\x94\x156\xa6vfT\x80\x9a\x0aVS\xfb\xb2c\xc7\x8eatt\x14\xe3\xe3\xe3\x88D\x22\xec0]\x09\xf3\x16\x8dFY\xe1\xe7\x0f~\xf0\x83\x05\xbdy\x01`ll\x0c\xbf\xfb\xdd\xef\xb0\x7f\xff~\xf8\xfd~6>\xcan-j\xb5\x1a\x17_|1jkk\x0b\xd4?\xb9\xd2[\xea`\xa7B\xd4\x1c\xe7\x0e\x8e\x1f?\xceH\x0f\x00\x5c}\xf5\xd5E\x09\xbf\x1c\x89D\x02\xaf\xbf\xfe:\xfb\xf9\xb6\xdbnCccc\xc9\xd7\xa8T*V\xe7O\xab\xd5\x22\x99L\xc2j\xb5\xe2\xddw\xdfea\x05\x8bE6f\xfa?\xa3\xd1\x88\x87\x1ez\x88\x95.\x12E\x11_\xfa\xd2\x97\x90\xcf\xe7\x91\xc9d\xe0\xf7\xfb155\x85X,V4\xeb\xfd\xe2\x8b/\x86^\xafg!\x18;v\xec@8\x1c\x9e\x96\xb8!\xb7a\x9f\xf8\xc4'\xd8\x99:66\x86\x91\x91\x11F\x9a\xc9s1W\x028[\xa2\x15\x95\x9d\x11E\x11\xd1h\x14\xd1htI]^g\xeb\xfd\xce\xb1\x0c\x14\xc0\xb9L\xba\xbc\x91|\xb1\xd7s\x17\xf2\xe2\x92\x08\xca\xfa\x0d\x87\xc3\x98\x9a\x9aBEE\x05\xaa\xab\xab\x0b6\x22\x95|\xa0\x96mD\xb0\x8a\x19\xfc\x8a\x8a\x0a\x18\x8dFV\x97\x8f\x12\x08\xe4\xf3-\x8a\x22\xca\xca\xcaPUU\x85\x86\x86\x06\xacZ\xb5\x0aF\xa3\x11\x99L\x86\xf5\x08\xa6^\xb6Tf\x81\x0eF\xeaTB\x17\x07\x9dN\x87\xc1\xc1A\x0c\x0d\x0da||\x9c\xb9b\x96\xb3\x0aH\xea\x1f\xc5\xfe\x0d\x0f\x0fc\xe3\xc6\x8d0\x99L\xd3\x9e\xb3\x7f\xff~\xf4\xf7\xf7#\x14\x0a1\x82\x9f\xcdf\xa7\x8dOGG\x07\x9a\x9a\x9a\x98\xfaG\x89<\xc5\x0c\xd9L\xfbu\xa6va\x1cK\x13\x87\x0f\x1fF0\x18\x84J\xa5\x82\xcdfc\x85\x9fK\xad\xbb\x9e\x9e\x1e\xd6\xef\x17\x00n\xbc\xf1\xc69\xc5\xdc>\xf1\xc4\x13\x18\x1f\x1fg\x04\xe9\x97\xbf\xfc%\x1c\x0e\xc7Y\xeb\xe0\x93\xc9\x9cT\xe5\x1e~\xf8a\xe6\xb6\x0d\x87\xc3\xb8\xef\xbe\xfb\xa0\xd1h\xb0w\xef^\xbc\xf7\xde{\xacs\x91\x92\xa0\x94\x97\x97\xe3\x82\x0b.`\x8f\x1dD\x22\x11f\x8c\xe4c\xe4\xf7\xfbq\xf8\xf0aLLL \x10\x080w\xba\xb2\x0b\x82N\xa7\xc3\xc6\x8d\x1bQ]]\x8d\x8a\x8a\x0a\x98L&F\xb2g2\xce\xc5B6\xf8E\xed\xdc\x00\xed\xa7W^y\x85\xcdeMMM\x01\xa1)\xb6\xe6T*\x15\xdez\xeb-\x00'c\xd9\xca\xcb\xcb\xf1\xa1\x0f}h\xd6\xdf511\x81\xef}\xef{\xec\xb1\xbb\xee\xba\x8b\xbdn\xb1\xd4\xbf\xb9\xf4\x16O\xa7\xd3\xe8\xe8\xe8\xc0u\xd7]\xc7\xe2\xf9\x9e\x7f\xfey\xbc\xfa\xea\xab\xac\x9da1\x12\xa6R\xa9\xa0\xd3\xe9p\xd1E\x17\xb1\xc7\xfa\xfb\xfb\xe1\xf7\xfbg\xfc<*\x95\x0a\x06\x83\x01UUUl\xff\x1d\x04\x83A\xdcv\xdbm,\xf1C\x8eP(\x84\xb1\xb11\x0c\x0e\x0e\x22\x1c\x0e#\x91H\xb0\xd8\x18\xb9\x92D\xf3q\xfe\xf9\xe7\xc3b\xb1\xc0f\xb317m\xa9\x182\xa5\x0aX[[\x8b\xe6\xe6f\xf4\xf6\xf6B\x14EV\x9a\x86\xe6]\x14E\xe6N\xa1\x9e\x9c\xf4\xb7(\x8a\x08\x87\xc3\xe8\xef\xefg\xad\xcc\x9cN'+G\xb3\x1c\x08\x09\x19\x85x<\x8e`0\x88\xc1\xc1A\x96Y(\x1f\xe7\xc9\xc9Itww#\x16\x8b\xb1\xb8\xa0T*UP\xb3\x91\x08\xde\xe5\x97_\x8e\x8a\x8a\x0a8\x9dN\x94\x95\x95\xb1q.\xa6\xd8\x92\xa23\x9b\xd2R,\xa4\x83c\xe9\xac!A\x10\x0a2y\x01`\xdd\xbau3v\xf1\xa0\xd7\xec\xdc\xb9\xb3\xe0\xf1\x8b.\xbah\xc6\xe2\xcf\xb4\xc6\x9ey\xe6\x19\x00'=:\xf5\xf5\xf5X\xb7n]\xc9.#\xb3|\xf8\xb90\x89y\xbfm*\x95\xc2e\x97]\x06\x8b\xc5\x82H$\x02\x00\xd8\xb9s'ZZZ\x98\x9d*v~\x85\xc3a\xdc\x7f\xff\xfd\xd0j\xb5\x88\xc7\xe3,\xfe\xae\x94\x22J\x9d\x8e(<\xc5\xe3\xf1\xa0\xbd\xbd\x9d\xb9h\xe5\xe5\xd3\x02\x81\x00\xd4j5\x12\x89\x04\xecv;\x9cN';\xc7i\x8c}>\x1f***\x10\x89DX8\xd5\xd8\xd8\xd8\xb4\xb9\xa40\xaa\x85\xdc\x97\xd4\x13Y\xa7\xd3!\x97\xcbadd\x04\x83\x83\x83\x00\x00\x8b\xc5\x82\xb6\xb66\xbe\xe18\x01\x9cy\xf1\x10\x01\xd4h4\xc8\xe5r0\x18\x0c,K\xd4\xedvcdd\x04\xfd\xfd\xfdp8\x1c,\xa1@\xd9\xe0\x9e\x03\xd3T\x1d\xfa\x9b\x5c\xa0\x99L\x86e\xd5\x86\xc3ax\xbd^\xf4\xf4\xf4\xc0n\xb7\xe3\xf6\xdboGUU\xd5\xb4\xb9\xc9d2\xd8\xb1c\x07v\xef\xde\x8d@ \xc0\x0a1\xa7R\xa9\x82Vp\xf4\xfb\xd7\xad[WP@X\x19\xffW\x0cD\xe4\xacV+\xaa\xab\xab\xd1\xd0\xd0\x80\xd5\xabWchh\x88\xa9}Db\xe5\xb1\x80\x14\x9fCa\x04\xf4\x1d\x0d\x06\x03:;;\xd1\xde\xde\x8e\x91\x91\x11\xe6\xd2\x9c\xcbg9W\xd4?r\xfb\x8c\x8c\x8c\xa0\xbe\xbe\x1eN\xa7\xb3\xe0`\x97$\x09\xfb\xf6\xedCww7+\xfbB\xea\x822\xcbp\xed\xda\xb5hjjBuu5\xecv;t:]\xc9\xd6Ss\xddo\xbc\x17\xf0\xd2?+\x0e\x1e<\xc8\xc8\x0f\xc5\xa5\xcd6g\x87\x0e\x1d\x02p2\xc1\xa0\xae\xaenN\x8a\xce\x1f\xfe\xf0\x07\xd6e\xe4o\xfe\xe6o\x0aT\xc7y\xe3\x14\xd6\xdf|\x5c\xc1\x9b7o\xc6\xb6m\xdb\x00\x00o\xbf\xfd6>\xf9\xc9O\xb2\xf2S3\xbd&\x1e\x8f\xb3R1\xb3\x91\xabL&\x83\xea\xeaj\x98\xcdf\x84\xc3a\xd6\xfb\xf7\xf2\xcb/G4\x1a-\x88\xe3U\xa9T\x18\x19\x19\xc1\xf0\xf00\xb2\xd9,\x9cN'6m\xda\xc4\xcef\x82\xd7\xebE\x7f\x7f?\xe2\xf18\xb3\x89>\x9fo\xda\xe7!\xaf\xcdlc_\xac\xc7{\xb1\xc7(\x16qdd\x04\xeb\xd6\xadCYY\x19\x02\x81\x00\xfb\x0cn\xb7\x9b\xd5YT\x8eY\xb19\xe1\xe7\xc5\x0a#\x80\xe4\x02\xa4\xd6?z\xbd\x1e\xa1P\x08\xa2(\xb2\x1e\x89\xc7\x8f\x1fg\xeeD\xca\x94\xa2 \xf5\xb3U>`)\x1e\xe8\xf2\x929D\xfa\xc8\xddKeU\xa2\xd1(\x82\xc1 <\x1e\x0fB\xa1\x10.\xbf\xfcr\x5ct\xd1EE\x8b\xf9\x0a\x82\x80\xbe\xbe>\xbc\xf3\xce;\x18\x1b\x1bC0\x18D4\x1ae\x99eJW\x82 \x08\xd8\xb0a\x03\xaa\xaa\xaa\xe0p8X\x06\xb0\xb2\x06\xa0r\x93\xd3!e4\x1aQQQ\x81\x86\x86\x06455\xa1\xae\xae\x0e\x03\x03\x03\x05\x87\x18)}r\x12(\xbfDP}\xb1L&\x83\xc3\x87\x0f\xa3\xa9\xa9\x09\xf5\xf5\xf5p8\x1c\xcc\xady.\xabR\xf2\xd8?\xaaQv\xfb\xed\xb7O\xcb\x8e\x9e\x9c\x9c\xc4\xd1\xa3G\xe1\xf1x\x98\xfb\xb7X\xe6\xaf(\x8a\xe8\xe8\xe8@MM\x0d*++QVV\x06\x9dN\xc7\x0e\xf0\xd3q\x17\x15\xdb\x97|\xaf.-\x02\xd8\xdf\xdf\xcf\xce\x0cy\xdb\xc7R\xaf9|\xf80\xbb\x84o\xdc\xb8\xb1\xe4kT*\x15\xc6\xc7\xc7Y\xf7\x1e\x00\xb8\xf9\xe6\x9b\xd9\xc5n\xde\x18\x0d\x03\xbf\xef\x06D\x01@\x91\xdfI\x0f]\xdb\x044\x9b\xe7E(T*\x15b\xb1\x18\xae\xb9\xe6\x1al\xdb\xb6\x0d:\x9d\x0e\xe3\xe3\xe3s\xda\x03\xf3\x11\x22\xb2\xd9,\xacVkA\xb9$\x9f\xcf\x07\x00\xac\x16k\xb1q\xa7\x18\xe7t:=\xed\x1c\xcdd2\x18\x19\x19\x99V[\xb7\x18\xf9\x94\xf2\x12\x90/>|\xf3\xdd\xbb\xb1X\x0c\x83\x83\x83\x88\xc7\xe3\xe8\xee\xeeFmm-s\xfb\x92::\x97\xb9^\x8c\xe4\x0f^?x\x09\x11@r\xdb\xc9'\x99\x8c~,\x16c\x1d\x1d\xa2\xd1(\xecv{A?Z\x8dF\x83\x13'N0\x22\x13\x8f\xc7QSS\xc32\xaf\xe6\xbb\x01\x97\x0b\x94\x07\x13);D\xfc\xa8TK,\x16+\x88\xa3\xd4h4hnn\xc6\x95W^9-XY\xbe\xd9\x8f\x1f?\x8em\xdb\xb6\xb1\xae\x11\x81@\xa0@IR\x06+\xd7\xd5\xd5a\xed\xda\xb5,\x8e\x8c\xdc\xbf\xc5H\xbf\x12\x94\xf9Ku\x01\xa90\xf4\xd8\xd8\xd84\xd7\xb5\xc1`(\xd8\xe0\x82 \xb0\x18R*\xe3@1<\xc3\xc3\xc3hhh`I)\xa2(\x9e\xb3\x8d\xd5\xe5\xb1\x7f\x91H\x04\xe3\xe3\xe3\xacc\x87RQ\xa7\xde\xca\xa4\xda\x16+\xfa\x0c\x00UUUX\xbf~=\x5c.\x17S\xffHe?\x9dCw9\x97\xdfYN\x04p``\x00Z\xad\x16\xe9t\x1a\x8d\x8d\x8dszMgg'\x8b\xdd\xa6Vj\xa5\xd6Kgg'K\xe42\x18\x0c(//?\xf5\x0f\x9e\xce\x01\x13Q@3\x03\x01\x14p\x92\xdc$\xb2\xa7\xe4\x06N\xa7\xd3\xd8\xb0aC\xc1c===hnn>\xed\xf5L\xaf\xa7s\xd3f\xb3\xb1\xc7\x02\x81@\xc1\xe5n&2\x94\xcb\xe5\x10\x8b\xc5X\x17\x95R\xf6`\xa6\xef'\xc3E\x87\xb0\x00\x00 \x00IDATe%\xe4uy\xa80s\xa2\xcf\xc0\xc0\x00\xb4:-\xaa\xab\xaaY\xd8\xc7\xc4\xc4\x04&''\xb1f\xcd\x1aX\xadVF\x5c\x93\xc9$k\x8fG\xb1\x88d\x8f\xb3\xd9,\x92\xc9$\xccf\xf3\xbc/\x80s\x1doyMZ\xb2gT\xab\x96B\x7ff\xb2\x9f\xc41\x96k\xac2\xc5\x89\xce\xf5\xb2%\x9e\xce\x22\x97\xdfJ\xe4\x7fK\x92\x84\xf2\xf2r\x8c\x8d\x8dM{>%#\x10,\x16\x0b3\xea:\x9d\x0e\xe5\xe5\xe50\x9b\xcdp:\x9d\x08\x87\xc3x\xf3\xcd7\x11\x08\x04P[[\x0b\xb3\xd9\xcc\x12\x00V2\x01\xa4\x80`\x22eJ\xa5\xc7\xe1p\xa0\xac\xac\x0c\xcd\xcd\xcd\xa8\xaa\xaaBmm-s\x9f*\x95#\x8a\x19\xe9\xed\xed\xc5\xb6m\xdbp\xe4\xc8\x11\x8c\x8c\x8c\xc0\xef\xf7#\x1a\x8d\xb2\xd2\x02\xf2\xa4\x0a*\x7f\xf0\xfe\xf7\xbf\x1f\x0d\x0d\x0d\xa8\xac\xac\x84\xd9l.(\xcf2\x97uE\xae\xdc\xf2\xf2r\xd4\xd5\xd5\xa1\xad\xad\x0dG\x8f\x1e\x85\xdf\xefg\xc9&\xa4l\xca\xe3\xcb\xe41\xa4\xa4&\x93[\xe6\xe8\xd1\xa3hhh@MM\x0dS\x8d\x89 \x9e\x8b\xf3M\xfb\xc5\xef\xf7#\x10\x08\xe0\xca+\xaf,\xd8\xdc*\x95\x0a^\xaf\x17\x9d\x9d\x9d\x98\x9a\x9ab\x074\x112\xf9\x9cK\x92\x84M\x9b6\xb1\xba\x7ff\xb3\xb9@\x95P\x92E\xa5\xda\x5c\xaa\xec\x07\xc5\xf0\xce\xe7f?\xdb\xfbr,<\x01\x9c\x9a\x9ab\x17\xaa\xe6\xe6\xe6\x92\xc6\x97\x1e\x0b\x87\xc3,\xde\xaf\xb6\xb6\xb6`\xee\x8a\xd9\x84\xd1\xd1QFz\xea\xeb\xeb\x0b.p\xf32\xf6*\x15TR\xfed\x0c\xa0<\xd9\xa9\xe0C\xaaNK\x05\x92$\x09f\xb3\x19f\xb3\x99\xd5\xcc\x1b\x1e\x1eF[[\x1b\x8bC\x9e\x8f=\xd4\xeb\xf5\xd3\xceK\xba\xa8;\x9d\xcei\x040\x91H`rr\xb2d\xdd\x5cj\xc7y*H\xa5R\xc8J\xd9\x821\x97'z\x09\x82\x80\x13'N\xa0\xab\xab\x0bZ\xad\x16\xa3#\xa30\x9b\xcd\x88F\xa3\xec\x1cN&\x93\xb8\xe4\x92K`4\x1a\xa7\x95\x95\x917\x05\xa0\xc7\xe3\x898\xca\xca\xca\x98m\x9a\xc9\xfd{\xaa\x9c\x83\x0a\xff\x93\xfd\xa0nV\xd9l\x16\xb5\xb5\xb5X\xbf~=\xcb\x19\xa0\xf5@\x9d\xa3B\xa1\x10\xf4z=.\xb8\xe0\x82\x82\x18\xd6\xb9\xac\x9bd2\xc9<\x5ct\xf1\x96$\x09\xb1X\x0c\x16\x8b\xa5\xe0}\x8a\xbd\x9f\xfc\xff\xe8\xbbP\x85\x0d\x9b\xcd6\xe3\xe5z\xa6\xcf\xa6t\xd1\x07\x02\x01\xf4\xf4\xf4@\x92$\xd4\xd4\xd4\xa0\xa1\xa1\x81\xbdvrr\x92\x09it\xe6\xe7\xf3y\x88\xa7K\xa2\xe4\x85\x9ciQi\xb5ZLNN\xa2\xa2\xa2\x02\xa9T\xaa\x80\x14P\xe0\xbe|Ah\xb5ZVC\xce\xe1p`\xcd\x9a5\xb8\xfd\xf6\xdbQYY\xc9\xcaY\xe4\xf3y\x84B!\x04\x83\xc1eY\xe6c\xae\xaa*\x15T\xb6\xdb\xed\x8c\x04\x91+\xddf\xb3\xc1d21uL9\xb7\xca\xf8\x0e\xbaA\xed\xde\xbd\x1b/\xbe\xf8\x22\xba\xbb\xbbY/\xdeh4\xca\x02\x9c)+\x8d\x16S.\x97\xc3\x86\x0d\x1bp\xc9%\x970\xf7/\x91\xf3\xf9\xb8[)\x09\xc8f\xb3\xb1X\xc0\xf3\xce;\x0f/\xbf\xfc2\x04A@*\x95b\xea\x94\xbc\x9c\x01\xb9\x82\xa9\x86\x17e\x03\xe7r9\x0c\x0e\x0ebpp\x10\xf5\xf5\xf5\xac\xbb\xc5\xe9(\x5cg\x1b\xe9t\x1a\x91H\x04>\x9f\x0f:\x9d\x0ek\xd7\xae\x9dv@P1l\x8f\xc7\xc3\x92?h\x8f\xc8\xc3\x04jjjX\xe1g\x87\xc31\xad\xed\xdbL\x87\x8d\xb2m\xa3\x92(\xca]\xf4\xb3\x19w\xba\xec\xc9K@q\xb7\xcd\xe2\x83\x92/.\xbc\xf0Bx\xbd^\x0c\x0f\x0f3\x028\xd3\xe5\xa8X\x92\x07)A\xa5\x14\x06\xb7\xdb\xcd.\xab.\x97\x0bf\xb3y\xda\xf3\xe7<\xe7\x1a\x0d \x08\x80\xa0\xa6E\xaf\xf8\xfb\xf4\xc6\x85\xca\xd4\xd4\xd4\xd4\xa0\xbb\xbb\x1b\xc0\xc9,\xddS\x09\x1bQ\xab\xd5\xa8\xa8\xa8\xc0\xd4\xd4T\x81}\xa2\xcb\xac\x5c\x09\x0d\x04\x02\x8c\xa0\x95\xb2e\x92$\xc1\xef\xf7\x17\x9d\x8b\xb9z\x10\x90GQ[\x90\xcf\xe7Y\xb9/\xba\xd4{<\x1ex<\x9e\x82\xe7\x06\x83A\x9c8q\x02k\xd7\xae\x85\xdb\xed\x9e\x95\x90\x0c\x0f\x0d#\x93>\x19\x92\x13\x08\x04\x10\x8dF\x17d\x8f\xabT*\x04\x83A\xb8\xddnv\x86\xc8\xc7\x98\xe2'\xa9\xbe%\xd9\xb8\x9e\x9e\x1e\x8c\x8e\x8e\x16(\xad\xe1p\x18v\xbb\x1d\xa2(\xa2\xb5\xb5\x15&\x93\x09\xbd\xbd\xbd\xe8\xe9\xe9\x81\xd1h\xc4\x05\x17\x5c\x80\xb2\xb22\xf6\x9d\x8e\x1f?\x8e\xc1\xc1A\xf6\xfc\x96\x96\x16\xf4\xf4\xf4```\x00\xd9l\x16:\x9d\x0e\x97^z)L&\x13\xb3\x95\x9d\x9d\x9d\x98\x98\x98\x80\xc1`@{{;\x5c.W\xc1\xda\x9f\x9c\x9c\xc4\x81\x03\x07X7\xa5\xf5\xeb\xd7#\x9b\xcd\xc2\xeb\xf52QDN`'''\x11\x89D \x8a\x22\xf3p\x112\x99\x0c:;;\xd9\xba\x9a\x9a\x9a\x82N\xa7\x83\xcb\xe5BWW\x17\xba\xbb\xbb\x91\xcf\xe71::\x8a\x0b/\xbc\x10f\xb3\xf9$\x97\x90W\x15\x9f\xaf\x14K\x03\x9f\xc9d\xe0v\xbb\x11\x0c\x06\x99\xf4O\x8bK\xe96\xa4/\x1b\x8f\xc7\x19Y$\x03m2\x99\x98q\x22bSYY\x89\x9a\x9a\x1a~\x82\xcfC!\x9cOl\xca\xeb\xaf\xbf\x8e\xed\xdb\xb7\xb3\x96jtS\xa0^\xbc\xf2\xac_\x9a?\x83\xc1\x80K/\xbd\x94\xd5\xa4\xa26l\xf3%\x80\xf4\x19\x92\xc9$\xa2\xd1(\x04A`\x89!\x1e\x8f\x07\x82 0\xb5\xd8h4N\xcb2%7\xb2N\xa7c\xaen\xbf\xdf\x8f\xee\xeenX\xadVh4\x1a\x84\xc3a\xd8l\xb6\x19\x13BJ\x8dU\xa9[9\xfb7\xf2,\xbeF\xe9bQ\x12ny(\x84\xf2&^p0#\x8f\x5c\xf6O1\x9d~\xbf\x1fCCC\xac\xa5\x94\xfc\xb9\xd1h\x14\x87\x0e\x1d\xc2\xe4\xe4$\x02\x81\x00\x8b\x87T\x96\xeb\xc9\xe7\xf3\xd8\xb8q#r\xb9\x1c\x22\x91\x08\x9bgRm\xe5\xdd\x08\xe4\xddz\xe8p\x09\x85B\x98\x9c\x9c,X\x17\xc5\xc2\x8f<\xf20\x8c%Q;\xe0A^#\x14\x84~X\xad\xd6\xe9$\xf4\x14\xd5\xe4\x5c.\x07\xbb\xdd\xce~\x8e\xc5b\xa7D\x00\xc9\x86)\xd73\xd9C\xa7\xd3\xc9\x88\x22e\x1d\xcf\xe5\xf3Ro\xf4\xf9|7\xaa\xd9(I\x12\xeb\x92\x14\x0e\x87Q^^\x0e\xadV\x8b\x9e\x9e\x1e\x0c\x0f\x0f\xcf\xd9;2<<\x8c\x91\x91\x919y\xde\xc6\xc7\xc7\xd9s\xb3\xd9,+F_\xcc^\x95R\xb8\x8a)p\xa4.[,\x16\x88\xa2\x88L&\x03\x87\xc3\x81\x96\x96\x16D\x22\x11\xf4\xf5\xf5\xb1\x8b\xb0\xd9d\xc6\xbb;\xdf\x85\xd7sR-t8\x1c\xec\xfc\xa2$\x1eI\x92011\x01\x8b\xc5\x02\x8f\xc7\x03\xb5Z\x8dP(\x84\xae\xae.ttt\xc0`0\xa0\xaf\xaf\x0f'N\x9c`cz\xf8\xf0atvv\x16x\xd3\xb2\xd9,v\xee\xdc\x89\xf6\xf6v\xf6\x9a\x89\x89\x09\xa8\xd5j\xa4R)\x1c8p\x00\xe7\x9dw\x1etz\x1db\xd1\x18&''\x0bzBG\xa3Q\xd6jQ\xadV\xa3\xaf\xaf\x0fn\xb7\x1b\xf5\xf5\xf5\x08\x87\xc38z\xf4h\x81\x08\xd2\xd9\xd9\x09\xb3\xd9\x8c\xc6\xc6F\xb8\x5c.\x0c\x0e\x0e\xb2\x5c\x0a\xaa\xa4\xf1\xce;\xef\xa0\xba\xba\x1a~\xbf\x9f\xed\xdf@ \x80\xbe\xbe>\xb4\xb4\xb4`jj\x0a\xe2{\xef\xbdw\xda\x12:\x19\x15\xb7\xdb] \x9b\x0b\x82\x80d29\xad\x03\x01\xb9\xecHB\xcdf\xb3L\xb1\x92+\x18\xf2N\x10\xa7BN\x973\x8a\xcdQ\xa9\x22\xbe\xf2\xc5\x9aH$\xd0\xdd\xdd\x8d\xd7^{\x0d]]]\xac\xf4\x8e\xcf\xe7C<\x1eg\xca\x1f\x91\x08e\xa0\xf1%\x97\x5c\xc2\xfaL\xba\xddnD\xa3\xd1\x02\xf7\xef|n\xfatK\x8dF\xa3H&\x93\x10E\x11\xb5\xb5\xb5\x98\x9c\x9cd\xe4\x93.\x15\xa4\x80\x12\x11$\x05\x93n\x83d\xb0\x8e\x1d;\x06\x9b\xcd\xc6n\x8c\x0e\x87\x03:\x9d\xee\xd4\xb3\x10K\xcd\xc3I\xe6\x87\x93\xb6?\xbf\xa0sKk?\x12\x89\xc0`0`\xcd\x9a5\xd3J:\xec\xdb\xb7\x0f\x03\x03\x03\xacc\x0b\x91?e1Y\xa7\xd3\x09\x8b\xc5\x82d2\x09\x9f\xcfW\x90}/\xff]\xc5\xe6-\x97\xcb!\x1e\x8f\xc3\xe3\xf1 \x91H\x14\xb8W(\x0bq\xae\xc9#\xd1h\x14\x1e\x8f\x07###\x88\xc5b\xacn\xe4B\xee\x83\xd39\xcf\x94\xef9/\x17 \xad\x85\x05\xfc\xfc\xf3\xfd\x0c\xb3\xc1\xe3\xf1\xb0\xf3\xa0T\xc1v\xb9\xaa%_GtY\xcbKy\xa8\x04\xd5\x8cdj\xcd\x9a5\xc8d2\xa8\xab\xabC2\x99\x9cV\x1ajN\xdfI\xa5\x82\x86B]\x00H\xb9?\x15\xa0\xcff\xb3\xa8\xac\xac<\xf9\x19\x16`\xdb\xc9\x09\xca\x5c\xd6\xa3<\xfb^\xfe\x98^\xaf\x9f\xf6\x7fd\xcb\xe4*\xde|\xe64\x9dN\xb3\xec\xe1\xb9\x90\x7f\xbd^\x0f\xab\xd5\x0a\xaf\xd7\x0bI\x92p\xe2\xc4\x09v\x8eR\xa1\xfeD\x22\xc1\xe2\x0a\xe5\x7f\x94\x97V\xf2\x10\xcc\xa7&/}\x7f:\xbf)1\xb1\x14\xf1\x9fK\x0b\xbfP(\x84d2\x09\xa7\xd3\x89\xd6\xd6V8\x1c\x0e\xc4b1\xb8\x5c.V\xcagdd\x04\x89D\x02CCC'\xd5\x5c\x9f\x1fz\xbd\x1eMMMhnn\x86\xd7\xeb\xc5\x91#G\x90H$\x98-\xa1\x84I\xb5Z\x8dL&\x03A\x10\xe0\xf3\xf90>>\x0e\xa3\xd1\x88\xbe\xbe>\x08\x82\x00\x83\xc1\xc0B\x8dr\xb9\x1c4\x1a\x0d\xca\xcb\xcbQQQ\x81\xf1\xf1q\xf8\xfd~\xec\xdb\xb7\x8fy\xc9\x8cF#\xf4z=\xc2\x91\x931\xd9\x9d\x9d\x9d\x90$\x09\xf1x\x9c\x952\xa3P\x1c\x9f\xcf\x87h4\xca\x08\x5c,\x16\xc3\xe8\xe8(\xc6\xc7\xc7Y\xc2lee%\x9cN'2\x99\x0c\xb3\xd7\xc7\x8f\x1f\xc7\xe8\xe8(\xa2\xd1(\xab\xefZQQ\xc1jwNMM1\xd2N\x89\xb6\x14\xe2\x15\x0a\x85 RP\xef)\x1dx*\x15rR\x0e\xb9l\x0eSSS\xac\xce\xd3L\x86DyP\x90A\xa6\xbaB\xe1p\x18Z\xad\x16\x1e\x8f\x07\xef\xbd\xf7\x1e\x1c\x0e\x07\x93T\xe9\xbd\xb8\xbbhn\xd2?\xfd-w\xdd\x05\x83Atuu\xe1\xd0\xa1Cl\xe1P\xcd\xc5X,\xc6b\xfe\xe4\x0a\x92R\xb5\xda\xbcy3n\xb8\xe1\x06\xac^\xbd\x1a\x8d\x8d\x8dp:\x9d\xd0\xeb\xf5,\x93t\x0eV\xb2P9P\x01\xd9\xcc\xc9\x1b\xae\xdb\xed\x86\xd3\xe9\x84(\x8a\x98\x9a\x9a\xc2\xc8\xc8\x08ssS\xbc#\x1d\xa2D\x00I)\xa6D\x0f\x8aY\x09\x04\x020\x9b\xcd\xac\xc4\x0c}Nf\xb4\xf2s$\x0bE\x94\xbd\x85\x9c\xa7bcF\xb13\xd1h\x94\xd5c\xbc\xed\xb6\xdb\xa6\xed\xa7P(\x84\xc3\x87\x0fchh\x08\xe1p\x98\xa9\x7fr\x97=\xe1\xfd\xef\x7f?.\xbd\xf4Rttt\xa0\xb1\xb1\x91%\xed(U\x19e\x8c(\x19 \x9f\xcf\x87\xee\xeen\x8c\x8d\x8d!\x14\x0a\xb1\xf7&W\xc2\x5c\xe1r\xb9\xd0\xde\xde\x8eK.\xb9\x04\xd5\xd5\xd5\xc5\x95\x9c%~\xd1Z\xca\x9f\xf5t\x09\xafr\x1d\x96\x95\x95\xb1\x90\x0b\x9a\xbf\xab\xae\xbaj\xc6:y\xc0\xc9\xba\xa0\x0f?\xfc0S\x92\xf5z\xfd\xa9\x8d\xa3J\x05\xa1?\x08\xf1\xf8\x11\xe4E\x81\xb9C#\x91\xc8\x9fTh\xb5l\xfd\x9e\xa2@@FY\xeeY\x98M5\xa63'\x10\x08L\xebU\xae\x0c\x85 \xc1CIv\xe6J\xfe\xe9RN\xaf\xb5\xdb\xed\xac\xf4J1\x98L&TTT0\xe5\x87~\x9fF\xa3a\x15\x1d\xe4kE\xadV\xa3\xb1\xb1\x11\x91H\x84\x95\xb6\xa1\xef\xb2f\xcd\x1a$\x93I\x1c:th\xd6\xd0\x0e\xf9\x19B\xef-\x0f\xe1QzB\x8a\x89\x143\x8d7u\xaf\xaa\xac\xacD}}=\xda\xdb\xdb\xa7\x9d\x1b&\x93\x09eeeH$\x12\x18\x1c\x1cd\x8aYMM\x0d\xd6\xacY\x03\x95J\x85\xba\xba:H\x92\xc4H]:\x9df\xe4O\x14E\xb8\x5c.VP\xbb\xab\xab\x8b\x11D\x97\xcb\x85\xf5\xeb\xd7C\x14E\xd6q\x8a\xcahi\xb5Z\xd4\xd7\xd7chh\x08\x13\x13\x13\xc8\xe5r\xb0Z\xadhii\x81\xc9d\xc2\xe0\xd0 \xfaz\xfb\x18\xc1s\xb9\x5c(++\x83\xd3\xe9Dyy94\x1a\x0d+\xd7F\xbd\xee\xa9\xd4N*\x95\x82^\xafG\xc7\x86\x0eT\xb9\xaa\x98\xfdkhh`\xe1N\xe1p\x18\x82 \xa0\xbc\xbc\x1ck\xd7\xaee\xe3B\xed\x01\x05A\xc0\xaaU\xabP^^\x8e\xa3G\x8f\x22\x1a\x8d\x22\x14\x0a\x9d\xf4\x04\x9cn\xd1F2V\x1a\x8d\x06n\xb7{\xc6x\xab\x99\xfa\x1b\x92\x8cK\xb7\x8c`0\x88\xc9\xc9I\xe6\xdaknnF{{;/0{\x0a\x86@\x92$x\xbd^\x8c\x8d\x8d\xe1\xd8\xb1c\x18\x18\x18`\x01\xb4>\x9f\x0fSSS\x88\xc5bl\xf1Q\xf0\xb2\x92@\xd0|vtt\xe0\xe6\x9bo\xc6\xea\xd5\xab\xd1\xd2\xd2\x02\x97\xcb\x05\xab\xd5zJ\xae_\xe5:\xa0x\xbfx<\x0e\x9f\xcf\x87\x81\x81\x01\x16B@EG)\xf1\x85bH)\x03\x8cjJR'\x0a\xb5Z\x8d\x9e\x9e\x1el\xdc\xb8\x91\xb5\x86\xd3h40\x99Lls\xcc\xd9 -\xa0\xb27\xd3<\xc9\x7f&E\x9dZ\xbey\xbd^\xb8\x5c.vp\x91\xbb\x96b\xffN\x9c8\x81P(\x84P(TP\xaeG\xfe\xbe\xb5\xb5\xb5\xe8\xe8\xe8@ss3\xea\xea\xeaX\xd2\x8e\xbc\xf3\x87\xf2\x10\x97\x7f&*(-/\xf2]\xcc}=\x97\x84\x0e\x0a\xdc\xa6\xc3\x9a\x5c9\xcbI\x89_*\x17\xc0\xd3\xfd\x0e\xa4\x8a\xe9\xf5z\xf6oR\xa3g*\x1e=\xd3\xef?\xe5q2\xa4N\xc6\x01jN\xfe.\xaa\x22!wK\x9f\xcew'\x8c\x8d\x8d\xb1KeEE\xc5\xac1\xe6r\xaf\x83\xfc;\x16ss+\x09\xe0|\xc7C\x1e\xca!I\x12\xaa\xab\xabYu\x8cb\xef\xa3\xd3\xe9`\xb7\xdba0\x18\xa6%\x90\x14K\xc8\xb0Z\xad\xd8\xb0a\x03+\x1bF\xcf\xd7\xe9tLY\x93\x87m\xd1z\x90\x8f\x11\x85\xd8P\x22\x0d\xbd\xbe\xa6\xa6\xa6\xe0s\x9e\xca\xfcP\xfc\x7f*\x95BYY\x19\xea\xea\xeaf\xf4\x80\x99\xcdf\x16\xa7Hjnuuu\xc1\xf3\xeb\xeb\xeba\xb5Z\x99+\xfe\xe8\xd1\xa3H\xa7\xd3hmmEkk+K\xaa#\x95\xb0\xa9\xa9\x09\xabV\xadb\x99\xcd---\xd3~\xb7V\xab\xc5\xea\xd5\xabQSS\x83L&Sp\xae556\xc1l2#\x10\x08\xc0d2\xa1\xaa\xaa\xaa \xf9\x0e\x00\x0c\x06\x03K\x96\x02N\x16hw8\x1c\x88\xc7\xe3\xb0X,'\xd5n\xd9w\xd0\xeb\xf5X\xb5j\x15\xacV+&&&\xa0\xd5j\xd1\xd0\xd0\xc0~gee%6n\xdc\x08\xaf\xd7\x0b\x8b\xc5\x82\xda\xdaZh\xb5Zd2\x19\x9c8q\x82er\x8b\x0b\x91\xea\xae\xcc\xe6\x9bO:7\x19t*2,\x08\x02\xdcn7#$\xc7\x8e\x1d\xc3\xf8\xf88+5B\x81\xeb\x92$\xcd\xe8v\x5c\x8c\x83\xf3l\xba\x9e\x95\xf5\x9f\x8cF#\x8b\x9d\x8b\xc7\xe30\x99L\xcc\xfd\x97H$X\xaf\xd8\xbe\xbe>\x04\x02\x01&gS-\xb9P(\xc4\xdc\xbc\x941ZL\xf5\x93+m\xe7\x9f\x7f>\xee\xbc\xf3N444\xa0\xa1\xa1\x01\xe5\xe5\xe5\x8cP\x9d\xae\x11$y\xdah4\xb2\x1b^[[\x1b\xba\xba\xbaX&\x9a\x5c\xa2\x97\xc7\xa8\xd1\xcd\x8d\x94\x86L&\xc3n\xcd\xc7\x8e\x1dCSS\x13jkkY]@r\x19\xcf\xc3\xaf\xb7h\x0a`1C@.\xf1D\x22\x81@ \x00\xaf\xd7\x8b\xff\xd7\xde\x99G\xc7]\x97\xfb\xff=\xfb\xbeg\xb6\xccdk\xd2\xa4{KiYZ\x0ae\x97\x02r\xa5\x02z\x11\x11\x5c\xa8\x82\x0a\xcaQ\xef\xfdq\xc5\x8b\xc7\x85\xe3\x82\x22\x02\xea\x11\xf1\x82\xe0\x02\x17/(\xdb\xbd*{i\x11*]\xd26M\x97\xecK\x93\xc9Lf\xdf\x7f\x7f\xc0\xf3\xf1;\x93I\x9a\xb4\x93t\x92<\xafs\xe64\xcd2\x99\xcc\xf7\xf3\xfd|\xde\x9f\xe7\xf3<\xefg\xfd\xfa\xf5cDR4\x1aE[[\x1b\xfa\xfa\xfaD\xee\x1f\x89?\xba\x86R\xe1N\xe6\xd1&\x93I\xf4\xe2\x96\x1e5O\xf4\x9a\xc6\xfb\xbe\xf9\x18e\x9bO\xa2\x94\x9e\xc7\xe9t\xa2\xbb\xbb\x1b\xc0{\x89\xeb3~=\xb3y@\x9e\x07d\x80R&\x87\x1c2\xe42YdSi@\xa5\xfeg\x95\xf0qN\xd1\xc9d\x12\xa3\xa3\xa3\xc2\xb8\xda\xe7\xf3\xa1\xaa\xaa\x0a}}}\xe3\xce\xfb\xc5E\x08\xf4\xb9RF\xca'*\x00\x8b\x85\x86\xd3\xe9D,\x16\xc3\x91#G\xa0V\xaba\xb5ZEZ\x07U\xe3Ses0\x18,x\x9d4g\x93\xe0\x90\xcb\xe5\xa2\x1bT\xb1\x08!\xf4z=\xccf\xb38\xa1kiiA\xfb\x81v\xc4\xe2\xff\x8c\x9a\x9a\xcdf\x18\x0c\x86\x02oB\xeabB\xa7\x0d':6\xb4Z-jjj\x84\x10+\x85\xcb\xe5\x12\x913\xaa\xbc.\xae\xb0\x95\xc9d\xa2\x98\xc9`4\x88\xe0\x82\xddn\x87\x5c.\x87\xd3\xe9\xc4\xa9\xa7\x9e\x8aX,\x06\x95J%r\xc8\x8b\xafk)aM\xaf\xad\xb8\xc8\xb5\xba\xba\xba\xa0\x96\xe1X\x91z\xb9\x5c.*\xee'\xdaL\x93\xa3Cq~\xbc\x5c.\x87\xdf\xef\x87\xd7\xeb-\xb8\xfe\xd5\xd5\xd5\xd0\xe9t\x22\xe0\xa2,\xc7\xe2}\x22\x17\x96r\x13h\x80P\xdfYz\xc8\xe5rtww\x8b\x81^UU\x85T*\x05\x85B\x01\x9b\xcdV\xf6\xc8`q\xa9\xf6T\x05\xe0t\x08E\xa9\xb0\xa6|,\xda\x0d%\x12\x09$\x12\x09\x1c=zT\x88\xc2@ \xc4!\xe5\xec\xd0\xcf\x90Ap:\x9d\x16\xc7\x85R\xe1Wl\x1a\x9c\xc9d\xb0n\xdd:\x5cv\xd9eX\xb0`\x01\x1a\x1b\x1bE\x05R\xb9\x8c\xb9\xe9\x18B\xa3\xd1\xc0b\xb1\xc0\xe7\xf3\xa1\xb6\xb6\x16---x\xe3\x8d7Dd\x8f\xc6\x09]#\xda\x11K\x13\x99)\x12\x98\xc9dp\xe8\xd0!\x1c:t\x08~\xbf\x1fN\xa7S\xb4<\xab\xd4h2\xbd\xff\xe4\xfbw\xf4\xe8Q1\xe1\x16\x8f\xcfC\x87\x0ea\xfb\xf6\xed\x08\x04\x02\xc2\xf7\x8f\xc4\xb1t\x822\x9b\xcdX\xb3f\x0d\xdcn\xb7H\xa9(\xe7{p\xa2\xe3\x9d\xc5_\xe5\xb3l\xd92\x1c>|X\x1c+\xcd\xe8\x86X\xad\x00\xbcFa\x04-3\x00\xe9L\x10\xb9l\x0ei\x87\x06:\x8b\xfe}u\xa0\x9c\xb2\xa5\x10u$\xda\xb3gO\xc1\xe7\xcf<\xf3L477#\x18\x0c\x8e\xdb\xdb\xb6T\xbb\xcb\xe2B\x17:^\xa4 \x07\x15~\xe4r9q\xb4>U\x9f<\x8dF\x03\x83\xc1\x80\x85\x0b\x17\xc2h4\xc2d2\xc1b\xb1`\xfb\xf6\xed\xe28Z\xab\xd5\x8a#j\xe9\xdaa\xb3\xd9\xb0j\xd5\xaa)\x1b\xe4\xd3\x111u&\xa9\xaf\xaf\x7f/H\x13\x8f\x89\xb5\xd2\xe9t\xc2\xe5r\x89\x9c\xb5|>/,\xde\xa4]\x9cJ5\x05\x98\xec\xdf\xeev\xbbE\xd7\x94\xf1\xae\xb3\xc3\xe1@KK\x0b\x0e\x1e<(\xe6\xce\x89\xda\x0f\xca \x83\xddn\x1f\xf3\x9a\xacVkIk\x96\x89^\xf3Tr\x5cO\xf4\xeb\xc5\xe3n\xa2kW\xfc\x7f*F\x02\xcal\x04]\x5c\xb41\x99\xc4pir1\x0d^24\x8eD\x220\x99L\xa2\xa4\x99\xbe\xa6R\xa9`4\x1a\x0b\x0c \xcb-\xb4*\x19\xba\x91\xc8u]zdC\xc7\xb8\x94\xd7Bm\xc4\xa4Q>\x12H\xd2hQ\xf1\xce\x86\x8eW\xaf\xb8\xe2\x0a\xac[\xb7\x0e\x8d\x8d\x8d\xa2}\x98\xd4\xf3\xaf\x5c\x0b8M\xc6\x06\x83A\x94\xc0755a\xef\xde\xbd\xc2\xe3\x89Z\xdb\x19\x8dF!\x0a\xe9\xef&s[\xaap\xa4\xe3\xef\xc3\x87\x0f\xa3\xae\xae\x0e~\xbf_\xb4\x16\x9cr\x14p\x86\xa0\xe2\x8dD\x22\x81P(\x84\x81\x81\x01\xacZ\xb5j\xcc\x0d\x9eN\xa7\xf1\xf2\xcb/chh\x08###\x22\xf2K\xc7\xc7\xd2\xe3\xa2u\xeb\xd6\x89]\xa2\xd9l.HR?Q\x91Wj\x93\xc4\x82n\xeeE\x12\xd7\xae]+:e\xbc\xf5\xd6[\x93\x9e\xdb\xcbB\x8d\x19\xb8e-\xbd \xa4GF\xd0\xb7#\x8bx\x22\x01\xcb\xcaf\x98\xab\xdf\x8f\x92\xc8e\x80\xe4\xf8q\xb2\x0b'\xd9`\xd1}e\xb7\xdb\xe1\xf5z\xa1V\xab'\xb4^1\x18\x0c0\x1a\x8d\x18\x1c\x1c\x14\xe3\x9e\x12\xf8\x09\xca\xd1\xa2H\xd4\xc0\xc0\x80\xf8ZUU\xd5q\xcdA4\xe7\x1a\x0c\x86\x82\x96k\x14\xed\xa3\xaf\xc9d2a\x15&\x15\x80\x14\xf9\x9a*\x1e\x8f\xa7\xc0\xc6\xc4f\xb3\x89\x82\x22\x99L\x06\xbd^\x0f\xab\xd5\x0a\xbb\xdd\x8e\xfe\xfe~\xe8t:8\x9dND\x22\x91\x82\xf9\xedx\x03+\x1e\x8f\x07\x0e\x87\xe3\x98\xf3\x8bB\xa1\xc0\x82\x05\x0b\x84\xc5\x11\xcfG\xc7\xb8\x07\xa6S\xa0LdGPj'\x90\xcb\xe5D\xb5\x93B\xa1\x10\xc9\xfc4\xf0\xa5\x09\xeat|\x5c*\x17\xe3d\x89\xb2\x99BZ>/\x15qd\x15@\xd1=iO`\x8a\x1eJm]J\x09?\xfa\xbc\xcdf\xc3\x87>\xf4!,^\xbc\x18\x0b\x16,(0|\xa6\x1dd\xb9o.2R\xb5\xd9l\xf0z\xbd\xa8\xa9\xa9\xc1\xe2\xc5\x8b\xf1\xf2\xcb/\x8b\x1cQJ\x98\xd5\xeb\xf5\x22e@\xab\xd5\x22\x99LB\xa5R\x89\xf4\x00\xaa:;p\xe0\x00\x9a\x9b\x9b\xd1\xdb\xdb+\xfa\x15Wb{8\xba\x06\xc9d\x12\x91H\x04CCCH$\x12X\xb3fM\x81\xb0\x92\xc9d\xd8\xbf\x7f?\xda\xdb\xdb\x11\x08\x04D\x82xq\xbf_\x00\xb0Z\xadX\xb5j\x15\x9cNgEF\xff\x98\xd9!\x00\xd7\xacY#\x16W\xdaLN5o\xf3\xb87\x07y\x14\x1c\xed\xcae\xef\x07\x19\xb2\xef\x15\x1fJ\xbf6U\xc3a\x12\x80/\xbf\xfc\xb28\xed8\xed\xb4\xd3\x0a\x1c(J\xfd\x1dF\xa3\x11\x8f\xff\xf6ql\xdf\xb6\x1d+V\xac@ss3\x1a\x1a\x1aD\xb4\xab\xb8\x15&\xbdw\x03\x03\x03\xe25R$k\xaa\x11@i\xe4Q\xfa\x9e\xfa|>\x04\x02\x01q2F\x22\xd3h4\x0a[\x1b\x83\xc1P\xb6\xb1Q\xaa;\x13\x00\xb4\xb4\xb4 \x99L\x8a<\xbbR\x1e\x80S\x9d7hS?U\xfd\xc1\xe2\xef$\x09\xc0\xa9F\xd3\xc8\xe8\x90\x84\x0a\x09\x1c\x8aZ\x91\xe8!\xf1\x22\xcdq:\x9e\xc5l\xb6-\x5c\xd2\x1b^\xba\xd0\x17w\x06\xa1\xef\xa1(\x90T\xec\x15G\xfa\x8a\xf3Q\xa8\xfd\x8fV\xab\xc5\xda\xb5kq\xce9\xe7\xa0\xa1\xa1\x01\xb5\xb5\xb5\xf0\xf9|\xb0\xd9l\xa2sD\xa9\xe3\x8frE\x01U*\x15t:\x9d\xc8\x05\x5c\xb0`\x01\xf6\xef\xdf\x8f\xa1\xa1!\xf1\x1a\xa9j\x9c*\xces\xb9\x9c8\xfaU\xab\xd5\x05\xbd(\xfb\xfa\xfap\xf0\xe0A\xd4\xd4\xd4\xc0\xe7\xf3\x15\xb4\xac\xab$\x11H\x1b\x1a\xca\xe1\xff\x22\xf6\xb4\xee\xc1\xeb\xaf\xbf\x8e\x05\x0b\x16\xe0G?\xfa\xd1\x98(`\xf1<\x1b\x08\x04\xc4\xef^\xb0`AAq\xcdT#\x80\xc5\xe2\x91\x0a\xf2\xe8$\x05x/wo\xcd\x9a5\xd8\xb9s'\xacV+\x1a\x1b\x1b\xcb\xb2\x0e\x01\xff\xec\xe0\x05@x3\x02\xef\xe5\xfd\x9ds\xce9\x00PP\x10R<&\xa6{\xdc\xb2\xf8\xab \x01x\xac\x9b\x92\xbaM\xd0\xc7\x94\x04O\xc7\x95\xc59j\xf3I\xdd\x97\xaa\xb4,\xf5\xb5R\x1f\x97\x12\xe1\xa5\xaa\xd6H|\xacX\xb1\x02\x1b6l@SS\x13jkk\xe1\xf5zE\xc9\xbaN\xa7\x13\xc7\x87\xd3\xf9\xdeS. u\x07\xa9\xa9\xa9Acc\xa3HrN&\x93B\x80\xd2$-\xf5\xb2\xa3\x9e\xa5\x1a\x8dF\x08\xc3\xdd\xbbw\xa3\xb9\xb9\x19\xfd\xfd\xfd\x22\x8aYi\xb9\x80T\xedL&\xcdj\xb5\x1a+W\xae\x1c\xb3h\xbe\xf3\xce;x\xfb\xed\xb7E\xcf_\xa9\xf1\xb3\xf4\xda\xba\x5c.\xb4\xb4\xb4\x08\xf1\xae\xd7\xebO\xb8Z{*\x0b\x043w\xe6\x1f\xb3\xd9\x8c\xfa\xfaz\x1c9r\x042\x99\x0c\x7f\xf8\xc3\x1f\xb0y\xf3\xe6I\xfd,\xdd\x9fO<\xf1\x04>\xfc\xe1\x0f\x9f\xf0\xdc=\x9e\x00:\x9e\x0d\x88F\xa3\xc1\x8b/\xbe(\xd6\x1e\xb7\xdb-\x92\xe6\xc7\x13\x11r\xb9\x1c\xfd\xfd\xfd\xe8\xe9\xed\x11Q\xc3\xb3\xce:K8ZH+\xe4\xa5~\xb8\xc3\xc3\xc3\x05\x82h\xd1\xa2E\xa2\xe5\x1a\xcd{\xe4\x91;\xd9\x08`1\xc5\x95\xa5\xc0{\xa7\x00\xeb\xd6\xad+{\xb1\xa4\xd4\xdaG\xa5R\x8d\xe9\xb7~\xac*j\xde,\xceq\x01(\xbd\xd0\xe3%G\x92\xc5\x04E\xfeb\xb1\x98\xc8c\xa3\xe8\x9f4\xf27\xdf\x0c\xa1K\xd9s\x90`\x9b(\x97b\xa2\xf7\xa9\xf8\x98D\xadV\xa3\xa1\xa1\x01\xe7\x9cs\x0e\x1a\x1b\x1b\xe1t:\xe1\xf3\xf9\xe0r\xb9`\xb7\xdb\xa1\xd7\xebE\x83\xed\x99h\xdbE\xbbh\x83\xc1\x00\xb7\xdb]\x10\x05\xa4\x8a6\x8a\x02R\xc4O.\x97\x17t\xa6\xa0\xb6p\xf4\xbd###\xd8\xbf\x7f\xbf\xa8\x086\x9b\xcd\x22\x87\xb1\x12D \x8dq\x8a\xfeuwwc\xed\xda\xb5b\xf1\xa0\xeb\x18\x8b\xc5\xb0s\xe7ND\x22\x911\x95\xbf\xc5cb\xf1\xe2\xc5\xa8\xad\xadEUU\x15,\x16\xcb\xb4\x19a3s\x1b\xda|\x5cv\xd9e\xb8\xef\xbe\xfb\xa0T*\xf1\xc6\x1bo\x1cS\xf0\xd3\xa6\xf2\xe7?\xff9\xbe\xf4\xa5/!\x9f\xcfc\xfd\xfa\xf5\xa2\xe2\xf4D^\xcfD\xfd\xc6\xa7\xb2.\xf4\xf4\xf4`\xfb\xf6\xed\xe2\xff\xeb\xd7\xaf/\xa8\xf8,57(\x95J\xec\xda\xb5K\xa4]\x00\xc0\xd2\xa5K\x0b\xc4Y\xa9\xea\xe0\x9e\x9e\x9e\xf7\x0c\xb4\xdf\x7f}\xabV\xad\x12\x96id\xca\xadR\xa9\x84?\xdbd\x04\xf0T\xafa9Q\xa9T0\x99L\x18\x1e\x1e\x86V\xab-Y=<\xde\x5cW\xaa\xb9\x03o\x1e\xe7\xa8\x00\x9c\xe8\xe6\xa4.!T\xb0@\xc9\xec\xa9TJ,\xf2\xc5\x1d\x0d\xe6\xd3\xce\xa1\xd8\x5c\xb3\xd4\xc7\xe3\x09;i\x84Oz\xf3\xd1\xee\xcd\xe5r\xa1\xb1\xb1\x11+V\xac@CC\x03\xecv;\x9cN'\xdcn\xb78\xee\xd5\xe9t\xc2\x98r&#<\xd4#\xd8b\xb1\xa0\xba\xba\x1a\x0d\x0d\x0dhhh\xc0\xbb\xef\xbe+\xf2\xfb\xd2\xe94t:\x9d8\xf2\x966\xe8V\xa9T\xe2\x98\x98\xc4\xe1\xfe\xfd\xfb\xb1h\xd1\x22\xf8\xfd~8\x1c\x0e\x98\xcd\xe6\x8a\xc9\x05\xa4<\xcdp8\xfc^{\x1e\xa5\x12---cr}\xde}\xf7]\x1c:t\x08}}}\xa2\xf2\x97\x22\xe4\xd2\xcd\x82\xd1h\xc4\x9a5k\xe0\xf5zQUUU\xf6\xdc?\xde\xcd\xcf\x1fh\xc3y\xc9%\x97\xe0\xbe\xfb\xeeC6\x9b\xc5\xf0\xf00^y\xe5\x15\x9c}\xf6\xd9\x13.\xf4\x1d\x1d\x1d\xb8\xe5\x96[\xc4\xbdy\xd7]w\xe1\x81\x07\x1e(\xcb\x9c(\x9d\xcf\x8a=\xe6\x8e5O\xd1\x91\xf43\xcf<#\x04\x97Z\xad\xc6y\xe7\x9dW \xe4J=\x8fL&\xc3[o\xbd%:c\xd8\xedv\xd1\xe3\x976\x94\xc5\xf7\x99\x5c.Goo\xaf\x10\x80J\xa5\x12\xd5\xd5\xd50\x9b\xcd\xe8\xe8\xe8\x80B\xa1\x10\xc5\x19\xc1`\xf0\x98bn\xaa\x86\xdf\xd3\x91\xab\xadT*\xd1\xdc\xdc\x8c\xe1\xe1aaf<\x99y\x82\xe7\x8ay*\x00\x8b/<\xe5\xb2\xa5\xd3i\x11\x05\x0c\x87\xc3\xa2B\xb5T\xb1\xc2|c\xb2;\x5c\xe9Q\x83t\xd2\x96\xe6\x0b\xaa\xd5j\xd4\xd7\xd7\xa3\xa9\xa9I\xf4\x0f\xa4\xbe\xcbv\xbb\x1dV\xabU\x1c\x15R\x0b\x9b\x89&\xc2\xe9^t\xc8\xcb\xca\xe3\xf1\xc0\xef\xf7c\xc9\x92%8p\xe0\x80\xb0\x06J\xa7\xd3\x88\xc5b\xd0j\xb5\x05\x93\x0fM\xe6\xd9lV\xfcK\x15\xc1\xed\xed\xed\x22\xa7\x91\xda\xc3\x9d\xec\x5c\xc0\xe2\xe8\xdf\xe0\xe0 \x9a\x9a\x9a\xc6x]\xa5R)\xbc\xf3\xce;\xa2Y{<\x1eG\x22\x91(\xc8\xfb\xa3\xeb\xb4|\xf9r\xd4\xd5\xd5\xc1\xe9t\xc2b\xb1@\xab\xd5\x96=\xfa'mIW\xca\xfb\x8c\x99;\x9b\xd0|>\x8f\xe5\xcb\x97\xc3\xeb\xf5\xa2\xaf\xaf\x0f\xb1X\x0cO?\xfd\xf4\x84\x02P.\x97\xa3\xa9\xa9\x09_\xf8\xc2\x17p\xef\xbd\xf7\x02\x00\x1e|\xf0A|\xeaS\x9f\xc2\xca\x95+\xcb2\x1eK\x1d3N6\xbf\xec\xd0\xa1C\xf8\xe5/\x7f)\x22j\xa7\x9f~:\x96/_\x8e\xc1\xc1A1\xef\x15\xcf\x0b\xf9|\x1e\x81@\x00;w\xee\x14\x9f[\xbat)<\x1e\x8f(H,NI!\xa1\xd6\xd3\xd3#^oMM\x0d\xdcn\xb7\xe88\x22\x97\xcb\xe1v\xbb\x91\xcdf'\xf4Y,\xd5\xa5\xe7d\xe2\xf1x\xe0\xf1x\xf8&a\x018y!C\xc2$\x91H\x88\x9eyT\xf9H\xbet\xd2\x82\x06fr\x93\xb4T\xa8)\x14\x0a\xd1\x9e\xa6\xaa\xaa\x0a---\xc2\x10\x93|\xa3l6\x9b\xf8\xbf\xc9d\x12\xa2\x8f\xec\x0fNv\x22-\x19;\x9b\xcdfTWW\xc3\xe7\xf3a\xe1\xc2\x85x\xe7\x9dw\x0a\x8e\x81\xa5cJj\x05Cm\x8f\xe8H&\x16\x8b\xa1\xbd\xbd\x1dMMM\xf0\xfb\xfd\x222v\xb2\xa3\x80\xb4\x09\xa2c\xddH$\x823\xce8\xa3\xe0~\x91\xc9dhmmE__\x1fz{{\x11\x0e\x87E\xf4\x5c\xbaQ\xa2\x8a\xf8\x0d\x1b6\xc0\xe1p\x88#\xfcJ\xf6>df\xc7\xfcRSS\x83\xb3\xcf>\x1b\xbf\xfb\xdd\xef\x00\x00/\xbc\xf0\x02\xee\xb8\xe3\x0e\x98\xcd\xe6\x09{\x80\xdfv\xdbmx\xec\xb1\xc7022\x82l6\x8b\x0f\x7f\xf8\xc38p\xe0@A\xa1\xd6\x89\xdc;\xe3E\xc2&\xaa\x08V(\x14\xb8\xe3\x8e;DG\xa1l6\x8b/\x7f\xf9\xcb\x18\x1d\x1d-\x88\xb2\x95\xca1\x1c\x18\x18\xc0\xe1\xc3\x87\xc5\xd7\xd7\xae]+r\x01\xa5U\xc0\xd2S\x9aT*\x85\xce\xceN\xf1<>\x9f\x0fn\xb7[XY\xa9\xd5j\x98\xcdf\xe1i:\x91\x88\xe5\xfb\x98\xa9x\x01(5\xb4\xa5\x85J\x0ay\xfeQ'\x0aJd\xa7\x9c\xbfb\x8cF#\xf4z\xbdh\xc6,\xbd\xc1\xe8f\x9f1_\xaacL\x94\xd3\xf5\x9c\xc5\xff\xd2b\xafV\xaba\xb1XD\x95'\xb5\xca!\xaf>\x12vz\xbd\x1eF\xa3\x11\x16\x8bE\x1c\xef\xd2\xd7(\xff\xa4\x12\x84\x9ft\xa2#\xbfG\xb7\xdb-\x8c\xa1)\x0aH\xa6\xd7\xe4AE\x16\x15\xf4\xbeP$\x90\x0a$\xb4Z-:;;\xd1\xd5\xd5\x85\xbe\xbe>\xd4\xd6\xd6\xc2j\xb5\x8a\xca\xd8\x93\xf17\xd315\xf9\xfeuuua\xf1\xe2\xc5\xa2I;]\x8bl6\x8b\xed\xdb\xb7\xa3\xb3\xb3S$\x93K;\xb7H\xef\x81s\xcf=\x17UUU\xa2\xf2w:\xa2\x7f\xcc\xfc\xe4\x8a+\xae\xc0\x13O<\x01\x00\xd8\xbd{7\xb6n\xdd\x8a\x8b.\xbah\x5c\x11\x96\xcf\xe7Q__\x8fo}\xeb[\xb8\xe9\xa6\x9b\xa0\xd5jq\xe4\xc8\x11|\xf0\x83\x1f\xc4\xb3\xcf>{B\x9bC\x12n\xa5N\x96(\xa7\x8e\x9c$\xa4si2\x99\xc47\xbf\xf9MaO\x92\xcb\xe5\xf0\x95\xaf|\x05Z\xad\x16\x89DB<\x7f\xb1\xd8\xcaf\xb3\xd0\xe9tB\x00+\x14\x0a\xe8t:\x9cw\xdeyH&\x93\xef}\xbfB>&\x07\x90\xd6\xc1\xbd{\xf7\x8a\xcf577\x0b\xf3\xfaK/\xbd\x14\x89d\x02&\xa3\x09\xd1h\x14\x0a\x85\x02\xd9lv\xdc\xf9h.\x09\xc0\xf9\x96\xd3?g\x05`\xb1MI>\x9f\x177\x98\xd4z\x82nX\xda\x9d\xd1\xf1/U\xfe\x16[\x95\xb8\x5c.\xac\x5c\xb9\x12\xd5\xd5\xd5\x22\x99]\x9a\xa0+\xad\x08\x9e\xe8\xa6\x99\xe9\x08\xdct\xdc$RGu\x12\xc1\xd4\x06\x8d\x1eT\x8dE\xff\xeat:!\x02\xe9c\xfa:y\xe6I\x05S\xa5\xa1P(\xa0\xd7\xeba\xb3\xd9\xe0\xf3\xf9\xe0\xf3\xf9\xd0\xd2\xd2\x82m\xdb\xb6\x89\xd7Lm|T*\x95h)(\xad\x10V\xa9T\x05\x1b\x8b={\xf6\x08c\xe8\xaa\xaa*\xe8t:\xd1\xc7r\xa6\xa1\x08\x1e\xf9\xfe\x85B!\x9cw\xdeyc*\x7f[[[\xd1\xd9\xd9\x89\xbe\xbe>D\xa3Q\x912Q\x5c\x1ce4\x1a\xb1b\xc5\x0ax\xbd^\xd1\xaaO\xa3\xd1L\xeb}q\xacj?fv,\xc4\x93\x19#\x1f\xfd\xe8G\xf1\xa5/}I\xb4g\xdc\xb2e\x0b:::D\xd5}\xa9y1\x93\xc9\xe03\x9f\xf9\x0c\xb6n\xdd\x8a\x87\x1f~\x18j\xb5\x1a\xcf=\xf7\x1cn\xb8\xe1\x06\xfc\xeaW\xbf\x9a\xb25\x8cT`\x8d'\x1ah\xdd\xc9\xe7\xf3H&\x93\x05\x7f[$\x12AGG\x87\xc8\x0d>\xf7\xdcs\xb1a\xc3\x06qZ \x15\x80\xd2M\xb7L&CGG\x07\xb6o\xdf.\xe6\x94k\xae\xb9\x06N\xa7S\xf4\x11V\xc8\x15\xc2\xa7P\x1a\x08\x19\x1a\x1aBww\xb7\xd8\x88m\xda\xb4I\xbc\x1e\x9a\x8b)7\xf0X\xf3\xf7\xa2\xfd=\x00\x00 \x00IDAT\xf0\x5c\x12J\x5c\xf81\xc7\x22\x80\xd2\x1085D\xa6\xddQ\xf1\xe0%\xdb\x8b\xe2\xfe\xa5\xf4=K\x96,\xc1Yg\x9d\x85\xfa\xfazQ\x98@\xc7\x93\xf31\x0c^\x9coE\xb9&\xb4\xd3%\x11#\x8d\xe6\xd1C*\x14\xe9{\xe89*\xf9&$qj4\x1a\xe1r\xb9\xd0\xd0\xd0\x80\xce\xceN\x1c|\x18g\x9ey\xe6\x98\xdc\xa3d2\x89\xdd\xbbw\xe3\xc0\x81\x03\x88F\xa3B\x00\x16wq\x01\x80SN9\x05>\x9f\x0f\x1e\x8f\x07V\xabU\xb4\xeac\x98\x13]\x88I$~\xe3\x1b\xdf\xc0\x96-[\xa0\xd1h\xd0\xd9\xd9\x89\xff\xfe\xef\xff\xc6\x95W^9\xae\x88\xa4{\xf3\xfe\xfb\xef\xc7\xae]\xbb\xf0\xf6\xdbo\x03\x00\x1e~\xf8a\xc8\xe5r\xfc\xe2\x17\xbf\x98\xb2\x08\x9c(\x02H\x7f\x8f^\xafG<\x1e\x1f\x93\x9f\xeap8\xf0\xf3\x9f\xff\x1c\xb7\xddv\x1b\xb4Z->\xfb\xd9\xcf\x8a\xcaT\xe9\xf3K\xdf\x97\x5c.\x07\x9b\xcd\x86;\xef\xbc\xb3`\xa3\xff\xe3\x1f\xff\x18\x7f\xfb\xdb\xdf\x0aOgd\x85\x01\x11\xb5Z\x8d\xd7^{M<\xaf\x5c.\xc7\xe5\x97_>\xee|\xa7\xd1hD$r\xbc\xf5u\xael:\xb8(d\x8e\x08\xc0R\x17\xd1n\xb7\x0b\x13g\xe9\xf7Q\x7fZi\xd4\xaf8\xfaWWW\x87\xf3\xcf?\x1f\x8b\x16-B}}=<\x1e\x0f\xf4z\xbd\x88fHo\x84\xc9V~\xcd\x85\x1bF*\x02\xa5\x0f\x12\x83R\xab\x96b\xaf<\xcaw\x99m\x93\x085.\xb7Z\xad\xf0x<\xa8\xaf\xaf\x87\xdf\xefG0\x18\x14\xd5\xb3\x14\xfd\x94&]K{\x03\x03\x10f\xc9\x0a\x85\x02\xbbv\xed\x12\xc50\xe4\x96?\xd3Q@\xca\xfd\x0b\x87\xc3\x18\x1a\x1aB.\x97\xc3\xb2e\xcb\x0a\x22\xe42\x99\x0c\x87\x0f\x1f\xc6\x8e\x1d;\x10\x0c\x06122\x22<3\xa9\xdf&a4\x1a\xb1t\xe9R\x11\xfd#\xdf?6Ee&\x8a\xf8\xb5\xb7\xb7\xe3\xd0\xa1C8\xf7\xdcs'\xec\xeeC\xc7\xa97\xddt\x13\xbe\xfa\xd5\xafbtt\x14j\xb5\x1a[\xb6l\xc1\x95W^9\xe1\x18\xa3\xcd\xe7\xd3O?\x8d\x0d\x1b6\xe0\xd0\xa1CP\xab\xd5x\xe8\xa1\x87p\xf0\xe0A<\xfe\xf8\xe3\xf0z\xbd\x93\x9a\xcb\xa5\x1b\xa4\x89\xc4\x03m\xfe\x8a\x85i.\x97\x83\xc1`\xc0\xb6m\xdb\xd0\xd6\xd6\x86D\x22!\xee%:U\xa2y\xc0j\xb3bxx\x18.\x97\x0b\xaf\xbe\xfa*v\xef\xde-:\x12\xdd~\xfb\xed\xc2\xfa\xa4\xb8\x08O\xda\xf7V\xa3\xd1\xe0/\x7f\xf9\x8b0\xd5^\xb7n\x9d(N+e\x17c2\x99D.\xa2\xc1`\x80\x5c.\x17=\x84I 2LEF\x00\xa5\x13\x0cE(\x8aof:\x9e\x8cD\x22\x05v/4\xc0\x95J%\xce>\xfbl466\x0a\xdb\x0e\xab\xd5Z\x90\xcb4\xdfv\x0d\xc5\x13\xccx\xbbA\xe9\xce\xb5\x94Y\xeal\x14\x024&\xccf\xb3\xe8\x0e\xb2h\xd1\x22\x1c\x9f\x18\x1fmmmH\xa7\xd3\xa2\x18\x86\xf2\xff\xc8\x08\x9a\xc6\x1e\x1d}\xd3\x04\x1a\x8f\xc7\xd1\xde\xde\x8e\xc6\xc6Faz\xad\xd7\xeb\xcbR\x998\x19\xa4\xbe\x7f###\x88\xc5b8\xe5\x94S\xc6\x5c\xdf\x9e\x9e\x1e\xbc\xf9\xe6\x9b\x08\x85B\x08\x85B\xc2$]\x9a\xfbJ\x1ekg\x9f}6\x1c\x0e\x87\xb8_\xa6\xbb\xeb\x874RY\x09\x05X\xcc\xd4\xb0X,x\xec\xb1\xc7\x84\xe0{\xe0\x81\x07p\xf7\xddwOx\x1cK\xd7\xfb\xd2K/\xc5\x05\x17\x5c\x80\xff\xfb\xbf\xff\x03\x00\xdc{\xef\xbd\xb8\xfe\xfa\xeb\xb1l\xd9\xb2\x09E[>\x9f\x87\xcf\xe7\xc3\xce\x9d;q\xd9e\x97\xe1\xe5\x97_\x86\x5c.\xc7\xd0\xd0\x10>\xfd\xe9O\xe3\x87?\xfc!\x1e~\xf8a\xac]\xbb\xb6 \x80P<\x8e\xa5\x11\xb6\xf1\x8e\x80K\xad\x17t2@\x1e\xa0\xa5\x02\x14\x14\x01\xa4\x8a\x5c\xbb\xdd\x8e\xfb\xef\xbf\x1f;v\xec\x10\xdf\xf3\xaf\xff\xfa\xafp\xbb\xddc\xde'\xe9\x06<\x9f\xcf\xc3b\xb1\xe0\xbf\xfe\xeb\xbf\x0a:|\x5c{\xed\xb5\x22-\xa5\xd4f\xd7b\xb1\x88cs\xa7\xd3YpoI\xd3w\xe6\x0a\xbcy<\xc9\x01\x96\x99\xbe\xd8T\xf9H\x85\x1b\xc5a\xffE\x8b\x16\xc1l6\xc3j\xb5\x8aH\x86\xb4R\x93\x1f\xf3\xefHO\x1a\x05\xa4N%\xcb\x96-\x13\x13<\xd9\x0aIm\x84h\xb2\x97\x16\xc5\x90(R\xab\xd5\xa2\x22\xb8\xb7\xb7\x17\xc3\xc3\xc3\xa2\x0b\xcdtOH\x94\xea\x90H$D\xdb\xb7\xea\xeajX\xad\xd61\x11\xb5m\xdb\xb6\xa1\xb7\xb7W\x1c\xfd\xc6b1Q8\x22\x15\x93k\xd6\xac\x11\xb9\x7f\x16\x8b\x05:\x9dnZ+\x9b+\xa5\xea\x9e9\xfe\x0d\x88\xdf\xef\xc7\xc6\x8d\x1b\x91N\xa7\xa1R\xa9\xf0\xbd\xef}\xef\x98Q5\xba\x17\xd5j5\xfe\xe3?\xfeC\x88,*\x88\xa0\xfb\xefX\x9bV\xadV\x8b\x97^z\x09\x9f\xfb\xdc\xe7\x0a*n\x0f\x1c8\x80\xd3N;\x0d\xcb\x97/\xc7\x1f\xff\xf8G\xf4\xf4\xf4\x8c\xc9\x87S*\x95\xa2\x8bOq\x0el\xf1\xeb,\xfe\xdd\xe4*`\xb3\xd9D\xbe\x1ed\xe3\xdf\xa72\x99\x0c;w\xee\xc4-\xb7\xdc\x22\xee\xa7\xea\xeaj\x5c~\xf9\xe5\x13\xfe\x9d\xb4\xd9\xe3LD\xffJEP\x98\xd9\xb1\xa1\xca\xe7\xf3\xf8\xc1\x0f~P\x10Q{\xf0\xc1\x07E\xca\xc4\xb1\xae\xff\xd9g\x9f\x8dO\x7f\xfa\xd3\xc2\x7fs\xef\xde\xbd\xf8\xc4'>!*\xf1\x8fu/g\xb3Y\xfc\xf4\xa7?\xc53\xcf<\x83u\xeb\xd6\x89\xa8\xb8N\xa7\xc3\xee\xdd\xbb\xb1y\xf3f477\xe3\xd2K/\xc5-\xb7\xdc\x82{\xef\xbd\x17\xbf\xfd\xedo\xf1\xdcs\xcf\xe1\xa5\x97^\xc2\xc8\xc8\xc81\xffF\xe9\xd8\xac\xad\xad\xc5\xc6\x8d\x1bq\xd6Yg\xc1\xe1p\xfcs3\x8d\xd2\xfd\x84\x95J%\xc2\xe1\xb0\xc8\x8d\xa4\xf7\xe8\x96[n\x81\xc9d\x9a\x94H\xfe\xdb\xdf\xfe&6\x96\x00p\xc3\x0d7\x1c\xb3\xe0E\xa1P`\xd1\xa2EX\xb6l\x19\x00\x14\xa4\xa6\xd0I\xc7l\xe5D{73\x15.\x00'\xb3\x18H\x8f\x8dJ-\xb6dUB\xbbK\x1e$\x0cM\xaa\xd4\x7f\xd2\xe5r\xa1\xb6\xb6\x16\x0b\x16,\x80\xc9d\x12\xd5\xe4\xe4+Y||D\x132E\x12\xc9^f\xef\xde\xbd\xe8\xed\xedEww\xb7\xc8\xb1\x9b\xce(`q\xee\xdf\xe1\xc3\x87q\xca)\xa7\x88\x02\x1d\xe9&\xe9\xed\xb7\xdfF[[\x1b\x82\xc1`A\xee\x9f\xf4\xbe\xc9\xe7\xf3\x22\xfa\xe7\xf5za\xb5ZO\xda\xa6\x89\xf3\x01g\xd7B\x9cN\xa7\xb1z\xf5j,\x5c\xb8\x10\x89D\x02J\xa5\x12\xdf\xf9\xcew&5\x8f\xd3\xe9\xcd\xfd\xf7\xdf\x8fe\xcb\x96!\x95JA\xa9T\xe2\xf1\xc7\x1f\xc7]w\xdd5)\x11I\x82\xea\xe2\x8b/\xc6_\xff\xfaW<\xf5\xd4Sp\xbb\xdd\x88\xc7\xe3P*\x95\xc2\xd4\xfd\xa5\x97^\xc2O\x7f\xfaS|\xf1\x8b_\xc4\xb5\xd7^\x8b\xcd\x9b7c\xcb\x96-hkk\x9b\xd0\xec\xb9\x18\xaa\x8a\xa7\x1c;\x00%\xc5\x1f\x09\xacd2\x89K.\xb9\x04###\xc2\xcc\xf9\xa6\x9bn\xc2\xca\x95+\x85\xdf\xe8D\x1c=z\x14/\xbf\xfc\xb2\xf8\xffE\x17]\x84\x85\x0b\x17Njc&\xcd\xdd\xa5>\xec\xf4\xda\xf4z\xfd\xac\x1dw<7\xccq\x01H\x15\xbd\xc7\xf2\xe5\xa3\x5c\xacR-\xa4\xa4\xed\xccJ5\xd6f\xe6\xb7\x08\x94\xe6\x02\xd6\xd5\xd5\xa1\xae\xaeN\x1c=I\x8bB\xa4\x91*z\xd01\xb0\xd4S\xf1\x9dw\xdeAOO\x0f\x06\x06\x06D\x7f\xdd\xe9\x8a\x02f\xb3Yd2\x19D\x22\x11\x11\xd9;\xed\xb4\xd3\x0a\xda\xaa\x01@WW\x17\xb6n\xdd*,b\x8a}\xff\x08\x9dNWP\xf9KQ\xf3\x99\xcc\x95e\xe17;!\x91v\xd7]w\x89\xb1\xd9\xd9\xd9\x89\xfb\xee\xbboR?O\xc6\xeb/\xbd\xf4R\x81\x0d\xd3\x9dw\xde\x89G\x1f}tR\xe2LZ\xa8u\xf9\xe5\x97\xa3\xbf\xbf\x1f\xcf?\xff\xf8\xa0\x98\x7f\xa4\xc7\xcb\xd2\x00\x85\xf4\xf7=\xf8\xe0\x83\xc2\xc7\xb4\xa1\xa1\x01\x9f\xf9\xccg\x8e\xeb:iuZ,jY\x84E\x8b\xdf\xb3D\x9b\x8dE \xc5\xc6\xd8\xcc\x1c\x15\x80\x93\xbd\xc0\xc5\xe2\xb08\x12\xc8\xdee\xccD\x22P\xab\xd5\xc2\xe1p\xa0\xa6\xa6\x06MMM\xa8\xae\xae\x16\xbez$\x04\xa9ZV\xba\x18\xa8\xd5j\xf1\xf3tL\x9aN\xa7\xd1\xda\xda\x8a\xae\xae.\x0c\x0e\x0e\x22\x1c\x0e\x1f3\x99\xfdx\x05 \xf5\xbc\x0e\x04\x02\x88F\xa3X\xbe|\xb9xm\xe4C\xb6\x7f\xff~\x1c8p\x00\x83\x83\x83\x08\x04\x02\xc2\xa7Lj\x94N\x0b\xcb\x19g\x9c\x01\xaf\xd7+r\xfff:\xfa\xc7\x85 \xb3_\x04\x9aL&l\xd9\xb2E\x88\xb0\xd6\xd6V<\xf5\xd4SSZ\xac/\xbc\xf0B<\xf2\xc8#B@\x01\xc0\xd5W_\x8d\x1f\xfd\xe8G\xc2~i2\xaf\x85\xbaw\xe4r98\x1c\x0e\x5c{\xed\xb5\xb8\xe7\x9e{\xf0\xe7?\xff\x19\x7f\xfd\xeb_\xf1\xdak\xaf\xe1\xb5\xd7^\xc33\xcf<\x83\xe6\xe6f\xa4\xd3\xe9I\x09\xc0\xe2\xc0\xc4x\xe2D&\x93\xe1\x8e;\xee\xc0}\xf7\xdd'N\xa9\xf4z=^\x7f\xfdux\xbd\xde1\xebSq\x852}|\xcf=\xf7\x08G\x81t:\x8d[o\xbd\xf5\xb87L2\xc8\xe0v\xbb\xd1\xd2\xd2\x02\x8b\xc52'\xc6\xdcd\x8d\xc7\x99Y&\x00'\xbb\xf8\x14W\xfe\x16G\x0a\xa7#\x02\xc3\xcc\x0d(\x11\x9ar\x01\xfd~?\x96/_.\xc4H,\x16\x13\xc7D\xa5\xa2\x80d\x8cM\x1fS\xc1\xc5\xe1\xc3\x87\xd1\xd5\xd5\x85\x91\x91\x91\x92\xfd\xab\xcb\x11\xfdK\xa5R\x18\x1d\x1d\xc5\xe0\xe0\xa08\xc6.\xe6\x9dw\xde\xc1\xf0\xf00\x06\x06\x06\x90L&\x0b\xbc2\xa5G_\x8d\x8d\x8dhnn\x86\xd3\xe9\x14\x95\xbf3\x19\xfdc\xe6\x0e\x9f\xfd\xecg\xe1\xf7\xfbE\xf5\xea\xf5\xd7_?\xa5{ \x97\xcb\xe1c\x1f\xfb\x18\xee\xb9\xe7\x1e\xc4\xe3qqzs\xdbm\xb7\xe1\x9b\xdf\xfc\xe6\x94\xac\x95J\xd9\xbe\x00\x80\xc9d\x82\xc7\xe3A]]\x1djkkE\x04p\xa2\xe78\x96\x07\xaa\xd4P:\x91H\xe0\xdf\xfe\xed\xdf\xb0u\xebV\xf17%\x12\x09\xec\xdf\xbf\x1fUUU\xc8d2c\x8c\xf8\xa5\xcfA\xddG\x8e\x1c9\x82{\xef\xbdW\xe40644\xe0\x92K.\x19\xf75\xcc\xb7\x0d\x07\xb5\x80e\xe6\xa0\x00\x9cj\xa8\xb7\x94\xad\x09G\xff\x98cA\x15\xc1v\xbb\x1d\xd5\xd5\xd5\x22\x12H\x96\x11\xa9T\x0a\xf1x\xbc\xa0\x070\x8d+*\x02!k!\xa5R\x89\xd1\xd1Q!\x00)\x0a(=J>Q\xa4\x95\xbf\xa1P\x08}}}\xd8\xb0a\xc3\x98{\xa1\xbd\xbd\x1d\x07\x0f\x1eDWW\x17\x82\xc1\xa0\xa8 \x94F\xff\xe8\xde\xa0j\xc6\xaa\xaa*q$\xc6>\x99\xcc\xf1\x8cM\x00\xf8\xc9O~\x22<5\xe5r9.\xb9\xe4\x12\xe1\x957\x99\x8d\x7f*\x95\xc2\xad\xb7\xde\x8a\x9f\xfe\xf4\xa7b\xbc*\x14\x0a|\xfd\xeb_\xc7\xd5W_\x8d\xde\xde\xde\xb2Dv\xa4y\xe1\x13\xdd\x9fr\xb9\x1cN\xa7SD\x09\xc7\xab@\xd5h4hmm\xc5\x96-[\xb0o\xdf>\xf1\xdcUUUhkk\x83\xd7\xeb\x1d\xb3NI\xc5\xa5\xf4y\xe5r9n\xb8\xe1\x06\x91\x8f\x0c\x00\x9f\xff\xfc\xe7\x11\x8f\xc7y\xa0\x15m\x18\x8aE!3\x07\x04`\xa9\x0b9\xd5s\x7f\xce\x13`&\xb3\xe0h4\x1aX,\x16x\xbd^\xd4\xd6\xd6b\xd1\xa2E\xc2\x0c\x9a,%\xa4\xc7\xc0R\xe3b\xaa2\xa7#aZ\x04\xba\xba\xba\xd0\xd9\xd9\x89@ \x80X,V\x96#N:\x9a\xa6\xe8_ww7\xbc^/\xbc^\xef\x98\xe7omm\x15\xbe\x84\xd1hTD\xff\xa4\x0b\x1d\xb5\x8c\xab\xaf\xaf\x87\xd7\xeb\x15\xb9\x7f\x95d\x974\xd5\xfb\x9d9\xb9Q\x99t:\x8d\x0f~\xf0\x83\xf8\xe8G?\x8aT*\x05\x85B\x81W_}\x15?\xfb\xd9\xcf&\xbd\xa9\xa0\x88\xdc\xe7>\xf79<\xf5\xd4Sb\xa3\xa6R\xa9\xf0\x87?\xfc\x01\xeb\xd7\xaf\xc7\xd0\xd0\xd0\x09\x8fQi\xca\xc4D\xbd\x80\x95J%\xea\xeb\xeb\xb1r\xe5J,]\xba\x146\x9bM|\x9d\x0c\xe35\x1a\x0d\x1ex\xe0\x01\xfc\xbf\xff\xf7\xff\x10\x0a\x85D\x8b\xb6\xfa\xfaz\xfc\xe3\x1f\xff\xc0\xc2\x85\x0b\x0b|\x0a\xa5\x05\x8a\xd2\x8fi\x1c?\xf4\xd0Cx\xe9\xa5\x97\xc4\xef\xd9\xb4i\x13\x1a\x1b\x1bgu\xe5n\xb9\x84^\xa9{\x9d\xd7\xfa9&\x00y\x82gfj\xd1\xa2\x8a\xe0\xaa\xaa*\xf8|>\xf8\xfd~444 \x95J\x15\xe4\x06\xa5R)1aK;\x04\xd0q\x17=\x12\x89\x04\xf6\xed\xdb'*\x82\xc3\xe1pY*\x82\xa9\xb0#\x16\x8baxx\x18\xbd\xbd\xbd8\xff\xfc\xf3\x0bz;\x03@OO\x0fv\xef\xde\x8d\xee\xeen\xf1\xbb\xa5\xa6\xcfR\xdf\xbfSN9\xa5 \xf7O\xab\xd5VD\xf4O\xfa^M\xf6\xbe\xe7h\xff\xc9\x87\xf2\xf4\xee\xbd\xf7^\xb8\xddnqDw\xe7\x9dw\xe2\xc8\x91#S\xda\x98\x01\xc0\xbf\xfc\xcb\xbf`\xcf\x9e=\xa2u\x1c\xf0\x9e\x0dJUUUY\xd6\x83\xc9\xf4\x02\xa6\xfb\xba\xb9\xb9\x19MMM\xa2\xea\x99ZF\xbe\xf2\xca+X\xb0`\x01\x9e|\xf2I1\x0e\xa9\x08\x85\x22\x7f\xc5\xbfS\x1a\x01\x94\x1e\xfd\xa6\xd3iD\xa3Q\xfc\xfb\xbf\xff\xbb\xf8\xba\xd3\xe9\xc4u\xd7]\x87\x5c.7'r\xf7\xca\xad\x07\x8a\xfdL\x999,\x00y\x92g\xca\x0d\xb5E2\x1a\x8dp\xbb\xdd\xa8\xaf\xafGCC\x03\xf4z=\xb2\xd9,\x92\xc9$\x92\xc9$r\xb9\x5c\x81Y4\x89@\x95JU\x109S\xa9ThkkCWW\x17\xfa\xfa\xfaDE\xf0\x89\x1c\x03\x93\xf8K&\x93\x08\x85B\xe8\xea\xeaBSS\x13\xecv\xfb\x98\x1c\xc5]\xbbv\xa1\xbd\xbd\x1d\xa3\xa3\xa3\x08\x87\xc3\x22\xff\xaf\xb8\xd2\xb1\xb1\xb1QTC\xda\xedv\xe1\x13V\xe9\x13~\xf1\xff)\xcf\xb7\x9c\xb9\x96\xcc\xf1\xa3T*QUU%z\xfa\xcad2\x0c\x0c\x0c\xe0\xfa\xeb\xaf/\xb0V\x9a\xec5onnF \x10\xc0\x86\x0d\x1b\xb0j\xd5*\xfc\xecg?C2\x99,\xcbZ0\x99\xcdN\xa9~\xc1\xf9|\x1e;w\xee\xc4\xe5\x97_\x8es\xce9\x07\xa3\xa3\xa3\x05\x91\xbc\x8f}\xeccx\xe4\x91GJ\xaeY\xb4i,6l\xa7t\x92\xdbo\xbf]\xf4\x1c\xcf\xe5r\xf8\xcew\xbe\x03\xaf\xd7\x0b\xbd^?\xa6\xc77\xc3\x81\xa2y#\x00\x19f: \xd1\xa6\xd7\xebEEpcc#\x5c.\x970L\x96V\xf3f\xb3\xd91\x91\x03\xa91\xb4L&C2\x99\xc4\x9e={\xd0\xd9\xd9\x89\xc1\xc1AD\x22\x91\x82<\xc2\xe3\x15\x80\x14\xfd\x0b\x87\xc38\xe5\x94S\xc6$\xba\x0f\x0c\x0c\xe0\xad\xb7\xde\xc2\xe0\xe0\xa0(B\xa1\xdf+\xcd\xfdS*\x95X\xb5j\x15jjj\xe0t:a0\x18\x0a|\x0dgj\x92&\x9fO\xfa\xdcd+1\xa5\xcf#=Zc*\x87\x8f}\xecc\xb8\xf4\xd2K\x85\x98y\xe5\x95Wp\xc3\x0d7L\xaa\xc3\x87\xf4\xba\xd3&\xeb\x85\x17^\xc0\xd6\xad[\x91\xcb\xe5\xa0\xd1h\xca\x22\x0a\xc6\x1b\xebTAL) 4\xbe\xe2\xf18\x1ey\xe4\x11\x5cq\xc5\x158\xf5\xd4S\xf1\xa7?\xfd\x09j\xb5Z\xe4\xd7.[\xb6\x0cw\xddu\x17\xae\xbb\xee:\xc4b1Q\x8d\x5c,\x00\x8b#\x80\xd4g\xfc\xdb\xdf\xfe6\xfa\xfa\xfa\xa0P(\x90N\xa7q\xc3\x0d7\xe0\x93\x9f\xfc$\x1a\x164\xe0\xb4\xd3N\x83V\xab\xe5\x81\xc5T\xe6\xc6\x8f\xdf\x02f\xd6\xee^\xde\xf7\xf5\xb3X,p\xbb\xdd\xa8\xa9\xa9\xc1\xa2E\x8b\xd0\xdb\xdb+\xaa\xf6\x92\xc9$\xf4z\xbd\x10-R?@Z\xa8(\x7f)\x93\xc9\xe0\xc8\x91#8|\xf80jkk\xe1r\xb9`2\x99\xc4\xf7L\x05\x8a8R\xf4\x8f*\x7fkjj\x0a\x16,\x99L\x86\xdd\xbbw\xa3\xa3\xa3\x03###\xe2\xf8\x97\x8e\xb1\xa5\xa2\xcb\xeb\xf5b\xc5\x8a\x15p\xb9\x5c\xb0Z\xad0\x18\x0c\xd3\xde\xf6\xedD\x16\xec\xf1\x16\xeab\x01\xc9T\x16\x7f\xfa\xd3\x9f\xe0\xf3\xf9088\x08\x85B\x81G\x1f}\x14\x8d\x8d\x8d\xf8\xc67\xbe1\xe5\x13\x9d\x135.\xa6\xfb\xa08\xe7N\xea\xfbIb/\x97\xcbA\xa9T\xc2`0\xc0b\xb1\xe0\x8d7\xde\xc0\xfd\xf7\xdf\x8f\xe7\x9f\x7f\x1e\x81@@\x8c5\x95J%\x8a4\xbe\xfc\xe5/\xe3\x8c3\xce\x80N\xa7\x13]H\xc6\x9bkJmZ\x1e{\xec1l\xdb\xb6M\xbc\xd63\xce8\x03\x0f<\xf0\x002\x99\x0cjkjy0\x1dC\xcc3'y\x0d\x9d\xc9\x8b\xcf\x83\x80)'\xd2(\xa0\xddn\x87\xdf\xefGcc#\xdcn\xb7\x88\x04\xe4r9Q\xd5+\xed\x17L\x22\x90\x1e\x9434::\x8a\xf6\xf6vtuuahh\xe8\xb8*\x82i\x9cSnP(\x14\xc2\xd0\xd0\x10V\xaf^=\xe6\xf5\xf7\xf7\xf7\xe3\xad\xb7\xdeB__\x1f\x82\xc1 \xb2\xd9\xac(^\xa1\xc8#5\xbe_\xb7n\x1d\x5c.\x17\x9cNg\xc5W\xfeR5i\xb1\x10\xa4\xbcF\x8e\xfcU.\xc9d\x12;v\xec\x10\xd7K&\x93\xe1[\xdf\xfa\x16\x9ex\xe2\x89\x19\xa9\xda\xa4\xe7\xdf\xb6m\x1b\xda\xda\xda\x10\x0a\x85\x10\x89D\x90H$\x84):U\xda\xc6\xe3qD\x22\x11\x8c\x8c\x8c\xe0\x9dw\xde\xc1SO=\x85\xaf}\xedk\xb0\xd9lX\xbf~=~\xf3\x9b\xdf`tt\xb4\xe0\xf9\xb5Z-n\xbe\xf9f\xf4\xf7\xf7\xe3\xa2\x8b.\x82B\xa1\x10=\xeaK\x8dY\xfa\xbf\xf4s*\x95\x0a\x7f\xfe\xf3\x9f\xf1\xa3\x1f\xfdH|\xde\xedv\xe3\xf1\xc7\x1f\x17Ef\x0cS\xe9\xcc\xd8(\xa5\x89\x84\xcdc\x99\xb2\xee`\xde\xaf\x08\xb6Z\xad\xf0z\xbd\xf0\xfb\xfdX\xb2d\x09zzz\xc4\x91\x0c\x1d\xf7\x90\x8b>E\x14\xb2\xd9\xac\x88\x06\x90\xe5E.\x97C{{;\x16-Z\x84\x9a\x9a\x1a\xb8\xddn\x98\xcd\xe61\x1d\x00hL\xd3sI\x05\x0dE\xee\x92\xc9$\xc2\xe10\x06\x06\x06\xa0\xd5j\xd1\xd4\xd44&\x82\xb2o\xdf>\xb4\xb5\xb5!\x12\x89 \x16\x8b\x89~\xc4\xd2$\xe9|>\x0f\xbf\xdf\x8f\xa5K\x97\x8a\xe8\x9f^\xaf\xaf\x88\xca\xdf\xf1\xaa0\xd5j5\x22\x91\xc8\x18\xa1G\x02\x90\xc49o\x0a+\x0f\x8dF\x03\x97\xcb\x85g\x9f}\x16\x17]t\x91\xe8\xdet\xf5\xd5W\xe3\xc9'\x9f\xc4\x87>\xf4!\xa4\xd3\xe9i\xebJ\x91\xcdf\xa1T*q\xcd5\xd7\xa0\xb3\xb3\x13\x00\xe0\xf1xPUU%r^GGGE\x8e\xef\xe8\xe8(\x8e\x1e=:\xe6o {\x1a\x8a\x10\xaeX\xb1\x02\x9b6m\xc2\xed\xb7\xdf\x0e\x87\xc3\x81\xe1\xe1a\xb1.QZ\xc2x\x1b\x13\x12\x80\xb9\x5c\x0ef\xb3\x19\x8f=\xf6\x18>\xfe\xf1\x8f\x8b{0\x95J\xe1\xe1\x87\x1fF}}=\x0f\xa0In\xde\x999.\x00yrgfb\x22!_@\x87\xc3\x01\x9f\xcf\x87\x86\x86\x06\xd4\xd6\xd6\xa2\xa3\xa3\x03\x0a\x85B\x08\xbcd2Y\xd0-\x83\x8eu(\xe7\x87\x04a(\x14B[[\x1b\xfc~?jjj`\xb5Z\xa1\xd5jE\x94\x90\xc6\xb64*X,\x00\xd3\xe94\xe2\xf18\x82\xc1 \x8e\x1c9\x82\xab\xaf\xbez\xcc\xc47<<\x8c\xad[\xb7bdd\x04\xc3\xc3\xc3\xa2\x0d]q\xee_>\x9f\xc7\xc6\x8d\x1b\xe1p8\xe0r\xb9`6\x9b\xc5\xeb\xa94\xb2\xd9,\xd4j5\xc2\xe1\xb0\xe8\x11K\x11L\x8a.\xa5\xd3\xe9\x13\xca\xaddf\x86\x0b/\xbc\x10w\xdf}7\xbe\xfa\xd5\xafB\xa3\xd1 \x99L\xe2\xaa\xab\xae\xc2\xa3\x8f>\x8a\x8f|\xe4#\xc8f\xb3\xd32\x06i\xdd\x18\x18\x18\x80\xd9lF4\x1aE\x7f\x7f?\xfa\xfb\xfb\xc7_\xc8\xde\xcf\xe5\xa5\xfb2\x99L\x8a\xcfo\xd9\xb2\x05\x9f\xf8\xc4'\xb0p\xe1B\xd8\xedv\xf1;hSW,\xf2&\x12,\x1a\x8d\x06\xbf\xf9\xcdo\xf0\xdd\xef~W|.\x95J\xe1\xf6\xdbo\xc7\x07>\xf0\x01.z<\x0em\xc0\xef\xd9\x1c\x13\x80\x5c\xe2\xcd\xcc$r\xb9\x1cj\xb5Z\xf8\x02RAHOO\x8f8*\xa2c^\xda\xf1\x17\xb7s\xa2\xa4u\x1a\xbf\xfb\xf6\xedCSS\x13:;;\x85\xd92\xe5\xdbI\xab\x00\x8b'/:\xbaM$\x12\x08\x06\x83\xe8\xee\xee\x86\xdb\xed\x86\xcf\xe7C.\x97+Xp:::p\xe8\xd0!\x04\x02\x01\xc4\xe3\xf11]?\xe8\xf9\x1a\x1a\x1aPWW\x87\xea\xea\xea\x93\xd2\xf3w\xbc\xfb\xbb\x18\x85B\x01\x93\xc9\x84\x91\x91\x11h4\x1ad2\x99\x82\xe35\xe0\xbdN-\xd4\xadE\x1a\x05\xe4\x05\xa0\xf2\xc8f\xb3\xf8\xcaW\xbe\x82X,\x86\xff\xfc\xcf\xff\x14\xd7\xe8\xba\xeb\xae\x83J\xa5\xc2\xe6\xcd\x9b\xa7\xe5\xf7\xaaT*Q\x80\xa5T*\x8f\xb9\x8e\xd0}H\xe3\xd2\xeb\xf5\xe2\x9ak\xae\xc1\x8d7\xde\x88%K\x96\x881{\xac\xce R\x93\xe9b\xb1\x22\x93\xc9`6\x9b\xf1\xf4\xd3O\xe3\xee\xbb\xef.\xd8\xe8}\xf1\x8b_\xc4E\x17]\xc4\x91\xad2\xcc!\xcc\x1c\x10\x80\x0c3\xd3\x02\x90\xfav\x92/`CC\x03\xf6\xed\xdb\x87\xbe\xbe>(\x95J\x11\x05\x94\xb6\x80#\xd1\xa2\xd1h\x84A4M\xe2\xa9T\x0a{\xf6\xec\x81\xcf\xe7\x83\xd3\xe9\x84\xd1h\x84Z\xad.\x10_$\x06\xa5\x93>\xf5!\x8eD\x22\x18\x1a\x1aB__\x1f\xae\xb8\xe2\x8a\x82D\xf6|>\x8fh4\x8a\xbf\xfc\xe5/\xa2\xf2\x97\x16<:V\x96.z\xcb\x97/GMM\x0d\xaa\xaa\xaaD\xf4o\xa6s\xe8\xc6\xeb\xa4 \xfd\x1aE[\x13\x89\x84\xe8\xb2\x22\xed\x05+\x93\xc9\xd0\xdd\xdd\x8dP($:\x9dX,\x16\x16\x80\x15\x0a\xf9c~\xe3\x1b\xdf\x80\x5c.\xc7\x9dw\xde)6P\x1f\xfe\xf0\x87\xf1\xc3\x1f\xfe\x10\xb7\xddv\xdb\x98\xd6\x9e\xe5\xc0h4\x22\x91H\xa0\xad\xad\x0dmmm8t\xe8\x10\xc2\xe10\xa2\xd1(\x86\x87\x87\xd1\xd9\xd9\x09\x9b\xcd\x86\xc5\x8b\x17\xc3\xe5ra\xc1\x82\x05\xa8\xaf\xaf\xc7\xc8\xc8\x08\x14\x0a\x05\x96.]\x0a\xa3\xd1\x88d2)\xa2\x83\xa5\xe6\x8d\x89\xc6x\xf1\xe7\x9f\x7f\xfey\xdc}\xf7\xdd\x22\xbf/\x93\xc9\xe0\x9e{\xee\xc1\xcd7\xdf\xcc\xe3\xf78`#\xe8y&\x00\xf9b3\xd36\x90\xdf\xaf\xd4\xb5X,\xa8\xae\xaeFCC\x03\x1a\x1b\x1bq\xf4\xe8Q\xd1\x98=\x93\xc9@\xaf\xd7\x8b\x89\x87\xf2\x00)\x82(\xb5\x83\x91\xc9d\xe8\xe9\xe9Akk+l6\x1b\x8cF#T*\x15\x9cN\xa78>\x92&\xc4S\x04\x22\x99L\x8a~\xbf]]]\xf0\xfb\xfd\xa8\xab\xab\x1b\x13M\xd8\xb9s'\xda\xda\xda\x84=\x0cY\xd6\x14\xe7\xc5\xb9\xddn\xac\x5c\xb9rL\x0e\xd4\xc9\x16\x80\xd2\x1d\x0c\xa3\xd1\x88\xcd\x9b7\xc3\xeb\xf5\x8eY$v\xef\xde\x8dW^y\x05\x07\x0e\x1c\xc0\xc8\xc8H\x81\xaf\x99\xb4\xf27\x97\xcb\xe1\x82\x0b.\x80\xddn\x87\xdb\xed\x86\xc5b\xa9\xa8\xe8_q\x15\x9f\xf4c\xb5Z-\x8cv\xe5r9\x0c\x06C\x81\xa7\xa1B\xa1\xc0\xe0\xe0 ^~\xf9e\xa4R)\xc4b1\xd4\xd5\xd5\x89\xc8\xeaLu6\x99/\xd0F&\x9f\xcfC\xa7\xd3\xc1h4\xc2h4\x8a\xb6\x89\x93\x8d\x04R\x84|\xe3\xc6\x8d\xd8\xb5k\x176m\xda\x84\xfd\xfb\xf7C\xab\xd5\x22\x9dN\xe3\xdak\xaf\xc5#\x8f<\x82\x9f\xfc\xe4'hjj\x12\xc2\xbf\xdc\x22\x89\xee\xdf1'J8~1A\xa6\xd2V\xab\x15o\xbf\xfd6n\xbd\xf5V\xbc\xf6\xdak\x05\x91\xeds\xce9GX\xbd\xb0\xf8cX\x00\x1ec\x91\xe0I\x9c\x99i\xe4r9\xb4Z\xad\xc8\x05\xf4\xfb\xfdX\xb4h\x11^}\xf5U$\x93I1iS^ y\x04\xd2\xcfR\x84M\xa3\xd1\xc0h4\xc2\xe7\xf3\xc1\xe3\xf1\xe0\x82\x0b.\xc0\x1bo\xbc\x81w\xdf}\x17\x89D\x02\xdd\xdd\xdd0\x18\x0cP(\x14H&\x93\x88F\xa3\xd0h48\xf3\xcc3\xb1|\xf9rq\x9c)]$R\xa9\x14v\xef\xde\x8d\xa3G\x8fb``@T\xfe\x16\x1f\xff\xe6\xf3y\xb8\xddn477\xa3\xba\xba\x1av\xbb\x1d:\x9dND\x1cO6\xd2\xfe\xc3\xc5]=\xe8\x08\x8e\x84\x9e4:H\xef\xb7\xc3\xe1@SS\x13\x94J%\xb6m\xdb\x86\xde\xde^,^\xbc\x18\x1e\x8fG\x1c\xad\xf3\xdcQ\x1e\xa1Dc0\x91H \x9b\xcd\xc2b\xb1\xc0\xe3\xf1\xa0\xba\xba\x1a2\x99\x0cz\xbd~JG\xee\xf4\xbd\x0b\x16,\xc0\xbb\xef\xbe\x8b\x9bn\xba\x09\xbf\xfe\xf5\xaf\xc5xx\xfe\xf9\xe7\xb1l\xd92\xdc|\xf3\xcd\xf8\xc1\x0f~ \x04h9\x8f\xf5e\xf2\x7f\x0a\xc0rT\x93\xe6r9\xe1\x12p\xe3\x8d7\xe2\xb7\xbf\xfd-\xe2\xf1\xb8x\x0fS\xa9\x14\xbe\xf0\x85/\xe0\xee\xbb\xef\x86V\xab-(\xe8b\x8e/0T\xfc13\xc7\x04`qd\x80afJ\x00R;\xa8\xaa\xaa*\xd4\xd4\xd4\xa0\xbe\xbe\x1emmm\x18\x1c\x1cD&\x93A\x22\x91\x00\xf0^Q\x02\xe5\x0dIs\x7f\x8cF\xa3\x10\x91\x16\x8b\x05MMMX\xb6l\x19\xd6\xacY\x03\x00\x18\x1c\x1cD2\x99DWW\x17\xd2\xe94\xccf3|>\x1f\x5c.\xd7\xb8\x13[6\x9b\xc5\x1f\xfe\xf0\x07\xbc\xfa\xea\xab8|\xf8\xb0\xb0\xa7!3\xdb\xe2\xe2\x8f\xb5k\xd7\xc2\xeb\xf5\xc2\xe5r\xc1b\xb1\x9c\x94\xca\xdf\xf1\x88\xc7\xe3\xa2x\xa6\xb8C\x8aF\xa3\x11\x06\xd0\x14\x0d\xccf\xb3\xd0\xe9t\xd0\xeb\xf50\x99L\xf0z\xbdhhh\xc0\xb9\xe7\x9e\x8b\xef}\xef{\xd8\xb3g\x0f\x8cF#\x02\x81\x00\x82\xc1 R\xa9\xd4\x94:\xaf0\x13\xcf\xbf===\xd8\xbbw/\x12\x89\x04t:\x1dZZZD\xd5<\x09\x9f\xe3\x19[r\xb9\x1c\x0f?\xfc06o\xde\x8cO}\xeaS\x18\x1c\x1c\x14\xe3\xff\x87?\xfc!~\xfc\xe3\x1f\xe3\x17\xbf\xf8\x05\xae\xbc\xf2JX,\x96\xf2\x09@\xc8D\xb1\xd0\x89\xe4\x95+\x95J(\x95J\xc4\xe3q\xbc\xf0\xc2\x0b\xf8\xf9\xcf\x7f.>O\xb8\x5c.\xfc\xfa\xd7\xbf\xc6\x05\x17\x5c \xc6$oN\xca?FY+\xb0\x00d\x98\xb2 \xcd\x05\xf4z\xbd\xa8\xaf\xafG}}\xbd\xa8\x08\xa6\x02\x8fT*U\x90\x0cO\x22\x8c\xaay\xa5\xf9m\xa9TJ\x1cu\x91\xd0\x93\xf6\xf5\xa5\x9f\x97V\x12RD,\x99L\xe2\xcd7\xdf\xc4\xae]\xbb088\x88H$\x82`0X\xb2\xf8\x03\x00\x9cN'\x16/^\x0c\xaf\xd7[q\xd1\xbf\xe2\x05Pz\x0cN\xef\x81\xb4\xad\x16u\x8c A\xa8\xd3\xe9\xa0\xd5jQ]]\x8d\xc6\xc6F\xa4\xd3iQ\xe5)\xad\x96f\xca\x0f\xb5s\xeb\xee\xee\x86\xcf\xe7\x83\xcdf\x83\xc1`8\xee\xe7#\x8b\x9fG\xa8\x91U\x00\x00\x1daIDAT\xcb/\xbf\x1c\xbbv\xed\xc2\xd7\xbe\xf65\xfc\xeaW\xbf\x12\xddwr\xb9\x1cn\xbc\xf1F<\xf7\xdcs\xf8\xfd\xef\x7f/\xa2\xee\xe5\x18\x7f\xc5\xde\x7f\x93]o\xa4'S\xad\xad\xadx\xec\xb1\xc7\xf0?\xff\xf3?\x18\x1a\x1a\x12v1d\x22}\xf3\xcd7\xe3\xfb\xdf\xff\xbe\xb8\xef\xa7\xab\xf3\xc9|\x15}\xac\x15* `2\x9dj\xbed\xa2.\xc3L\xf7\xa0~?\xbf\xcfl6\xc3\xe5r\x89c`\xb3\xd9,\xc4])\xcf=\x8aj%\x12\x09\xa4\xd3i\x84\xc3a\x0c\x0e\x0e\xe2\xc8\x91#hoo?\xe6\x22 5\x89\x96V\xc8\xbe\xf2\xca+x\xf6\xd9g\xd1\xde\xde\x8e\xfe\xfe~Q\xf8!\xb5O\xa1\x85I.\x97c\xc9\x92%\xa8\xab\xab\x83\xc7\xe3)\x88\xfeUZ\xe4\x81Z\xbeI\x0bSH\xe4Q!\x00\xe5VJ;\x81\x00\xefuA\x19\x1a\x1a\x82B\xa1\x18\x131\xa5k\xc2\x8f\xe3\x7f\xd0\xfbH\xe9\x0d\xdf\xf9\xcew\xf0\xc4\x13O\xa0\xab\xab\x0b\xb1XL\xe4\x9fR\xf4\xf9\xb8#\x08\xef\x1b5\xbb\x5c.<\xf4\xd0C\xd8\xbe};N?\xfdtQu\x0f\x00\x9f\xfd\xecg\x91\xcb\xe5\xcav\x0c,\x15\x80S][d2\x19^x\xe1\x05l\xdc\xb8\x11\x17^x!~\xf9\xcb_\x8aqH\xe3\xf3\xfc\xf3\xcf\xc7\x8e\x1d;p\xdf}\xf7\x89#_\x16\x7f\xe5\xd3\x08\x13\x8d7\xd6\x0a\xb38\x02X\xaep.\x87\xd8\x99rD\x01\xa5\xb9\x80\xb5\xb5\xb5hll\xc4;\xef\xbc\x03\x00\xa2\xe8\x22\x99L\x8a\xcaS\xa9\xb1s2\x99D*\x95B8\x1cF0\x18\xc4\xdbo\xbf\x0d\x97\xcb\x05\xb7\xdb=\xa9\xc5@.\x97c\xdf\xbe}\xd8\xb9s'^}\xf5U\xf4\xf7\xf7#\x18\x0c\x22\x12\x89\x08\xb3d\xa9\x08\xa5\xdfKy\x84.\x97\x0b6\x9b\xad\xa2*\x7fK\xdd\xef\x91HDD\x82\xa4\x22Pj\xa7Cy\x82$\x88\xbb\xba\xba`\xb5Z\xf1\xd2K/\xe1\xe2\x8b/\x86\xdb\xed\xe6\xfb\x7f\x9a\x04z<\x1e\xc7\xa3\x8f>\x8a'\x9f|\x12\x9d\x9d\x9d\x88\xc5bH$\x12H$\x12\x22\x1a^\x8e\xdfC\xd7}\xf5\xea\xd5x\xf3\xcd7\xf1\xfc\xf3\xcf\xe3\xe3\x1f\xff8\xd6\xacY\x833\xcf<\xb3\xac\xd7\x956Jt\x0fOE<<\xf1\xc4\x13\xb8\xea\xaa\xabJ\xf6\xa8\xf6\xfb\xfdx\xec\xb1\xc7\xb0a\xc3\x06\xf1\xdc\x5c\x90\x84\xb2\x8d\x0f\x16ys\x5c\x00N$\x0a\xa7\xb2\xcb\xe4\x1b\x8e)\xc7\xa4\xa3P(`6\x9bEw\x90%K\x96`\xdf\xbe}\x22\xc7,\x99L\x8a\x9d?\x1dO\xa5R)\xc8d2\xe8t:D\x22\x11a\x1f\x93L&\xf1\xe8\xa3\x8f\xe2\xd4SO\xc5\x8a\x15+\xa0\xd5ja6\x9b\x0b\xc6j6\x9bE(\x14B__\x1f\xda\xda\xda\xb0o\xdf>\xb4\xb6\xb6\x22\x12\x89`xx\x18\xa1P\x08\xb1XlL\xcf_\x22\x97\xcba\xfd\xfa\xf5p\xbb\xddp:\x9d\xa2\xebG\xa5\x0a@z\x9fI\xc8J\xefy\x8a\xa6\xd2\x91\x1fE^3\x99\x0cl6\x1bzzz\x10\x89D\x10\x8dF\xb1z\xf5j\xb4\xb4\xb4\xc0h4\x0a!\xce\x9c\xf8\xbc{\xe8\xd0!\xfc\xf1\x8f\x7f\xc43\xcf<\x83\xfe\xfe~\x91r maX\xceD|\xba\xe7R\xa9\x14>\xf0\x81\x0f`pp\x10\xdd\xdd\xdd\xd0j\xb5e\x1fs\xd2H\xfbT\xb8\xf8\xe2\x8b\xb1d\xc9\x12\xb4\xb6\xb6\xbe\xb7\x00*\x95X\xb2d\x096n\xdc\x88\x8f|\xe4#X\xb3f\x0d\x1f\xf7\xce\x80\x1e`\xe6\xa8\x00\x94\xde\x98\x94\x0fT\x5c\xe1\xc8032\xb8\x95Jh4\x1a8\x1c\x0e\xf8|>\xf8|>,_\xbe\x1c\xaf\xbf\xfe\xbaXD\xa8\xe2T\xaf\xd7\x8b\xf1I\xc6\xc5\xe9tZ\x88\xbfp8\x0c\x97\xcb\x85@ \x807\xdf|\x13~\xbf\x1f\x1e\x8fGT\x11SqIGG\x07\xfa\xfb\xfb\x0b\x04_8\x1c\x16\xc7\xceT\xf8!\xb5\xe6\xa0{\xc2d2a\xf5\xea\xd5p\xbb\xddp8\x1c\xc2\x1c\xb9\xd2\xc4\xdfxG\x8e\xd2\xaf\x93\xc8\xc8d2B\xd4\xc5b1h4\x1a\x04\x02\x01\xa4\xd3i\x04\x02\x01$\x93Itww\xc3\xe1p\xa0\xaa\xaa\x0a\xb5\xb5\xb5\xd0\xe9t\xc2\xf2\xc6h4B\xa7\xd3\x9d\x94\x85\xa7\x9c\xe2\x88\x9e\xebx\xaf\xe5d_\x7f2\x99D(\x14\xc2\xc1\x83\x07\xf1\xc6\x1bo`\xc7\x8e\x1dhooG(\x14\x12Q-i\xc7\x95\xe9\x18[j\xb5Z<\xb7\xdf\xef\x9f\x96MG\xa9\x8e1\x93y\x8fL&\x13\xd6\xad[\x87\xbe\xbe>|\xees\x9f\x13\xf3\x82Z\xadF$\x12A&\x93)\xbb`e\xfey}H\x0b\xb0G\xf0\x1c\x15\x80\xd2\x5c&\xbaY\xa5\xf9K\x1c\xddcf2:\xa5P(`4\x1a\x0b*\x82\xf7\xee\xdd\x8b\xd1\xd1QQ\xdc\xa1R\xa9D\xa7\x0a\xeap\x00\xbc\x17\xd1\xa3j]\x83\xc1\x80\x9e\x9e\x1etww\xc3\xe9t\xa2\xbd\xbd]\xfc\x0e\xb2\x8c\x19\x1c\x1cD.\x97C<\x1eG\x22\x91@4\x1aE4\x1a\x15Gm\xf1x\x5c\x88?\xa9\xef\x1fAG\xbf\x1e\x8f\x07V\xabU\x1cMWb\xee\x9f\xf4\xbe\x9e\xa8\xa7*E\x03\xb3\xd9,4\x1a\x8d\xb0\xcbI\xa7\xd3\xd0\xe9t\x18\x1a\x1a\x12\x1dN\xc8\x9f.\x16\x8b\x8d1\xd0\x9e\xca\x22_\x1cY\xad\xa4H\xc7tZ_P46\x12\x89 \x10\x08```\x00CCC\x22\xed\x80~7\xe5eR\x9e\xe6t\xbd\x96\xe9B*`\x8f\xe7\xfd\xbc\xf7\xde{\xf1\xb3\x9f\xfd\x0c\xb1X\x0c[\xb7n\x15\xc7\xe2F\xa3\x91\xd7\xa7i\x9e7\x8aSm\x989\x22\x00\xa5\x8b\xc2\xf0\xf00Z[[\x91\xc9d\x90N\xa7\xd1\xd1\xd1\x81\xd1\xd1Q\x84\xc3a$\x12\x09\x11\x0d\x91\xee\x06fb\xe2`\xe6\xe1\x00\x7f?\x0ah\xb7\xdb\xe1\xf5zQSS\x83\xc6\xc6F\xbc\xf5\xd6[\xa2Wm\x22\x91\x80\xc1`\x10BE\xea\x0bFQl\x12\x88z\xbd\x1e\xa1PHT\x19\x93P\xa4\xde\xb7\x99LF\x88<\xe9d\x97H$D\xe5\xb14\xfaG\xd8l6\xb4\xb4\xb4\xc0\xe7\xf3\x89\xca_\x8dFS1\xc7\xa1R\x93\xeaP(\x84}\xfb\xf6A\xa1P \x18\x0c\x8a\xc2\x96p8,\x22\xa7T|@\xa2'\x97\xcb!\x1a\x8d\x0a[\x18\x00\xc2k-\x14\x0a\x89B\x97t:\x0d\x83\xc1\x80\xae\xae\xaec\xce\x09\xb3\xcdG\xecD\xc5\xd6\xb1~>\x9b\xcd\x22\x95J!\x93\xc9 \x12\x89 \x16\x8batt\xb4\xc0V\x87\x22\x80\xd2\x0d\xfal\x9bs\xc7+\x02\x99lT\x89\xa2\xf6\xf4^p$jz\xaf\x15\xcdu}}}hmm\xc5\xc8\xc8\x08\x06\x06\x060<<\x8c\x91\x91\x91\x82\xd3\x96\xe2\x8d1\xfd\xcb}\xc2+\x5c\x00J/\x18%\xcf\xd3b\x11\x0e\x87\x0b\x92\xe0\xc73\xc0\x95F\x10\x19\xa6\x1c(\x14\x0a\x11\x05\xf4x<\xa8\xaf\xaf\xc7\xc2\x85\x0bq\xe0\xc0\x011Fe2\x99\xc8\x01\xa4\x1e\xb6T!IQ\x12\x95J\x05\x99L\x86H$\x02\x8dF\x83d2\x89`0\x88|>\x0f\xbd^/\x8e8\xa59\xaft\xdcKQ\xb0x<.\x04e\xf1D\xb6h\xd1\x22,X\xb0\x00.\x97\x0bV\xabU\x1c\x81V\xd2\xbd@\x133\xe5:\xaaT*\x84\xc3a\x0c\x0d\x0d\xa1\xbf\xbf_\xe4\xf4\xd1\x91x\xf1u\x00 r,\xa5\x0b\x84B\xa1@(\x14\x12\xdf\x17\x08\x04Dk\xbc\xa9F\x99\xa6k\x81\x98-\x0b\x0fy3R\x11\x13\xe5]J\xe7U\xe9b:[\xf3-O$\x02Hs\x02\xa5'\xb0\xb0\x98\xfey\x83\xdc\x15\x82\xc1 b\xb1\x18B\xa1\x10zzz\x10\x8dF1::\x8aX,&r\x89KE\xf0\xf9\xfa\xcc\x82\x08 M2\xc3\xc3\xc3\xd8\xb7o\x1f\x86\x87\x87E\x97\x04Z\x1c\xa4\x11\x02i\x04\x90&'\xee\x02\xc0\x94\x1b\x95J\x05\x8dF\x03\x9b\xcd\x06\xbf\xdf\x8f\xfa\xfaz\xd4\xd5\xd5a\xf7\xee\xddb\x13B\xd1\xbbX,V\x10!\xa1\xc8\x1e\x99\xe5*\x95J$\x93I\xa8T*\xf1 \xe3c\xe9B\x22\xb5\x96\xa1^\xc1\xd9lv\x8c\xfd\x0c\x15\x9c\xac]\xbb\x16n\xb7\x1bUUU\xd0\xeb\xf5\xa2%Z\xa5-\xba\x00\x10\x0e\x87\xd1\xd6\xd6&&n\x9a\xc4\xa3\xd1\xa8\x10\xbc\xc5\x02\xf0X\xd1\xbcb\x11CsA%L\xfc\xb3i\xf1\xa1\xb1%\x1dk\xd2\xb6\x9c\xd2\x08\xdal\xed\xc4p\x22>\x80\xe3\x8d5f\xfa\x85z \x10\xc0\x9e={\xd0\xdd\xdd-\xf4\xc0\xf0\xf0\xb0\x08\x08\xd1\xc6\xb1x\xbc\xd2ub\x11X\xc1\x02\x90\x8eyd2\x19FGG\x11\x0c\x06E\xd4\x83\x16\x06\xca\x85\x92\x1e\xb5\x95\xf2\x0ed\x01\xc8\x94;*\xa2R\xa9`4\x1a\xe1p8P[[\x8b\x95+Wb\xef\xde\xbdb\xa1$\x1b\x93\xe2\xb1'\x97\xcbE\xb50\xf5\x1a&\x93[\xaa\x10\xd6\xe9t\xc8d2\x05\x9b\x17\xb2\x92\xa1EY:\xfe\xa5c=\x9f\xcfc\xc9\x92%\xa8\xaf\xaf\x87\xcb\xe5\x82\xd9l\x86N\xa7\xab\xc8\x8d\x10\xbdn\xca1\xa3\xa3\xf1H$\x22\x8e\xb8\x8b\x0b[&\xbb8\x17\xe7\x0d\xce\xf5]\xfft\xe7\xdeI7\xd7\xf4\xb1TXOV\x8cW\xe2\xfb&\xed\x05|\x22\x22\xb6\xf8~\xe7ugz6$t\x8d\xfa\xfa\xfa\xd0\xdf\xdf\x8fd2)R\xc2h\xfe\x95\xce\x1d\xc5\x91\x7fi\xee S\xa1\x02\x90\x16K\x85B\x81H$\x82P($\xaa\x1e\xa59O\xc5\xea\x9e&)\xba\xa19A\x94\x99\x96\x81\xaeTB\xa7\xd3\x89\x5c@\x9f\xcf\x87U\xabV\xe1\xf5\xd7_\x1f\xd3\xcd\xa2xA\x90V\x0c\xd3\x18\xd6j\xb5H\xa5R\xa2\xa0A\xa5R\x15$\xd9\xd3\xc4GB\x90\xa2\x80\xd2\x85\x99\xee\x97\xf5\xeb\xd7\xc3\xe9t\x8a\xca_i\xff\xdcJ\x8b\xba(\x95JD\xa3Q\xd1U\x85v\xee\xe3\x1d\xdd0'O(\x8d'\xa4\xa9\x05\xdal\x8e*\x8d\x97\x03x\xbc\x82\x99\x05\xe0\xf4\x5c'\x9a\xc7\xe4r9\x22\x91HA\x0a\x98t\xde(\xbe~\xc5u\x01\xe5\xec%\xcdL\x83\x00\xa4\xe31\xbd^\x8fL&\x83\x91\x91\x91\x82\x04\xd0\xc9F\x05t:\x9d\xc8}\xe2\x1b\x92)\xe7\xf8\xa4\xee \x1e\x8f\x07uuu\xc2\x0f,\x18\x0cNj\xc1\x91\xf6\xec\xcd\xe7\xf30\x18\x0cB\x10R\xa7\x03\xfa\x7f\x22\x91\x80Z\xad\x169qT\x85Y\xdc\x0c\xfd\xcc3\xcfDuu5\xdcn\xb7\xe8\xfaA\xf9\x86\x95\x04\xfd}\x1a\x8d\x06\xf9|^\xe4?J\xff~\xa6\xb2\x04\xe0xc\xf9D\xfa\xffV\x8a\xb0(\xb7@\xa19\x82)\xffuR\xa9T\xd0\xeb\xf5H$\x12\x18\x1e\x1e\x16\xf3E\xa9`\xd0\xb1\xc6,U\xb03\x15$\x00I\xfc\xe9t:\x98\xcdf\x11\x15)\x95\x13u,\xccf\xb3h/\xc5\x17\x9a)'\x0a\x85\x02:\x9d\x0eV\xab\x15~\xbf\x1f\xb5\xb5\xb58\xe3\x8c3\xf0\xdcs\xcf\x8d\x9b\x08^*\xd2@\xc9\xe3\xd4\xf3T:V\xa9\xe2W\xa7\xd3\x09\xc3gz\x14\x1f\x8f\xeat:,_\xbe\x1c\x1e\x8f\x07UUU0\x1a\x8d\x15\x99\xfbG\x93\xb8F\xa3\x81\xd5j\x85V\xab\x15\xc7\xe2'\x9a\x83\xc5\xcc\xdcbLcY\xab\xd5Vt\x87\x99\xa9\xfc-'*\x109\x028\xbd\xd7I\xadVC\xa7\xd3\xc1b\xb1@\xa5R\x89\x22\xb0RQ\xbf\xf1\xae\x8b\x5c./\xf0\x06\xadDk\xacy+\x00)<\xabV\xaba2\x99\xe0\xf3\xf9D\x94`*\x0b\x02\x0d\x88\xaa\xaa*\x98L&\xa1\xf8yQa\xca\x05mT,\x16\x0b<\x1e\x0f\x9a\x9a\x9a\x10\x8b\xc5\xf0\x8f\x7f\xfc\x03}}}\xc7\x8c\xa4H\xad4\xc80Zz\xc4!\x15v\xe4\xf5G\xd1\xbfb\xdb\x17\x00X\xbat)\xea\xeb\xeb\x85\xef\x9f4\xfa]i\xc2\x99L\x99\xa9HE\xfa~0\x95\x8f4\xb7\xcal6\x8b\xcd\xc6l=Z\x1b/8 \xb5\x0d\x99\xaa\x00daQ\xfe\xeb#\x97\xcb\x85\x03\x834Mf2\xe3\x95H\xa5R\xb0Z\xad\x228\xc4\xd7\xa9\x82\x04 \xf5d\xd4\xe9t\x22\xc1~\xfd\xfa\xf5\x22G\xaa\xb8\x92\x87vo\xd2\xfc*\x95J\x05\xadV\x0b\x93\xc9\x04\xaf\xd7\x0b\x87\xc3!\x12\xe1\xf9\xec\x9f)\xe7\x8e\x94z\x04\xdb\xedv,X\xb0\x00\x00p\xd5UWa\xc7\x8e\x1d\xc2\x8bJ\x9a\xabJ\x95\x94\xa9TJT\xaee\xb3Ya\x19\xa3\xd5j\xa1\xd1h\xc4\xf8\xa6/\x04`:\x9d\xc6e\x97]\x86\xd3O?\x1d\xa3\xa3\xa3\xa20N\x1a\x18\xa2\xebI\xa2P\xa3\xd1\xc0`0\xc0n\xb7\x8f\x19\xb3\x9c\x1eVA\x02\x90&\x18\xab\xd5*\x12\xc5\xddn7\x12\x89\x84\x88\x10\x94*\xb9\x97VB\xaa\xd5j\x18\x0c\x06\x98L&q<\xc1\xa1^f:&&:\x8a\xa0vkf\xb3\x19\xe1p\x18\xb1XlL\xa1\x07\xf0\x9e\x17\xe0\xc8\xc8\x88hS\x16\x08\x04\xa0\xd7\xebQ[[+\xbe/\x9dNC\xaf\xd7\x17\xb4{\x93\x8e\x7fi\xe5/\x80\x82\xca_\x8a\xfeU\xea\xa4F\xef\x99\xd1h\x04\x00h\xb5Z8\x9dN\xd1\xd5G:\x91K==\xf9\xde\x9dY\x8a#`\xd2\xe3PZP\xb5Zm\xc1\x11\xf0l\xcd\x03,\xfe\x9b\x8b\xff\xfe\xc9\xfeM\x1c\x01\x9c>\xc8D\x9f\x8e\x80u:\x1d\x5c.\x17\x12\x89\x84h\xb7Y\xea:HuA\xf1\x98\xa5\x9ck\xa6\x82\x04 -\x10\x00`\xb1X\xa0\xd7\xeb\x0b\x8e\x87\x8e\xe58O\x17\x9bz\xb1\xd2\x83U>3]\x82\x86\x04\x17%\x18\xdbl\xb6\x92-\xda\xf2\xf9<\x22\x91\x08\x06\x07\x07\x11\x89Dp\xf0\xe0A\xa8\xd5j,^\xbcX\x08\xc8@ \x04$\xfd,\x89\xc0\xe2\xa3\xdf\x5c.\x87e\xcb\x96\xa1\xa9\xa9\x09n\xb7[\xecj+}!\xa6|\x5c\x12\x0e\x16\x8b\xa5\xe0\xfe\xe6hJ\xe5\x08\xa2R\xd11\x8a\xe2\xd2f[\xadV\xcf\xfaH\x0a\xb9L\x9c\xe8s\x14\x7f\xccc\xb7\xbc\x22\x10\x80\xf07\x95\xea\x82\xf1\xdeo\xe9\xc6E:fU*\xd5\xac\xdf\xb0\xccI\x01(\x15\x81$\xe4t:\xdd\xa4\x0bA\xa4G\x15\xec\x03\xc8\xcc\x04\x14\xb5V(\x14\xc8f\xb3\xd0\xeb\xf5\x05bFZ\xf0A\xad\xca\xe8hw\xf9\xf2\xe5\xb8\xfe\xfa\xeb\x11\x0e\x87q\xe4\xc8\x118\x1c\x0e\x1cW\xc5\x95\xbfk\xd7\xae\x85\xd7\xeb\x85\xd3\xe9\x84\xc1`\x98\x15\xb9\x7f\x0cS\x89\xd0=u\x22-\x08K9S0\xcc\xbc\xba\x8f\xf8-`\xe6;\x94\xaf\x07@\x18\xe3\xe6\xf3y$\x12\x09D\x22\x11\x0c\x0f\x0fC\xa3\xd1\xa0\xa5\xa5\xa5`\xb1\x88\xc7\xe3hkkC$\x12A,\x16C4\x1a\x15\xfe\x7f\xd2\xe8_>\x9fGuu5V\xacX\x01\xa7\xd39k*\x7f\x19\xa6R!\xcf\xccr\x1e\x01\xf3\xbd\xc8\xb0\x00d\x98y\xb8\x98\x90\xcd\x00\x1d\x07e\xb3Y\xe1\xff\xd7\xd5\xd5\x85\xb3\xce:k\xcc\xcfuvvb``\x00\xc1`\x10\x91H\xa4\xe0\xf8Wj\x7f\x94\xcf\xe7q\xe1\x85\x17\xc2n\xb7\xc3\xe9t\xce\x9a\xca_\x86\xa9\xd8\x85\xeb}\xe1&\xad\xd6\x97\x1e\xe5N\x16\x8e\x002,\x00\x19f\x1eS\xdc\xc1\x82L\x9cC\xa1\x10zzz`4\x1a\xd1\xd4\xd44\xe6\xb8\xa9\xad\xad\x0d}}}\x08\x87\xc3\xa2\x8b\x08\x89?\x22\x97\xcba\xe1\xc2\x85\xa8\xa9\xa9\x81\xd7\xeb\x85\xcdf\x83N\xa7\xab\xd8\xb6o\x0c3[6mr\xb9\x1c\xf1x\x5c\x08\xc0P($\x22\xf9\x93\x15\x91l\x04\xcd\xccg8\x07\x90a$bM*\xfe\x06\x07\x07\xd1\xd5\xd5\x85\xeb\xae\xbb\x0e\xb9\x5c\xae`\xb1\xe8\xed\xedEkk+\xba\xba\xbaD\xf4Oj\xfd\x22=\x8eZ\xb3f\x8d\xc8\xfd3\x99L\xa2\xcb\x0d\xc30'\xb6i\x8bF\xa3\xd8\xb3g\x0f\xd2\xe94\x06\x06\x06\xa6l\x0c\xcdG\xc0\x0c\x0b@\x86\x99\xc7\x90`\x93\x8a\xbf\xfe\xfe~\x1c\xe8gs\xb9\x1c\xd2\xe94\x12\x89\x04FGGq\xf4\xe8Q\x1c\x9f\xc7\xf0\xf00\x06\x07\x07100\x80\x03\x07\x0e\xe0\xc0\x81\x03\xe8\xec\xec\x14\x85&\xc1`P\x08?\x12\x7f\xd2\xc2\x0f\x85B\x81L&\x83\x8d\x1b7b\xc9\x92%\xa8\xa9\xa9\x81\xc3\xe1\x80\xc1`8\xa6Pe\x18\xe6\xf8\xe6\x84\xf16qS\xb9\xdf\x5c.\x17\x16/^\x0c\xadV\x0b\x8f\xd7\xc3o,\xc3\x02\x90a*\x09\x8a\xe6e\xb3YD\xa3Q\x0c\x0d\x0d!\x9dN\x17\xe4\x00\x92\xa9\xeb\xd0\xd0\x10\xe2\xf18\x86\x87\x87\x11\x0e\x87E\xa4 \x9f\xcfC\xa9T\xa2\xbf\xbf\x1f\x81@\x00n\xb7\x1b\xfd\xfd\xfd\x88\xc7\xe3\x08\x85B\x88\xc7\xe3\xe8\xed\xedE$\x12A\x7f\x7f?\xa2\xd1(b\xb1\x18\xc2\xe1\xb0\xb0\x9e\x91\xb6{\xa3\xe7\x94\xc9d\xc8f\xb3X\xbat)\xce>\xfbl\xf8\xfd~x\xbd^\x98L&\xf6\xfdc\x98\x93(\x10'\xb5\x00*\x95X\xb8p!\xbfa\x0c\x0b@\x86\xa9\xe4\xc9\x9c\xaam\xa9\x02W&\x93\xc1h4\xe2\xe8\xd1\xa3\xe8\xee\xee\xc6\x8b/\xbe\x88\xa3G\x8fb``\x00\x89DB\xe4\x0a\xd2sd\xb3Y\xb8\x5c.(\x95J\x8c\x8c\x8c \x91H@\xa1P\x08\x91\x17\x89D\x90\xcdf\x91J\xa5\x84\xc7 \x09?i\xab7\x12\x7fd\xf8\x5cUU\x85\x8b.\xba\x08\x0b\x17.Dmm\xad\xc8\xfd\xd3h4,\xfe\x18\xa6\x82\x05\xa0t#\xc70,\x00\x19\xa6\xc2\x90\x8a\xae\x5c.\x87@ \x80\xbf\xfd\xedop:\x9d\xc8\xe5r\xe8\xec\xecDww7\xf6\xee\xdd\x8b`0\x88t:\x8dT*U\x10\xa9\xcbd2\x00\x80\xde\xde^$\x12\x09qTL\xad\xe0\xf2\xf9\xe8\xf5zTUUA.\x97\xc3l6#\x1c\x0e\x0b\xc3\xe7l6[\xb0\x00\x94\x8a\x00\xd01\xb1\xb4\xb8C\xa1P\x88|\x22\x8dF#\xa2{Z\xadV|\xacR\xa9\xa0\xd1h\x0a*|9\xc2\xc00'\x17\xa3\xd1\x08\x83\xc1\x80\xba\xba:ttt\x08\xafO\x99\x9c\xab\x80\x19\x86\x05 3\xa7v\xfcj\xb5\x1af\xb3\x19\x1a\x8d\x06V\xabUt\xfc\x18\xaf\x95S\xa9*`\xea\xe0AB\x8e\xa2\x80$\x04\xe9\xb8Y\xadV\x8bJC\x85B\xc1G\xbd\x0cS\x81P\xe4_Z\x11,\xcb\xf3}\xca0,\x00\x999\x83L&\x83Z\xad\x16\x22M\xa7\xd3\x89N\x1d\xc5\x22o2\xcf%E*\xf0H\x0c\x96:6f\x18\xa6\xf2\xe6\x851~\x9f\xb2\xf7\x1f\x0c\xc3\xb0\x00d\xe6\xcedO\x11\xba\xe2N\x1f\xe5x\xeeR\xe2\x90a\x98\xcaG\x9a\x8f+\x93\xc9 \x97q~.\xc3\xb0\x00d\xe6\xfc\x84\xcf0\x0c\xc30\xcc\x14\xd7Q~\x0b\x18\x86a\x98\xd9\x0cG\xee\x19\x86\x05 \xc30\x0c3\x8f\x05 \xe7\xee2\x0c\x0b@\x86a\x18f\x1e\x8bA\x86aX\x002\x0c\xc30\xf3D\xf4\xb1\x08d\x18\x16\x80\x0c\xc30\xcc<\x13\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb8\xf8\xe3\x1c@\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe62\xd2N \x1c\x01d\x18\x16\x80\x0c\xc30\xcc<\x11\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb1\x18dA\xc80,\x00\x19\x86a\x98\xb9\xbe\x90qkH\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe60\xc560\x1c\x11d\x18\x16\x80\x0c\xc30\xcc<\x15\x83\x0c\xc3\xb0\x00d\x18\x86aX\xf41\x0c\xc3\x02\x90a\x18\x86a\x01\xc80,\x00\x19\x86a\x18\x86a\x18\x16\x80\x0c\xc30\x0cS\x99P\xe4\x8f#\x80\x0c\xc3\x02\x90a\x18\x86a\x18\x86a\x01\xc80\x0c\xc3\xcce8\x02\xc80,\x00\x19\x86a\x98\xf9\xb6\x90\xc9\xe5\xc8\xe7\xf3\xfcF0\x0c\x0b@\x86a\x18\x86a\x18\x86\x05 \xc30\x0c3'\xe1#`\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe6*2\x99\x8c#\x80\x0c\xc3\x02\x90a\x18\x86\x99\x8f\x22\x90a\x18\x16\x80\x0c\xc30\xcc<\x16\x83,\x08\x19\x86\x05 \xc30\x0c3\x0fD\x1f\xa1P(\xf8\x0da\x18\x16\x80\x0c\xc30\xcc\xbcZ\xd4\xe4\xbc\xac1\x0c\x0b@\x86a\x18f\xce#\x8d\x00\xf2\xf1/\xc3\xb0\x00d\x18\x86a\xe6\x99\x00\xe4\x08 \xc3\xb0\x00d\x18\x86aX\x002\x0c\xc3\x02\x90a\x18\x86\x99{\x0a\xb0\xb4\x18d\x18\x86\x05 \xc30\x0c3g\xf5\x1f\xe7\x002\x0c\x0b@\x86a\x18f~\x09@.\x02a\x18\x16\x80\x0c\xc30\xcc\xfc\x15\x80\x9c\x03\xc80,\x00\x19\x86a\x98y,\x06\x19\x86a\x01\xc80\x0c\xc3\xcc\x87EM\xc6\xcb\x1a\xc3\xb0\x00d\x18\x86a\xe6<\x05Q?^\xd5\x18\x86\x05 \xc30\x0c3\xbf\x04 G\x00\x19\x86\x05 \xc30\x0c3\x1f\x162\xb9\x1c\xf9|\x9e\xdf\x08\x86a\x01\xc80\x0c\xc3\xccGdr.\x02a\x18\x16\x80\x0c\xc30\xcc\xdc\x16|2Y\x81\xf5\x8bB\xae\xe07\x85aX\x002\x0c\xc30\xf3A\x042\x0c\xc3\x02\x90a\x18\x86\x99\xaf\x8b\x1a\x1bA3\x0c\x0b@\x86a\x18f~\xc1\xd1@\x86a\x01\xc80\x0c\xc3\xcc#\xf2\xf9<\x0b@\x86a\x01\xc80\x0c\xc3\xcc7X\x002\x0c\x0b@\x86a\x18\x86\x05 \xc30%\xf8\xff\x82\xe5f\x8a\x9e\x05\x9f\x09\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xbf\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\xaa\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05_IDATx\xdab\xfc\xff\xff?\x036\xc0\xc4\x80\x03`H\x18\xe4\xf1\x81\x8d`\x81\x09\xd8\xf5\x08\xff\xff\xf4\xe87\x03\x9f\x1c+\x98\xcf\x88\xcb\x0e\xb8\x0e\xe32\xfe\xff\x7f\x7f@\x14]\x98\xf4\x89\x11 \x80\x18)s\x15\xccEp;\xecz\x84\x80.\xfa\x83\xa9\xe3\xfb\xbb\xbfpK\xadZ\x05!\xba@\x96\x83\xb0Q)\xef\x7f\xfd\x5c\xde\xff0>@\x00\xe1t\x15\xd1AE\x08\xb0\xa0\x0b8N\x16\x81X\xc9\x08F`\x86\x82\x84\x08\xc3\xbc\x90\xeb\x8c(n\x05a\x8b&\x01\x14\xf7\xc2\xb0]\xaf\x10\x5c\x0c\xc5\x86\xef\xaf\xff2\xb0\xf131\x98\xd5\x0a\xfcg\x04\xda\xf3\x1fj\x0b\xb2/Q<\xcd\xc8\xc8\xc8\x0e\xa4\xe4\x818\x18\x88O\x00\xf1] \xfe\x04\xc4\xdf\x80\xea~\x81\xd4\x00\x04\x10\xedCi\x805\xa4l\xd0\xc6\xf0 \x8a\xa7\x1d\xa7\x88\x82\x02\x93\x01\x1e\xa4P\xa9w\xb7\x7f\x81\x13\x1a\x86\x0d\x7f\x7f\xfccx\x7f\xeb\x17X\x01\x88\xfe\xf3\xf3?\xc3\xbe\xdc7\x8c8\x93\xc6\x9f\xef\xff\xe0&\xc1\x80}\x9f\xf0\x7f\x9c~\xf8\xf6\xfa/\x86?\xfe\x00m\xc5i\x83\xba\x96\x10\x8390Y\x80\x8d\xfc\xcf\x00O\x17\xc8\xb6\xa2h\xb8y\xed\x1d\x033\x07#\x03,\x1b\xc3r\x15\x03\x9e\xb4\xc4\x0d\xa4\xf4\x80\xd8\x04\x88\x8f\x02\xf1#P\x9a\x04\xaa\xf9\x0aS\x03\x10@$\xa7%\x9a\xa7\x8c\xe1c\x01r!\x89\x0f\x10\x8c\x03|\x06\x09k\xb2\x83\xca(\x86\xb77~a\xa4\x06\x82\x16\x80\x0c\x16Pb\x03&\xbb\xff\x0c\xa8:\x19Q\x13%\x14|\xb8\xf7\x1b\xab%,\xb8\x5c\xc7#\xc9\x02\xce\x05_\x9e\xfd\xc1\xaa\x11\xe63\x90\x1c\x88-\xa8\xc2\x06\x16CW\x8b\xd3\x82?\xdf\xff\x83]\x8f\x0b \x1b\xc4#\xc3\xc2\xf0\x1b-K\x92\x14\x07\x1az\x22\x0c\x0f\x9f\xbdg\xf8\xfe\xe6/V5\x1c\xc2\xcc\xe0\xe0\xfa\xf1\xf6\x1f\xfe\x9c\x89\x0cL*\xf9\xff3\xc2\xc3\x1c\x16\xda\xb0:\x89\x11\x1a\x0b\xff\xe1\xb1\xf1\xfb\xeb\x7f\xacAITN\x06\xf9\x82\x11\xd8f8\xdf\x8b=\xa5P\x94L)\x05\x00\x014Z\x16\x8dZ0j\x01\x05\x16\x80\xdaM\xc4T:dW8\xc2\x1a\xec\xe0\xf6\x96\xa2\xb8\x18\xc3\xd9\x03\x8f\xc9\xabp\x04\xd5\xd8 \x0d7p\x13\x05\xa9z\xf9\x0f-\x02\x19\x11U\x8f\x82\x84(\xa2%NL\x10\xf1+\xb12\xfc\x07vC\xfe\xfd\xfd\xcf\xf0\xef\x0f2\x06\x89A\xf1\xef\xff\x105@|\xee\xd0S\xe2\xfa\x090\xd7\x83\xba\x5c \xdf}z\xfc\x1b\xabF^\xa0<\xc8\xb9\x9f\x1e\xfd\x82\xd7jD[\x00\x02 \xd7)H\x8b0,)\xbd\xc5\x88\xcb\x11\xb0pw\x9c$\xfc\xdf\xc0F\x9a\xb4T\xf4\x17h\xc1\xbf_\xff\x08V\x99 \x8b\xfe\x82\x83\xed\x1fi\x16\x80\xc2\xfb\xef\xdf\x7fD%\xd9\xff\xd0\xf8!\xc9\x02\x90\xa6\x9bW\xdf\xc0\x83\x03_\x9a\xff\xfb\x07\x12\xd9$\xc5\xc1\xa9\xb6\x8f\xe0 \x88\x98\xa3\xf4\x1fT\xa9\xffx\xfb\x17k\x9e\x00\xc9\xfd\xfb\x8d;\x88\xf0\xe6\x035-\x11\x86\x07O\xdf\xc1\x13;#J\xe5\xcf\x88\xe0\x03\x89_\x9f\xfe\x11_\xe9\x83\x0cg\xe1fDjA\xfc\xc7h\xd31\xa0uB\xcf\xb4\x7fd$\xb9\xa8\x00Y\xc4\xcc\x0e\x0d\xe7\x9f\x0cD5\xc2\xc8*\x8b\x98X\x19\xc0\xe1\x8c\xcf \xa2{\xfcH\xdd#N %\x08\xc4\x86@\xac\x0f\xec\xd8/\x00\x8a\xf9\x01\xd9\xa0\xa4\xf5\x1e\x88\xdf\x82Z\x98P\x0c\xeaz\xff\x01:\xf6\x0f\xdd\x9b-\x00\x01\xda\xb5\x9a\x95\x06b \x9c\xa8U(\x05Q\x16\x11\x17\xa1\x1e\xfc\xa1\x0aB\xef\xe2\xcb\xf8\x0e>\x96\x8f\xe0A\x8f\xeaE\x0f\x1e<\xb4hA\xa1\x94j\x11\xb5\xdb\xce:\x93MLv\xb7\xd8M\xeaV\x94\x0d\x0c\xcd\x12\x9a\xe4K6\x99\xf9\xbe\xd9\xdc\x07\xf8\xf3\x1e\xb3\x00P\x00(\x00\x14\x00\xdc\x1cA^\xc5\x8c\x18\x5c<\xd7\xaf\x03\xa0\xe2\xd5\x16D\x00qt\xb2+\xbc\xd0\xe5\xe9\xbd3\x18gG\xa6V\xb2~\xb8\x8e\x9d\x80\xd4|gd\x04\x03\xfa\xed\xe4\xc0\x92bP\xe3\xa9m\xe8)\xa4\x17sVE\xfe\xc2\xb0\x1f\x15\xfdg\x05d\x0d \xd2\xb4JB^\x13\x83\x1b\x9a\x1a\xe7\xba\xaf\x10\x11qn\x86gZ\xf6\xe1*`\xa3\x04\x03\xe7:|\x0b5\x7f\xea\xde\xf53\x81\xb0\x02 \xe8N\xb5\xc4R\xbd\x1aA\xa4\xaeF\xa8\xd4\xa4\xa26\x15\xc4\x86\xb1\xffR\xfbs#p:\x17\xd6g \x0c\xa2\xe1\x05m]\xf5\xd8\xf5\xc5\xa3\xd5\xa0\xb4\x08\x15\x7fNL\xba\xf70H1$\xb2\xfa\x81\xcf\xae\xceZ\x99\xfa\xb5\x06@\x14L\xa9\x800t;\xc4\xe7\xc7\x1d\x9e\x04EL\x99\xb4_\xda\x12\xc8\xc8\xd6\xdcv`\x00\x0c\x84\xc4\x09V\x03\x8d\xbb:\x896\xaa\x06\xa2\xfb\xb992\xc1\xf0\xa4\x860\x0c\xc0J1\xf9~a\xa8o\x90|9\xc73\xf0\xf1\x02\xa9\x95T\x13\xae\xed\xaf0WvAJ\x82:\xd5\x10d\xdf\xd9\x89\xfd\xc0\xf6\x9e\xc7\x9aH\x9d\x8d\x5c\x17\xab\xfaK\xb2F~\x01\xc6\xbe\x00\x8dV;\x96\xdd\xd8\xf0\x97\xd9\xedM;\x9fkts\xc7\x93\x83v\x8c\x8b1\xcd\xf7Y,G\x9c&\xeb<\xe6\x13\xf4\xc5J\xcf\xfd\x1e\xe4\xeb\xc8\xe8w\xb6\xcc\xbf\xb2\xad\xe9L\xb6tJ\xcay\xa9\x94z\x98h70Q\xfe\xc6\xc5\x0fL\xcc\x89\x85v1\xcf\xb5\x0a\x89\x15\xe8\x87?\x12\xa8M%\x98\x1bu\x98\xa7Y\xacw@~\xda0#m\x11\xad\x82VF#\x1df\x0bmM.L\x17\xad\x89\xd6\xa3\xcb\x0b\xed\x1d\xed\x0d\xedU\xd6!aCi\xe2y\x94F\xf3/e\x95O\x08\xef\x09\x03\x8d\x1a\xcd\xa5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" +qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x06\x07\xa7(\x98\x00s\x00p\x00l\x00a\x00s\x00h\x00\x0a\x08\x94\x19\x07\x00s\x00p\x00l\x00a\x00s\x00h\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x08\x00FX'\x00s\x00y\x00n\x00c\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" +qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x17\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x12\x00\x00\x00\x05\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x12C\x00\x00\x00z\x00\x00\x00\x00\x00\x01\x00\x00\xbc\xce\x00\x00\x01\xaa\x00\x00\x00\x00\x00\x01\x00\x01\xe9\x0a\x00\x00\x01F\x00\x00\x00\x00\x00\x01\x00\x01\x9dy\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x01r\x07\x00\x00\x00\xf4\x00\x00\x00\x00\x00\x01\x00\x01W\xe8\x00\x00\x00\xc2\x00\x00\x00\x00\x00\x01\x00\x016\x0e\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x01Hn\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x01\xd90\x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x00\xad&\x00\x00\x00\xaa\x00\x00\x00\x00\x00\x01\x00\x01%\xb3\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01\xc9J\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x02\x02a\x00\x00\x00J\x00\x00\x00\x00\x00\x01\x00\x00\x9e\x08\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x01\x16\x10\x00\x00\x01b\x00\x00\x00\x00\x00\x01\x00\x01\xb9Y\x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x01\xf1\xcc\x00\x00\x01.\x00\x00\x00\x00\x00\x01\x00\x01\x8d\xb6\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x18\x00\x00\x02\x08\x00\x00\x00\x00\x00\x01\x00\x02\x22\xf1" def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index a97057d4..ea662b24 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -12,8 +12,25 @@ import numpy as np import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime +from pylot.core.pick.run_autopicking import run_autopicking import warnings import pdb + +def autopickevent(data, param): + stations = [] + + for n in len(data): + station = data[n].stats.station + if station not in stations: + stations.append(station) + else: + continue + + for station in stations: + topick = data.select(station=station) + + stat_picks = run_autopicking(topick, param) + def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) @@ -254,7 +271,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): FM = '+' elif P1[0] > 0 and P2[0] <= 0: FM = '+' - + print 'fmpicker: Found polarity %s' % FM if iplot > 1: @@ -530,7 +547,7 @@ def wadaticheck(pickdic, dttolerance, iplot): print 'wadaticheck: Not enough S-P times available for reliable regression!' print 'Skip wadati check!' wfitflag = 1 - + # plot results if iplot > 1: plt.figure(iplot) @@ -615,12 +632,12 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): print 'checksignallength: Signal shorter than required minimum signal length!' print 'Presumably picked noise peak, pick is rejected!' returnflag = 0 - + if iplot == 2: plt.figure(iplot) p1, = plt.plot(t,x, 'k') p2, = plt.plot(t[inoise], e[inoise], 'c') - p3, = plt.plot(t[isignal],e[isignal], 'r') + p3, = plt.plot(t[isignal],e[isignal], 'r') p2, = plt.plot(t[inoise], e[inoise]) p3, = plt.plot(t[isignal],e[isignal], 'r') p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ @@ -642,7 +659,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): def checkPonsets(pickdic, dttolerance, iplot): ''' - Function to check statistics of P-onset times: Control deviation from + Function to check statistics of P-onset times: Control deviation from median (maximum adjusted deviation = dttolerance) and apply pseudo- bootstrapping jackknife. @@ -722,7 +739,7 @@ def checkPonsets(pickdic, dttolerance, iplot): for i in range(0, len(Ppicks)): plt.text(i, Ppicks[i] + 0.2, stations[i]) - plt.xlabel('Number of P Picks') + plt.xlabel('Number of P Picks') plt.ylabel('Onset Time [s] from 1.1.1970') plt.legend([p1, p2, p3], ['Skipped P Picks', 'Good P Picks', 'Median'], \ loc='best') @@ -751,7 +768,7 @@ def jackknife(X, phi, h): : param: h, size of subgroups, optinal, default = 1 : type: integer ''' - + PHI_jack = None PHI_pseudo = None PHI_sub = None @@ -786,7 +803,7 @@ def jackknife(X, phi, h): phi_sub = np.var(xx) elif phi == 'MED': phi_sub = np.median(xx) - + PHI_sub.append(phi_sub) # pseudo values phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) @@ -799,21 +816,21 @@ def jackknife(X, phi, h): def checkZ4S(X, pick, zfac, checkwin, iplot): ''' - Function to compare energy content of vertical trace with - energy content of horizontal traces to detect spuriously + Function to compare energy content of vertical trace with + energy content of horizontal traces to detect spuriously picked S onsets instead of P onsets. Usually, P coda shows larger longitudal energy on vertical trace than on horizontal traces, where the transversal energy is larger within S coda. - Be careful: there are special circumstances, where this is not + Be careful: there are special circumstances, where this is not the case! : param: X, fitered(!) time series, three traces - : type: `~obspy.core.stream.Stream` + : type: `~obspy.core.stream.Stream` : param: pick, initial (AIC) P onset time : type: float - - : param: zfac, factor for threshold determination, + + : param: zfac, factor for threshold determination, vertical energy must exceed coda level times zfac to declare a pick as P onset : type: float @@ -841,7 +858,7 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): ndat = X.select(component="N") if len(ndat) == 0: # check for other components ndat = X.select(component="1") - + z = zdat[0].data tz = np.arange(0, zdat[0].stats.npts / zdat[0].stats.sampling_rate, @@ -863,7 +880,7 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): # calculate energy levels zcodalevel = max(absz[isignal]) encodalevel = max(absen[isignal]) - + # calculate threshold minsiglevel = encodalevel * zfac @@ -873,7 +890,7 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): print 'checkZ4S: Maybe S onset? Skip this P pick!' else: print 'checkZ4S: P onset passes checkZ4S test!' - returnflag = 1 + returnflag = 1 if iplot > 1: te = np.arange(0, edat[0].stats.npts / edat[0].stats.sampling_rate, diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index 4fdc4943..4bf4b85c 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -4,8 +4,9 @@ from PySide.QtCore import QThread, Signal class WorkerThread(QThread): message = Signal(str) - def __init__(self, func, data, param): + def __init__(self, parent, func, data, param): super(WorkerThread, self).__init__() + self.setParent(parent) self.func = func self.data = data self.param = param @@ -13,7 +14,12 @@ class WorkerThread(QThread): def run(self): sys.stdout = self - self.func(self.data, self.param) + picks = self.func(self.data, self.param) + + try: + self.parent().addPicks(picks) + except AttributeError: + print picks def write(self, text): self.message.emit(text) From 2bd31f433be21365a57d02409c702e7fdb26876b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 9 Jul 2015 15:50:29 +0200 Subject: [PATCH 0493/1144] Changed labeling within overview window, station IDs are shown instead of running number. --- QtPyLoT.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 8ffe3cef..068933ca 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -463,7 +463,9 @@ class MainWindow(QMainWindow): self.getPlotWidget().plotWFData(wfdata=wfst, title=title) self.draw() pos = self.getPlotWidget().getPlotDict().keys() - labels = [int(act) for act in pos] + labels = [] + for i in range(0, len(wfst)): + labels.append(wfst[i].stats.station) self.getPlotWidget().setYTickLabels(pos, labels) def plotZ(self): From ea976295d0cf70f3eaa74ef9cf909b13e73d53f0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 10 Jul 2015 09:22:58 +0200 Subject: [PATCH 0494/1144] huge structural rearrangement to resolve circular import problems [add] new feature added to QtPyLoT capable of automatically picking an event from overview window --- QtPyLoT.py | 29 +++++-- autoPyLoT.py | 79 +++---------------- pylot/core/pick/Picker.py | 38 ++++----- .../pick/{run_autopicking.py => autopick.py} | 32 +++++++- pylot/core/pick/utils.py | 17 +--- pylot/core/read/__init__.py | 5 +- pylot/core/read/data.py | 6 +- pylot/core/read/io.py | 5 +- pylot/core/util/__init__.py | 9 --- pylot/core/util/structure.py | 2 +- pylot/core/util/thread.py | 3 +- pylot/core/util/utils.py | 31 +++----- pylot/core/util/widgets.py | 34 +++++--- 13 files changed, 128 insertions(+), 162 deletions(-) rename pylot/core/pick/{run_autopicking.py => autopick.py} (96%) diff --git a/QtPyLoT.py b/QtPyLoT.py index 8ffe3cef..18d5c7a8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -34,17 +34,23 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen, \ - QActionGroup, QListWidget + QActionGroup, QListWidget, QDockWidget import numpy as np from obspy.core import UTCDateTime -from pylot.core.read import Data, FilterOptions, AutoPickParameter -from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ - checkurl, FormatError, FilterOptionsDialog, \ - NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ - DatastructureError, createAction, getLogin, createCreationInfo, PickDlg -from pylot.core.util.thread import WorkerThread +from pylot.core.read.data import Data +from pylot.core.read.inputs import FilterOptions, AutoPickParameter +from pylot.core.pick.autopick import autopickevent +from pylot.core.util.defaults import FILTERDEFAULTS +from pylot.core.util.errors import FormatError, DatastructureError +from pylot.core.util.connection import checkurl +from pylot.core.util.utils import fnConstructor, createEvent, getLogin,\ + createCreationInfo +from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg,\ + MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg from pylot.core.util.structure import DATASTRUCTURE +from pylot.core.util.thread import WorkerThread +from pylot.core.util.version import get_git_version as _getVersionString import icons_rc # Version information @@ -586,6 +592,13 @@ class MainWindow(QMainWindow): def autoPick(self): list = QListWidget() + logDockWidget = QDockWidget("AutoPickLog", self) + logDockWidget.setObjectName("LogDockWidget") + logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea) + logDockWidget.setWidget(list) + logDockWidget.show() + logDockWidget.setFloating(False) + list.addItem('loading default values for local data ...') autopick_parameter = AutoPickParameter('autoPyLoT_local.in') list.addItem(str(autopick_parameter)) @@ -597,6 +610,8 @@ class MainWindow(QMainWindow): self.thread.message.connect(list.addItem) self.thread.start() + self.drawPicks() + def addPicks(self, station, picks): stat_picks = self.getPicksOnStation(station) diff --git a/autoPyLoT.py b/autoPyLoT.py index 2c816516..1b377f07 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -9,10 +9,10 @@ import glob import matplotlib.pyplot as plt from obspy.core import read from pylot.core.util import _getVersionString -from pylot.core.read import Data, AutoPickParameter -from pylot.core.pick.run_autopicking import run_autopicking +from pylot.core.read.data import Data +from pylot.core.read.inputs import AutoPickParameter from pylot.core.util.structure import DATASTRUCTURE -from pylot.core.pick.utils import wadaticheck, checkPonsets +from pylot.core.pick.autopick import autopickevent __version__ = _getVersionString() @@ -49,12 +49,6 @@ def autoPyLoT(inputfile): parameter = AutoPickParameter(inputfile) - # get some parameters for quality control from - # parameter input file (usually autoPyLoT.in). - wdttolerance = parameter.getParam('wdttolerance') - mdttolerance = parameter.getParam('mdttolerance') - iplot = parameter.getParam('iplot') - data = Data() # getting information on data structure @@ -74,43 +68,19 @@ def autoPyLoT(inputfile): datastructure.modifyFields(**dsfields) datastructure.setExpandFields(exf) - # multiple event processing + # multiple event processing # read each event in database datapath = datastructure.expandDataPath() if not parameter.hasParam('eventID'): for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) - print 'Working on event %s' %event + print 'Working on event %s' %event print data wfdat = data.getWFData() # all available streams ########################################################## - # !automated picking starts here! - procstats = [] - # initialize dictionary for onsets - picks = None - station = wfdat[0].stats.station - allonsets = {station: picks} - for i in range(len(wfdat)): - stationID = wfdat[i].stats.station - # check if station has already been processed - if stationID not in procstats: - procstats.append(stationID) - # find corresponding streams - statdat = wfdat.select(station=stationID) - ###################################################### - # get onset times and corresponding picking errors - picks = run_autopicking(statdat, parameter) - ###################################################### - # add station and corresponding onsets to dictionary - station = stationID - allonsets[station] = picks - - # quality control - # median check and jackknife on P-onset times - checkedonsetsjk = checkPonsets(allonsets, mdttolerance, iplot) - # check S-P times (Wadati) - checkedonsetwd = wadaticheck(checkedonsetsjk, wdttolerance, iplot) + # !automated picking starts here! + picks = autopickevent(wfdat, parameter) print '------------------------------------------' print '-----Finished event %s!-----' % event @@ -121,41 +91,16 @@ def autoPyLoT(inputfile): data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) print 'Working on event ', parameter.getParam('eventID') print data - + wfdat = data.getWFData() # all available streams ########################################################## - # !automated picking starts here! - procstats = [] - # initialize dictionary for onsets - picks = None - station = wfdat[0].stats.station - allonsets = {station: picks} - for i in range(len(wfdat)): - #for i in range(0,10): - stationID = wfdat[i].stats.station - #check if station has already been processed - if stationID not in procstats: - procstats.append(stationID) - # find corresponding streams - statdat = wfdat.select(station=stationID) - ###################################################### - # get onset times and corresponding picking parameters - picks = run_autopicking(statdat, parameter) - ###################################################### - # add station and corresponding onsets to dictionary - station = stationID - allonsets[station] = picks - - # quality control - # median check and jackknife on P-onset times - checkedonsetsjk = checkPonsets(allonsets, mdttolerance, iplot) - # check S-P times (Wadati) - checkedonsetswd = wadaticheck(checkedonsetsjk, wdttolerance, iplot) + # !automated picking starts here! + picks = autopickevent(wfdat, parameter) print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' - + print '####################################' print '************************************' print '*********autoPyLoT terminates*******' @@ -165,7 +110,7 @@ def autoPyLoT(inputfile): if __name__ == "__main__": # parse arguments parser = argparse.ArgumentParser( - description='''autoPyLoT automatically picks phase onset times using higher order statistics, + description='''autoPyLoT automatically picks phase onset times using higher order statistics, autoregressive prediction and AIC''') parser.add_argument('-i', '-I', '--inputfile', type=str, diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index f5e48a63..ae41a82e 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -16,11 +16,11 @@ The picks with the above described algorithms are assumed to be the most likely For each most likely pick the corresponding earliest and latest possible picks are calculated after Diehl & Kissling (2009). -:author: MAGS2 EP3 working group / Ludger Kueperkoch +:author: MAGS2 EP3 working group / Ludger Kueperkoch """ import numpy as np import matplotlib.pyplot as plt -from pylot.core.pick.utils import * +from pylot.core.pick.utils import getnoisewin, getsignalwin from pylot.core.pick.CharFuns import CharacteristicFunction import warnings @@ -28,7 +28,7 @@ class AutoPicking(object): ''' Superclass of different, automated picking algorithms applied on a CF determined using AIC, HOS, or AR prediction. - ''' + ''' warnings.simplefilter('ignore') @@ -84,9 +84,9 @@ class AutoPicking(object): PickWindow=self.getPickWindow(), aus=self.getaus(), Tsmooth=self.getTsmooth(), - Pick1=self.getpick1()) + Pick1=self.getpick1()) + - def getTSNR(self): return self.TSNR @@ -116,7 +116,7 @@ class AutoPicking(object): def getSNR(self): return self.SNR - + def getSlope(self): return self.slope @@ -140,12 +140,12 @@ class AICPicker(AutoPicking): ''' Method to derive the onset time of an arriving phase based on CF derived from AIC. In order to get an impression of the quality of this inital pick, - a quality assessment is applied based on SNR and slope determination derived from the CF, + a quality assessment is applied based on SNR and slope determination derived from the CF, from which the AIC has been calculated. ''' def calcPick(self): - + print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' self.Pick = None @@ -179,15 +179,15 @@ class AICPicker(AutoPicking): #find NaN's nn = np.isnan(diffcf) if len(nn) > 1: - diffcf[nn] = 0 + diffcf[nn] = 0 #taper CF to get rid off side maxima tap = np.hanning(len(diffcf)) diffcf = tap * diffcf * max(abs(aicsmooth)) icfmax = np.argmax(diffcf) - + #find minimum in AIC-CF front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): if aicsmooth[i - 1] >= aicsmooth[i]: self.Pick = self.Tcf[i] break @@ -198,7 +198,7 @@ class AICPicker(AutoPicking): if diffcf[i -1] >= diffcf[i]: self.Pick = self.Tcf[i] break - + # quality assessment using SNR and slope from CF if self.Pick is not None: # get noise window @@ -275,7 +275,7 @@ class AICPicker(AutoPicking): if self.Pick == None: print 'AICPicker: Could not find minimum, picking window too short?' - + class PragPicker(AutoPicking): ''' @@ -287,7 +287,7 @@ class PragPicker(AutoPicking): if self.getpick1() is not None: print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' - self.Pick = None + self.Pick = None self.SNR = None self.slope = None pickflag = 0 @@ -333,7 +333,7 @@ class PragPicker(AutoPicking): for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: + if cfpick1 >= self.cf[i]: pick_r = self.Tcf[i] self.Pick = pick_r flagpick_l = 1 @@ -344,7 +344,7 @@ class PragPicker(AutoPicking): for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: + if cfpick1 >= self.cf[i]: pick_l = self.Tcf[i] self.Pick = pick_l flagpick_r = 1 @@ -360,7 +360,7 @@ class PragPicker(AutoPicking): pickflag = 1 elif flagpick_l == 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: self.Pick = pick_l - pickflag = 1 + pickflag = 1 else: print 'PragPicker: Could not find reliable onset!' self.Pick = None @@ -372,7 +372,7 @@ class PragPicker(AutoPicking): p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') if pickflag > 0: p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) + plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) plt.yticks([]) plt.title(self.Data[0].stats.station) @@ -380,7 +380,7 @@ class PragPicker(AutoPicking): raw_input() plt.close(p) - else: + else: print 'PragPicker: No initial onset time given! Check input!' self.Pick = None return diff --git a/pylot/core/pick/run_autopicking.py b/pylot/core/pick/autopick.py similarity index 96% rename from pylot/core/pick/run_autopicking.py rename to pylot/core/pick/autopick.py index 27c7b411..8afda587 100755 --- a/pylot/core/pick/run_autopicking.py +++ b/pylot/core/pick/autopick.py @@ -11,9 +11,37 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np -from pylot.core.pick.Picker import * -from pylot.core.pick.CharFuns import * +from pylot.core.pick.Picker import AICPicker, PragPicker +from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf +from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ + getSNR, fmpicker, checkPonsets, wadaticheck +def autopickevent(data, param): + stations = [] + all_onsets = {} + + # get some parameters for quality control from + # parameter input file (usually autoPyLoT.in). + wdttolerance = param.getParam('wdttolerance') + mdttolerance = param.getParam('mdttolerance') + iplot = param.getParam('iplot') + + for n in range(len(data)): + station = data[n].stats.station + if station not in stations: + stations.append(station) + else: + continue + + for station in stations: + topick = data.select(station=station) + all_onsets[station] = run_autopicking(topick, param) + + # quality control + # median check and jackknife on P-onset times + jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, iplot) + # check S-P times (Wadati) + return wadaticheck(jk_checked_onsets, wdttolerance, iplot) def run_autopicking(wfstream, pickparam): """ diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 17bd3f4d..40845d7c 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -12,24 +12,9 @@ import numpy as np import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime -from pylot.core.pick.run_autopicking import run_autopicking import warnings import pdb -def autopickevent(data, param): - stations = [] - - for n in len(data): - station = data[n].stats.station - if station not in stations: - stations.append(station) - else: - continue - - for station in stations: - topick = data.select(station=station) - - stat_picks = run_autopicking(topick, param) def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' @@ -538,7 +523,7 @@ def wadaticheck(pickdic, dttolerance, iplot): # calculate vp/vs ratio after check cvpvsr = p2[0] + 1 print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr - print 'wadatacheck: Skipped %d S pick(s).' % ibad + print 'wadatacheck: Skipped %d S pick(s).' % ibad else: print '###############################################' print 'wadatacheck: Not enough checked S-P times available!' diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index a1918e18..8b137891 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -1,4 +1 @@ -from pylot.core.read.inputs import AutoPickParameter, FilterOptions -from pylot.core.read.io import readPILOTEvent -from pylot.core.read.data import GenericDataStructure, SeiscompDataStructure, \ - PilotDataStructure, Data + diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index bcf841d7..2ca981ed 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -7,9 +7,9 @@ from obspy.core import (read, Stream, UTCDateTime) from obspy import readEvents, read_inventory from obspy.core.event import (Event, Catalog) -from pylot.core.read import readPILOTEvent -from pylot.core.util import fnConstructor, FormatError, \ - getGlobalTimes +from pylot.core.read.io import readPILOTEvent +from pylot.core.util.utils import fnConstructor, getGlobalTimes +from pylot.core.util.errors import FormatError class Data(object): diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index bbe5b496..926e3ca3 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -7,9 +7,8 @@ import scipy.io as sio import obspy.core.event as ope from obspy.core import UTCDateTime -from pylot.core.util import getOwner, createPick, createArrival, createEvent, \ - createOrigin, createMagnitude - +from pylot.core.util.utils import getOwner, createPick, createArrival,\ + createEvent, createOrigin, createMagnitude def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): """ diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 07538df1..2d4a37e4 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1,10 +1 @@ -from pylot.core.util.connection import checkurl -from pylot.core.util.defaults import FILTERDEFAULTS -from pylot.core.util.errors import OptionsError, FormatError, DatastructureError -from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ - createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ - getHash, getLogin, createCreationInfo, createResourceID, prepTimeAxis, \ - getGlobalTimes, scaleWFData, demeanWFData -from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ - PropertiesDlg, NewEventDlg, MPLWidget, createAction from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/structure.py b/pylot/core/util/structure.py index 1b39dd4e..05a0c7f9 100644 --- a/pylot/core/util/structure.py +++ b/pylot/core/util/structure.py @@ -6,7 +6,7 @@ Created on Wed Jan 26 17:47:25 2015 @author: sebastianw """ -from pylot.core.read import SeiscompDataStructure, PilotDataStructure +from pylot.core.read.data import SeiscompDataStructure, PilotDataStructure DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure, None: None} diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index 4bf4b85c..69890119 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -17,7 +17,8 @@ class WorkerThread(QThread): picks = self.func(self.data, self.param) try: - self.parent().addPicks(picks) + for station in picks: + self.parent().addPicks(station, picks[station]) except AttributeError: print picks diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 9fddb15b..2d3b0e8b 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,7 +10,6 @@ import hashlib import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope -from pylot.core.pick.utils import getnoisewin def runProgram(cmd, parameter=None): """ @@ -119,28 +118,20 @@ def scaleWFData(data, factor=None, components='all'): return data -def demeanWFData(data, t0, win, gap): +def demeanTrace(trace, window): """ returns the DATA where each trace is demean by the average value within - a desired time window WIN before T0 and a GAP - :param data: waveform stream object - :type data: `~obspy.core.stream.Stream` - :param t0: time before which the noise - :type t0: float - :param win: time window for average calculation - :type win: tuple - :param gap: gap window to avoid polluting the average - :type gap: float - :return: data - :rtype: `~obspy.core.stream.Stream` + WINDOW + :param trace: waveform trace object + :type trace: `~obspy.core.stream.Trace` + :param inoise: range of indices of DATA within the WINDOW + :type window: tuple + :return: trace + :rtype: `~obspy.core.stream.Trace` """ - starttime = getGlobalTimes(data)[0] - for tr in data: - stime = tr.stats.starttime - starttime - t = prepTimeAxis(stime, tr) - inoise = getnoisewin(t, t0, win, gap) - tr.data -= tr.data[inoise].mean() - return data + trace.data -= trace.data[window].mean() + return trace + def getGlobalTimes(stream): min_start = UTCDateTime() diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 2796b6a5..e043f32a 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -19,12 +19,12 @@ from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime -from pylot.core.read import FilterOptions +from pylot.core.read.inputs import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ getResolutionWindow from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS -from pylot.core.util import prepTimeAxis, getGlobalTimes, scaleWFData, \ - demeanWFData +from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ + demeanTrace def createAction(parent, text, slot=None, shortcut=None, icon=None, @@ -191,6 +191,8 @@ class PickDlg(QDialog): else: self.data = data + self.stime, self.etime = getGlobalTimes(self.getWFData()) + # initialize plotting widget self.multicompfig = MPLWidget(self) @@ -336,6 +338,12 @@ class PickDlg(QDialog): self.cidrelease = self.connectReleaseEvent(self.panRelease) self.cidscroll = self.connectScrollEvent(self.scrollZoom) + def getStartTime(self): + return self.stime + + def getEndTime(self): + return self.etime + def getComponents(self): return self.components @@ -446,13 +454,16 @@ class PickDlg(QDialog): x_res = getResolutionWindow(snr) - data = demeanWFData(data=self.getWFData().copy(), t0=ini_pick, - win=noise_win, gap=gap_win) + # remove mean noise level from waveforms + for trace in wfdata: + t = prepTimeAxis(self.getStartTime(), trace) + inoise = getnoisewin(t, ini_pick, noise_win, gap_win) + trace = demeanTrace(trace=trace, window=inoise) self.setXLims([ini_pick - x_res, ini_pick + x_res]) self.setYLims(np.array([-noiselevel * 2.5, noiselevel * 2.5]) + trace_number) - self.getPlotWidget().plotWFData(wfdata=data, + self.getPlotWidget().plotWFData(wfdata=wfdata, title=self.getStation() + ' picking mode', zoomx=self.getXLims(), @@ -482,7 +493,10 @@ class PickDlg(QDialog): filteroptions = self.getFilterOptions(phase).parseFilterOptions() data.filter(**filteroptions) - data = demeanWFData(data=data, t0=ini_pick, win=noise_win, gap=gap_win) + for trace in data: + t = prepTimeAxis(self.getStartTime(), trace) + inoise = getnoisewin(t, ini_pick, noise_win, gap_win) + trace = demeanTrace(trace, inoise) horiz_comp = ('n', 'e') @@ -576,9 +590,9 @@ class PickDlg(QDialog): else: return - mpp = picks['mpp'] - epp = picks['epp'] - lpp = picks['lpp'] + mpp = picks['mpp'] - self.getStartTime() + epp = picks['epp'] - self.getStartTime() + lpp = picks['lpp'] - self.getStartTime() spe = picks['spe'] ax.fill_between([epp, lpp], ylims[0], ylims[1], From 707618c6b1e4a6d0d1aba6651ee1f0a35a6b0afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 10 Jul 2015 15:23:48 +0200 Subject: [PATCH 0495/1144] Found bug in fmpicker, copy-and-paste error. --- pylot/core/pick/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 40845d7c..807fda4c 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -231,7 +231,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) if imax2 == 0: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) - if imax1 == 0: + if imax2 == 0: print 'fmpicker: Zero crossings too close!' print 'Skip first motion determination!' return FM From 641cb498f0d5c98d967ca52a4fd2b392e24c7afa Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Jul 2015 06:36:33 +0200 Subject: [PATCH 0496/1144] make labeling the trace slightly more efficient --- QtPyLoT.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index de9165b7..1cd856c7 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -468,10 +468,9 @@ class MainWindow(QMainWindow): wfst = self.getData().getWFData().select(component=comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title) self.draw() - pos = self.getPlotWidget().getPlotDict().keys() - labels = [] - for i in range(0, len(wfst)): - labels.append(wfst[i].stats.station) + plotDict = self.getPlotWidget().getPlotDict() + pos = plotDict.keys() + labels = [plotDict[n][0] for n in pos] self.getPlotWidget().setYTickLabels(pos, labels) def plotZ(self): From d79165a9f202f331b3bf4be5740f57ce4b037ca8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Jul 2015 06:39:25 +0200 Subject: [PATCH 0497/1144] [bugfix] pick times are absolute times, thus for plotting the start time has to be subtracted --- QtPyLoT.py | 10 ++++++---- pylot/core/util/widgets.py | 13 ++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1cd856c7..df9241a9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -45,7 +45,7 @@ from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import FormatError, DatastructureError from pylot.core.util.connection import checkurl from pylot.core.util.utils import fnConstructor, createEvent, getLogin,\ - createCreationInfo + createCreationInfo, getGlobalTimes from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg,\ MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg from pylot.core.util.structure import DATASTRUCTURE @@ -659,9 +659,11 @@ class MainWindow(QMainWindow): picks = stat_picks[phase] colors = phase_col[phase[0].upper()] - mpp = picks['mpp'] - epp = picks['epp'] - lpp = picks['lpp'] + stime = getGlobalTimes(self.getData().getWFData())[0] + + mpp = picks['mpp'] - stime + epp = picks['epp'] - stime + lpp = picks['lpp'] - stime spe = picks['spe'] ax.fill_between([epp, lpp], ylims[0], ylims[1], diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e043f32a..e3b5b9a1 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -455,8 +455,9 @@ class PickDlg(QDialog): x_res = getResolutionWindow(snr) # remove mean noise level from waveforms + wfdata = self.getWFData().copy() for trace in wfdata: - t = prepTimeAxis(self.getStartTime(), trace) + t = prepTimeAxis(trace.stats.starttime - self.getStartTime(), trace) inoise = getnoisewin(t, ini_pick, noise_win, gap_win) trace = demeanTrace(trace=trace, window=inoise) @@ -494,7 +495,7 @@ class PickDlg(QDialog): data.filter(**filteroptions) for trace in data: - t = prepTimeAxis(self.getStartTime(), trace) + t = prepTimeAxis(trace.stats.starttime - self.getStartTime(), trace) inoise = getnoisewin(t, ini_pick, noise_win, gap_win) trace = demeanTrace(trace, inoise) @@ -530,14 +531,20 @@ class PickDlg(QDialog): channel = self.getChannelID(round(gui_event.ydata)) wfdata = self.getWFData().copy().select(channel=channel) + stime = self.getStartTime() # get earliest and latest possible pick and symmetric pick error [epp, lpp, spe] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) # get name of phase actually picked phase = self.selectPhase.currentText() + # return absolute time values for phases + epp = stime + epp + mpp = stime + pick + lpp = stime + lpp + # save pick times for actual phase - phasepicks = {'epp': epp, 'lpp': lpp, 'mpp': pick, 'spe': spe} + phasepicks = {'epp': epp, 'lpp': lpp, 'mpp': mpp, 'spe': spe} try: oldphasepick = self.picks[phase] From da22dcce90874af8536785037d2e6aa224f52f78 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Jul 2015 06:40:51 +0200 Subject: [PATCH 0498/1144] renaming run_autopicking to autopickstation --- pylot/core/pick/autopick.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 8afda587..31fc1da9 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -35,7 +35,7 @@ def autopickevent(data, param): for station in stations: topick = data.select(station=station) - all_onsets[station] = run_autopicking(topick, param) + all_onsets[station] = autopickstation(topick, param) # quality control # median check and jackknife on P-onset times @@ -43,7 +43,7 @@ def autopickevent(data, param): # check S-P times (Wadati) return wadaticheck(jk_checked_onsets, wdttolerance, iplot) -def run_autopicking(wfstream, pickparam): +def autopickstation(wfstream, pickparam): """ :param: wfstream :type: `~obspy.core.stream.Stream` @@ -144,7 +144,7 @@ def run_autopicking(wfstream, pickparam): if algoP == 'HOS' or algoP == 'ARZ' and zdat is not None: print '##########################################' - print 'run_autopicking: Working on P onset of station %s' % zdat[ + print 'autopickstation: Working on P onset of station %s' % zdat[ 0].stats.station print 'Filtering vertical trace ...' print zdat @@ -161,7 +161,7 @@ def run_autopicking(wfstream, pickparam): Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime Ldiff = Lwf - Lc if Ldiff < 0: - print 'run_autopicking: Cutting times are too large for actual ' \ + print 'autopickstation: Cutting times are too large for actual ' \ 'waveform!' print 'Use entire waveform instead!' pstart = 0 @@ -234,7 +234,7 @@ def run_autopicking(wfstream, pickparam): (aicpick.getSlope(), aicpick.getSNR()) print 'Go on with refined picking ...' # re-filter waveform with larger bandpass - print 'run_autopicking: re-filtering vertical trace ...' + print 'autopickstation: re-filtering vertical trace ...' z_copy = zdat.copy() tr_filt = zdat[0].copy() tr_filt.filter('bandpass', freqmin=bpz2[0], freqmax=bpz2[1], @@ -292,7 +292,7 @@ def run_autopicking(wfstream, pickparam): else: FM = 'N' - print 'run_autopicking: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ + print 'autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) Sflag = 1 @@ -302,7 +302,7 @@ def run_autopicking(wfstream, pickparam): Sflag = 0 else: - print 'run_autopicking: No vertical component data availabler!, ' \ + print 'autopickstation: No vertical component data availabler!, ' \ 'Skip station!' if edat is not None and ndat is not None and len(edat) > 0 and len( @@ -395,7 +395,7 @@ def run_autopicking(wfstream, pickparam): cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), round(aicarhpick.getpick() + Srecalcwin)] # re-filter waveform with larger bandpass - print 'run_autopicking: re-filtering horizontal traces...' + print 'autopickstation: re-filtering horizontal traces...' h_copy = hdat.copy() # filter and taper data if algoS == 'ARH': @@ -498,7 +498,7 @@ def run_autopicking(wfstream, pickparam): elif Serror > timeerrorsS[3]: Sweight = 4 - print 'run_autopicking: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( + print 'autopickstation: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( Sweight, SNRS, SNRSdB) else: @@ -507,7 +507,7 @@ def run_autopicking(wfstream, pickparam): 'AIC-Slope=', aicarhpick.getSlope() else: - print 'run_autopicking: No horizontal component data available or ' \ + print 'autopickstation: No horizontal component data available or ' \ 'bad P onset, skipping S picking!' ############################################################## From e31890d8fcbc07b0f0e31a7119f21b178cc05ecd Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Jul 2015 09:24:16 +0200 Subject: [PATCH 0499/1144] export and save picks to hard drive --- pylot/RELEASE-VERSION | 2 +- pylot/core/read/data.py | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 52c00f77..d034b5be 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -1abc-dirty +108d-dirty diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 2ca981ed..cd24eccd 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -3,9 +3,9 @@ import os -from obspy.core import (read, Stream, UTCDateTime) +from obspy.core import read, Stream, UTCDateTime from obspy import readEvents, read_inventory -from obspy.core.event import (Event, Catalog) +from obspy.core.event import Event, Catalog, ResourceIdentifier from pylot.core.read.io import readPILOTEvent from pylot.core.util.utils import fnConstructor, getGlobalTimes @@ -156,10 +156,20 @@ class Data(object): def getEvtData(self): return self.evtdata - def applyEVTData(self, data, type='pick'): + def applyEVTData(self, data, type='pick', authority_id='rub'): def applyPicks(picks): - pass + firstonset = None + for phase in picks: + onset = picks[phase]['epp'] + if firstonset is None or firstonset > onset: + firstonset = onset + + if 'smi:local' in self.getID(): + fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') + ID = ResourceIdentifier(fonset_str) + ID.convertIDToQuakeMLURI(authority_id=authority_id) + def applyArrivals(arrivals): pass def applyEvent(event): From 8802267628d11a0d35965f0c40d1f67277f977ce Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Jul 2015 10:03:43 +0200 Subject: [PATCH 0500/1144] fix zooming issue after S Pick is set --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e3b5b9a1..e87031d5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -324,7 +324,7 @@ class PickDlg(QDialog): self.updateCurrentLimits() if phase: if self.zoomAction.isChecked(): - self.zoomAction.toggle() + self.zoomAction.trigger() self.disconnectReleaseEvent() self.disconnectScrollEvent() self.disconnectMotionEvent() From 092852d7459df83ee4b945238a97fb2c0a43e846 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 13 Jul 2015 15:15:37 +0200 Subject: [PATCH 0501/1144] fix import error on debian machine --- pylot/core/util/widgets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e87031d5..496928d4 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -9,7 +9,10 @@ import datetime import numpy as np from matplotlib.figure import Figure -from matplotlib.backends.backend_qt4agg import FigureCanvas +try: + from matplotlib.backends.backend_qt4agg import FigureCanvas +except ImportError: + from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT from matplotlib.widgets import MultiCursor from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ From 49bf0ecd3cd140a36ce50705b15ed4b44c11078e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 14 Jul 2015 08:08:02 +0200 Subject: [PATCH 0502/1144] make the naming of the Thread self explaining --- pylot/core/util/thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index 69890119..6b0342f7 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -1,11 +1,11 @@ import sys from PySide.QtCore import QThread, Signal -class WorkerThread(QThread): +class AutoPickThread(QThread): message = Signal(str) def __init__(self, parent, func, data, param): - super(WorkerThread, self).__init__() + super(AutoPickThread, self).__init__() self.setParent(parent) self.func = func self.data = data From 1e58a86110dafc7d221389a8817081da3f9c353f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 14 Jul 2015 08:10:49 +0200 Subject: [PATCH 0503/1144] [bugfix] forgot to commit changed imports due to class naming changes in [49bf0ecd3cd140a36ce50705b15ed4b44c11078e] --- QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index df9241a9..cdf88629 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -49,7 +49,7 @@ from pylot.core.util.utils import fnConstructor, createEvent, getLogin,\ from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg,\ MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg from pylot.core.util.structure import DATASTRUCTURE -from pylot.core.util.thread import WorkerThread +from pylot.core.util.thread import AutoPickThread from pylot.core.util.version import get_git_version as _getVersionString import icons_rc @@ -604,7 +604,7 @@ class MainWindow(QMainWindow): list.addItem(str(autopick_parameter)) # Create the worker thread and run it - self.thread = WorkerThread(parent=self, + self.thread = AutoPickThread(parent=self, func=autopickevent, data=self.getData().getWFData(), param=autopick_parameter) From 2115864d5c33e86e9f968efe1070f25afb961573 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 14 Jul 2015 08:32:05 +0200 Subject: [PATCH 0504/1144] revert RELEASE-VERSION manually --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index d034b5be..52c00f77 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -108d-dirty +1abc-dirty From 17933c75f0ee8a4e220e1b0fc7bf78906cb5dcff Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 18 Jul 2015 15:59:42 +0200 Subject: [PATCH 0505/1144] file format for exporting event data is controlled by the extension used; this behavior is more convenient for a GUI driven file selection --- pylot/core/util/defaults.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index 1fde1c3a..b2bf2a6d 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -13,6 +13,6 @@ FILTERDEFAULTS = {'P': {'filtertype': None, 'order': 4, 'freq': [.5, 5]}} -OUTPUTFORMATS = {'QuakeML':'QUAKEML', - 'VelEst':'CNV', - 'NonLinLoc':'NLLOC_OBS'} +OUTPUTFORMATS = {'.xml':'QUAKEML', + '.cnv':'CNV', + '.obs':'NLLOC_OBS'} From fc86179c39950b09ccf0572075be4a9cc964cba6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 18 Jul 2015 16:09:50 +0200 Subject: [PATCH 0506/1144] [closes #145], [addresses #146] this commit introduces the handling of picks as obspy event objects --- pylot/core/read/data.py | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index cd24eccd..fcd33226 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -5,7 +5,7 @@ import os from obspy.core import read, Stream, UTCDateTime from obspy import readEvents, read_inventory -from obspy.core.event import Event, Catalog, ResourceIdentifier +from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID from pylot.core.read.io import readPILOTEvent from pylot.core.util.utils import fnConstructor, getGlobalTimes @@ -46,6 +46,7 @@ class Data(object): else: # create an empty Event object self.newevent = True self.evtdata = Event() + self.getEvtData().picks = [] self.wforiginal = None self.cuttimes = None self.dirty = False @@ -160,27 +161,47 @@ class Data(object): def applyPicks(picks): firstonset = None - for phase in picks: - onset = picks[phase]['epp'] - if firstonset is None or firstonset > onset: - firstonset = onset + for station, onsets in picks.items(): + print 'Reading picks on station %s' % station + for label, phase in onsets.items(): + onset = phase['mpp'] + epp = phase['epp'] + lpp = phase['lpp'] + error = phase['spe'] + pick = Pick() + pick.time = onset + pick.time_errors.lower_uncertainty = onset - epp + pick.time_errors.upper_uncertainty = lpp - onset + pick.time_errors.uncertainty = error + pick.phase_hint = label + pick.waveform_id = WaveformStreamID(station_code=station) + self.getEvtData().picks.append(pick) + try: + polarity = phase['fm'] + except KeyError, e: + print 'No polarity information found for %s' % phase + if firstonset is None or firstonset > onset: + firstonset = onset if 'smi:local' in self.getID(): fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') - ID = ResourceIdentifier(fonset_str) + ID = ResourceIdentifier('event/' + fonset_str) ID.convertIDToQuakeMLURI(authority_id=authority_id) + self.getEvtData().resource_id = ID def applyArrivals(arrivals): pass + def applyEvent(event): pass - applydata = {'pick':applyPicks, - 'arrival':applyArrivals, - 'event':applyEvent} + applydata = {'pick': applyPicks, + 'arrival': applyArrivals, + 'event': applyEvent} applydata[type](data) + class GenericDataStructure(object): ''' GenericDataBase type holds all information about the current data- From f4f744e22cb184511ee806ce94fd28b6d6ceafe0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 18 Jul 2015 16:11:20 +0200 Subject: [PATCH 0507/1144] export and save picks implemented successfully --- QtPyLoT.py | 35 ++++++++++++++++++++++++++--------- pylot/core/read/data.py | 28 ++++++++++++---------------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index df9241a9..4cabe758 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -23,7 +23,7 @@ https://www.iconfinder.com/iconsets/flavour (http://www.gnu.org/copyleft/lesser.html) """ -import sys +import os, sys import matplotlib matplotlib.use('Qt4Agg') @@ -64,7 +64,6 @@ class MainWindow(QMainWindow): super(MainWindow, self).__init__(parent) self.createAction = createAction - self.dirty = False settings = QSettings() if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") @@ -102,8 +101,8 @@ class MainWindow(QMainWindow): self.data = Data(self) # load and display waveform data - self.loadWaveformData() self.dirty = False + self.loadWaveformData() self.loadData() self.updateFilterOptions() @@ -333,6 +332,10 @@ class MainWindow(QMainWindow): self.fileMenu.addSeparator() self.fileMenu.addAction(self.fileMenuActions[-1]) + def getRoot(self): + settings = QSettings() + return settings.value("data/dataRoot") + def loadData(self, fname=None): if fname is None: try: @@ -390,18 +393,27 @@ class MainWindow(QMainWindow): else: return + def getEventFileName(self): + return self.getData().getEventFileName() + def saveData(self): settings = QSettings() exform = settings.value('data/exportFormat', 'QUAKEML') + self.getData().applyEVTData(self.getPicks()) try: - self.data.exportEvent(self.fname, exform) + self.getData().exportEvent(self.fname, exform) except FormatError: return False except AttributeError, e: print 'warning: {0}'.format(e) - fname = QFileDialog.getSaveFileName(self, 'Save event') - fname = fname[0] - self.getData().exportEvent(fname, exform) + directory = os.path.join(self.getRoot(), self.getEventFileName()) + file_filter = "Seismic observation files (*.cnv *.obs *.xml)" + fname = QFileDialog.getSaveFileName(self, 'Save event data ...', + directory, file_filter) + fbasename, exform = os.path.splitext(fname[0]) + if not fbasename: + return False + self.getData().exportEvent(fbasename, exform) return True def getComponent(self): @@ -455,7 +467,7 @@ class MainWindow(QMainWindow): def loadWaveformData(self): if self.fnames and self.okToContinue(): - self.dirty = True + self.setDirty(True) self.data.setWFData(self.fnames) elif self.fnames is None and self.okToContinue(): self.data.setWFData(self.getWFFnames()) @@ -585,6 +597,7 @@ class MainWindow(QMainWindow): station=station, picks=self.getPicksOnStation(station)) if pickDlg.exec_(): + self.setDirty(True) self.updateStatus('picks accepted ({0})'.format(station)) self.addPicks(station, pickDlg.getPicks()) self.drawPicks(station) @@ -593,6 +606,7 @@ class MainWindow(QMainWindow): def autoPick(self): list = QListWidget() + self.setDirty(True) logDockWidget = QDockWidget("AutoPickLog", self) logDockWidget.setObjectName("LogDockWidget") logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea) @@ -700,11 +714,14 @@ class MainWindow(QMainWindow): cinfo = createCreationInfo(agency_id=self.agency) event = createEvent(evtpar['origintime'], cinfo) self.data = Data(self, evtdata=event) - self.dirty = True + self.setDirty(True) def draw(self): self.getPlotWidget().draw() + def setDirty(self, value): + self.dirty = value + def closeEvent(self, event): if self.okToContinue(): self.closing.emit() diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index fcd33226..9cf2c8d7 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -68,29 +68,25 @@ class Data(object): def updateCutTimes(self): self.cuttimes = getGlobalTimes(self.getWFData()) - def exportEvent(self, fnout=None, evtformat='QUAKEML'): + def getEventFileName(self): + ID = self.getID() + # handle forbidden filenames especially on windows systems + return fnConstructor(str(ID)) + + def exportEvent(self, fnout, fnext='.xml'): from pylot.core.util.defaults import OUTPUTFORMATS - if evtformat.strip() not in OUTPUTFORMATS.values(): - errmsg = 'selected format {0} not available'.format(evtformat) + try: + evtformat = OUTPUTFORMATS[fnext] + except KeyError, e: + errmsg = '{0}; selected file extension {1} not ' \ + 'supported'.format(e, fnext) raise FormatError(errmsg) - if fnout is None: - ID = self.getID() - # handle forbidden filenames especially on windows systems - fnout = fnConstructor(str(ID)) - else: - fnout = fnConstructor(str(fnout)) - - evtformat = evtformat.upper().strip() - - # establish catalog object (event object has no write method) - cat = Catalog() - cat.append(self.getEvtData()) # try exporting event via ObsPy try: - cat.write(fnout + evtformat.lower(), format=evtformat) + self.getEvtData().write(fnout + fnext, format=evtformat) except KeyError, e: raise KeyError('''{0} export format not implemented: {1}'''.format(evtformat, e)) From 0b6fbd22c5dd95ca48ec5f3ba9830e354cc39c61 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 18 Jul 2015 16:13:11 +0200 Subject: [PATCH 0508/1144] reformatting code to meet coding conventions --- pylot/core/read/data.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 9cf2c8d7..dad3bf56 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -116,7 +116,7 @@ class Data(object): assert isinstance(fnames, list), "input parameter 'fnames' is " \ "supposed to be of type 'list' " \ "but is actually {0}".format(type( - fnames)) + fnames)) if self.dirty: self.resetWFData() @@ -147,7 +147,7 @@ class Data(object): st = self.getWFData() inv = read_inventory(fninventory) st.attach_response(inv) - pre_filt = (0.005, 0.006, 30.0, 35.0) # set in autoPyLoT.in + pre_filt = (0.005, 0.006, 30.0, 35.0) # set in autoPyLoT.in st.remove_response(output='VEL', pre_filt=pre_filt) def getEvtData(self): @@ -307,14 +307,13 @@ class PilotDataStructure(GenericDataStructure): ''' def __init__(self, **fields): - if not fields: - fields = {'database':'2006.01', - 'root':'/data/Egelados/EVENT_DATA/LOCAL'} + fields = {'database': '2006.01', + 'root': '/data/Egelados/EVENT_DATA/LOCAL'} GenericDataStructure.__init__(self, **fields) - self.setExpandFields(['root','database']) + self.setExpandFields(['root', 'database']) class SeiscompDataStructure(GenericDataStructure): @@ -331,7 +330,6 @@ class SeiscompDataStructure(GenericDataStructure): filesuffix=None, **kwargs): super(GenericDataStructure, self).__init__() - edate = UTCDateTime() halfyear = UTCDateTime('1970-07-01') sdate = UTCDateTime(edate - halfyear) @@ -351,9 +349,9 @@ class SeiscompDataStructure(GenericDataStructure): # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS self.dsFields = {'root': '/data/SDS', 'YEAR': year, 'NET': '??', - 'STA': '????', 'CHAN': 'HH?', 'TYPE': 'D', 'LOC': '', - 'DAY': '{0:03d}'.format(sdate.julday) - } + 'STA': '????', 'CHAN': 'HH?', 'TYPE': 'D', 'LOC': '', + 'DAY': '{0:03d}'.format(sdate.julday) + } self.modifiyFields(**kwargs) def modifiyFields(self, **kwargs): From 0e3576d1934b60ab8ef0ae97169cd566b2f03018 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 21 Jul 2015 07:41:41 +0200 Subject: [PATCH 0509/1144] [closes #157] filter defaults are now read from text file like the sample filter.in coming with this commit simply copy to .pylot folder in your home directory (this is preliminary because the filter parameters should be settable project wide by one responsible person) --- filter.in | 2 ++ pylot/core/util/defaults.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 filter.in diff --git a/filter.in b/filter.in new file mode 100644 index 00000000..16aed0b7 --- /dev/null +++ b/filter.in @@ -0,0 +1,2 @@ +P +S bandpass 4 0.5 5.0 \ No newline at end of file diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index b2bf2a6d..c22ad865 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -6,12 +6,37 @@ Created on Wed Feb 26 12:31:25 2014 @author: sebastianw """ -FILTERDEFAULTS = {'P': {'filtertype': None, - 'order': None, - 'freq': None}, - 'S': {'filtertype': 'bandpass', - 'order': 4, - 'freq': [.5, 5]}} +import os + +def readFilterInformation(fname): + def convert2FreqRange(*args): + if len(args) > 1: + return [float(arg) for arg in args] + elif len(args) == 1: + return float(args[0]) + return None + filter_file = open(fname, 'r') + filter_information = dict() + for filter_line in filter_file.readlines(): + filter_line = filter_line.split(' ') + for n, pos in enumerate(filter_line): + if pos == '\n': + filter_line[n] = '' + filter_information[filter_line[0]] = {'filtertype': filter_line[1] + if filter_line[1] + else None, + 'order': int(filter_line[2]) + if filter_line[1] + else None, + 'freq': convert2FreqRange(*filter_line[3:]) + if filter_line[1] + else None} + return filter_information + + +FILTERDEFAULTS = readFilterInformation(os.path.join(os.path.expanduser('~'), + '.pylot', + 'filter.in')) OUTPUTFORMATS = {'.xml':'QUAKEML', '.cnv':'CNV', From d9cb3517cc4403b946c0625527ef0805f5535692 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 21 Jul 2015 08:10:15 +0200 Subject: [PATCH 0510/1144] [closes #160] the picking window has now a home button for easy reset of zoom --- icons.qrc | 1 + icons/zoom_0.png | Bin 0 -> 7081 bytes icons_rc.py | 8 ++++---- pylot/core/util/widgets.py | 12 ++++++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) create mode 100755 icons/zoom_0.png diff --git a/icons.qrc b/icons.qrc index 442d4f29..0b815788 100644 --- a/icons.qrc +++ b/icons.qrc @@ -16,6 +16,7 @@ icons/key_Z.png icons/filter.png icons/sync.png + icons/zoom_0.png icons/zoom_in.png icons/zoom_out.png splash/splash.png diff --git a/icons/zoom_0.png b/icons/zoom_0.png new file mode 100755 index 0000000000000000000000000000000000000000..e1617bc98116c9a2db38276e199aedfd3cc92a6d GIT binary patch literal 7081 zcmeI1S5#Bmx5fjZH)$e>2Vzt}APK!AgeJX<2m%QyfOH5ZpmYR5L7McAqJZ=&y;lV+ zh*AtirAY6c8_WM(@5?>oKHRsn_sH0D?fuPf&ivL&#u{UX8|q(Wq&rIo000=ZG}Vkr zSCJnd8Y%#Q3`E(>LK<-4)Xi}wu2>u%>45>j(XO@_kd`yj4r7c#qP^XFF$w?x-IRl= zInG>9R}SUs3`PFXKnc!nq-+2{L5bjoL^)z`AX|)`gNq_~rM?*qazHDBuS@EQ=((w4 z>>V_HJTN9c`lcu!N0clYtaOD=fgndRaK_+}AcC`#i>Dkx5&Y9vjuij64FiLIN^p*f z;9pId>luPnT|F=$38*LpB_b{bl8}OmNlQtKi3o%ATwOpK7+a92EJ#d5L>eR}CMPN` zCm{~{eSojfffPK@SUF=gjo%eXBSo-14(BEZgW>UbC|(@u>R|^Hm6er+iHO0(#2_RE z#M9daha^B;JTLsP_~oI7@kDtzxZxZ~MSplAZC$-^ieND5Iq0`J8uhogo0o^vPiHg= zhH=6;V_a~aFj1%|>`y%b>1OO;XOHvzJI%%OH)s-a80j%7MFi&YQ}ZkH?-V&bq=zGh z1iUT(KVO%0<-0(D%m08O3$pbF88|uI#(02CNt29r#d|tB{4D<87S}PhCO9PSXLo+M`{A?fWl$WI9HS_7K_0^(U_kkKniG-+^>FqfccFE?u7d_`bEmg;Rg>nC!~v= zBA5U{W3WgsCmi_7|D{s}*bl}|4!^7X9Q~(^0_=Y@@k{#0^pJ9X-6KsqY0<&{UUk2l z{Das3NBEP=|A+CPp8pp4lg2;B|Kj>bM1KnY#q}qRe~kad^^b`D6#R?pPa6Li|BLG% z5&bFn-*D0W_r4Y5LfSjwNxM?50*OfgfZ1A04Q@&pwLLsJQ36V}P=G&_*qr#UM-jVb zp%V-t{QqXNdi=XkgEhz0-)r6HOK%dTs6rL+RH2;Q8fD@&kKnj~ z)e8K{Cpga7_^p3wqX;dUA(ny$q|s2Uoq7CuchZY$=BZ91PpFoNj*iZW0aI1RwJTff z!9h{|#bf-}KYyZzr+2K8%dd7z=4~26!{cE4+Bod1G~dY7u#?~jEyF9{M;yCOtq?Q(rpGBmb?o$yeD~Hd z51X3&j&sx>1eH?Bicy6I2?@{zY+~YN+E}<%lwXi{RMXMXDJokqfCAjQ{(`oUqc#na zbD^)gZ_f;7DlueU@wF8Y5I}U~kvX+LOs-&RoT*$m=_**&G>8d<8$<<8^_5B5si>%M zmW{T+1P0dAEstV~#Ds!VE&_uAXMn10SG3|_HXMgqnwp5#`(Z>@>%C&17YupY3=I6m zO~}>nM}w2IF0WU2R&1~cg&tnN(xsCo9m^BoCt<|I`l~aMRTy3U`h+yZkYp)hRTPad zj6)!5PY$Kqw}pgw**o92#180`@>aVNkDI?I#e_Eo$^?^(+_2Xc6>&Rx$zOBlpo@CM zKjUM8W04^3+tU2`FQ%o885tQ54KfGzU>#2r^NY~9?X^fCTc+PWdT)B^PUxqiLi6C@ zU}^uu-L>WAiF!}N>8UnVDl=yXs@rB9p)gQWb0)7ywz!yr?|%Hrt7P%a$&64kpm!)y zq!U!?{*F%rA%=)fAkWkOV%SEloD!fF-#SQKQPS2Wts5%{zXW70_$GBniEDm=6+75}HoU%o-u^~=`V6A&4g3&X)z;Hl&kgVsd6+8Rn2nsu+csyM{nR7tAHV{Eu zE~Y7NC4RiK2OqB~$rL_0I>29sK=d*%(%1R$b86|_&+uJ0P@4N{vv;-a?hA%kl|=?t z@`uW=T0m{UO(o_HHxZFV1rNPt;%wM=>qZ|8Coivm;PdhJ?$_?y)b+-E%>myoFv77^ z?pLU&+!6NcWba-wO9R`xOPaaL51?WSy8<0qc?DZNH&-qBW-{$&;ZY%7w1%gFB8_6D zz%@&dA0zHisg_c>#k4rj*~3gXfNRmhkZON@=2ZK1(RD5kp54~07Y+kiIks(C^z5_L zXD{{T`fqO?&?ABa)wc?P1|#n0>ko;iG(xARr*EO@kNx}`{Bz1v99;$r4jAP1n{1;^ z%K!;U`wZ}weKm@?3rLsyCCYHGjaS>@N`8mC^nE_FvNdH^GW{*3IXbEWK z&}V}g89=&JAY@%14HI3ae6K{(Q|g- zX%po~fHGjTP%^JbjG;MA)nLqX6n@9YPSI)zqTwbp8?cIcPUcGUreh5ydcL+J$R%8; zcN)jnB^r*5=-;g<<%Hc3>*$z5%XKqkVf0O^Z@`ZV)Rx&CD^;IDc2V5Ph3PTLTlUd67KZkVa)n z)8GNh|LBB@utP}f?C|u@AHKEPDTRwVfcT2ObVpvO=@Ghq^8AKX#)MAG>P>bDDJlI4 z4&|#?sdGoZt9n$;>#hvPn&oORg&65sP?}3z*0{vJHnua%w0G3>tnoN0$6;`J2y%x- z0tuHb+*_O81QrDalChNle66A8M!B0ks%N2{>iY})7U@JL4#W3%Cy7L2X;Ag{&I_z> z)23%-@%t>yOq~Lkx%b)41@_*U$q=i?V*v$!H0RxysR9Qd`jvzUiC%iJ#T&H_lnkep z^mn`Z3a2%4tUt2GN@j9!-D8-1Kf*&eIaC;`qpP*)iR`v*XJ#x(YZ=V!M1tPkPoN;& z)f~f$tSM6kl1m;fgaJs?Ts+YbZ1`HY>**pMh+exnJ zI6FJrX=gT+XgKr)&Iz`3tQs>Di4Q=wtN|4KIz!n{$D`@8y6n*7)KRF!^94l#>*WxH z3Zw^R!k(dQ7X+XUYj@9jah!2hng zr~JZ>S*UO-r`avtX*hc-BQH*^UF_j{X?XMIIBOqm%lEAn^uFKj498GH&;7RKSU{R~ z)uX(5IS6-dv3}Uh)%tD(I`m#_PJQf>wtloArE<`$5bC`^2K9UDgO(QMOrtYGnO4^F z9K@S@tgJ?$c*tN{FzKIHphWx-H);AjnRqDRRF%j6@OjGlblGG9@;Q$Tc|}=SBm0`M z`Gtjrfi|j6oz7~>GoI==RomF~Wx3tn>R0C~1S5U?T=;5FM7Oe)F56!d`aq5?Xd&8I zsoFJuR^JGg*UoLwt;-ad~_r`LGWkEA!y{^PI+w3zkg^68g&~ z9#v%~DCmNC3WQ!47iE*S^4=A-rduUE^dl+8e# zFQV1f&UkDvQ*gVT!?VCM`pyEX(UfpObqCv63GN+A^C-ns$pDESv2B|q=1ayjA751G zcu9MwJHGMJNoax|Z3xheJ;}GgR447t=g*%%RJvE@iyyB(gec?9c(~QMI*9{|r1RCX z)8(gQ9GYa8*|lmzlmO1QhB`uFQd?%r&(9sH+DfD-t4W;Wi04oN(M%c2ngrcEPodp* zz6VqGl)Vr0i6_3XPJYZ8KeF?!C1^MxLTdTj*VM(u#bdvX#vR{gV(Qgn>PP!qLloa& zH;C%xA)e+Y5|5C{E^FozT+^c0B{)htm!9$LWO2W#b*S$JFL^g3w2%Dt5iV1B;|Rsd zp{|N|GEo)L5?LbajAVuVpn0Wo{w~e+hvb7bSzvPvUPdWt^~3j8|43ycBcoV7W`p}d zMf`$d<4^CIWMJJ#rcxWX-Our|5vZIvbA}mWBZu4i3iwhe*atO|M+@FMJ)Iv&`jGG~ zTaDqQP>#bc)F5RSJ8|KXVZ+@{F)em1Y4$`bZhGWp+pR5W^26-Tw>)I}$e^YIbTP(6 z1#DY(Fw5vYe7n$(N2+-T&MZ4paYrM#j?v6=QtpH z(xNyzfn@>FgURtU!qGgSfZ8{K9ga)%+(BQ3&)U=Vm22uS^cDD);v6Lv#teGh!}T_v zdqt$$^oG!~S?^w@6}G-9qZ%7xOU^qR_dv)ptfYdTqm&-|7!b&p#grD4XUb>lM^_LjHqqTJNp`*yaM_D8P_1t_wk*Sj|7S6uexDVE)t zu;Svum9+Vj(M3DUJhAr|MxK!KpBAnr(`mhcpf8Lhi+`syl@6^Rr{UhY9?U-t_#8u( z9+wfof7M=Gy@D0tvwT})Ts)u8wrs9j{a`Bf#$e#+z{RCUfiV6f%S)B_%KEBnaOq+_`Vze-+Sb$4an38k^KhX$NUh>h8V%?f}o#H{hiD@sVoX3|Q@5hfHA282IC|tw4QYRr{)8S3Hw3a$HtMCbKhSGR3 zT+QW@lHr=R&umq0MN$;c`dtIt>RMtHn0+qVkA8ek=9tP=vC!NqxZE*zmJqQX7gpm1 z6&4=fBbezv>4NDBmO=Nh8C ziiIi;ASSL@3$uF5WTs%31<-n#FTz`XjPGGwZ%|FK2iycA*%~5ZhUs1`w%f|S>B|^98zZ5 z)7x7t`#Pag(Q(H*EY+IlZmWRXwuIXy{(NXOHY`D3ABOe1^fu8D*Z>c?Qg7@h{A}Q; zUqOtcmTpN{K!DTW(e?H9^$4p^i!>ThqqN<}l+VU4t&WGOR=$4MO?7KW{gDf?$roFH z(2@YLG|Tx!u=KTH7foC&T(B4y-rkAz^_bg2yiktkbAB);;~JeisK_X7Ja9;`%}&Pb zzhS*)tNUiAJ8JN1@*bsJ&Tar1unUdFHlhBS;^nBRI&|pod14;FYSi5u5fSltF75Ww zlzxNlmPX=h)}ug-?!-0M0ds?@6#1p~u?jbSEUIf1Ha9t}r_0}(Pw{3VA@em8Won*x z<7bzMoPa^zfC*|Q72?CstJ;1%5>gU`RjA+XxuI;yYuB!Mn3;WuCmqj{x>Lj~I-}WV zQm={WikBn>tC%d7<9e5F^OAPrR>8a@m5v9?{t$~QA~N!(6-8N(=?lgL?7hlpBy}Vx zR)a0bFK&}r{UU4lG%JA_(_5Nv!Y4nNXch3<60fZORlPLH)~&vn$5|&@?gej*(y(OR z6Y7$7(AAGm!c?5Mo1bXl(f6bNp0T|DpUeK{; zBF3eAD1CwgyFG^{aO46h>p@Cg77A_XZ%#QY^f#WZIcJ%*W9G_4)k;T8p@= z4}x~{(?HMqnD*uwxuxQjOc@mrnP1^NA4}`jt17u7MF!qv()v8o7&?%|77q7?wIg9X lcSM%w;CJAY{%_3oPU|yrX;uOIAN+WErlqd0R;+>u`Y*5a6#)PM literal 0 HcmV?d00001 diff --git a/icons_rc.py b/icons_rc.py index 33603df2..80ed858a 100644 --- a/icons_rc.py +++ b/icons_rc.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Do. Juli 9 09:55:07 2015 +# Created: Di. Juli 21 07:58:08 2015 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x9e\x04\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x80\x00\x00\x01\x00\x08\x06\x00\x00\x00#\x95\xc7f\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x17\x12\x00\x00\x17\x12\x01g\x9f\xd2R\x00\x00\x00\x07tIME\x07\xdf\x07\x07\x090\x11\xbal\xcd}\x00\x00 \x00IDATx\xda\xec}w\x94\x5c\xe5y\xfe3w\xee\xf4\x99\x9d\xb2\xb3;\xdb\x9bV\xbb*+\xd1-\x0c\xa2\x83\xb0\x0d\x1cSbL\xe28v0v|lp\x0eq\x09\x04\x88c\x1cl\x9c\xe08\x80K\x82c\xe3\xb8!sL\x5c\xe4P\x02\x18\x04\x085TWm{/\xb3\xd3{\xbf\xf3\xfbC\xbf\xf7\xf3\x9d\xbb\xb3\xb3\xbb\xd2\xae\xb4\xda\xfd\x9est\xa4\x1d\xcd\xcc\xce|\xed}\xbe\xe7m\xaa|>\x9f\x07\x07\x07\x07\x07\x07\xc79\x8c|>\x8fT*\x85\xed\xdb\xb7#\x95J!\x97\xcb\xe1\xb6\xdbn\x83$I\x10\x04\x81\x0f\x10\x07\x87\x02|Wppppp\x9c\xf3P\xa9T\xc8\xe7\xf3\xc8\xe7\xf3P\xa9T\xc8\xe5r|P888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0\x0e-\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0* \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqppp\x02\xc8\xc1\xc1\xc1\xc1\xc1I \x07\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqpp\x02\xc8\xc1\xc1\xc1\xc1\xc1\xc1\x09 \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x9c\x00rppppp,%P!h\x0e\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\xe3\x9c\x07w\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xc1\x01\x95J\xc5\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\xe5\x0aI\x92\xb8\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7J\x05W\xfe888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83c\xb9A\xe9\xfe\x95$\x89\x0f\x0a\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\xcb\x9d\x00\xcaI\x1f\x8f\x07\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x10@\xe5\xcf<\x16\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\x02\xb9\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a \x7fr\xc5\x8f\x13@\x0e\x0eN\x0098888V\x08\x09\xe4\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\xfe\xe4\xa4\x8fg\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x08 A\xa5Rq\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95F\x029\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x91?N\x00988\x01\xe4\xe0\xe0\xe0\xe0X!\x04\x90\xc7\x00rpp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x09 \x95\x82\xe1\x0a \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0cR\x9e+\x80\x1c\x1c\x9c\x00rpppp\xac(p\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15F\xfc\xf2\x12'\x80\x1c\x1c\x9c\x00rpppp\xacH\x22\xc8\xc1\xc1\xc1\x09 \x07\x07\x07\x07\x07'\x80\x1c\x1c\x9c\x00\xf2!\xe0\xe0\xe0\xe0\xe0Xn\xe0\x9d@888\x01\xe4\xe0\xe0\xe0\xe0X\x81\xe0\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x86\x5c.\xc7\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eN\x0098888\x96-x+8\x0e\x0eN\x0098888V\x18x\x0c \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0c\x5c\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x85\x81+\x80\x1c\x1c\x9c\x00rpppp\xac \xf0:\x80\x1c\x1c\x9c\x00rpppp\xac@p\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a\x03\x8f\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\x13@\x0e\x0e\x0eN\x0098888\x963\xb8\x0b\x98\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x04\x95J\x85L&\xc3\x07\x82\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95\x04\xde\x0b\x98\x83cf\x88\x8b\xf5\xc6$\xbds\x09~\xf1o\xb9\xf2\xbf\xf3\xf9<\xfb7\x07\xc7b\xedm\xf9:+\xb5\xc7\xe9y\xf4\xb7r\xbdrpp\xfc\x09\x14\xb3\xc8K\xd8,\xdf\xb3S~n\xe6\xf3y\xa8\xd5\xeai\xf6\xfc\x9c$\x80\xf9|\x1e\xd9l\x16*\x95\x0a\x92$A\x92\xa4i\xc6\x82c\xe1\x17\x92\xdc\xb0\x0a\x82P\xf0X\xb1\xe7pp\x9c*\xe9\xa3}M\xc6\xaa\xd4\xfe\xce\xe7\xf3\xc8\xe5r\x10\x04\x81\xad?A\x10\xa0V\xab\xa1R\xa9\x0a\x0e>\x0e\x8e\x85\x14\x1e\x94\x84j\xa9\x93>I\x92\x98ZI{\x8a\xdb\xcd\xe5\xb76%I\x82J\xa5*8\x17\xe9L\x14\x04\x81\xfd9\xe7\x08`6\x9bE6\x9b\x85$I\xc8d2H\xa5R\xecg2\x14\x1c\x0b\x07Z\xb8$IB:\x9d\x86$Il\x7fg\xb3Y\xf6\xff\xc5@\x97\xc1|>\x0f\xadV\x0b\xbd^\x0f\xbd^\x0f\xadV\x0b\x00\x9c\x04r,\x0a\x01\xa4\xbf\x97\xba\x0b8\x9b\xcd\x02\x002\x99\x0c\xb2\xd9,2\x99\x0c\xd2\xe94r\xb9\x5c\xc1e\x8bcy\x81\xe6W\xa3\xd1@\x14E\x98\xcdfh\xb5Zh\xb5\xda3J\xfc\xc5\x85\xfa2\xf4'\x1e\x8f#\x1c\x0e#\x1c\x0e#\x16\x8b!\x99L\x22\x97\xcbq\x02\xb8\xc0 \x05EN\xfch1i\xb5Zh4\x1ah4\x1ah\xb5Z\xf6\x98(\x8aP\xab\xd5\x10E\x11*\x95\x0a\x1a\x8d\xa6@1\xe4\xe0(\xa5R\xa4\xd3i\xa4\xd3i\xb6\xbf#\x91\x08\x92\xc9$\xbb\xe8\x15\x83Z\xadfF\xd8h4\xc2n\xb7\xa3\xbc\xbc\x1cv\xbb\x1d\x1a\x8d\x86\x0f,\xc7\xa2\x13\xac\x5c.\xb7\xa4/\x1a\x99L\x06\x89D\x02\x89D\x02\xa1P\x08\xe1p\x18\x89D\x02\x99L\x86\x13\xc0eL\x00\xb3\xd9,DQ\x84\xd5jEyy9\x9cN',\x16\x0b4\x1a\xcd\x19[\xaf\xe2B~\xa1X,\x86`0\x88\xee\xeenx\xbd^\xf6E\xe4j\x15\xc7\xc2\x19\xe4h4\xca\x16\x92\x5ce\x15E\x11\x06\x83\x01&\x93\x09v\xbb\x1dF\xa3\x11F\xa3\x11z\xbd\x1e:\x9d\x0ez\xbd\x9e\x91C\xadV\x0bA\x10\x18)\xe4\xf3\xc4QLU\xc9\xe5rH\xa7\xd3\x08\x06\x83\xe8\xeb\xeb\xc3\xe8\xe8(\xbbP\x90\xb2\x5c\x0c\xf1x\x1c{\xf7\xeeE,\x16Css3\xd6\xaf_\x8f\x96\x96\x16\x08\x82\x00\xbb\xdd^\xf2\xb5\x1c\x1c\xa7z9\x96\x9f\x93K\x95\x00\x92r\x9eN\xa7\x11\x8dF1>>\x8e\xae\xae.\xe6\xa5!\xdb\xc9\xb1\xfc\xce\xd2\xfd\xfb\xf7cxx\x18v\xbb\x1d\xabW\xaf\xc6\xfa\xf5\xeb!\x08\x02\x13e\xce\x19\x02H_(\x9b\xcd\x22\x16\x8badd\x04\x95\x95\x95\xb8\xfa\xea\xab\x0bT*\x8e\x85_Hr\xf7\x1b\x11\xc0L&\x83\x89\x89\x09\xc4\xe3qLNN\xe2\xd8\xb1c\xc8f\xb30\x1a\x8dp:\x9d\xb0\xdb\xed0\x18\x0c\xd0\xeb\xf50\x99L0\x9b\xcd0\x18\x0c\xd0\xe9t\xd0h4|\xce8f\xdc\xe3\xf1x\x1c\x13\x13\x13p8\x1c\xd8\xb4i\x13[+\xa5\x0e\xabl6\x8b\x1bo\xbc\x11\xdb\xb7o\xc7\x0f~\xf0\x03\x08\x82\x00\xadV\x0b\x9b\xcd\x06\x8b\xc5\xc2\x14i\x0e\x8e\x85Z\xab\xca\x8b\xf2RU\xd1\xc8\x0d\x98L&\x11\x0c\x06\x11\x0e\x87q\xe7\x9dw2e\x88\x0b'\xcbw\x8dn\xd9\xb2\x05G\x8e\x1c\xc1\xd3O?\x8d#G\x8e \x16\x8b\xa1\xac\xac\x0cF\xa3\x11:\x9d\xee\x8c\xb9\x81O\xfb\xe4\xa5\xa0\xc6t:\x8dx<\x8e\xa9\xa9)\xe8\xf5z\x98\xcdf\xee\xf6]dP\x1c\x95r\x9c+++\x0b~\x0e\x06\x83p\xbb\xdd\xd8\xbf\x7f?\xba\xba\xba \x08\x02\x0c\x06\x03l6\x1b\x1c\x0e\x07\xacV+\xca\xca\xca`6\x9ba4\x1a\xd9\xe2#U\x90\x83\x1fX\x14\xd7\xeb\xf3\xf9PYY\x09\x8b\xc52\xeb\xfe\xa6\xb8\xbf\xae\xae.<\xf7\xdcs\xe8\xe9\xe9\xc1\xaaU\xabP__\x8fh4\x0a\xbb\xdd\xce\xd60\x07\xc7b`\xa9\xc7\xd1e\xb3Y$\x93I\x84\xc3a\xf8\xfd~\x18\x0c\x06fS9\x96'T*\x15\xa6\xa6\xa6\xf0\x9b\xdf\xfc\x06===\x98\x9a\x9a\x82\xdb\xedF0\x18\x84\xcb\xe5bb\xce9A\x00\x05A@:\x9dF2\x99D,\x16C\x22\x91\x80N\xa7c_\x94\xe3\xcc,\xa8b\xb7`\xca$\xb3\xd9l\xb0\xd9lhooG>\x9fGgg'zzz\xb0o\xdf>\x08\x82\x80\xd6\xd6VTTT\xc0\xe9t\xc2\xe1p\xb0\x9b\x08\xbd7w\xd3\xadl\xe4r9d2\x19\xc4\xe3q\xc4b1\xa4\xd3\xe9Y\xf7w>\x9f\x87 \x08\x18\x1d\x1d\xc57\xbf\xf9M\x9c8q\x02\x1e\x8f\x07\x91H\x04\xf1x\x1c\x99L\xa6d\xf2\x08\x07\xc7\xa9^V\x94?/U!\x82\x12&\x13\x89\x04\xdb[\xb4\xaf\xf8y\xbb<\xd7\xa5J\xa5B8\x1c\xc6/~\xf1\x0b\xec\xdc\xb9\x13###,|.\x1e\x8f\xb3\x98\xd53\x85\x05q\x01\x93\x8b\x88\xbe\xc4\x5c\x82\xbb\xb9:\xb8p\xe4Oy[\x98\xa9\xd6\x1a=o\xe3\xc6\x8d\xd8\xb8q#6o\xde\x8c\xb7\xdez\x0b/\xbe\xf8\x22\xea\xea\xea\xd0\xda\xda\x0a\x97\xcb\x85\xaa\xaa*8\x9dN\xe6\x1e\xa6\x0cb\xee\x16^\xb9kL\x92$\xc4\xe3qD\xa3\xd1Y\x89\x1f\x91\xbf}\xfb\xf6\xe1\xa7?\xfd)\x0e\x1e<\x08\xaf\xd7\x8bL&\xc3\xfePl\x167t\x1c\x8bmx\x97\xaa\xad!\x17o&\x93A \x10(i7\x17\xab\xae\xee|\xf6\xdfB\xfe\xee\x95\xb4\xef\xe5\x84\xde\xeb\xf5\xe2\xfb\xdf\xff>\xde|\xf3M\xf4\xf7\xf7#\x16\x8bA\xadV\xb3\x90\xae3\x9d0\xbb \x04\x90\x0cD4\x1a\xc5\xd8\xd8\x18\xbc^/_\x00gx\x81\x9d\xca\xf3\x5c.\x17\xee\xb8\xe3\x0e\xdcz\xeb\xad\xf8\xe7\x7f\xfeg\xbc\xf2\xca+hkkCss3\x9a\x9b\x9bQUU\x05\x87\xc3\x01\xb3\xd9\xcc\xe6\x9a\x97\xedX\xb9F4\x99Lbjj\x0aUUU%\xd7X<\x1e\xc7[o\xbd\x85\x17^x\x01\x87\x0f\x1f\x86\xdf\xefG<\x1e\x9fv\x18\xf23\x80c\xb1\x94\x16\xc2Rv\x01+\xe3\xe7\xa7\xa6\xa6f=\xbb\x17\xf2\xbb\x9c\xa9\xb1Q\xce\x89\xbc4\xd4\xd9\xba\xcc\x9e\xa95\xa1R\xa9\x90L&\x11\x8dF\xd1\xdd\xdd\x8d\xd7_\x7f\x1dG\x8f\x1eEWW\x17b\xb1\x18KP\x22q\xe5L\x8b,\xe2B.\xa6\x5c.\x87H$R\x92\x00F\x22\x11\xe4\xf3yLNN2\xe6;\xd3\xfbQ\x8d\xa4RFi)\xdd\xeeN\xf5\xf3\x9c\xcakT*\x15R\xa9\x14\xaa\xaa\xaa\x10\x0e\x87a\xb5Z!I\x12\xca\xca\xca\x90\xcb\xe5`6\x9b\xa1R\xa9\xa0\xd5jg,\xd2+7\xc4_\xfb\xda\xd7\xb0{\xf7n\xfc\xe4'?A\x7f\x7f?.\xb8\xe0\x02\xb4\xb6\xb6\xa2\xbe\xbe\x1e.\x97\x0b6\x9b\x8d\x05\xebs\x12\xb8\xf2@\xfb;\x1e\x8f\xc3\xe7\xf3\x15}N.\x97\xc3\xc0\xc0\x00^y\xe5\x15\xec\xd9\xb3\x07'N\x9c\xc0\xc8\xc8\x08b\xb1\x18;p)\xcb\x8d\x07\xb8s\xccv\x86.\xc4\x85s\xa9\x87\x19\x90\x1b8\x1e\x8fc||\xbc\xe8\xf9\x1c\x8b\xc50<<\x0c\xb7\xdb\x8d\xde\xde\xde\x19\xc9\xd3\x5c\xec\x88\xdc\x15y*\x9f\xf5T^W,\xc9\xebTm\xe5b\xb8H\xe7\xf3YJ=\xaf\x98\xc7M\x10\x04D\xa3Q\x04\x02\x01\x8c\x8f\x8fcdd\x04\x83\x83\x83\x88F\xa3,\x0c\x86\x88\xdf\xd9h\xda .\xd4\x00\x92\x01\x88\xc7\xe3,FH\x8e\xd1\xd1Q\xf4\xf7\xf7\xa3\xbb\xbb\x1b\xf1x\x1c\x1e\x8f\xa7(\xdb\xa5\x85\x9fJ\xa5\x90N\xa7Y\x11c\xe5\x00+[K-$\xb9:]\xe26\xd7\x00\xce\xf9\xc8\xfa\xb4\xf9\xe4\xef-\xaf\x01HcAn[\x8b\xc5\x82\xf2\xf2r\x18\x8dF\x94\x95\x95\xa1\xac\xac\x0cv\xbb\x1dv\xbb\x1d:\x9d\x8e\x1d\xac\xf4\x1e\xd9l\x16\x9b6m\xc2\xea\xd5\xab\xf1\xf4\xd3O\xe3\xc5\x17_\xc4\xfb\xdf\xff~\x04\x83A\xa4\xd3id\xb3Y\xf6ZN\x02W.\xb2\xd9,\x82\xc1`\x01\xe9K\xa7\xd3\x98\x9a\x9a\xc2\xfe\xfd\xfb\xb1\x7f\xff~\xf4\xf6\xf6bhh\x08n\xb7\x1b\x89D\xa2\xc0\xad!\xdf\xb3<\x0c\x84C\xde\x05\x83J\x9f(\xcf\xceP(\x04\xb7\xdb\x8d@ P\xd0UF\xaf\xd7\xa3\xbd\xbd\xbd\x80`\xccG\x01<\x1d2\xb4P \xfb\x96\xc9d\x0ab\x00\x01\xc0\xef\xf7\xa3\xbf\xbf\x1f;v\xec@\x7f\x7f?\xc2\xe10\x82\xc1`A\xfd\xd6S\xb5u\x0bE\x8a\xe5cX\xec\xf7SR\x8b\xb2\xc5\x9d\xb2\x1d\x9arLf\xfa^D\x98\x8b\xa9\x8a\xa5~.5N\x0b\xd5v\xaf\xd8{\xe4r9$\x12\x09V?\xd5\xe7\xf31\x8e$O\xf6\xa0j\x0ag\xfaL\x5c\xd0\xfa\x0b\xc4\xce'&&\xd8\x97\x0f\x06\x83\xe8\xe9\xe9\xc1\xc1\x83\x071::\x0a\x9f\xcf\x87d2\x09\xaf\xd7[@\xee\x94\x93F\x93\x12\x8f\xc7\xd9\x82 \xb9\x5c.\x87'\x93\xc9\x05'p\x8bI\x0eO\xf5\x90\xa0X<\xe5\xe3t\xf8i4\x1a\xe8t:\x18\x0c\x06$\x12\x098\x1c\x0eH\x92\x04\xb3\xd9\x0c\x93\xc9\xc4J\xc0TVV\xc2f\xb3\xc1n\xb7\xa3\xa2\xa2\x02&\x93\x09\x06\x83\x01\x82 \x97\xcb!\x95J\xe1\xc6\x1bo\x84\xc5b\xc1\x0b/\xbcPP\xec7\x97\xcb\xc1\xe1p\xc0h4\xb2R\x05\x1c+\x03tI\xc8f\xb3\xf0z\xbdH&\x93\xd0\xe9tH&\x93\xe8\xec\xec\xc4\xee\xdd\xbb\xd1\xdb\xdb\x8b\x91\x91\x11LNN\xc2\xe7\xf3!\x12\x89\xb0RE\xb4v\xc9\xc0s\xf2\xc7\x89\x1f)$\xa2(B\x14Ex<\x1e\xbc\xfd\xf6\xdbx\xeb\xad\xb7\xf0\xf6\xdboc``\xa0 ^T\xbefr\xb9\x1cZ[[\xf1\xd6[o\xc1\xe9t\xce\xe8:+\xe5j\xccI9\xa8p\xf6[\x12\x92]\x8bF\xa3\xe8\xec\xec\x84 \x08\x18\x1f\x1fGoo/\x06\x06\x06\xd0\xdd\xdd\x8d@ \x80d2\x89D\x22\x01\xe0d\xf1\xe8\xb3I\x5c\xf3\xf9<#\xecr\xb5\x8bHL>\x9f\x87\xc9d\x9aF\xc0\x95s\xa1\xd5j\x0b\xc6\x9fBMJ\xa9k\xc5\xbc\x834\x1er\x01\xe9L\x9d1\xf4{\x8b\xfd>\xba\xacd\xb3YV\xec;\x95JM[\xcf\xf2\x8bM\xb1\xb8\xfe%O\x00I\xae\xcf\xe7\xf3\xac\x04L:\x9df$.\x9dN#\x16\x8b\xc1\xe7\xf3\xb1\xfaG\xca~\x87$\x8bR\xab\xa9L&\x03\x8dF\xc3\xda\xe3H\x92\x04A\x10\x90J\xa5\xd8k\xa8v\xddb\xa8t\xa7\xb3\x80\x16r\xf2h,\x09T`\x9bz\x0aR\xc9\x16Q\x14!I\x12L&\x13<\x1e\x0f4\x1a\x0d\xab+\xd4\xd3\xd3\x03\x87\xc3\x01\xbd^\x8f\xf2\xf2r\xd4\xd7\xd7\xc3j\xb5\xa2\xb6\xb6\x16N\xa7\x13\xa2(\xb2[h:\x9d\x86F\xa3\xc1\xc5\x17_\x8c\xbd{\xf7\x16\x14\x9c&\xc5P\xa7\xd3-\xf9\x0a\xfb\x1c\x0b{\xc0\xc9\x0f)\xba|%\x12\x09$\x93I\xa6\x12+[X)\x0fF\xa5r\xcf\x13\x8bV\x06\x94\xe7m,\x16\x83\xc7\xe3\xc1;\xef\xbc\x83_\xfd\xeaWx\xe9\xa5\x97\x0a\xd6\x09u6\x92\xaf!\xe5:\x9a\xcb\x99-\xef\xb1+\x7fN\x22\x91\xc0\x1f\xff\xf8G\xd4\xd4\xd4\xa0\xa3\xa3cV;\xb2\xd8\xe3\x92\xcb\xe5\x10\x8dF\xf1\xf2\xcb/#\x16\x8b!\x93\xc9\xa0\xaf\xaf\x0f\x1e\x8f\x07^\xaf\x97u\x06\xc9d2KfNi\xaf\x13\x91#\x11\x81:R%\x12\x09&\x14P\xe7)R\x04\xe9\x5c\xa1\xccW\xf9x(\xc3\x96H\x80\x90\x17\xa4\xa7\x0eC\xc4\x17\xe8w\x93\xa7\xecL\xf7\xd4-\xc5\x15\xe8<$\x1e\x93\xc9d\x0a\xfa>\xcb/\xc6g\x1a\x0bF\x00\xc9(\x00@\x7f\x7f?\xf6\xec\xd9\x03I\x92\xe0\xf3\xf9\xd0\xdf\xdf\x8f\xfe\xfe~LNN\x22\x1c\x0e#\x9b\xcd\xb2\xa0pb\xef\xc4\xe0\x05A`\x841\x97\xcb\xb1Vr\xc4\xa2\xcf\x04\xab_\xca\xea\x04\xa9n\xb4\x80\xa8`(\x19g\xb5Z\x8dX,\x06\x95J\xc5\x5c\xbd\x1a\x8d\x06\x16\x8b\x05\x81@\x00eee\x98\x9c\x9cD__\x1f\xecv;\x1c\x0e\x07\x0c\x06\x03V\xaf^\xcdnc\xe1p\x18\xa2(B\xaf\xd7\xa3\xb5\xb5\x15G\x8f\x1ee\xad\xfeh\xa1\x92;\x98\xc7r\xad\x1c\xd0\x9as\xbb\xdd8r\xe4\x08\x04A\xc0\xd4\xd4\x14\x06\x07\x071<<\x8c\xf1\xf1q\xf8|>D\xa3\xd1\x02\xd5X\x0ey\xb8\xc2R\xdfk\x1c\x0b\xb3fH-\x02\x80\xd7_\x7f\x1d\xdb\xb6m\xc3\xee\xdd\xbb\xb1k\xd7\xae\x82K-\x11\x05\xf9e\xa2\x14\xc6\xc6\xc6fue\x16S\xa0T*\x15N\x9c8\x81t:\x8d\xf1\xf1q477\xc3n\xb7\x9fUrL\xf5\x00\xdf|\xf3M\x18\x0c\x06\xa6\xa2\xc7b1\xa4R)\xa4R\xa9\xb3\x9681\x1b\x04A`\xaa\x1d\xa9\x80\x94\xd8\xa0\xd5j\x19\x09$\xe2M\x82\x05\x915\xa5\xfd \x92K]R\xe89\xd4\x8aR\x9e<#\x9fc\xb9r\xb6XY\xd3\xf3\x15|\xe8\xf7\xcb\x9b5\x14\x9b\xc7\xb3U\xfaG\x5c\x8cA\x10E\x91)D\xd4aB\xde\xda\x86\x9e\xa7V\xab\x11\x8f\xc7\xd9\xe4\x91\x9f\x0f\xe5\xe5\xe5\xf0\xf9|0\x9b\xcd\xa8\xa8\xa8\x00\x00x<\x1ev\x135\x9b\xcd\xe8\xec\xecd]\x1bh.m6\x1b\xdb\xcc\x1c+\x07t\xa9 %\x98\x0es\xba\x8c\xc8\xd7\xecL5*9\x96\xbf\xe2\x97\xcdf\x99\x8a\xf7\xf4\xd3O\xe3{\xdf\xfb\x1e\x06\x07\x07\x91J\xa5\xd8\xda\xa0\xb3C\xaenY\xadV\x5cs\xcd5hkkCCC\x03\xfbc\xb5Z\x0b\xde_\xa3\xd1\xa0\xa2\xa2\xa2\xc0\x16\xccu}%\x93I\xf6\xbaR\xc9\x86gb\x9c\x88\x10\xe4r9\xf4\xf5\xf5!\x99L\x22\x95J!\x99L2\xb5\x88D\x10\xb9z\xb6\x14m\x14\x8d)\x9d\x11\xe9t\x1a:\x9d\x0e\xd9l\x96\xf5\xa8'\x8f\x16u\xbc(f\xd3\x88\xf0\xd2\xbf3\x99\x0c\xb2\xd9,\xe3\x04t\xb1P\xd6\xbc\x95\x7f\x9e\xa5r\xd6(\x95k%9%\xe1\xe6L\xb9}\x17\x8d\x00\xcewP\x22\x91\x08\x93ri\xc1\xd3m\x87\xfc\xe4rW\x92\xfc\xef3Y,q\xa9\x93Be\xf6\x10-(\x22\xe3Z\xad\x96\xf5\x07\xa6q\xa6\x9f\xa9\x83\x8b\xc9d\x82\xcb\xe5B(\x14b\xad\xe2\xd4j5\x8cF#\xdb|\x07\x0f\x1edq\x86\xd4K\x98nv\x5c\x05\xe4\x06\x9f\x83C~&\x85\xc3a<\xf5\xd4Sx\xf4\xd1G\xd9Y\xa44\x86z\xbd\x1e\x95\x95\x95\xb8\xf3\xce;q\xc3\x0d7\xe0\xf2\xcb/\x9f\xd6\x1d\x86\xc8Q\xb15\xb6\x10Y\xc2\x14^s6\xce1y\x22\x9f \x08\x18\x1b\x1b\x83Z\xadf\x1e0y\xa8\xd4R\xdc\xefJ;$'\xaa\xe4\x0e&\xb2N\xa4\x8cD\x84L&3\xcdU+O\xf2 \xc50\x9b\xcd2AH9&\xa7\x9b\x88y6\xc6l\xa6X\xfe\xb3\xa1\x02\x8a\x8b\xb5\xf9U*\x15\xeb\x0c\x92L&\xd9d\xc9\xcbI\xe8t:\xa4\xd3\xe9\x82\xe0H\x8au(\x15GT,\x93h\xa5\xa9\x7f\xf2\x9b\xa0|\x1c\xe4A\xa7\x14\x17\x91\xc9d \x8a\x22\x0b\xdc\xcff\xb3\xcc}\xeb\xf5z\xa1\xd7\xeb\xd9\x1cP\x1b\xbfd2\x09\x8dF\x83p8\xcc$\xfd`0\x88}\xfb\xf6\x15\xf4\x10\xa6x\x0d\xde6n\xf9\x13f\xf2\x18\x8b\x5c.\xc7\x08!\x05\xd2\x92{\x97\xdc\xec\x14\x9bID\x906\xb3Z\xadF&\x93\x81Z\xad\xc6\xd0\xd0\x10\x8e\x1e=\xcaJ\xcb\x18\x8dFF\xfe\xb8+\x98+\x7f\x1c+\x0ft~h\xb5Z<\xf2\xc8#\xf8\xe1\x0f\x7f\x08\xb7\xdb\x0d\x00,\x06L\x92$\xacY\xb3\x06\x8f<\xf2\x086o\xde\x8c\x86\x86\x86\xa2kh\xb1\xcf\x10eAe\x95J\x05)/\xcd\xba\xc6\xe5\xee\xec\x85T\xff\xc8\xcbb\xb5ZY\xf8\x8d\xd2\xce\xcd&\x06\x9cm\x05\xb0X\xfb=\xa5r)\x0f\xfb\x22\xc5\x8b\xc2Fh.\x94\xe4\x8f\x04!\xb2M\xc5\xde\x93\x88\xe5\xb9$>(\xe7\xd4`0\xc0f\xb3\xc1h4N\xcb\x8a>\xa7\x08\xa0\xbc\xae\x8d\xdb\xedFUU\x15\xe2\xf18K@ \xd7$\x11\x0br\xf5\xc6b\xb1\x82x\x07\xb9\xaa\xa0\xcc>,\xa64\xae$\x05p\xa6\x1aF\xc5T\x18\xf9\xd8\x90\x22H\xb7(\x9dN\xc7\xe4v\x8b\xc5\x02\x95J\x85H$\x02\xbd^\xcfn\xc5\x92$\xb1\x22\xbe\xf2\xf2\x1d\x9d\x9d\x9dp:\x9d\xacw0\x91@\xee\x0a\xe6d\x90c\xe5\x81\x5c\x97\xd7]w\x1d\x06\x06\x06\x0a\x5cz\xe9t\x1a\x97]v\x19\xbe\xff\xfd\xef\xe3\xbc\xf3\xce\x9b\xb1T\xc6\x99\x5c\x83rwr>\x9fG^*}\xc9>r\xe4\x08\xc6\xc6\xc6\xb0a\xc3\x06\xd4\xd6\xd6.\xd89G\x99\xb1F\xa3\x11.\x97\x0b\xf5\xf5\xf5\x05\x04j&\x22\xb8\x14I\x8c2\x16Oi\x93\xc8\x0bEv%\x95J\xb1\xd2f\xa4\xd6\x91:H\xc9\x1f\x14\x06&O\x08*V6\xee\x5c%\x7f\x04\xb5Z\x0d\x97\xcb\x05\x8b\xc5rn\x13@\x22\x81\x82 \xa0\xa1\xa1\x81\x05\xd7\xca\xbf4e\xf1\xa4R)\xa6\x14\x92\x8f_\x1e\xf3\x07\x00z\xbd\x1e\xf5\xf5\xf5X\xb5j\x15+[B\x81\xe6\xf2E\xb7\x9cI\x87|<\xe8\xdf\xd9l\x16\xa1P\x88e[\xd2f\x19\x1d\x1dE4\x1aE8\x1cf\x8f\xd1\xa6Q\xb6\xdf\xa1C\x862\xb8r\xb9\x1cL&\x13{\x1e5)'\xd7\xb1^\xafg\x85J\x83\xc1 \x0e\x1f>\x8c\xea\xeajTWW\xc3b\xb1\xb0\x98A^\x16fe+A\x8be\xd89\x96\x16\xc8\xdd\x9bJ\xa5\xf0\x9d\xef|\x07\x0f>\xf8 \x04A`e\xbb\x00\xe0\xaa\xab\xae\xc2?\xfc\xc3?`\xcb\x96-,\xd6\xeel\x94[Q\xaeKe\xd8B)\x050\x99Lb||\x9c\x950\xab\xad\xad]P2 \x08\x02\xccf3jkkq\xe9\xa5\x97\xe2\xa7?\xfd)\xc6\xc7\xc7\x0bB\xa1\xe4b\xc7R =r\xd7\xab<\xb35\x93\xc9 \x99Lbxx\x18\x03\x03\x03\x18\x19\x19A$\x12a\x17\x82L&\xc3\x1a\x09P9\x18\x22\x85\xc0\xc9Lp*\x1cO|\x80\x94?9\xf9\xd3h4\x8c\x17\xd4\xd5\xd5\xa1\xbc\xbc\x9c%\xa5\x15k\x16q\xb6/\xc5\xca\x905\xb9\xdb\xdfn\xb7\xa3\xaa\xaa\x0av\xbb\x1dV\xab\x95y\xde\xcei\x17\xb0\xb2\xb1\xb1\xcdf\xc3\xc0\xc0\x00#\x17\x82 0\xb7/\xc9\xbdJ\xe5\xcf\xe5ra\xf3\xe6\xcdX\xb7n\x1d\x9cN'\x1b\x1cr7\xcao\x99+A\x01T\xc6\xaa\xd0x\x11y\xa6\x04\x9ah4\x0a\xb7\xdb\x8d`0\xc8Z\xcf\xf4\xf5\xf5\x15,D\xb9\x9cN\x8a \x118yk\x1aJ\xceQ\xab\xd5,k\x8e2\x81\xfb\xfa\xfa\xd0\xd5\xd5\x85\x9a\x9a\x1a8\x9dN\x94\x95\x95\xb1ZP\xdc\xf0\xaf\x1c\x92W\x8a\xf0qEpy\x93\xbf\xa3G\x8f\xe2\xe3\x1f\xff8\x0e\x1c8p\xd2\x98\x88\x22\xd2\xe94\xd6\xacY\x83'\x9f|\x127\xdcpCA\xf8\x892\xb9c1\x8cm1\xa3\xaf\xfcY^/\x0e\xc0\x8c\x0a\xa0\xbc$\x8d \x08\xec\x02\xbc\x90g\xbbZ\xadf\x09xZ\xad\x16eee\xa8\xac\xacd\xd51\x8ay\xc0\x96\xc2\xbeR\xc6\xda\x11YK\xa7\xd3X\xb7n\x1d&''\xd1\xdd\xdd\x8d\xfd\xfb\xf7\xa3\xbf\xbf\x9f}\x07\xaa\xe8\x91\xcdf\x99`@\xff&\xce@\xb1\x7fD\x80\xe5j\xa2\xcdf\xc3\xd5W_\x8d\x8e\x8e\x0eTVV\xc2j\xb5\xb2J#d\xb7\x96\xd2\x19\xa4\x8c\xcf\x97\xab~T-\xc5j\xb5\x16\x10\xc0sZ\x01\x94\x7fqb\xf8\xb4\x89\x08\xf2\xac\x1e\xba9\xc8\x0d\x8b\xddn\xc7\x07>\xf0\x01\xac[\xb7\x0e---p\xb9\x5c\x05\x04C\x9e\xf6\x7f\xb6\xea\xe7\x9c\xe9\x03\x97\xc6\x94\xfe-'p\x14\xcfG1\x13\xd1h\x14\xd1h\x14\xc1`\x10>\x9f\x0f\x13\x13\x138|\xf80:;;Y\x11m\xda\xb4\x82 \xb0\xc2\x9a\xc9d\xb2\xe0\xa6I%d\xe8\x00\x94\xbb\x81s\xb9\x1c\x0e\x1f>\xcc\xfa\x05;\x9dN\xe6\x0a\xe6*\xe0\xf2\xc5\xd9l\xe4\xceq\xf6A1\xc1/\xbf\xfc2\xfe\xec\xcf\xfe\x8c\x91\x22:\xe7\xef\xbf\xff~<\xf1\xc4\x13\x05\x86\xf8L\x9f\x07\xb3%&)\xeb\xc9\x96Z\xcbD\x16\x17\x83\x00\xd2\xb8Q\xd7\x0c\xadV\x0b\xab\xd5\xcaD\x11%i\x90\x17d_*kAN\x02\xc9\xae\x93\x08A\x84&\x9f\xcfctt\x94\xa9w\xe4\xe6\xa4\xf8P\xbd^\xcf:w\x10\x1f\x90\x8b\x14\xf4\xddM&\x13n\xbf\xfdv\xb4\xb7\xb7\xa3\xb9\xb9\x99\xb9M\xa9\xde\xadR%]*c\xa4\xbc\x84\xd0\xe5\x83l%%\x80P\x99\x9c39\xbf\x8b\x1aqK\x0bV\xde\xdaE\xb9`\xc8%,\x1f\xa4\xcd\x9b7c\xcd\x9a5X\xbf~=\x93x\x0d\x06\x03\x0b\x10V\xca\xe0\xcb\xd5\x18\xcd\x14`+\x1f/\xda(tsR\x12A\x9f\xcf\x07\x8f\xc7\x83\xa6\xa6&\x9cw\xdeyx\xe7\x9dw\xd0\xd7\xd7\xc7Z\xea\x91\xbb\x86\x12v\xe8\xbd\x89\x94S\x89\x04y\x0b>\xfa{jj\x0a===hllDuu5/\x0e\xcd\xc1\xb1\x8c\x89\x1f\x19\xd7\x1f\xfc\xe0\x07\xf8\xdc\xe7>W@\xec\x9a\x9b\x9b\xf1\xb3\x9f\xfd\x0c\x9b6mb\xc1\xfcg\x03\x92$\xa1\xaf\xaf\x0f555\x05IjJ\xdbS\xaa=Y\xb1\xf3V\x10\x04\xd6\x86m!]\x8bt\xd1&\x85\x94b\xb3g\x22\x0f\xf2\xdf\x7f\xb6\xec\x9e\xd2\xcd*\xff\x9b*|P\xd7)I\x92\x10\x0a\x85\xe0\xf5z\x0b\x08\x9a^\xaf\x07\x00\x18\x8d\xc6\x82\xf6v\xca,_\xb9\xbd\xdb\xb2e\x0bZZZ\xb0f\xcd\x1a455\xb1\xdfA\xea\xdfR\xb49J\x05P\xe9\x0a&[)o\xe8p&!.\xf6\xa1Q,iA\x1e\xd8)\x7f\x8e$I\xa8\xae\xae\xc6\xda\xb5k\xd1\xd8\xd8\x88\xba\xba:TUU\xc1b\xb1\xb0\xf6g\xf2\xc5\xb6\x12\xdaH\xcd\xa5\x06\x94\xfc@\xa3MDD\xb0\xba\xba\x1a\xc1`\x10\x1e\x8f\x07UUU\xa8\xae\xae\xc6\xae]\xbb\xb0w\xef^\x84B!6\x1f\xf4|Z\x94\xb4A\xe5E*S\xa9T\x81\x0b>\x16\x8b\xa1\xbf\xbf\x1f\xa3\xa3\xa3hjj\x82\xcb\xe5\x82\xc9d\xe2\xb1\x80+\x84\x10p\xac\x1c\x90\xd1\xfa\xd4\xa7>\x85\x1f\xff\xf8\xc7\xac\x96[.\x97\xc3=\xf7\xdc\x83g\x9ey\x86]H\xcf\x06\xf9#\x92v\xe4\xc8\x11<\xf0\xc0\x03\xa8\xab\xabCmm-V\xb5\xb4\xe0\xaf?\xf5)\x5c\x7f\xddu\x8c\xfc\xc9E\x07\x22-\xb3\xa9\x89\xe4)Y\xac\xb1%5H\xab\xd5\x16\xcdt]\xaa(\x16_Iud\xa9\x8e\xdf\xf8\xf88\xea\xea\xea055\x05\xa3\xd1XP9\x82TARe\x95\x8d\x1f\x08\xabW\xafFSS\x13\x9a\x9b\x9b\xd1\xd8\xd8\x88\xca\xcaJ\xa6\xfe)\xc3\xc1\xce\x15^\xa0$\x83g\x03\xe2\x99\xfe\xc2\xa4\x08*\xb3Wi\x00\xd6\xae]\x0b\xbb\xdd\x8e\xca\xcaJ\xd8l6\x98\xcd\xe6\x82\xfa8+\xf5\xe0-\x059\xd9\xd2j\xb5\x8c\x10f\xb3YX,\x16X\xadV\xd8l6\x94\x97\x97\xc3f\xb3A\xab\xd5\xa2\xbc\xbc\x1c\xff\xf7\x7f\xffW@\x02\xd3\xe94+\xd0I2}>\x9f/\x88\xd1\xa4\xc3\x8a\xc8\xe0\xe0\xe0 \xc6\xc7\xc7\xe1v\xbbY\xa2\x8e\x9c\xacs,_,\x85:[\x1cgf\x9eU*\x15\xee\xba\xeb.<\xff\xfc\xf3\xec\x02\x98L&\xf1\xcc3\xcf\xe0\xd3\x9f\xfe\xf4\x9f\x0c\xcaY*\x05E\xc6\xf4\x8f\x7f\xfc#\x00`tt\x14\xa3\xa3\xa3\xd8\xbd{7~\xfb\xbb\xdf\xa1\xa9\xa9\x09\x9f\xfb\xdc\xe7\xf0\xf9\xcf\x7f\x9e\x95\xb5\x9a\xcbE\x86\xce\xd23q\x9e\x9d\x8bY\xad\xcaq\xa1\xd86\xe0d(\x97\xcb\xe5b\x7f\x02\x81\x00\xb3\xe7\x0e\x87\x83%0R\xbc#\x91\xf1b\xed\xd2jjj\xe0r\xb9PUU\x05\x87\xc3\x01\x8b\xc5\x02\x83\xc1pN\xf3\x82\xa50\xcf\xe2\x99>H\xe80Q\xa6\xe2\x13(\xde\xcfl6\xc3d2\x9d\xb5&\xdd\xe7\xf2\xa2\x22\x05\x8e\xba\x80h4\x1a\xe8\xf5z\x18\x8dF\xe8\xf5z\xd6\xbe+\x9f\xcf\xe3\xa5\x97^b}\x99\x95-\x87\xe4\xe4\x9c$jy\xcb\x1aA\x10\x10\x0e\x87\xe1v\xbb\xe1\xf1x\x10\x0a\x85PQQ\xc1:\x85ppp\x9c\xdb\xa0K\xfa\xddw\xdf\xcd\xc8\x1f\xa9,o\xbf\xfd66o\xde\xccz\xbd.\x05\xec\xd8\xb1\xa3\xa0\xf6,p\xb2\xf2DOO\x0f\xee\xbd\xf7^\xdc\x7f\xff\xfdx\xf8\xe1\x87\xb1v\xedZ\xe8t\xbaY\x0b\x06\xcf\xd4\xb7\xb5\x94\x9a\xc3m\xd0I{a2\x99`\xb1X`\xb3\xd9\xd8\xdfv\xbb\x9d\xf5]\xa6\xb2dF\xa3\x91\xcd\x13\xcd\x87\xbc\xc4\x9bV\xab\x85\xc3\xe1`\xbc@\xa7\xd3\xb1\xe2\xe2\x1cK\x90\x00\xca'Q\xf98\xc5\xac\xc9\xff\xc8AA\xa2D\xfcVB\x92\xc7b\x93A\xea\xd8!\xef\x0dL\x99\xd8\xf1x\x1c\xaf\xbe\xfa*#\x7f\xca\xc0^j\xe5C\x89 D\x02\xe57\xfe\xa3G\x8f\xe2\xfc\xf3\xcfG0\x18D\x22\x91@YY\x19\x0b\x9c\xe6X\xfe\x04a>\x8fs\x9c{g\xc8_\xfe\xe5_b\xeb\xd6\xad,h\xbf\xac\xac\x0c\xef\xbe\xfb.\xd6\xad[\xc7\x12\xfdf[\x1f\x8b}\x86Sm\xd9\xef~\xf7\xbbx\xf1\xc5\x17\xd1\xdf\xdf\x8f\xde\xde^\x1c9r\x84\x91A\xbd^\x8fl6\x8b\xaf~\xf5\xab0\x9b\xcd\xb8\xe9\xa6\x9bp\xfb\xed\xb7\xb3\xb8\xbbb%D\x94\xa5\xc9\x94\xe7\x1a\xfdL\xf1\xd4\xfc\xe2\xfb\xa7\xf9 \x976u9\xb1\xdb\xed\xa8\xad\xad\x85\xc1``%\xc6\xa8\x12\x88<\xa9\xb1Xm[\x12/\xc8\x8eq^\xb0\x04\x09\xa0r\x03\x15K`\xa0\x85A\x05\xa1\x95\xc5\x9c\xc9}H$\x83O\xf2\xc2\x80\xb2\x8d\xe8\xdf\xf9|\x1e\xf1x\x1c\xe1p\x18\x93\x93\x938t\xe8\x10\x80\xc2\x12\x09\xf2lk\x22{\xf1x\xbc\xe0\xff\x04A\xc0\xe4\xe4$B\xa1\x10\x82\xc1 \xe2\xf18#\xf9\x9c\x00.op\x17\xf0\xf2\xc7'>\xf1\x09l\xdd\xba\x95\x15\xe9\xd5\xe9t8t\xe8\x10\x1a\x1b\x1bg\x8d\xb7\x92\xb7\x06;S\xeb\xb1\xaa\xaa\x0a\x97]v\x19\xae\xb9\xe6\x1aD\x22\x11\x04\x83A\x04\x02\x01\xfc\xe2\x17\xbf\xc0\x81\x03\x07\xd8%8\x1a\x8d\xe2W\xbf\xfa\x15^{\xed5<\xf0\xc0\x03\xf8\xd2\x97\xbe\xc4\xea\x9d\xceDdU*\x15|>\x1f\x92\xc9$\x02\x81\x00\x9a\x9b\x9ba\xb1X\xe0\xf1x\xf0\xf5\xaf\x7f\x1dO=\xf5\xd4\x92RC\x97\x0a\x11$\x8f\x13\x95\xbb\xa1\xf8Q\xaf\xd7\x8bL&\x03\xb7\xdb\xcdb3\x8b\xad\x17y\x1b9\xce\x07\x16x~\x16\xe3\xc6(\x872\xd1CN\x0e\x8b\xc9\xebt\xa8\xcc\xd4\xfd\x83\xe3\xf4H\xa0V\xab\x85^\xafGEE\x05\x1a\x1b\x1b\xb1j\xd5*\xb4\xb7\xb7\xa3\xbc\xbc\xbc\xb06\xd6\xff\x9f\x17\xbau\xa9\xd5j\xe4r\xb9\xa2\x1d?(\xf3.\x12\x890\x02\xc8\x15\xa0\xe5\x0f>\xc7\xcb{^\xbf\xfb\xdd\xef\xe2\xa7?\xfd);\xc7\x8dF#zzzPSS3\xa7\xcb\xb9 \x08x\xf5\xd5W\xb1k\xd7\xae3\xb6^\xa8\xb2D\x22\x91\x80Z\xad\x86\xd3\xe9\xc4\x1dw\xdc\x81\xfd\xfb\xf7\xe3\xbd\xf7\xde\xc3\xc6\x8d\x1bY\xfc\x1fp\xb2\xa8\xfd\x97\xbf\xfcel\xd9\xb2\xa5\xa4\x1d#\xbb\xb4o\xdf>\x1c?~\x1c}}}\x18\x1e\x1e\xc6\x81\x03\x07\xd0\xd6\xd6\x86\xef}\xef{x\xf2\xc9'9\xf9S\xcc?\x09=\x82 \xa0\xae\xae\x0e\xabV\xadB[[\x1b\xf3\xf0%\x12\x09X,\x16&P\xc8\xcb\xc5\x15\xe3\x16\xe4\xd5\xe2\xbc`\x89\x12\xc0\xb9lt\xb9\xebW9\x91\x9c\xf8-.\xa8\xf8\xa4\xd9lf$\xb0\xb9\xb9\x19\xf5\xf5\xf5Lu\xa59\xd0\xeb\xf5H&\x93\xac\x04\x0c\x91\xbfb\xca\xad\xc7\xe3A8\x1cF4\x1ae\xdd]8\x96\x0ff\xda\x8fs\xc9R\xe78w@1\xc0\xaf\xbf\xfe:\xee\xbb\xef>f\xbc\x8dF#v\xef\xde\x8d\xda\xda\xda\x92nN\xf9Zx\xea\xa9\xa7\xb0e\xcb\x16\x5cv\xd9ep\xbb\xddg\xa5t\x09)J\xd9l\x16\x17]t\x11\xf6\xed\xdb\x87\xe7\x9e{\x0e\xeb\xd7\xafg\xdfW\x14E\xbc\xfa\xea\xabhoo\xc7\xdbo\xbf\x0d\x00\x05$Q\x0e*\xb1%I\x12|~\x1fn\xbc\xf1F\x04\x83A\xe4\xf3y|\xe5+_\xc1\xce\x9d;\xf9\x22\x92\xd9y9\x014\x99L\xa8\xa9\xa9\x81\xd1hD}}=DQdmH\x95\xf6\xbf\xd8:\x91W\x0b\xe1g\xce\x12$\x80r\xf5h.\x13T\xcc\xa8PM;>\xc1\x8b\x07J\x10\xb1Z\xadp:\x9d\xa8\xad\xadEcccAp-\xb9J,\x16\x0b\x8cF#s\xf5\xd0\x86Vf\x7fMLL \x1e\x8f#\x91H\xb02\x0b|\x0e\x97\x8f\x224\x9f\xb9\xe4\xf3~n\x9f\x0d]]]\xb8\xf9\xe6\x9b\x99\xfbN\x92$\xfc\xcf\xff\xfc\x0f\xd6\xacY3\xeb\xdcR\xec\xf0\x87?\xfca\xfc\xed\xdf\xfe-T*\x15t:\x1d.\xbd\xf4R$\x12\x89E\xbf\xd8\x17\xf3(E\xa3\xd1\x82f\x04W\x5cq\x05\xbe\xf1\x8do\xe0\x8b_\xfc\x22\x8b\x0b\x14E\x11\xc3\xc3\xc3\xd8\xb2e\x0b\xfe\xe5_\xfe\x05\x1a\x8dfZ\xfc\x9f\xfc;\xaa\xd5j\xd8]\xe1/\x0f\x00\x00 \x00IDAT\xac6<\xf5\xd4S\xec2\x9cN\xa7q\xd3M7!\x1c\x0e\x17\xd4\xbe]\xa9{\x83\x92<\x88\xd4\x95\x97\x97\xa3\xa6\xa6\x06\xf9|\x1e\xc1`\xb0\xa0\x86\x9f\x92\x03\xcc\xa4\xc0r,a\x02(\xdf \xf2\x94\xf6\x996\xd1L\x87\x01/$\xbc\xf8j\x8e(\x8a\xd0\xeb\xf5(//\x87\xcb\xe5Buu5\x1c\x0e\x07{\xdcl6\x17\xa4\xe6\xd3\xebf:\xc0\xd2\xe94\xc2\xe10\xe2\xf18S\x009\x11X>\xeb\x85\xf6%\xc7\xf2\xc7G>\xf2\x11\x96\x91\x99H$\xf0\xd4SO\xe1\xc6\x1bo,j\x98\x8b\x91\xad\xcd\x9b7\xe3\xf7\xbf\xff=#\x94\xa9T\x0a\x7f\xfd\xd7\x7f\x0d\x83\xc1\xb0\xe8g\x82\xf2\xfd%I\xc2\xe0\xe0 \xdez\xeb-\xec\xdd\xbb\x97\xd53\x15\x04\x01W]u\x15~\xf2\x93\x9f\xa0\xbd\xbd\x9dy9R\xa9\x14\xfe\xfe\xef\xff\x1e\x0f=\xf4\x10\x8b{\x9ciO\xa4\xd3i\xdcu\xd7]\xb8\xef\xbe\xfb\x98\xda\x15\x08\x04p\xe5\x95W2b\xa9\xfc,+)n\x96\xba\xc6\xd0w\x0e\x85B\x08\x85B\xa8\xaa\xaaBCC\x03\xcb\xfe-v\xc9,\x96@\xca\xed\xc99B\x00\xe5\x13z*\x0b\xbeTaN\x8e\x85\xbb\x9dQ\x86\xb0\xddnGEE\x05\xacV+\xabF/\x8a\x22l6\x1bk\xe7C\xedvH\xceW\xcek<\x1e\x87\xcf\xe7\x9b\xd6\xf6o\xb6\xf5!/\x05\xa4|Ly\xa0\xf3\x03\xe0\xd4\xf7a\xb11\x9ei\xbc\x8b\xa9(3\xdd\xcc9\x96\xcf:\x01\x80\xc7\x1e{\x0c\x9d\x9d\x9d\xcc\x13\xf3\xc9O~\x12\xf7\xddw\x1f\x0b\xd0/\xf5Z\xaf\xd7\x8b\xcb.\xbb\x0c;w\xeed\xcf\xb5Z\xadx\xeb\xad\xb7\xf0\x8f\xff\xf8\x8f\x05\xdd\x84\xce\x04\xf9\x93?\x9e\xc9dX\xa9*\xf9z\x06\x80\x17^x\x01\x0f?\xfcpA\xd7\x8fo|\xe3\x1b\xf8\xf2\x97\xbf\xccb\x9fg\xda\x0f\x99L\x06O>\xf9$\xb6l\xd9\x82\x5c.\x07\x8dF\x83C\x87\x0e\xe1[\xdf\xfa\x16\x0b\x99\x01N\xba\x8e\xfb\xfa\xfap\xe8\xd0!\xd6\xf9b%\xd8\x18\xea\xf3\xabR\xa9PSS\x83\xb6\xb66\x96\x8c8\x1fn\xc0\x15\xc0s\x8c\x00\x16c\xf1\x9c\xc9/-UG\x1e\x0fh\xb3\xd9PQQ\x81|>\xcfZ(\xe9\xf5z\x16#Cs\xa9\x8c\x13\x94oPR\xff(\xad\x9f68\x15\xa5\xa6^\x91\xd4\x09F\xfe\x98\xfcg\x0a\xe4\xa6\xee$\xd45F\xdeA\x86\xa3\xf8!)o\xcc>\xdb\x18\xd3\xf8R_n\xf98+\xc30\x94\x8a\xfdL$\x91\xe3\xdcD&\x93A__\x1f\x1e~\xf8a\xf6X{{;\xfe\xf3?\xff\x93\xd5b+u\x96D\xa3Q\x5c}\xf5\xd5\xe8\xec\xecd\xc5\xe8\xdb\xda\xdap\xf4\xe8Q\x5cq\xc5\x15\xec\x1c9\x955M\xeb9\x9dN\x97\xdc\xfbs\x89S\x0d\x06\x83\xcc;A\xeei\xbf\xdf\x8f\xaf\x7f\xfd\xeb\xf8\xcdo~\x03\x00\xac+\xc7\x13O<\x81G\x1ey\x04v\xbb\xbd\xe8\xa5\x96\x08_:\x9d\xc6\xb6m\xdbPSS\x83L&\x03A\x10\xf0\xe8\xa3\x8f\xa2\xab\xab\x8b}\xa6@ \x80\xae\xae.\x0c\x0f\x0f#\x14\x0a\xad(;#o\x137\xd7\x02\xdc\xa5\xe6\x90c\xe1pFR\x96x\xb9\x88\xa5M\x02\x0d\x06\x03,\x16\x0b\xccf3\xacV+\xacV+\xeb\xb3\x98N\xa7\xe1\xf3\xf9\x10\x8b\xc5\xd8\x0d\xac\xd8A\x9b\xcb\xe5\x90L&\x19\xf9\x937\xf3&2A\x07@\xa9,\xf0b\x87\x87<\xfbK\xde\xa1\x84g\xdc\x15\xee1\x1ag\x22x\xf2\xdaZ\xb3\x1d\xac\xf22L\x1a\x8d\x06:\x9d\x8e\x198\xf9\xf3h.\xe4e~\xf2\xf9\xfcY\xeb\xfd\xca\xb10\xe4O\xab\xd5\xe2\xe6\x9bofk@\x92$|\xf7\xbb\xdf-I\xfc\x08\xa9T\x0a\x1f\xf8\xc0\x07p\xec\xd81\xd62\xed\xa2\x8b.\xc2\xae]\xbb\xd8\x1e\x9do'\x0d\xba@\x16\x8b7>U\x15\x10\x00\xa2\xd1\xe84%\x89>\xe3\xad\xb7\xde\x8a\x83\x07\x0f\xe2\xfc\xf3\xcfg$\xf6\xbf\xff\xfb\xbf\x99\x0b[\xa9\xdc\xd1\xfe\xa21\xfa\xe1\x0f\x7f\x88\x9bn\xba\x09\x92$!\x1e\x8f\xe3\xcb_\xfe2~\xfd\xeb_\xb3\xf2Y\xa4bq{\xc89\xc1\x8a\x22\x80\x1cK\x9b\x00\xeat:\x98L&\x98\xcdf\x88\xa2\x08\xbb\xdd^P\x0f\x8bj8\xe5r9v\x80)\x89C.\x97C,\x16c7u\xf9\xe3\x92$\xb1\xe4\x10\xca\xa2\xa3\xdb|\xa9\x9b\x9d\xbc96e\x22\xd2\xe7\xa4 m\xder\xeeO*\x09\x91py2\x0e\x11\xee\x99\x0e\x5c*\x08\x9e\xc9d\x0a\xaa\xf7\x93\x0a,\x8f\xff$\xe2\xa7\xd7\xeb\x0b\x94`y\xed\xb3\xd9\xdc4\xdc\x03\xb0\xf4\xd6\x8eF\xa3\xc1\x13O<\x81\x13'N@\xab\xd5\x22\x9dN\xe3\x9e{\xee\xc1\xf5\xd7__r\x8f\xd1\xff\xdds\xcf=\xd8\xb1c\x073\xec\x97\x5cr\x09\xf6\xec\xd9\xc3\x8a\xfd\x9e\x0ab\xb1\x18\x02\x81\x00\x8e\x1c9\x82\x1d;v`\xdf\xbe}\xe8\xea\xea\xc2\xc7?\xfeq<\xfa\xe8\xa3\xa7\xdcy#\x93\xc9L\xfb>\xf2\xb3j\xfd\xfa\xf5\xe8\xea\xeaB{{;[\xaf[\xb7nE}}=\xae\xbc\xf2\xca\x0276\x9d\x83\xf4\xf3\x87>\xf4!\xdcz\xeb\xad\xf8\xedo\x7f\x0b\xb5Z\x8dm\xdb\xb6\xe1\xd5W_\xc5\x87>\xf4!\xd6\xee\x8c\x8aK\xaf4;\xc3\xc3GV \x01\xe4\xedq\x96>\x04A`\xad\xe2L&\x13$IbE^EQD(\x14B.\x97c\x07\x98\xbc\xf2\xbfrn\xe5\xaa\x13\x1d\x90*\x95\x0a\xc9d\x12\xd1h\x14\xe1p\x18^\xaf\x17>\x9f\x0f\xa1P\x08\xc9drNY\x85\x00\xa0\xd3\xe9X\xb2\x0a\xf5\x89\xd6\xe9t+~\x8d\xc9\xdd\xbe\xd1h\x14\x81@\x00~\xbf\x1f\x13\x13\x13\xac3K)\xd0\xeb\xf2\xf9<\xccf3\xea\xea\xeaP[[\xcb\x02\xb8\xa9\xd9\xba\x9c\xf4'\x93I\xb6\x0e(K4\x95J1e\x96\xe3\xdc2\xce\xb1X\x0c\x8f?\xfe8#\x7f\x8d\x8d\x8d\xf8\xe1\x0f\x7fX\xb202\x91\xfe\xa7\x9f~\x1a?\xff\xf9\xcfY\xb0\x7fKK\x0b^{\xed5\xe4\xf3y\x18\x0c\x869}\x06\xf9\x05b\xe7\xce\x9d\xd8\xb6m\x1b^{\xed5\xec\xdd\xbbw\xdas\x87\x87\x87g,!6\x177a)\x22B%c\xda\xda\xda\xf0\xea\xab\xaf\xe2\x86\x1bn`\x17\x96'\x9f|\x12k\xd7\xae\x85\xd5je\x04R\xe9\xc1\x08\x06\x83\xb8\xfb\xee\xbb\xf1\xca+\xaf0\x97\xf1_\xfd\xd5_\xc1\xe7\xf3!\x1a\x8d\xb2D\x11^\x22\x8bcE\x10@~\xdb_\xfa\x87\xbf\xbc\xca:\xb9=jjj`\xb7\xdb\x11\x0c\x06\x11\x8b\xc5\x98\xb2D\x07d\xb1\x04\x0e:\x10)\xbe\x86\x0eiR\x04\x03\x81\x00\x04A\x80V\xab\x85\xc1`\x80\xc3\xe1`\x8d\xc1\x8b)\x0c\xa4j\xa5\xd3itww#\x14\x0a\xe1\xbd\xf7\xdeCkk+#\x1f\xd4\xd2n\xa5\x13\xc0l6\x8bD\x22\x01\x9f\xcf\x87\xb1\xb11\x0c\x0f\x0f\xa3\xbe\xbe\x9e5M'\xb7y1\x8c\x8f\x8f\xa3\xb7\xb7\x17{\xf6\xecA<\x1e\x87V\xab\xc5\x95W^\x09\xa3\xd1\x08\xb3\xd9\x5c\xe0\x02T\xb6f\xcc\xe7\xf3\x88D\x22\x10E\x11f\xb3\x99\xb9\x9e\xe7c\x909\xce>\xbe\xf5\xado\xc1\xe7\xf3\xb1y}\xf6\xd9ggu\xeb\x8b\xa2\x88]\xbbv\xe1\x0b_\xf8\x02\xdb\x8b\x00\xb0g\xcf\x1e\x94\x95\x95\xcd\xf9w\xa7\xd3ih\xb5Z\xec\xda\xb5\x0b\xf7\xde{/\xba\xbb\xbb\x11\x89D\x0a.\xa7\x94l\x01\x00###L\xad>\x15\xcc\xd4\x83^N\x02\x01\xe0\xfa\xeb\xaf\xc73\xcf<\x83\xcf|\xe63,\xce\xef\xa1\x87\x1e\xc2\x8f\x7f\xfcc\xc4\xe3q\x88\xa2X\xe0\xc1\xc8\xe5r\xd8\xb9s'4\x1a\x0d>\xfb\xd9\xcf\xe2;\xdf\xf9\x0e\xb4Z-\xfc~?\xfe\xf5_\xff\x15\xb7\xdcr\x0b\xc6\xc7\xc7\x91\xcf\xe7\xe1\xf1xPYY\x09\xa3\xd1\xb8b\xce..\x04-s\x02\xa8\xec!;\xdbm\x8bci\x90\x07r\xb3R\xdd?\x8dF\x83\xf7\xbd\xef}\xe8\xea\xeab\xaa[(\x14\x82\xc3\xe1\xc0\xc8\xc8H\xc1\x5c\x17K\x04\x90\xf7\xfe\xa4\xf5\x90N\xa7\x91L&\xd1\xdd\xdd\x8d\x8f}\xecc\xf3\xfe\x9c\x17^x!\xa2\xd1(\xee\xb9\xe7\x1e\xa6J\x10y\x5c\xc9\xbd7i\xbc\xd3\xe94\xa2\xd1(\xbc^/\xba\xbb\xbbq\xf3\xcd7\xa3\xae\xaenN\xef\xb1f\xcd\x1a\xc4b1x\xbd^tvv\xc2\xe7\xf3\xe1\xf2\xcb/G8\x1cFee%\xfb=\xf2\xf9T\x1e\xec\x82 \x14\x0a\xf1,\xbds\x0c\x92$abb\x02?\xfe\xf1\x8f\xd9<_{\xed\xb5\xb8\xf4\xd2Kg5\xda\x99L\x06w\xddu\x17DQ\x84$IH&\x93\xd8\xbbw/l6\xdb\xbcT\xf9h4\x8a\xbf\xf8\x8b\xbf\xc0+\xaf\xbc2MA\xa6z\xa5uuu\xd8\xbcy3\x1a\x1b\x1bq\xe1\x85\x17\xb2\x8b\xdfL{\xa2\xd4\xef\x9ek\x11\xe1\x5c.\x87O\x7f\xfa\xd3x\xf1\xc5\x17\x99Kwrr\x12\xff\xfe\xef\xff\x8e\xaf|\xe5+\x88D\x22\x05\x1e\x91\x91\x91\x11\xd6&\xf3\xba\xeb\xae\xc3\xd6\xad[111\x01Q\x14\xf1\xf8\xe3\x8f\xe3\xa2\x8b.b\x97\xb0\x89\x89\x09\xf8|>\xb4\xb5\xb5a\xd5\xaaU\xcb\xde\x8b\xc1\xfb\x85\xaf\x10\x05P\xbe\x88\x95\xea\x1f\x9f\xec\xa5y+#\xb7\x04\x05\x5c\x9b\xcdf\xb4\xb5\xb5\xa1\xae\xae\x0e\xdd\xdd\xdd\x88\xc7\xe3\xd0\xeb\xf5\xd3\x0e\xdc\x992\xbc\x8b)r\x94!\xec\xf3\xf9\xf0\xf6\xdbo\xe3\x8a+\xae\x98\xf3\xe5\x80n\xd7\xdb\xb6m\x83\xdf\xefG4\x1a\x85\xc3\xe1@EE\x05\xca\xca\xcaX\xab\xba\x95z\xd1\xc8f\xb3H\xa5R\x08\x04\x02\x18\x1c\x1c\x84\xddnG]]\x1d+\xbfP\x8a\xf8'\x93I\xfc\xe4'?\xc1\xb3\xcf>\x8b\xde\xde^\x04\x02\x01\x5cs\xcd5\xacN\x9a\xbc\xdb\x8b8\x96>\xf9S\xa9Tx\xe1\x85\x17066\xc6\xce\x80\xcf\x7f\xfe\xf3sr\xdd>\xfe\xf8\xe3\x18\x1a\x1ab??\xf6\xd8c\xb8\xf8\xe2\x8bY\x9c\xf0l{Z\xa5R\xe1\xd9g\x9f\xc5\xa7?\xfdi\xe4r9\x18\x0c\x06\x16\xae\xd0\xda\xda\x8a\x1bn\xb8\x017\xddt\x13\xae\xbf\xfe\xfa9'\x18\xcd\xc5\xc6\x90\x87b\xb6\xe7\xaa\xd5j\xc4b1|\xeb[\xdf\xc2\xde\xbd{166\x06A\x10\xf0\xc6\x1bo\xe0\xc6\x1boD{{{\xc1\xfb\xd0\x05\x88\x92`>\xf3\x99\xcf\xe0k_\xfb\x1a\xb2\xd9,\xfc~?\x9e}\xf6Y|\xecc\x1fC*\x95\x82J\xa5bY\xd7\xabV\xad\xe2\x8b\x91\xe3\xac\xe1\x8c\x97\x81\xe1\xd9?K\xebfFA\xc9j\xb5\x1a\xe9t\x9ae\xc0\xe9t:\xe6\xa6\xb0Z\xad\x88\xc7\xe3\xecfL%E\x94\x07)\x95O(X`\xff_]\xa4\xf7\xdf\xb6m\x1b\xb2\xd9\xec\xb4l8\xb9{Q\x99iz\xf0\xe0A\xec\xd8\xb1\x03^\xaf\x17\xbd\xbd\xbd\x18\x1c\x1c\xc4\xf8\xf88\x02\x81\x00\x8bG[\x89\xa0\xb9\x88D\x22\xf0x<\xf0x<\xb8\xe5\x96[\x0aJ\xf7(\xdd\xb64']]]x\xe0\x81\x07\xf0o\xff\xf6o8~\xfc8\xc2\xe10\x0c\x06\x03\x9a\x9b\x9ba\xb5Za4\x1a\xa7\xc5z*/x\x84p8<\xef\x0b^\xb1\xf7\xe48s\xc8d2P\xa9Tx\xe4\x91G\xd8\xfa\xb8\xf0\xc2\x0bq\xfb\xed\xb7\x97\x9c\xcbl6\x8b\xf1\xf1q<\xfa\xe8\xa3\xec\xb1\xf7\xbd\xef}x\xf0\xc1\x07Y\xdch)P7\x90?\xff\xf3?\xc7\xddw\xdf\x8d\x5c.\x07\xadV\x8bD\x22\x81\xc6\xc6F<\xf7\xdcs\xd8\xb1c\x07\xbe\xff\xfd\xef\xe3\xa6\x9bnb1\xa6\xf2\x18\xd4\xd3!\x81\xa9T\x8a%\xab\xcd\x16\xa2\xa4\xd3\xe9\x90\xc9d\xf0\x85/|\x81\x85\xc7\xa4\xd3i\xfc\xf2\x97\xbfD2\x99d\x09V\xf2\xefE\xfb\xeb\xe2\x8b/\xc6\xa6M\x9b\xd8{\xed\xd8\xb1\x03~\xbf\xbf\xa0\xd6 \x9d\xa9\x00\x10\x89D\xd0\xdf\xdf\x0f\xaf\xd7\xcb\xc5\x12\x8e\xe5A\x00y\x0c\xd0\xd2W\x00Ia\xa3@\xfe\x8d\x1b7\x02\x00\x0c\x06\x03k\x17\xe7\xf5z\x91\xcdf\xa1\xd7\xeb\x0b\x5c\xbcJ\x90\x0b\x99\x0a\x7f\xca{\x0aS\xad\xc1\x81\x81\x01\xfc\xd3?\xfd\x13N\x9c81k\xdfgA\x10p\xe4\xc8\x11\xec\xde\xbd\x1b]]]\x88D\x22\xf0\xfb\xfd\xe8\xe9\xe9\xc1\xd8\xd8\x18|>\x1f\xe2\xf1x\xc1A\xbc\x92\xc8;eW\x87\xc3a\x0c\x0f\x0fc\xc3\x86\x0d\x05m\xfb\x8a\xcd\xb7\xdf\xef\xc7\xcf\x7f\xfes<\xf8\xe0\x83\xf8\xedo\x7f\x8b\xd1\xd1Qf\x10\x9b\x9a\x9a\xd0\xd4\xd4\x04\xbb\xdd\x0e\x93\xc9\xc4\xe6\xb2\xd8\xd8\xcac\xfd\x94*!\xc7\xd2_;:\x9d\x0e\xbf\xff\xfd\xef\x11\x0e\x87\xa1\xd3\xe9\x90\xcdf\xf1\xd5\xaf~uV\xf5L\x14E|\xf1\x8b_d\xff\xd6h4x\xec\xb1\xc7\xe6\xd4\xbf]\x92$\x18\x0c\x06\x9c\x7f\xfe\xf9\xd8\xbau+{~:\x9d\xc6\xc3\x0f?\x8c\x81\x81\x01|\xf4\xa3\x1fe\xa1\x07\xf2s`.=\xe2\xe7\x12s.I\x12FGG\xe7\x1c\x9f.I\x12:::p\xf5\xd5W\xb3\xdf\xf1\xde{\xef\xa1\xab\xabkZ\xc2\x9b\x1ceee\xf8\xc8G>\xc2>\xfb\xc0\xc0\x00\xc6\xc6\xc6\x8a\xc6\x1f\xe6\xf3y\x0c\x0c\x0c\xe0\xc4\x89\x13\xe8\xef\xefG:\x9d\xe6\x8b\x94\xe3\x8c\x80\x97\x81Y\xe1\x86\x80n\xd7\xa4&Q\x00\xb7$I\xa8\xa8\xa8@6\x9b\x85\xd9l\x86Z\xadF&\x93\x99V\xdbO~\x90\xe9\xf5z\x88\xa2\xc8\x12\x07\xa8\xcc\x8cV\xab\x85\xc5bAee%\x9a\x9b\x9b\xf1\xc6\x1bo`\xff\xfe\xfdhoo\xc7\xb5\xd7^\x8b\x86\x86\x06TUU\xc1h4\x22\x9dN#\x9dN#\x91H`hh\x08\xef\xbd\xf7\x1e\x8e\x1f?\x8e@ \x80x<\x8et:\x8d\xfe\xfe~\x8c\x8c\x8c\xa0\xa9\xa9\x09\x15\x15\x15\xac|\xcdJ\xaa\x0bH\xf3E\xf1{\xa9T\x0a\x1b6l(J\xce\xf3\xf9<\xbc^/\x8e\x1d;\x86\xe7\x9f\x7f\x1e{\xf6\xec\xc1\xd0\xd0\x10\x22\x91\x08S\x82DQ\xc4E\x17]\x84\xea\xeaj\xb8\x5c.F\x00\xe5FWi\xe4\x94\xe5,\xb8\x9awn\x80\xc2>\x1ey\xe4\x11\xb6\xaf\xadV+n\xb9\xe5\x16\x96\x94Q\x0cj\xb5\x1a\x13\x13\x13\xd8\xbau+\xf4z=\x92\xc9$n\xbb\xed\xb6Y\xcb\xc5\xc8\x09\xe4e\x97]\x86\xc3\x87\x0fC\x14Ed\xb3Y\xb4\xb4\xb4\xe0\x97\xbf\xfc%6m\xda\x84t:}Z1\xbdsq\xeb\x0e\x0e\x0e\xe2\xfe\xfb\xefGMM\x0d\xbe\xf9\xcdo\x96\xfc}*\x95\x8a\x85X\xdc}\xf7\xdd\xd8\xb1c\x07+L\xff\xcc3\xcf\xe0G?\xfa\xd1\x8c%]\xe2\xf18>\xfa\xd1\x8f\xe2\xf1\xc7\x1fG(\x14\x82$I\xf8\xfd\xef\x7f\x8f\x07\x1ex\x80e\xcc\xcb?w8\x1cF6\x9bE0\x18D2\x99\x5c\xb6u5\x8b\x85\x85\xf1K\xe32!\x80|\x22\xcf]\x02H\x99u\xf2\xfaW\xf9|\x1eF\xa3\x91\xd5\x0a\xa3\x8e\x11\xf2b\xce\xf4>\x1a\x8d\x06\x16\x8b\xa5\xa0e\x1c\xbd\x8fF\xa3AYY\x19\xaa\xab\xab\xd1\xdc\xdc\x8c\x9e\x9e\x1e\x1c\x8f\x91\x91\x11\x84B!LNN\x22\x99L\xb2\xcf499\x89\xbe\xbe>444\xc0\xe5r\xc1f\xb3\x15(\x8f+\xc1\x80Sfv(\x14\xc2\xd4\xd4\x14\xaa\xaa\xaa\xa6)'4?\x07\x0f\x1e\xc4K/\xbd\x84\x1d;v\xa0\xaf\xaf\x0f^\xaf\x17\x91H\xa4\xa0\xd9}[[\x1b:::P]]\x0d\x9b\xcd\x06\x83\xc1\xc0\xc63\x97\xcbM\xeb\xef]l\xbf\xf3\xf0\x8es\x03j\xb5\x1a\xfb\xf7\xef\xc7\xe1\xc3\x87\xa1\xd3\xe9\x90J\xa5\xf0\xf8\xe3\x8f\x9f4\x083\x5c\xa2(\x1c\xe3S\x9f\xfaTA\xf8\xc6\xcf~\xf63d2\x99\x92D\x8aH\xe5\xad\xb7\xde\x8a}\xfb\xf61bu\xfd\xf5\xd7\xe3\xb9\xe7\x9e\x83\xd3\xe9D6\x9b\x9dS\xd1\xe9\xd3\xd93^\xaf\x17\xf7\xde{/\xd2\xe94zzz\xf0\xd8c\x8f\x15t>Q\x92\xd5\xae\xae.LLL\x00\x00\xca\xcb\xcbq\xe7\x9dw\xe2\xd9g\x9f\x85V\xab\xc5\xe0\xe0 v\xef\xde\x8d\xcb/\xbf|\xc6\xd7\xd7\xd5\xd5\xe1\xd6[o\xc5\x7f\xfd\xd7\x7fA\x14E\xbc\xf3\xce;\x88D\x22\xd0j\xb5E\xdb/R\x9f\xe2\x85\xaa\x13\xc8\xcb\xb0q\xcc\x86\x05s\x01\x173\x06|\xf1-}\x15I\xde\xb5C\xa3\xd1\xa0\xad\xad\x8d\x110jh\x1e\x8f\xc7Y\xbc\x9d\xb2\x06\x96<\x19\xc0`0L\xeb\x19L\x06\xc7h4\xc2\xe1p\xa0\xbe\xbe\x1e\xad\xad\xad\xb0\xd9l\x98\x9a\x9a\xc2\xf0\xf00\x0e\x1e<\x887\xdex\x03\xbbw\xef\xc6\x9e={\xb0\x7f\xff~\xec\xdc\xb9\x13\x03\x03\x03\x08\x87\xc3\x88\xc5b\x05\xbf+\x9dN\xa3\xb7\xb7\x17\xa3\xa3\xa3p\xbb\xdd\x08\x85B\xb3\xb6\x89Z\x8e\xea\x1f\xd5\xfd\xf3z\xbd\xb8\xe2\x8a+\x8a\x1a\xde]\xbbv\xe1\xe7?\xff9\xdex\xe3\x0d\xf4\xf6\xf6\xc2\xe3\xf1 \x12\x89\xb09'R}\xfd\xf5\xd7\xc3\xe9t\xa2\xb2\xb2\x92\x95\x8eQ\xba\x92\x95Y\xfe3\xa9&\x1cK\x1f?\xfb\xd9\xcf\x00\x9c\x8c\x89\xd3\xe9t\xb8\xe3\x8e;X\x1b\xb3\xa2\x86B\x10p\xfc\xf8ql\xdf\xbe\x9d\xc5\xf3\xde{\xef\xbd0\x99L%\xe7\x9cb\xfc\xbe\xf4\xa5/\xe1\xa5\x97^b\xcf\xbd\xf2\xca+\xf1\xd2K/\xc1\xe9t\x96$\x9e\x0b)BX\xadV\xac_\xbf\x9e\xfd|\xe0\xc0\x01<\xff\xfc\xf3E_322\x82\xbe\xbe>6&\xa1P\x08w\xdcq\x07\x8b\x0bT\xab\xd5\xf8\x8f\xff\xf8\x0fx\xbd^$\x12\x09\xa6\xa4\xcb\xf7\xc1\xa4{\x12\xdf\xf8\xc67\x0a\xbe\xdfK/\xbd\xc4.\xd5r[\xa9\x8c\x87>\x1d\xc4\xe3qvy^\x0a\xe0g\xc2\x0a \x80\xf31\x12\xa5\x16\x87\xbc\x84\x0c\xc7\xe2\xaa\x7fD\x12R\xa9\x14\xfc~?\xee\xb8\xe3\x0eF\xd8\x08\xe1p\x98\xa9x\x94\xe1V\xac\x9f\xa3N\xa7\x83\xddng\xc9\x03r\x02(W\x01].\x17\x9a\x9b\x9b\xd1\xde\xde\xce\x8a\x10\x87\xc3a\xf8\xfd~x\xbd^\x8c\x8f\x8fcbb\x02\xb1X\x8c\xd5\x0fL&\x93\xec}(\xeehhh\x08\xc3\xc3\xc3\x18\x1d\x1den\x13e\xff\xda\xe5:oT\x1f1\x1c\x0ec||\x1c\xd5\xd5\xd5(//\x9f\xb6gz{{\xf1\xe2\x8b/\xe2\xe0\xc1\x83\x18\x19\x19\x81\xc7\xe3A\x22\x91(p\xdfK\x92\x84\x0b/\xbc\x10UUU\xa8\xaa\xaabsH\xf3]l\xaf\x16#\x86\x1c\xe7\x0e$I\xc2\x9e={\xd8\xcf\x1f\xfe\xf0\x87a\xb3\xd9fu\xe1n\xdd\xba\x95\xc5\xdc\x02\xc0\xfd\xf7\xdf_\xb2\xc7/%\x85l\xdf\xbe\x1d\xdf\xfe\xf6\xb7Y\x0cq[[\x1b\xb6o\xdf~F3\xc7\xa9\x06\xe9\x83\x0f>\x88\xd6\xd6Vv\xce\xfd\xeaW\xbf\xc2\xb1c\xc7\xa6\xadc\xea\x19L \xd2{\xf7\xddw\xb3\xe7MMM\xe1\xd7\xbf\xfe5\x0e\x1c8P\x90\xd0A\xc8\xa43\xa8\xa8\xa8\xc0\xa6M\x9b\xd8\xe5\xf9\xcd7\xdf\x84\xc1`(8\x7f\x95\x04\xf0t\xf7S\x7f\x7f?\xf6\xee\xdd\x8b\xce\xce\xce%aG\xb9\xabw\x05\x11@9\xa1S.>\xf9\x02\x98)\xb0w\xa6zc\x1c\x0bo\x04\x88H\x84B!\xc4\xe3q\xd4\xd7\xd7\x17(z\xc1`\x10\xe9t\x1a\xc1`\x10\xe1p\x98e\xff\xca\xfb\xce\x12\x0c\x06\x03*++\xa1\xd5j\xa7\xa9G\xd4\xc7\xd7h4\xa2\xbc\xbc\x1c\xf5\xf5\xf5hllDKK\x0b\xe2\xf18#\x81\xe1p\x18\xd1h\x94\x156\xa6vfT\x80\x9a\x0aVS\xfb\xb2c\xc7\x8eatt\x14\xe3\xe3\xe3\x88D\x22\xec0]\x09\xf3\x16\x8dFY\xe1\xe7\x0f~\xf0\x83\x05\xbdy\x01`ll\x0c\xbf\xfb\xdd\xef\xb0\x7f\xff~\xf8\xfd~6>\xcan-j\xb5\x1a\x17_|1jkk\x0b\xd4?\xb9\xd2[\xea`\xa7B\xd4\x1c\xe7\x0e\x8e\x1f?\xceH\x0f\x00\x5c}\xf5\xd5E\x09\xbf\x1c\x89D\x02\xaf\xbf\xfe:\xfb\xf9\xb6\xdbnCccc\xc9\xd7\xa8T*V\xe7O\xab\xd5\x22\x99L\xc2j\xb5\xe2\xddw\xdfea\x05\x8bE6f\xfa?\xa3\xd1\x88\x87\x1ez\x88\x95.\x12E\x11_\xfa\xd2\x97\x90\xcf\xe7\x91\xc9d\xe0\xf7\xfb155\x85X,V4\xeb\xfd\xe2\x8b/\x86^\xafg!\x18;v\xec@8\x1c\x9e\x96\xb8!\xb7a\x9f\xf8\xc4'\xd8\x99:66\x86\x91\x91\x11F\x9a\xc9s1W\x028[\xa2\x15\x95\x9d\x11E\x11\xd1h\x14\xd1htI]^g\xeb\xfd\xce\xb1\x0c\x14\xc0\xb9L\xba\xbc\x91|\xb1\xd7s\x17\xf2\xe2\x92\x08\xca\xfa\x0d\x87\xc3\x98\x9a\x9aBEE\x05\xaa\xab\xab\x0b6\x22\x95|\xa0\x96mD\xb0\x8a\x19\xfc\x8a\x8a\x0a\x18\x8dFV\x97\x8f\x12\x08\xe4\xf3-\x8a\x22\xca\xca\xcaPUU\x85\x86\x86\x06\xacZ\xb5\x0aF\xa3\x11\x99L\x86\xf5\x08\xa6^\xb6Tf\x81\x0eF\xeaTB\x17\x07\x9dN\x87\xc1\xc1A\x0c\x0d\x0da||\x9c\xb9b\x96\xb3\x0aH\xea\x1f\xc5\xfe\x0d\x0f\x0fc\xe3\xc6\x8d0\x99L\xd3\x9e\xb3\x7f\xff~\xf4\xf7\xf7#\x14\x0a1\x82\x9f\xcdf\xa7\x8dOGG\x07\x9a\x9a\x9a\x98\xfaG\x89<\xc5\x0c\xd9L\xfbu\xa6va\x1cK\x13\x87\x0f\x1fF0\x18\x84J\xa5\x82\xcdfc\x85\x9fK\xad\xbb\x9e\x9e\x1e\xd6\xef\x17\x00n\xbc\xf1\xc69\xc5\xdc>\xf1\xc4\x13\x18\x1f\x1fg\x04\xe9\x97\xbf\xfc%\x1c\x0e\xc7Y\xeb\xe0\x93\xc9\x9cT\xe5\x1e~\xf8a\xe6\xb6\x0d\x87\xc3\xb8\xef\xbe\xfb\xa0\xd1h\xb0w\xef^\xbc\xf7\xde{\xacs\x91\x92\xa0\x94\x97\x97\xe3\x82\x0b.`\x8f\x1dD\x22\x11f\x8c\xe4c\xe4\xf7\xfbq\xf8\xf0aLLL \x10\x080w\xba\xb2\x0b\x82N\xa7\xc3\xc6\x8d\x1bQ]]\x8d\x8a\x8a\x0a\x98L&F\xb2g2\xce\xc5B6\xf8E\xed\xdc\x00\xed\xa7W^y\x85\xcdeMMM\x01\xa1)\xb6\xe6T*\x15\xdez\xeb-\x00'c\xd9\xca\xcb\xcb\xf1\xa1\x0f}h\xd6\xdf511\x81\xef}\xef{\xec\xb1\xbb\xee\xba\x8b\xbdn\xb1\xd4\xbf\xb9\xf4\x16O\xa7\xd3\xe8\xe8\xe8\xc0u\xd7]\xc7\xe2\xf9\x9e\x7f\xfey\xbc\xfa\xea\xab\xac\x9da1\x12\xa6R\xa9\xa0\xd3\xe9p\xd1E\x17\xb1\xc7\xfa\xfb\xfb\xe1\xf7\xfbg\xfc<*\x95\x0a\x06\x83\x01UUUl\xff\x1d\x04\x83A\xdcv\xdbm,\xf1C\x8eP(\x84\xb1\xb11\x0c\x0e\x0e\x22\x1c\x0e#\x91H\xb0\xd8\x18\xb9\x92D\xf3q\xfe\xf9\xe7\xc3b\xb1\xc0f\xb317m\xa9\x182\xa5\x0aX[[\x8b\xe6\xe6f\xf4\xf6\xf6B\x14EV\x9a\x86\xe6]\x14E\xe6N\xa1\x9e\x9c\xf4\xb7(\x8a\x08\x87\xc3\xe8\xef\xefg\xad\xcc\x9cN'+G\xb3\x1c\x08\x09\x19\x85x<\x8e`0\x88\xc1\xc1A\x96Y(\x1f\xe7\xc9\xc9Itww#\x16\x8b\xb1\xb8\xa0T*UP\xb3\x91\x08\xde\xe5\x97_\x8e\x8a\x8a\x0a8\x9dN\x94\x95\x95\xb1q.\xa6\xd8\x92\xa23\x9b\xd2R,\xa4\x83c\xe9\xac!A\x10\x0a2y\x01`\xdd\xbau3v\xf1\xa0\xd7\xec\xdc\xb9\xb3\xe0\xf1\x8b.\xbah\xc6\xe2\xcf\xb4\xc6\x9ey\xe6\x19\x00'=:\xf5\xf5\xf5X\xb7n]\xc9.#\xb3|\xf8\xb90\x89y\xbfm*\x95\xc2e\x97]\x06\x8b\xc5\x82H$\x02\x00\xd8\xb9s'ZZZ\x98\x9d*v~\x85\xc3a\xdc\x7f\xff\xfd\xd0j\xb5\x88\xc7\xe3,\xfe\xae\x94\x22J\x9d\x8e(<\xc5\xe3\xf1\xa0\xbd\xbd\x9d\xb9h\xe5\xe5\xd3\x02\x81\x00\xd4j5\x12\x89\x04\xecv;\x9cN';\xc7i\x8c}>\x1f***\x10\x89DX8\xd5\xd8\xd8\xd8\xb4\xb9\xa40\xaa\x85\xdc\x97\xd4\x13Y\xa7\xd3!\x97\xcbadd\x04\x83\x83\x83\x00\x00\x8b\xc5\x82\xb6\xb66\xbe\xe18\x01\x9cy\xf1\x10\x01\xd4h4\xc8\xe5r0\x18\x0c,K\xd4\xedvcdd\x04\xfd\xfd\xfdp8\x1c,\xa1@\xd9\xe0\x9e\x03\xd3T\x1d\xfa\x9b\x5c\xa0\x99L\x86e\xd5\x86\xc3ax\xbd^\xf4\xf4\xf4\xc0n\xb7\xe3\xf6\xdboGUU\xd5\xb4\xb9\xc9d2\xd8\xb1c\x07v\xef\xde\x8d@ \xc0\x0a1\xa7R\xa9\x82Vp\xf4\xfb\xd7\xad[WP@X\x19\xffW\x0cD\xe4\xacV+\xaa\xab\xab\xd1\xd0\xd0\x80\xd5\xabWchh\x88\xa9}Db\xe5\xb1\x80\x14\x9fCa\x04\xf4\x1d\x0d\x06\x03:;;\xd1\xde\xde\x8e\x91\x91\x11\xe6\xd2\x9c\xcbg9W\xd4?r\xfb\x8c\x8c\x8c\xa0\xbe\xbe\x1eN\xa7\xb3\xe0`\x97$\x09\xfb\xf6\xedCww7+\xfbB\xea\x822\xcbp\xed\xda\xb5hjjBuu5\xecv;t:]\xc9\xd6Ss\xddo\xbc\x17\xf0\xd2?+\x0e\x1e<\xc8\xc8\x0f\xc5\xa5\xcd6g\x87\x0e\x1d\x02p2\xc1\xa0\xae\xaenN\x8a\xce\x1f\xfe\xf0\x07\xd6e\xe4o\xfe\xe6o\x0aT\xc7y\xe3\x14\xd6\xdf|\x5c\xc1\x9b7o\xc6\xb6m\xdb\x00\x00o\xbf\xfd6>\xf9\xc9O\xb2\xf2S3\xbd&\x1e\x8f\xb3R1\xb3\x91\xabL&\x83\xea\xeaj\x98\xcdf\x84\xc3a\xd6\xfb\xf7\xf2\xcb/G4\x1a-\x88\xe3U\xa9T\x18\x19\x19\xc1\xf0\xf00\xb2\xd9,\x9cN'6m\xda\xc4\xcef\x82\xd7\xebE\x7f\x7f?\xe2\xf18\xb3\x89>\x9fo\xda\xe7!\xaf\xcdlc_\xac\xc7{\xb1\xc7(\x16qdd\x04\xeb\xd6\xadCYY\x19\x02\x81\x00\xfb\x0cn\xb7\x9b\xd5YT\x8eY\xb19\xe1\xe7\xc5\x0a#\x80\xe4\x02\xa4\xd6?z\xbd\x1e\xa1P\x08\xa2(\xb2\x1e\x89\xc7\x8f\x1fg\xeeD\xca\x94\xa2 \xf5\xb3U>`)\x1e\xe8\xf2\x929D\xfa\xc8\xddKeU\xa2\xd1(\x82\xc1 <\x1e\x0fB\xa1\x10.\xbf\xfcr\x5ct\xd1EE\x8b\xf9\x0a\x82\x80\xbe\xbe>\xbc\xf3\xce;\x18\x1b\x1bC0\x18D4\x1ae\x99eJW\x82 \x08\xd8\xb0a\x03\xaa\xaa\xaa\xe0p8X\x06\xb0\xb2\x06\xa0r\x93\xd3!e4\x1aQQQ\x81\x86\x86\x06455\xa1\xae\xae\x0e\x03\x03\x03\x05\x87\x18)}r\x12(\xbfDP}\xb1L&\x83\xc3\x87\x0f\xa3\xa9\xa9\x09\xf5\xf5\xf5p8\x1c\xcc\xady.\xabR\xf2\xd8?\xaaQv\xfb\xed\xb7O\xcb\x8e\x9e\x9c\x9c\xc4\xd1\xa3G\xe1\xf1x\x98\xfb\xb7X\xe6\xaf(\x8a\xe8\xe8\xe8@MM\x0d*++QVV\x06\x9dN\xc7\x0e\xf0\xd3q\x17\x15\xdb\x97|\xaf.-\x02\xd8\xdf\xdf\xcf\xce\x0cy\xdb\xc7R\xaf9|\xf80\xbb\x84o\xdc\xb8\xb1\xe4kT*\x15\xc6\xc7\xc7Y\xf7\x1e\x00\xb8\xf9\xe6\x9b\xd9\xc5n\xde\x18\x0d\x03\xbf\xef\x06D\x01@\x91\xdfI\x0f]\xdb\x044\x9b\xe7E(T*\x15b\xb1\x18\xae\xb9\xe6\x1al\xdb\xb6\x0d:\x9d\x0e\xe3\xe3\xe3s\xda\x03\xf3\x11\x22\xb2\xd9,\xacVkA\xb9$\x9f\xcf\x07\x00\xac\x16k\xb1q\xa7\x18\xe7t:=\xed\x1c\xcdd2\x18\x19\x19\x99V[\xb7\x18\xf9\x94\xf2\x12\x90/>|\xf3\xdd\xbb\xb1X\x0c\x83\x83\x83\x88\xc7\xe3\xe8\xee\xeeFmm-s\xfb\x92::\x97\xb9^\x8c\xe4\x0f^?x\x09\x11@r\xdb\xc9'\x99\x8c~,\x16c\x1d\x1d\xa2\xd1(\xecv{A?Z\x8dF\x83\x13'N0\x22\x13\x8f\xc7QSS\xc32\xaf\xe6\xbb\x01\x97\x0b\x94\x07\x13);D\xfc\xa8TK,\x16+\x88\xa3\xd4h4hnn\xc6\x95W^9-XY\xbe\xd9\x8f\x1f?\x8em\xdb\xb6\xb1\xae\x11\x81@\xa0@IR\x06+\xd7\xd5\xd5a\xed\xda\xb5,\x8e\x8c\xdc\xbf\xc5H\xbf\x12\x94\xf9Ku\x01\xa90\xf4\xd8\xd8\xd84\xd7\xb5\xc1`(\xd8\xe0\x82 \xb0\x18R*\xe3@1<\xc3\xc3\xc3hhh`I)\xa2(\x9e\xb3\x8d\xd5\xe5\xb1\x7f\x91H\x04\xe3\xe3\xe3\xacc\x87RQ\xa7\xde\xca\xa4\xda\x16+\xfa\x0c\x00UUUX\xbf~=\x5c.\x17S\xffHe?\x9dCw9\x97\xdfYN\x04p``\x00Z\xad\x16\xe9t\x1a\x8d\x8d\x8dszMgg'\x8b\xdd\xa6Vj\xa5\xd6Kgg'K\xe42\x18\x0c(//?\xf5\x0f\x9e\xce\x01\x13Q@3\x03\x01\x14p\x92\xdc$\xb2\xa7\xe4\x06N\xa7\xd3\xd8\xb0aC\xc1c===hnn>\xed\xf5L\xaf\xa7s\xd3f\xb3\xb1\xc7\x02\x81@\xc1\xe5n&2\x94\xcb\xe5\x10\x8b\xc5X\x17\x95R\xf6`\xa6\xef'\xc3E\x87\xb0\x00\x00 \x00IDATe%\xe4uy\xa80s\xa2\xcf\xc0\xc0\x00\xb4:-\xaa\xab\xaaY\xd8\xc7\xc4\xc4\x04&''\xb1f\xcd\x1aX\xadVF\x5c\x93\xc9$k\x8fG\xb1\x88d\x8f\xb3\xd9,\x92\xc9$\xccf\xf3\xbc/\x80s\x1doyMZ\xb2gT\xab\x96B\x7ff\xb2\x9f\xc41\x96k\xac2\xc5\x89\xce\xf5\xb2%\x9e\xce\x22\x97\xdfJ\xe4\x7fK\x92\x84\xf2\xf2r\x8c\x8d\x8dM{>%#\x10,\x16\x0b3\xea:\x9d\x0e\xe5\xe5\xe50\x9b\xcdp:\x9d\x08\x87\xc3x\xf3\xcd7\x11\x08\x04P[[\x0b\xb3\xd9\xcc\x12\x00V2\x01\xa4\x80`\x22eJ\xa5\xc7\xe1p\xa0\xac\xac\x0c\xcd\xcd\xcd\xa8\xaa\xaaBmm-s\x9f*\x95#\x8a\x19\xe9\xed\xed\xc5\xb6m\xdbp\xe4\xc8\x11\x8c\x8c\x8c\xc0\xef\xf7#\x1a\x8d\xb2\xd2\x02\xf2\xa4\x0a*\x7f\xf0\xfe\xf7\xbf\x1f\x0d\x0d\x0d\xa8\xac\xac\x84\xd9l.(\xcf2\x97uE\xae\xdc\xf2\xf2r\xd4\xd5\xd5\xa1\xad\xad\x0dG\x8f\x1e\x85\xdf\xefg\xc9&\xa4l\xca\xe3\xcb\xe41\xa4\xa4&\x93[\xe6\xe8\xd1\xa3hhh@MM\x0dS\x8d\x89 \x9e\x8b\xf3M\xfb\xc5\xef\xf7#\x10\x08\xe0\xca+\xaf,\xd8\xdc*\x95\x0a^\xaf\x17\x9d\x9d\x9d\x98\x9a\x9ab\x074\x112\xf9\x9cK\x92\x84M\x9b6\xb1\xba\x7ff\xb3\xb9@\x95P\x92E\xa5\xda\x5c\xaa\xec\x07\xc5\xf0\xce\xe7f?\xdb\xfbr,<\x01\x9c\x9a\x9ab\x17\xaa\xe6\xe6\xe6\x92\xc6\x97\x1e\x0b\x87\xc3,\xde\xaf\xb6\xb6\xb6`\xee\x8a\xd9\x84\xd1\xd1QFz\xea\xeb\xeb\x0b.p\xf32\xf6*\x15TR\xfed\x0c\xa0<\xd9\xa9\xe0C\xaaNK\x05\x92$\x09f\xb3\x19f\xb3\x99\xd5\xcc\x1b\x1e\x1eF[[\x1b\x8bC\x9e\x8f=\xd4\xeb\xf5\xd3\xceK\xba\xa8;\x9d\xcei\x040\x91H`rr\xb2d\xdd\x5cj\xc7y*H\xa5R\xc8J\xd9\x821\x97'z\x09\x82\x80\x13'N\xa0\xab\xab\x0bZ\xad\x16\xa3#\xa30\x9b\xcd\x88F\xa3\xec\x1cN&\x93\xb8\xe4\x92K`4\x1a\xa7\x95\x95\x917\x05\xa0\xc7\xe3\x898\xca\xca\xca\x98m\x9a\xc9\xfd{\xaa\x9c\x83\x0a\xff\x93\xfd\xa0nV\xd9l\x16\xb5\xb5\xb5X\xbf~=\xcb\x19\xa0\xf5@\x9d\xa3B\xa1\x10\xf4z=.\xb8\xe0\x82\x82\x18\xd6\xb9\xac\x9bd2\xc9<\x5ct\xf1\x96$\x09\xb1X\x0c\x16\x8b\xa5\xe0}\x8a\xbd\x9f\xfc\xff\xe8\xbbP\x85\x0d\x9b\xcd6\xe3\xe5z\xa6\xcf\xa6t\xd1\x07\x02\x01\xf4\xf4\xf4@\x92$\xd4\xd4\xd4\xa0\xa1\xa1\x81\xbdvrr\x92\x09it\xe6\xe7\xf3y\x88\xa7K\xa2\xe4\x85\x9ciQi\xb5ZLNN\xa2\xa2\xa2\x02\xa9T\xaa\x80\x14P\xe0\xbe|Ah\xb5ZVC\xce\xe1p`\xcd\x9a5\xb8\xfd\xf6\xdbQYY\xc9\xcaY\xe4\xf3y\x84B!\x04\x83\xc1eY\xe6c\xae\xaa*\x15T\xb6\xdb\xed\x8c\x04\x91+\xddf\xb3\xc1d21uL9\xb7\xca\xf8\x0e\xbaA\xed\xde\xbd\x1b/\xbe\xf8\x22\xba\xbb\xbbY/\xdeh4\xca\x02\x9c)+\x8d\x16S.\x97\xc3\x86\x0d\x1bp\xc9%\x970\xf7/\x91\xf3\xf9\xb8[)\x09\xc8f\xb3\xb1X\xc0\xf3\xce;\x0f/\xbf\xfc2\x04A@*\x95b\xea\x94\xbc\x9c\x01\xb9\x82\xa9\x86\x17e\x03\xe7r9\x0c\x0e\x0ebpp\x10\xf5\xf5\xf5\xac\xbb\xc5\xe9(\x5cg\x1b\xe9t\x1a\x91H\x04>\x9f\x0f:\x9d\x0ek\xd7\xae\x9dv@P1l\x8f\xc7\xc3\x92?h\x8f\xc8\xc3\x04jjjX\xe1g\x87\xc31\xad\xed\xdbL\x87\x8d\xb2m\xa3\x92(\xca]\xf4\xb3\x19w\xba\xec\xc9K@q\xb7\xcd\xe2\x83\x92/.\xbc\xf0Bx\xbd^\x0c\x0f\x0f3\x028\xd3\xe5\xa8X\x92\x07)A\xa5\x14\x06\xb7\xdb\xcd.\xab.\x97\x0bf\xb3y\xda\xf3\xe7<\xe7\x1a\x0d \x08\x80\xa0\xa6E\xaf\xf8\xfb\xf4\xc6\x85\xca\xd4\xd4\xd4\xd4\xa0\xbb\xbb\x1b\xc0\xc9,\xddS\x09\x1bQ\xab\xd5\xa8\xa8\xa8\xc0\xd4\xd4T\x81}\xa2\xcb\xac\x5c\x09\x0d\x04\x02\x8c\xa0\x95\xb2e\x92$\xc1\xef\xf7\x17\x9d\x8b\xb9z\x10\x90GQ[\x90\xcf\xe7Y\xb9/\xba\xd4{<\x1ex<\x9e\x82\xe7\x06\x83A\x9c8q\x02k\xd7\xae\x85\xdb\xed\x9e\x95\x90\x0c\x0f\x0d#\x93>\x19\x92\x13\x08\x04\x10\x8dF\x17d\x8f\xabT*\x04\x83A\xb8\xddnv\x86\xc8\xc7\x98\xe2'\xa9\xbe%\xd9\xb8\x9e\x9e\x1e\x8c\x8e\x8e\x16(\xad\xe1p\x18v\xbb\x1d\xa2(\xa2\xb5\xb5\x15&\x93\x09\xbd\xbd\xbd\xe8\xe9\xe9\x81\xd1h\xc4\x05\x17\x5c\x80\xb2\xb22\xf6\x9d\x8e\x1f?\x8e\xc1\xc1A\xf6\xfc\x96\x96\x16\xf4\xf4\xf4```\x00\xd9l\x16:\x9d\x0e\x97^z)L&\x13\xb3\x95\x9d\x9d\x9d\x98\x98\x98\x80\xc1`@{{;\x5c.W\xc1\xda\x9f\x9c\x9c\xc4\x81\x03\x07X7\xa5\xf5\xeb\xd7#\x9b\xcd\xc2\xeb\xf52QDN`'''\x11\x89D \x8a\x22\xf3p\x112\x99\x0c:;;\xd9\xba\x9a\x9a\x9a\x82N\xa7\x83\xcb\xe5BWW\x17\xba\xbb\xbb\x91\xcf\xe71::\x8a\x0b/\xbc\x10f\xb3\xf9$\x97\x90W\x15\x9f\xaf\x14K\x03\x9f\xc9d\xe0v\xbb\x11\x0c\x06\x99\xf4O\x8bK\xe96\xa4/\x1b\x8f\xc7\x19Y$\x03m2\x99\x98q\x22bSYY\x89\x9a\x9a\x1a~\x82\xcfC!\x9cOl\xca\xeb\xaf\xbf\x8e\xed\xdb\xb7\xb3\x96jtS\xa0^\xbc\xf2\xac_\x9a?\x83\xc1\x80K/\xbd\x94\xd5\xa4\xa26l\xf3%\x80\xf4\x19\x92\xc9$\xa2\xd1(\x04A`\x89!\x1e\x8f\x07\x82 0\xb5\xd8h4N\xcb2%7\xb2N\xa7c\xaen\xbf\xdf\x8f\xee\xeenX\xadVh4\x1a\x84\xc3a\xd8l\xb6\x19\x13BJ\x8dU\xa9[9\xfb7\xf2,\xbeF\xe9bQ\x12ny(\x84\xf2&^p0#\x8f\x5c\xf6O1\x9d~\xbf\x1fCCC\xac\xa5\x94\xfc\xb9\xd1h\x14\x87\x0e\x1d\xc2\xe4\xe4$\x02\x81\x00\x8b\x87T\x96\xeb\xc9\xe7\xf3\xd8\xb8q#r\xb9\x1c\x22\x91\x08\x9bgRm\xe5\xdd\x08\xe4\xddz\xe8p\x09\x85B\x98\x9c\x9c,X\x17\xc5\xc2\x8f<\xf20\x8c%Q;\xe0A^#\x14\x84~X\xad\xd6\xe9$\xf4\x14\xd5\xe4\x5c.\x07\xbb\xdd\xce~\x8e\xc5b\xa7D\x00\xc9\x86)\xd73\xd9C\xa7\xd3\xc9\x88\x22e\x1d\xcf\xe5\xf3Ro\xf4\xf9|7\xaa\xd9(I\x12\xeb\x92\x14\x0e\x87Q^^\x0e\xadV\x8b\x9e\x9e\x1e\x0c\x0f\x0f\xcf\xd9;2<<\x8c\x91\x91\x919y\xde\xc6\xc7\xc7\xd9s\xb3\xd9,+F_\xcc^\x95R\xb8\x8a)p\xa4.[,\x16\x88\xa2\x88L&\x03\x87\xc3\x81\x96\x96\x16D\x22\x11\xf4\xf5\xf5\xb1\x8b\xb0\xd9d\xc6\xbb;\xdf\x85\xd7sR-t8\x1c\xec\xfc\xa2$\x1eI\x92011\x01\x8b\xc5\x02\x8f\xc7\x03\xb5Z\x8dP(\x84\xae\xae.ttt\xc0`0\xa0\xaf\xaf\x0f'N\x9c`cz\xf8\xf0atvv\x16x\xd3\xb2\xd9,v\xee\xdc\x89\xf6\xf6v\xf6\x9a\x89\x89\x09\xa8\xd5j\xa4R)\x1c8p\x00\xe7\x9dw\x1etz\x1db\xd1\x18&''\x0bzBG\xa3Q\xd6jQ\xadV\xa3\xaf\xaf\x0fn\xb7\x1b\xf5\xf5\xf5\x08\x87\xc38z\xf4h\x81\x08\xd2\xd9\xd9\x09\xb3\xd9\x8c\xc6\xc6F\xb8\x5c.\x0c\x0e\x0e\xb2\x5c\x0a\xaa\xa4\xf1\xce;\xef\xa0\xba\xba\x1a~\xbf\x9f\xed\xdf@ \x80\xbe\xbe>\xb4\xb4\xb4`jj\x0a\xe2{\xef\xbdw\xda\x12:\x19\x15\xb7\xdb] \x9b\x0b\x82\x80d29\xad\x03\x01\xb9\xecHB\xcdf\xb3L\xb1\x92+\x18\xf2N\x10\xa7BN\x973\x8a\xcdQ\xa9\x22\xbe\xf2\xc5\x9aH$\xd0\xdd\xdd\x8d\xd7^{\x0d]]]\xac\xf4\x8e\xcf\xe7C<\x1eg\xca\x1f\x91\x08e\xa0\xf1%\x97\x5c\xc2\xfaL\xba\xddnD\xa3\xd1\x02\xf7\xef|n\xfatK\x8dF\xa3H&\x93\x10E\x11\xb5\xb5\xb5\x98\x9c\x9cd\xe4\x93.\x15\xa4\x80\x12\x11$\x05\x93n\x83d\xb0\x8e\x1d;\x06\x9b\xcd\xc6n\x8c\x0e\x87\x03:\x9d\xee\xd4\xb3\x10K\xcd\xc3I\xe6\x87\x93\xb6?\xbf\xa0sKk?\x12\x89\xc0`0`\xcd\x9a5\xd3J:\xec\xdb\xb7\x0f\x03\x03\x03\xacc\x0b\x91?e1Y\xa7\xd3\x09\x8b\xc5\x82d2\x09\x9f\xcfW\x90}/\xff]\xc5\xe6-\x97\xcb!\x1e\x8f\xc3\xe3\xf1 \x91H\x14\xb8W(\x0bq\xae\xc9#\xd1h\x14\x1e\x8f\x07###\x88\xc5b\xacn\xe4B\xee\x83\xd39\xcf\x94\xef9/\x17 \xad\x85\x05\xfc\xfc\xf3\xfd\x0c\xb3\xc1\xe3\xf1\xb0\xf3\xa0T\xc1v\xb9\xaa%_GtY\xcbKy\xa8\x04\xd5\x8cdj\xcd\x9a5\xc8d2\xa8\xab\xabC2\x99\x9cV\x1ajN\xdfI\xa5\x82\x86B]\x00H\xb9?\x15\xa0\xcff\xb3\xa8\xac\xac<\xf9\x19\x16`\xdb\xc9\x09\xca\x5c\xd6\xa3<\xfb^\xfe\x98^\xaf\x9f\xf6\x7fd\xcb\xe4*\xde|\xe64\x9dN\xb3\xec\xe1\xb9\x90\x7f\xbd^\x0f\xab\xd5\x0a\xaf\xd7\x0bI\x92p\xe2\xc4\x09v\x8eR\xa1\xfeD\x22\xc1\xe2\x0a\xe5\x7f\x94\x97V\xf2\x10\xcc\xa7&/}\x7f:\xbf)1\xb1\x14\xf1\x9fK\x0b\xbfP(\x84d2\x09\xa7\xd3\x89\xd6\xd6V8\x1c\x0e\xc4b1\xb8\x5c.V\xcagdd\x04\x89D\x02CCC'\xd5\x5c\x9f\x1fz\xbd\x1eMMMhnn\x86\xd7\xeb\xc5\x91#G\x90H$\x98-\xa1\x84I\xb5Z\x8dL&\x03A\x10\xe0\xf3\xf90>>\x0e\xa3\xd1\x88\xbe\xbe>\x08\x82\x00\x83\xc1\xc0B\x8dr\xb9\x1c4\x1a\x0d\xca\xcb\xcbQQQ\x81\xf1\xf1q\xf8\xfd~\xec\xdb\xb7\x8fy\xc9\x8cF#\xf4z=\xc2\x91\x931\xd9\x9d\x9d\x9d\x90$\x09\xf1x\x9c\x952\xa3P\x1c\x9f\xcf\x87h4\xca\x08\x5c,\x16\xc3\xe8\xe8(\xc6\xc7\xc7Y\xc2lee%\x9cN'2\x99\x0c\xb3\xd7\xc7\x8f\x1f\xc7\xe8\xe8(\xa2\xd1(\xab\xefZQQ\xc1jwNMM1\xd2N\x89\xb6\x14\xe2\x15\x0a\x85 RP\xef)\x1dx*\x15rR\x0e\xb9l\x0eSSS\xac\xce\xd3L\x86DyP\x90A\xa6\xbaB\xe1p\x18Z\xad\x16\x1e\x8f\x07\xef\xbd\xf7\x1e\x1c\x0e\x07\x93T\xe9\xbd\xb8\xbbhn\xd2?\xfd-w\xdd\x05\x83Atuu\xe1\xd0\xa1Cl\xe1P\xcd\xc5X,\xc6b\xfe\xe4\x0a\x92R\xb5\xda\xbcy3n\xb8\xe1\x06\xac^\xbd\x1a\x8d\x8d\x8dp:\x9d\xd0\xeb\xf5,\x93t\x0eV\xb2P9P\x01\xd9\xcc\xc9\x1b\xae\xdb\xed\x86\xd3\xe9\x84(\x8a\x98\x9a\x9a\xc2\xc8\xc8\x08ssS\xbc#\x1d\xa2D\x00I)\xa6D\x0f\x8aY\x09\x04\x020\x9b\xcd\xac\xc4\x0c}Nf\xb4\xf2s$\x0bE\x94\xbd\x85\x9c\xa7bcF\xb13\xd1h\x94\xd5c\xbc\xed\xb6\xdb\xa6\xed\xa7P(\x84\xc3\x87\x0fchh\x08\xe1p\x98\xa9\x7fr\x97=\xe1\xfd\xef\x7f?.\xbd\xf4Rttt\xa0\xb1\xb1\x91%\xed(U\x19e\x8c(\x19 \x9f\xcf\x87\xee\xeen\x8c\x8d\x8d!\x14\x0a\xb1\xf7&W\xc2\x5c\xe1r\xb9\xd0\xde\xde\x8eK.\xb9\x04\xd5\xd5\xd5\xc5\x95\x9c%~\xd1Z\xca\x9f\xf5t\x09\xafr\x1d\x96\x95\x95\xb1\x90\x0b\x9a\xbf\xab\xae\xbaj\xc6:y\xc0\xc9\xba\xa0\x0f?\xfc0S\x92\xf5z\xfd\xa9\x8d\xa3J\x05\xa1?\x08\xf1\xf8\x11\xe4E\x81\xb9C#\x91\xc8\x9fTh\xb5l\xfd\x9e\xa2@@FY\xeeY\x98M5\xa63'\x10\x08L\xebU\xae\x0c\x85 \xc1CIv\xe6J\xfe\xe9RN\xaf\xb5\xdb\xed\xac\xf4J1\x98L&TTT0\xe5\x87~\x9fF\xa3a\x15\x1d\xe4kE\xadV\xa3\xb1\xb1\x11\x91H\x84\x95\xb6\xa1\xef\xb2f\xcd\x1a$\x93I\x1c:th\xd6\xd0\x0e\xf9\x19B\xef-\x0f\xe1QzB\x8a\x89\x143\x8d7u\xaf\xaa\xac\xacD}}=\xda\xdb\xdb\xa7\x9d\x1b&\x93\x09eeeH$\x12\x18\x1c\x1cd\x8aYMM\x0d\xd6\xacY\x03\x95J\x85\xba\xba:H\x92\xc4H]:\x9df\xe4O\x14E\xb8\x5c.VP\xbb\xab\xab\x8b\x11D\x97\xcb\x85\xf5\xeb\xd7C\x14E\xd6q\x8a\xcahi\xb5Z\xd4\xd7\xd7chh\x08\x13\x13\x13\xc8\xe5r\xb0Z\xadhii\x81\xc9d\xc2\xe0\xd0 \xfaz\xfb\x18\xc1s\xb9\x5c(++\x83\xd3\xe9Dyy94\x1a\x0d+\xd7F\xbd\xee\xa9\xd4N*\x95\x82^\xafG\xc7\x86\x0eT\xb9\xaa\x98\xfdkhh`\xe1N\xe1p\x18\x82 \xa0\xbc\xbc\x1ck\xd7\xaee\xe3B\xed\x01\x05A\xc0\xaaU\xabP^^\x8e\xa3G\x8f\x22\x1a\x8d\x22\x14\x0a\x9d\xf4\x04\x9cn\xd1F2V\x1a\x8d\x06n\xb7{\xc6x\xab\x99\xfa\x1b\x92\x8cK\xb7\x8c`0\x88\xc9\xc9I\xe6\xdaknnF{{;/0{\x0a\x86@\x92$x\xbd^\x8c\x8d\x8d\xe1\xd8\xb1c\x18\x18\x18`\x01\xb4>\x9f\x0fSSS\x88\xc5bl\xf1Q\xf0\xb2\x92@\xd0|vtt\xe0\xe6\x9bo\xc6\xea\xd5\xab\xd1\xd2\xd2\x02\x97\xcb\x05\xab\xd5zJ\xae_\xe5:\xa0x\xbfx<\x0e\x9f\xcf\x87\x81\x81\x01\x16B@EG)\xf1\x85bH)\x03\x8cjJR'\x0a\xb5Z\x8d\x9e\x9e\x1el\xdc\xb8\x91\xb5\x86\xd3h40\x99Lls\xcc\xd9 -\xa0\xb27\xd3<\xc9\x7f&E\x9dZ\xbey\xbd^\xb8\x5c.vp\x91\xbb\x96b\xffN\x9c8\x81P(\x84P(TP\xaeG\xfe\xbe\xb5\xb5\xb5\xe8\xe8\xe8@ss3\xea\xea\xeaX\xd2\x8e\xbc\xf3\x87\xf2\x10\x97\x7f&*(-/\xf2]\xcc}=\x97\x84\x0e\x0a\xdc\xa6\xc3\x9a\x5c9\xcbI\x89_*\x17\xc0\xd3\xfd\x0e\xa4\x8a\xe9\xf5z\xf6oR\xa3g*\x1e=\xd3\xef?\xe5q2\xa4N\xc6\x01jN\xfe.\xaa\x22!wK\x9f\xcew'\x8c\x8d\x8d\xb1KeEE\xc5\xac1\xe6r\xaf\x83\xfc;\x16ss+\x09\xe0|\xc7C\x1e\xca!I\x12\xaa\xab\xabYu\x8cb\xef\xa3\xd3\xe9`\xb7\xdba0\x18\xa6%\x90\x14K\xc8\xb0Z\xad\xd8\xb0a\x03+\x1bF\xcf\xd7\xe9tLY\x93\x87m\xd1z\x90\x8f\x11\x85\xd8P\x22\x0d\xbd\xbe\xa6\xa6\xa6\xe0s\x9e\xca\xfcP\xfc\x7f*\x95BYY\x19\xea\xea\xeaf\xf4\x80\x99\xcdf\x16\xa7Hjnuuu\xc1\xf3\xeb\xeb\xeba\xb5Z\x99+\xfe\xe8\xd1\xa3H\xa7\xd3hmmEkk+K\xaa#\x95\xb0\xa9\xa9\x09\xabV\xadb\x99\xcd---\xd3~\xb7V\xab\xc5\xea\xd5\xabQSS\x83L&Sp\xae556\xc1l2#\x10\x08\xc0d2\xa1\xaa\xaa\xaa \xf9\x0e\x00\x0c\x06\x03K\x96\x02N\x16hw8\x1c\x88\xc7\xe3\xb0X,'\xd5n\xd9w\xd0\xeb\xf5X\xb5j\x15\xacV+&&&\xa0\xd5j\xd1\xd0\xd0\xc0~gee%6n\xdc\x08\xaf\xd7\x0b\x8b\xc5\x82\xda\xdaZh\xb5Zd2\x19\x9c8q\x82er\x8b\x0b\x91\xea\xae\xcc\xe6\x9bO:7\x19t*2,\x08\x02\xdcn7#$\xc7\x8e\x1d\xc3\xf8\xf88+5B\x81\xeb\x92$\xcd\xe8v\x5c\x8c\x83\xf3l\xba\x9e\x95\xf5\x9f\x8cF#\x8b\x9d\x8b\xc7\xe30\x99L\xcc\xfd\x97H$X\xaf\xd8\xbe\xbe>\x04\x02\x01&gS-\xb9P(\xc4\xdc\xbc\x941ZL\xf5\x93+m\xe7\x9f\x7f>\xee\xbc\xf3N444\xa0\xa1\xa1\x01\xe5\xe5\xe5\x8cP\x9d\xae\x11$y\xdah4\xb2\x1b^[[\x1b\xba\xba\xbaX&\x9a\x5c\xa2\x97\xc7\xa8\xd1\xcd\x8d\x94\x86L&\xc3n\xcd\xc7\x8e\x1dCSS\x13jkkY]@r\x19\xcf\xc3\xaf\xb7h\x0a`1C@.\xf1D\x22\x81@ \x00\xaf\xd7\x8b\xff\xd7\xde\x99G\xc7]\x97\xfb\xff=\xfb\xbeg\xb6\xccdk\xd2\xa4{KiYZ\x0ae\x97\x02r\xa5\x02z\x11\x11\x5c\xa8\x82\x0a\xcaQ\xef\xfdq\xc5\x8b\xc7\x85\xe3\x82\x22\x02\xea\x11\xf1\x82\xe0\x02\x17/(\xdb\xbd*{i\x11*]\xd26M\x97\xecK\x93\xc9Lf\xdf\x7f\x7f\xc0\xf3\xf1;\x93I\x9a\xb4\x93t\x92<\xafs\xe64\xcd2\x99\xcc\xf7\xf3\xfd|\xde\x9f\xe7\xf3<\xefg\xfd\xfa\xf5cDR4\x1aE[[\x1b\xfa\xfa\xfaD\xee\x1f\x89?\xba\x86R\xe1N\xe6\xd1&\x93I\xf4\xe2\x96\x1e5O\xf4\x9a\xc6\xfb\xbe\xf9\x18e\x9bO\xa2\x94\x9e\xc7\xe9t\xa2\xbb\xbb\x1b\xc0{\x89\xeb3~=\xb3y@\x9e\x07d\x80R&\x87\x1c2\xe42YdSi@\xa5\xfeg\x95\xf0qN\xd1\xc9d\x12\xa3\xa3\xa3\xc2\xb8\xda\xe7\xf3\xa1\xaa\xaa\x0a}}}\xe3\xce\xfb\xc5E\x08\xf4\xb9RF\xca'*\x00\x8b\x85\x86\xd3\xe9D,\x16\xc3\x91#G\xa0V\xaba\xb5ZEZ\x07U\xe3Ses0\x18,x\x9d4g\x93\xe0\x90\xcb\xe5\xa2\x1bT\xb1\x08!\xf4z=\xccf\xb38\xa1kiiA\xfb\x81v\xc4\xe2\xff\x8c\x9a\x9a\xcdf\x18\x0c\x86\x02oB\xeabB\xa7\x0d':6\xb4Z-jjj\x84\x10+\x85\xcb\xe5\x12\x913\xaa\xbc.\xae\xb0\x95\xc9d\xa2\x98\xc9`4\x88\xe0\x82\xddn\x87\x5c.\x87\xd3\xe9\xc4\xa9\xa7\x9e\x8aX,\x06\x95J%r\xc8\x8b\xafk)aM\xaf\xad\xb8\xc8\xb5\xba\xba\xba\xa0\x96\xe1X\x91z\xb9\x5c.*\xee'\xdaL\x93\xa3Cq~\xbc\x5c.\x87\xdf\xef\x87\xd7\xeb-\xb8\xfe\xd5\xd5\xd5\xd0\xe9t\x22\xe0\xa2,\xc7\xe2}\x22\x17\x96r\x13h\x80P\xdfYz\xc8\xe5rtww\x8b\x81^UU\x85T*\x05\x85B\x01\x9b\xcdV\xf6\xc8`q\xa9\xf6T\x05\xe0t\x08E\xa9\xb0\xa6|,\xda\x0d%\x12\x09$\x12\x09\x1c=zT\x88\xc2@ \xc4!\xe5\xec\xd0\xcf\x90Ap:\x9d\x16\xc7\x85R\xe1Wl\x1a\x9c\xc9d\xb0n\xdd:\x5cv\xd9eX\xb0`\x01\x1a\x1b\x1bE\x05R\xb9\x8c\xb9\xe9\x18B\xa3\xd1\xc0b\xb1\xc0\xe7\xf3\xa1\xb6\xb6\x16---x\xe3\x8d7Dd\x8f\xc6\x09]#\xda\x11K\x13\x99)\x12\x98\xc9dp\xe8\xd0!\x1c:t\x08~\xbf\x1fN\xa7S\xb4<\xab\xd4h2\xbd\xff\xe4\xfbw\xf4\xe8Q1\xe1\x16\x8f\xcfC\x87\x0ea\xfb\xf6\xed\x08\x04\x02\xc2\xf7\x8f\xc4\xb1t\x822\x9b\xcdX\xb3f\x0d\xdcn\xb7H\xa9(\xe7{p\xa2\xe3\x9d\xc5_\xe5\xb3l\xd92\x1c>|X\x1c+\xcd\xe8\x86X\xad\x00\xbcFa\x04-3\x00\xe9L\x10\xb9l\x0ei\x87\x06:\x8b\xfe}u\xa0\x9c\xb2\xa5\x10u$\xda\xb3gO\xc1\xe7\xcf<\xf3L477#\x18\x0c\x8e\xdb\xdb\xb6T\xbb\xcb\xe2B\x17:^\xa4 \x07\x15~\xe4r9q\xb4>U\x9f<\x8dF\x03\x83\xc1\x80\x85\x0b\x17\xc2h4\xc2d2\xc1b\xb1`\xfb\xf6\xed\xe28Z\xab\xd5\x8a#j\xe9\xdaa\xb3\xd9\xb0j\xd5\xaa)\x1b\xe4\xd3\x111u&\xa9\xaf\xaf\x7f/H\x13\x8f\x89\xb5\xd2\xe9t\xc2\xe5r\x89\x9c\xb5|>/,\xde\xa4]\x9cJ5\x05\x98\xec\xdf\xeev\xbbE\xd7\x94\xf1\xae\xb3\xc3\xe1@KK\x0b\x0e\x1e<(\xe6\xce\x89\xda\x0f\xca \x83\xddn\x1f\xf3\x9a\xacVkIk\x96\x89^\xf3Tr\x5cO\xf4\xeb\xc5\xe3n\xa2kW\xfc\x7f*F\x02\xcal\x04]\x5c\xb41\x99\xc4pir1\x0d^24\x8eD\x220\x99L\xa2\xa4\x99\xbe\xa6R\xa9`4\x1a\x0b\x0c \xcb-\xb4*\x19\xba\x91\xc8u]zdC\xc7\xb8\x94\xd7Bm\xc4\xa4Q>\x12H\xd2hQ\xf1\xce\x86\x8eW\xaf\xb8\xe2\x0a\xac[\xb7\x0e\x8d\x8d\x8d\xa2}\x98\xd4\xf3\xaf\x5c\x0b8M\xc6\x06\x83A\x94\xc0755a\xef\xde\xbd\xc2\xe3\x89Z\xdb\x19\x8dF!\x0a\xe9\xef&s[\xaap\xa4\xe3\xef\xc3\x87\x0f\xa3\xae\xae\x0e~\xbf_\xb4\x16\x9cr\x14p\x86\xa0\xe2\x8dD\x22\x81P(\x84\x81\x81\x01\xacZ\xb5j\xcc\x0d\x9eN\xa7\xf1\xf2\xcb/chh\x08###\x22\xf2K\xc7\xc7\xd2\xe3\xa2u\xeb\xd6\x89]\xa2\xd9l.HR?Q\x91Wj\x93\xc4\x82n\xeeE\x12\xd7\xae]+:e\xbc\xf5\xd6[\x93\x9e\xdb\xcbB\x8d\x19\xb8e-\xbd \xa4GF\xd0\xb7#\x8bx\x22\x01\xcb\xcaf\x98\xab\xdf\x8f\x92\xc8e\x80\xe4\xf8q\xb2\x0b'\xd9`\xd1}e\xb7\xdb\xe1\xf5z\xa1V\xab'\xb4^1\x18\x0c0\x1a\x8d\x18\x1c\x1c\x14\xe3\x9e\x12\xf8\x09\xca\xd1\xa2H\xd4\xc0\xc0\x80\xf8ZUU\xd5q\xcdA4\xe7\x1a\x0c\x86\x82\x96k\x14\xed\xa3\xaf\xc9d2a\x15&\x15\x80\x14\xf9\x9a*\x1e\x8f\xa7\xc0\xc6\xc4f\xb3\x89\x82\x22\x99L\x06\xbd^\x0f\xab\xd5\x0a\xbb\xdd\x8e\xfe\xfe~\xe8t:8\x9dND\x22\x91\x82\xf9\xedx\x03+\x1e\x8f\x07\x0e\x87\xe3\x98\xf3\x8bB\xa1\xc0\x82\x05\x0b\x84\xc5\x11\xcfG\xc7\xb8\x07\xa6S\xa0LdGPj'\x90\xcb\xe5D\xb5\x93B\xa1\x10\xc9\xfc4\xf0\xa5\x09\xeat|\x5c*\x17\xe3d\x89\xb2\x99BZ>/\x15qd\x15@\xd1=iO`\x8a\x1eJm]J\x09?\xfa\xbc\xcdf\xc3\x87>\xf4!,^\xbc\x18\x0b\x16,(0|\xa6\x1dd\xb9o.2R\xb5\xd9l\xf0z\xbd\xa8\xa9\xa9\xc1\xe2\xc5\x8b\xf1\xf2\xcb/\x8b\x1cQJ\x98\xd5\xeb\xf5\x22e@\xab\xd5\x22\x99LB\xa5R\x89\xf4\x00\xaa:;p\xe0\x00\x9a\x9b\x9b\xd1\xdb\xdb+\xfa\x15Wb{8\xba\x06\xc9d\x12\x91H\x04CCCH$\x12X\xb3fM\x81\xb0\x92\xc9d\xd8\xbf\x7f?\xda\xdb\xdb\x11\x08\x04D\x82xq\xbf_\x00\xb0Z\xadX\xb5j\x15\x9cNgEF\xff\x98\xd9!\x00\xd7\xacY#\x16W\xdaLN5o\xf3\xb87\x07y\x14\x1c\xed\xcae\xef\x07\x19\xb2\xef\x15\x1fJ\xbf6U\xc3a\x12\x80/\xbf\xfc\xb28\xed8\xed\xb4\xd3\x0a\x1c(J\xfd\x1dF\xa3\x11\x8f\xff\xf6ql\xdf\xb6\x1d+V\xac@ss3\x1a\x1a\x1aD\xb4\xab\xb8\x15&\xbdw\x03\x03\x03\xe25R$k\xaa\x11@i\xe4Q\xfa\x9e\xfa|>\x04\x02\x01q2F\x22\xd3h4\x0a[\x1b\x83\xc1P\xb6\xb1Q\xaa;\x13\x00\xb4\xb4\xb4 \x99L\x8a<\xbbR\x1e\x80S\x9d7hS?U\xfd\xc1\xe2\xef$\x09\xc0\xa9F\xd3\xc8\xe8\x90\x84\x0a\x09\x1c\x8aZ\x91\xe8!\xf1\x22\xcdq:\x9e\xc5l\xb6-\x5c\xd2\x1b^\xba\xd0\x17w\x06\xa1\xef\xa1(\x90T\xec\x15G\xfa\x8a\xf3Q\xa8\xfd\x8fV\xab\xc5\xda\xb5kq\xce9\xe7\xa0\xa1\xa1\x01\xb5\xb5\xb5\xf0\xf9|\xb0\xd9l\xa2sD\xa9\xe3\x8frE\x01U*\x15t:\x9d\xc8\x05\x5c\xb0`\x01\xf6\xef\xdf\x8f\xa1\xa1!\xf1\x1a\xa9j\x9c*\xces\xb9\x9c8\xfaU\xab\xd5\x05\xbd(\xfb\xfa\xfap\xf0\xe0A\xd4\xd4\xd4\xc0\xe7\xf3\x15\xb4\xac\xab$\x11H\x1b\x1a\xca\xe1\xff\x22\xf6\xb4\xee\xc1\xeb\xaf\xbf\x8e\x05\x0b\x16\xe0G?\xfa\xd1\x98(`\xf1<\x1b\x08\x04\xc4\xef^\xb0`AAq\xcdT#\x80\xc5\xe2\x91\x0a\xf2\xe8$\x05x/wo\xcd\x9a5\xd8\xb9s'\xacV+\x1a\x1b\x1b\xcb\xb2\x0e\x01\xff\xec\xe0\x05@x3\x02\xef\xe5\xfd\x9ds\xce9\x00PP\x10R<&\xa6{\xdc\xb2\xf8\xab \x01x\xac\x9b\x92\xbaM\xd0\xc7\x94\x04O\xc7\x95\xc59j\xf3I\xdd\x97\xaa\xb4,\xf5\xb5R\x1f\x97\x12\xe1\xa5\xaa\xd6H|\xacX\xb1\x02\x1b6l@SS\x13jkk\xe1\xf5zE\xc9\xbaN\xa7\x13\xc7\x87\xd3\xf9\xdeS. u\x07\xa9\xa9\xa9Acc\xa3HrN&\x93B\x80\xd2$-\xf5\xb2\xa3\x9e\xa5\x1a\x8dF\x08\xc3\xdd\xbbw\xa3\xb9\xb9\x19\xfd\xfd\xfd\x22\x8aYi\xb9\x80T\xedL&\xcdj\xb5\x1a+W\xae\x1c\xb3h\xbe\xf3\xce;x\xfb\xed\xb7E\xcf_\xa9\xf1\xb3\xf4\xda\xba\x5c.\xb4\xb4\xb4\x08\xf1\xae\xd7\xebO\xb8Z{*\x0b\x043w\xe6\x1f\xb3\xd9\x8c\xfa\xfaz\x1c9r\x042\x99\x0c\x7f\xf8\xc3\x1f\xb0y\xf3\xe6I\xfd,\xdd\x9fO<\xf1\x04>\xfc\xe1\x0f\x9f\xf0\xdc=\x9e\x00:\x9e\x0d\x88F\xa3\xc1\x8b/\xbe(\xd6\x1e\xb7\xdb-\x92\xe6\xc7\x13\x11r\xb9\x1c\xfd\xfd\xfd\xe8\xe9\xed\x11Q\xc3\xb3\xce:K8ZH+\xe4\xa5~\xb8\xc3\xc3\xc3\x05\x82h\xd1\xa2E\xa2\xe5\x1a\xcd{\xe4\x91;\xd9\x08`1\xc5\x95\xa5\xc0{\xa7\x00\xeb\xd6\xad+{\xb1\xa4\xd4\xdaG\xa5R\x8d\xe9\xb7~\xac*j\xde,\xceq\x01(\xbd\xd0\xe3%G\x92\xc5\x04E\xfeb\xb1\x98\xc8c\xa3\xe8\x9f4\xf27\xdf\x0c\xa1K\xd9s\x90`\x9b(\x97b\xa2\xf7\xa9\xf8\x98D\xadV\xa3\xa1\xa1\x01\xe7\x9cs\x0e\x1a\x1b\x1b\xe1t:\xe1\xf3\xf9\xe0r\xb9`\xb7\xdb\xa1\xd7\xebE\x83\xed\x99h\xdbE\xbbh\x83\xc1\x00\xb7\xdb]\x10\x05\xa4\x8a6\x8a\x02R\xc4O.\x97\x17t\xa6\xa0\xb6p\xf4\xbd###\xd8\xbf\x7f\xbf\xa8\x086\x9b\xcd\x22\x87\xb1\x12D \x8dq\x8a\xfeuwwc\xed\xda\xb5b\xf1\xa0\xeb\x18\x8b\xc5\xb0s\xe7ND\x22\x911\x95\xbf\xc5cb\xf1\xe2\xc5\xa8\xad\xadEUU\x15,\x16\xcb\xb4\x19a3s\x1b\xda|\x5cv\xd9e\xb8\xef\xbe\xfb\xa0T*\xf1\xc6\x1bo\x1cS\xf0\xd3\xa6\xf2\xe7?\xff9\xbe\xf4\xa5/!\x9f\xcfc\xfd\xfa\xf5\xa2\xe2\xf4D^\xcfD\xfd\xc6\xa7\xb2.\xf4\xf4\xf4`\xfb\xf6\xed\xe2\xff\xeb\xd7\xaf/\xa8\xf8,57(\x95J\xec\xda\xb5K\xa4]\x00\xc0\xd2\xa5K\x0b\xc4Y\xa9\xea\xe0\x9e\x9e\x9e\xf7\x0c\xb4\xdf\x7f}\xabV\xad\x12\x96id\xca\xadR\xa9\x84?\xdbd\x04\xf0T\xafa9Q\xa9T0\x99L\x18\x1e\x1e\x86V\xab-Y=<\xde\x5cW\xaa\xb9\x03o\x1e\xe7\xa8\x00\x9c\xe8\xe6\xa4.!T\xb0@\xc9\xec\xa9TJ,\xf2\xc5\x1d\x0d\xe6\xd3\xce\xa1\xd8\x5c\xb3\xd4\xc7\xe3\x09;i\x84Oz\xf3\xd1\xee\xcd\xe5r\xa1\xb1\xb1\x11+V\xac@CC\x03\xecv;\x9cN'\xdcn\xb78\xee\xd5\xe9t\xc2\x98r&#<\xd4#\xd8b\xb1\xa0\xba\xba\x1a\x0d\x0d\x0dhhh\xc0\xbb\xef\xbe+\xf2\xfb\xd2\xe94t:\x9d8\xf2\x966\xe8V\xa9T\xe2\x98\x98\xc4\xe1\xfe\xfd\xfb\xb1h\xd1\x22\xf8\xfd~8\x1c\x0e\x98\xcd\xe6\x8a\xc9\x05\xa4<\xcdp8\xfc^{\x1e\xa5\x12---cr}\xde}\xf7]\x1c:t\x08}}}\xa2\xf2\x97\x22\xe4\xd2\xcd\x82\xd1h\xc4\x9a5k\xe0\xf5zQUUU\xf6\xdc?\xde\xcd\xcf\x1fh\xc3y\xc9%\x97\xe0\xbe\xfb\xeeC6\x9b\xc5\xf0\xf00^y\xe5\x15\x9c}\xf6\xd9\x13.\xf4\x1d\x1d\x1d\xb8\xe5\x96[\xc4\xbdy\xd7]w\xe1\x81\x07\x1e(\xcb\x9c(\x9d\xcf\x8a=\xe6\x8e5O\xd1\x91\xf43\xcf<#\x04\x97Z\xad\xc6y\xe7\x9dW \xe4J=\x8fL&\xc3[o\xbd%:c\xd8\xedv\xd1\xe3\x976\x94\xc5\xf7\x99\x5c.Goo\xaf\x10\x80J\xa5\x12\xd5\xd5\xd50\x9b\xcd\xe8\xe8\xe8\x80B\xa1\x10\xc5\x19\xc1`\xf0\x98bn\xaa\x86\xdf\xd3\x91\xab\xadT*\xd1\xdc\xdc\x8c\xe1\xe1aaf<\x99y\x82\xe7\x8ay*\x00\x8b/<\xe5\xb2\xa5\xd3i\x11\x05\x0c\x87\xc3\xa2B\xb5T\xb1\xc2|c\xb2;\x5c\xe9Q\x83t\xd2\x96\xe6\x0b\xaa\xd5j\xd4\xd7\xd7\xa3\xa9\xa9I\xf4\x0f\xa4\xbe\xcbv\xbb\x1dV\xabU\x1c\x15R\x0b\x9b\x89&\xc2\xe9^t\xc8\xcb\xca\xe3\xf1\xc0\xef\xf7c\xc9\x92%8p\xe0\x80\xb0\x06J\xa7\xd3\x88\xc5b\xd0j\xb5\x05\x93\x0fM\xe6\xd9lV\xfcK\x15\xc1\xed\xed\xed\x22\xa7\x91\xda\xc3\x9d\xec\x5c\xc0\xe2\xe8\xdf\xe0\xe0 \x9a\x9a\x9a\xc6x]\xa5R)\xbc\xf3\xce;\xa2Y{<\x1eG\x22\x91(\xc8\xfb\xa3\xeb\xb4|\xf9r\xd4\xd5\xd5\xc1\xe9t\xc2b\xb1@\xab\xd5\x96=\xfa'mIW\xca\xfb\x8c\x99;\x9b\xd0|>\x8f\xe5\xcb\x97\xc3\xeb\xf5\xa2\xaf\xaf\x0f\xb1X\x0cO?\xfd\xf4\x84\x02P.\x97\xa3\xa9\xa9\x09_\xf8\xc2\x17p\xef\xbd\xf7\x02\x00\x1e|\xf0A|\xeaS\x9f\xc2\xca\x95+\xcb2\x1eK\x1d3N6\xbf\xec\xd0\xa1C\xf8\xe5/\x7f)\x22j\xa7\x9f~:\x96/_\x8e\xc1\xc1A1\xef\x15\xcf\x0b\xf9|\x1e\x81@\x00;w\xee\x14\x9f[\xbat)<\x1e\x8f(H,NI!\xa1\xd6\xd3\xd3#^oMM\x0d\xdcn\xb7\xe88\x22\x97\xcb\xe1v\xbb\x91\xcdf'\xf4Y,\xd5\xa5\xe7d\xe2\xf1x\xe0\xf1x\xf8&a\x018y!C\xc2$\x91H\x88\x9eyT\xf9H\xbet\xd2\x82\x06fr\x93\xb4T\xa8)\x14\x0a\xd1\x9e\xa6\xaa\xaa\x0a---\xc2\x10\x93|\xa3l6\x9b\xf8\xbf\xc9d\x12\xa2\x8f\xec\x0fNv\x22-\x19;\x9b\xcdfTWW\xc3\xe7\xf3a\xe1\xc2\x85x\xe7\x9dw\x0a\x8e\x81\xa5cJj\x05Cm\x8f\xe8H&\x16\x8b\xa1\xbd\xbd\x1dMMM\xf0\xfb\xfd\x222v\xb2\xa3\x80\xb4\x09\xa2c\xddH$\x823\xce8\xa3\xe0~\x91\xc9dhmmE__\x1fz{{\x11\x0e\x87E\xf4\x5c\xbaQ\xa2\x8a\xf8\x0d\x1b6\xc0\xe1p\x88#\xfcJ\xf6>df\xc7\xfcRSS\x83\xb3\xcf>\x1b\xbf\xfb\xdd\xef\x00\x00/\xbc\xf0\x02\xee\xb8\xe3\x0e\x98\xcd\xe6\x09{\x80\xdfv\xdbmx\xec\xb1\xc7022\x82l6\x8b\x0f\x7f\xf8\xc38p\xe0@A\xa1\xd6\x89\xdc;\xe3E\xc2&\xaa\x08V(\x14\xb8\xe3\x8e;DG\xa1l6\x8b/\x7f\xf9\xcb\x18\x1d\x1d-\x88\xb2\x95\xca1\x1c\x18\x18\xc0\xe1\xc3\x87\xc5\xd7\xd7\xae]+r\x01\xa5U\xc0\xd2S\x9aT*\x85\xce\xceN\xf1<>\x9f\x0fn\xb7[XY\xa9\xd5j\x98\xcdf\xe1i:\x91\x88\xe5\xfb\x98\xa9x\x01(5\xb4\xa5\x85J\x0ay\xfeQ'\x0aJd\xa7\x9c\xbfb\x8cF#\xf4z\xbdh\xc6,\xbd\xc1\xe8f\x9f1_\xaacL\x94\xd3\xf5\x9c\xc5\xff\xd2b\xafV\xaba\xb1XD\x95'\xb5\xca!\xaf>\x12vz\xbd\x1eF\xa3\x11\x16\x8bE\x1c\xef\xd2\xd7(\xff\xa4\x12\x84\x9ft\xa2#\xbfG\xb7\xdb-\x8c\xa1)\x0aH\xa6\xd7\xe4AE\x16\x15\xf4\xbeP$\x90\x0a$\xb4Z-:;;\xd1\xd5\xd5\x85\xbe\xbe>\xd4\xd6\xd6\xc2j\xb5\x8a\xca\xd8\x93\xf17\xd315\xf9\xfeuuua\xf1\xe2\xc5\xa2I;]\x8bl6\x8b\xed\xdb\xb7\xa3\xb3\xb3S$\x93K;\xb7H\xef\x81s\xcf=\x17UUU\xa2\xf2w:\xa2\x7f\xcc\xfc\xe4\x8a+\xae\xc0\x13O<\x01\x00\xd8\xbd{7\xb6n\xdd\x8a\x8b.\xbah\x5c\x11\x96\xcf\xe7Q__\x8fo}\xeb[\xb8\xe9\xa6\x9b\xa0\xd5jq\xe4\xc8\x11|\xf0\x83\x1f\xc4\xb3\xcf>{B\x9bC\x12n\xa5N\x96(\xa7\x8e\x9c$\xa4si2\x99\xc47\xbf\xf9MaO\x92\xcb\xe5\xf0\x95\xaf|\x05Z\xad\x16\x89DB<\x7f\xb1\xd8\xcaf\xb3\xd0\xe9tB\x00+\x14\x0a\xe8t:\x9cw\xdeyH&\x93\xef}\xbfB>&\x07\x90\xd6\xc1\xbd{\xf7\x8a\xcf577\x0b\xf3\xfaK/\xbd\x14\x89d\x02&\xa3\x09\xd1h\x14\x0a\x85\x02\xd9lv\xdc\xf9h.\x09\xc0\xf9\x96\xd3?g\x05`\xb1MI>\x9f\x177\x98\xd4z\x82nX\xda\x9d\xd1\xf1/U\xfe\x16[\x95\xb8\x5c.\xac\x5c\xb9\x12\xd5\xd5\xd5\x22\x99]\x9a\xa0+\xad\x08\x9e\xe8\xa6\x99\xe9\x08\xdct\xdc$RGu\x12\xc1\xd4\x06\x8d\x1eT\x8dE\xff\xeat:!\x02\xe9c\xfa:y\xe6I\x05S\xa5\xa1P(\xa0\xd7\xeba\xb3\xd9\xe0\xf3\xf9\xe0\xf3\xf9\xd0\xd2\xd2\x82m\xdb\xb6\x89\xd7Lm|T*\x95h)(\xad\x10V\xa9T\x05\x1b\x8b={\xf6\x08c\xe8\xaa\xaa*\xe8t:\xd1\xc7r\xa6\xa1\x08\x1e\xf9\xfe\x85B!\x9cw\xdeyc*\x7f[[[\xd1\xd9\xd9\x89\xbe\xbe>D\xa3Q\x912Q\x5c\x1ce4\x1a\xb1b\xc5\x0ax\xbd^\xd1\xaaO\xa3\xd1L\xeb}q\xacj?fv,\xc4\x93\x19#\x1f\xfd\xe8G\xf1\xa5/}I\xb4g\xdc\xb2e\x0b:::D\xd5}\xa9y1\x93\xc9\xe03\x9f\xf9\x0c\xb6n\xdd\x8a\x87\x1f~\x18j\xb5\x1a\xcf=\xf7\x1cn\xb8\xe1\x06\xfc\xeaW\xbf\x9a\xb25\x8cT`\x8d'\x1ah\xdd\xc9\xe7\xf3H&\x93\x05\x7f[$\x12AGG\x87\xc8\x0d>\xf7\xdcs\xb1a\xc3\x06qZ \x15\x80\xd2M\xb7L&CGG\x07\xb6o\xdf.\xe6\x94k\xae\xb9\x06N\xa7S\xf4\x11V\xc8\x15\xc2\xa7P\x1a\x08\x19\x1a\x1aBww\xb7\xd8\x88m\xda\xb4I\xbc\x1e\x9a\x8b)7\xf0X\xf3\xf7\xa2\xfd=\x00\x00 \x00IDAT\xf0\x5c\x12J\x5c\xf81\xc7\x22\x80\xd2\x1085D\xa6\xddQ\xf1\xe0%\xdb\x8b\xe2\xfe\xa5\xf4=K\x96,\xc1Yg\x9d\x85\xfa\xfazQ\x98@\xc7\x93\xf31\x0c^\x9coE\xb9&\xb4\xd3%\x11#\x8d\xe6\xd1C*\x14\xe9{\xe89*\xf9&$qj4\x1a\xe1r\xb9\xd0\xd0\xd0\x80\xce\xceN\x1c|\x18g\x9ey\xe6\x98\xdc\xa3d2\x89\xdd\xbbw\xe3\xc0\x81\x03\x88F\xa3B\x00\x16wq\x01\x80SN9\x05>\x9f\x0f\x1e\x8f\x07V\xabU\xb4\xeac\x98\x13]\x88I$~\xe3\x1b\xdf\xc0\x96-[\xa0\xd1h\xd0\xd9\xd9\x89\xff\xfe\xef\xff\xc6\x95W^9\xae\x88\xa4{\xf3\xfe\xfb\xef\xc7\xae]\xbb\xf0\xf6\xdbo\x03\x00\x1e~\xf8a\xc8\xe5r\xfc\xe2\x17\xbf\x98\xb2\x08\x9c(\x02H\x7f\x8f^\xafG<\x1e\x1f\x93\x9f\xeap8\xf0\xf3\x9f\xff\x1c\xb7\xddv\x1b\xb4Z->\xfb\xd9\xcf\x8a\xcaT\xe9\xf3K\xdf\x97\x5c.\x07\x9b\xcd\x86;\xef\xbc\xb3`\xa3\xff\xe3\x1f\xff\x18\x7f\xfb\xdb\xdf\x0aOgd\x85\x01\x11\xb5Z\x8d\xd7^{M<\xaf\x5c.\xc7\xe5\x97_>\xee|\xa7\xd1hD$r\xbc\xf5u\xael:\xb8(d\x8e\x08\xc0R\x17\xd1n\xb7\x0b\x13g\xe9\xf7Q\x7fZi\xd4\xaf8\xfaWWW\x87\xf3\xcf?\x1f\x8b\x16-B}}=<\x1e\x0f\xf4z\xbd\x88fHo\x84\xc9V~\xcd\x85\x1bF*\x02\xa5\x0f\x12\x83R\xab\x96b\xaf<\xcaw\x99m\x93\x085.\xb7Z\xad\xf0x<\xa8\xaf\xaf\x87\xdf\xefG0\x18\x14\xd5\xb3\x14\xfd\x94&]K{\x03\x03\x10f\xc9\x0a\x85\x02\xbbv\xed\x12\xc50\xe4\x96?\xd3Q@\xca\xfd\x0b\x87\xc3\x18\x1a\x1aB.\x97\xc3\xb2e\xcb\x0a\x22\xe42\x99\x0c\x87\x0f\x1f\xc6\x8e\x1d;\x10\x0c\x06122\x22<3\xa9\xdf&a4\x1a\xb1t\xe9R\x11\xfd#\xdf?6Ee&\x8a\xf8\xb5\xb7\xb7\xe3\xd0\xa1C8\xf7\xdcs'\xec\xeeC\xc7\xa97\xddt\x13\xbe\xfa\xd5\xafbtt\x14j\xb5\x1a[\xb6l\xc1\x95W^9\xe1\x18\xa3\xcd\xe7\xd3O?\x8d\x0d\x1b6\xe0\xd0\xa1CP\xab\xd5x\xe8\xa1\x87p\xf0\xe0A<\xfe\xf8\xe3\xf0z\xbd\x93\x9a\xcb\xa5\x1b\xa4\x89\xc4\x03m\xfe\x8a\x85i.\x97\x83\xc1`\xc0\xb6m\xdb\xd0\xd6\xd6\x86D\x22!\xee%:U\xa2y\xc0j\xb3bxx\x18.\x97\x0b\xaf\xbe\xfa*v\xef\xde-:\x12\xdd~\xfb\xed\xc2\xfa\xa4\xb8\x08O\xda\xf7V\xa3\xd1\xe0/\x7f\xf9\x8b0\xd5^\xb7n\x9d(N+e\x17c2\x99D.\xa2\xc1`\x80\x5c.\x17=\x84I 2LEF\x00\xa5\x13\x0cE(\x8aof:\x9e\x8cD\x22\x05v/4\xc0\x95J%\xce>\xfbl466\x0a\xdb\x0e\xab\xd5Z\x90\xcb4\xdfv\x0d\xc5\x13\xccx\xbbA\xe9\xce\xb5\x94Y\xeal\x14\x024&\xccf\xb3\xe8\x0e\xb2h\xd1\x22\x1c\x9f\x18\x1fmmmH\xa7\xd3\xa2\x18\x86\xf2\xff\xc8\x08\x9a\xc6\x1e\x1d}\xd3\x04\x1a\x8f\xc7\xd1\xde\xde\x8e\xc6\xc6Faz\xad\xd7\xeb\xcbR\x998\x19\xa4\xbe\x7f###\x88\xc5b8\xe5\x94S\xc6\x5c\xdf\x9e\x9e\x1e\xbc\xf9\xe6\x9b\x08\x85B\x08\x85B\xc2$]\x9a\xfbJ\x1ekg\x9f}6\x1c\x0e\x87\xb8_\xa6\xbb\xeb\x874RY\x09\x05X\xcc\xd4\xb0X,x\xec\xb1\xc7\x84\xe0{\xe0\x81\x07p\xf7\xddwOx\x1cK\xd7\xfb\xd2K/\xc5\x05\x17\x5c\x80\xff\xfb\xbf\xff\x03\x00\xdc{\xef\xbd\xb8\xfe\xfa\xeb\xb1l\xd9\xb2\x09E[>\x9f\x87\xcf\xe7\xc3\xce\x9d;q\xd9e\x97\xe1\xe5\x97_\x86\x5c.\xc7\xd0\xd0\x10>\xfd\xe9O\xe3\x87?\xfc!\x1e~\xf8a\xac]\xbb\xb6 \x80P<\x8e\xa5\x11\xb6\xf1\x8e\x80K\xad\x17t2@\x1e\xa0\xa5\x02\x14\x14\x01\xa4\x8a\x5c\xbb\xdd\x8e\xfb\xef\xbf\x1f;v\xec\x10\xdf\xf3\xaf\xff\xfa\xafp\xbb\xddc\xde'\xe9\x06<\x9f\xcf\xc3b\xb1\xe0\xbf\xfe\xeb\xbf\x0a:|\x5c{\xed\xb5\x22-\xa5\xd4f\xd7b\xb1\x88cs\xa7\xd3YpoI\xd3w\xe6\x0a\xbcy<\xc9\x01\x96\x99\xbe\xd8T\xf9H\x85\x1b\xc5a\xffE\x8b\x16\xc1l6\xc3j\xb5\x8aH\x86\xb4R\x93\x1f\xf3\xefHO\x1a\x05\xa4N%\xcb\x96-\x13\x13<\xd9\x0aIm\x84h\xb2\x97\x16\xc5\x90(R\xab\xd5\xa2\x22\xb8\xb7\xb7\x17\xc3\xc3\xc3\xa2\x0b\xcdtOH\x94\xea\x90H$D\xdb\xb7\xea\xeajX\xad\xd61\x11\xb5m\xdb\xb6\xa1\xb7\xb7W\x1c\xfd\xc6b1Q8\x22\x15\x93k\xd6\xac\x11\xb9\x7f\x16\x8b\x05:\x9dnZ+\x9b+\xa5\xea\x9e9\xfe\x0d\x88\xdf\xef\xc7\xc6\x8d\x1b\x91N\xa7\xa1R\xa9\xf0\xbd\xef}\xef\x98Q5\xba\x17\xd5j5\xfe\xe3?\xfeC\x88,*\x88\xa0\xfb\xefX\x9bV\xadV\x8b\x97^z\x09\x9f\xfb\xdc\xe7\x0a*n\x0f\x1c8\x80\xd3N;\x0d\xcb\x97/\xc7\x1f\xff\xf8G\xf4\xf4\xf4\x8c\xc9\x87S*\x95\xa2\x8bOq\x0el\xf1\xeb,\xfe\xdd\xe4*`\xb3\xd9D\xbe\x1ed\xe3\xdf\xa72\x99\x0c;w\xee\xc4-\xb7\xdc\x22\xee\xa7\xea\xeaj\x5c~\xf9\xe5\x13\xfe\x9d\xb4\xd9\xe3LD\xffJEP\x98\xd9\xb1\xa1\xca\xe7\xf3\xf8\xc1\x0f~P\x10Q{\xf0\xc1\x07E\xca\xc4\xb1\xae\xff\xd9g\x9f\x8dO\x7f\xfa\xd3\xc2\x7fs\xef\xde\xbd\xf8\xc4'>!*\xf1\x8fu/g\xb3Y\xfc\xf4\xa7?\xc53\xcf<\x83u\xeb\xd6\x89\xa8\xb8N\xa7\xc3\xee\xdd\xbb\xb1y\xf3f477\xe3\xd2K/\xc5-\xb7\xdc\x82{\xef\xbd\x17\xbf\xfd\xedo\xf1\xdcs\xcf\xe1\xa5\x97^\xc2\xc8\xc8\xc81\xffF\xe9\xd8\xac\xad\xad\xc5\xc6\x8d\x1bq\xd6Yg\xc1\xe1p\xfcs3\x8d\xd2\xfd\x84\x95J%\xc2\xe1\xb0\xc8\x8d\xa4\xf7\xe8\x96[n\x81\xc9d\x9a\x94H\xfe\xdb\xdf\xfe&6\x96\x00p\xc3\x0d7\x1c\xb3\xe0E\xa1P`\xd1\xa2EX\xb6l\x19\x00\x14\xa4\xa6\xd0I\xc7l\xe5D{73\x15.\x00'\xb3\x18H\x8f\x8dJ-\xb6dUB\xbbK\x1e$\x0cM\xaa\xd4\x7f\xd2\xe5r\xa1\xb6\xb6\x16\x0b\x16,\x80\xc9d\x12\xd5\xe4\xe4+Y||D\x132E\x12\xc9^f\xef\xde\xbd\xe8\xed\xedEww\xb7\xc8\xb1\x9b\xce(`q\xee\xdf\xe1\xc3\x87q\xca)\xa7\x88\x02\x1d\xe9&\xe9\xed\xb7\xdfF[[\x1b\x82\xc1`A\xee\x9f\xf4\xbe\xc9\xe7\xf3\x22\xfa\xe7\xf5za\xb5ZO\xda\xa6\x89\xf3\x01g\xd7B\x9cN\xa7\xb1z\xf5j,\x5c\xb8\x10\x89D\x02J\xa5\x12\xdf\xf9\xcew&5\x8f\xd3\xe9\xcd\xfd\xf7\xdf\x8fe\xcb\x96!\x95JA\xa9T\xe2\xf1\xc7\x1f\xc7]w\xdd5)\x11I\x82\xea\xe2\x8b/\xc6_\xff\xfaW<\xf5\xd4Sp\xbb\xdd\x88\xc7\xe3P*\x95\xc2\xd4\xfd\xa5\x97^\xc2O\x7f\xfaS|\xf1\x8b_\xc4\xb5\xd7^\x8b\xcd\x9b7c\xcb\x96-hkk\x9b\xd0\xec\xb9\x18\xaa\x8a\xa7\x1c;\x00%\xc5\x1f\x09\xacd2\x89K.\xb9\x04###\xc2\xcc\xf9\xa6\x9bn\xc2\xca\x95+\x85\xdf\xe8D\x1c=z\x14/\xbf\xfc\xb2\xf8\xffE\x17]\x84\x85\x0b\x17Njc&\xcd\xdd\xa5>\xec\xf4\xda\xf4z\xfd\xac\x1dw<7\xccq\x01H\x15\xbd\xc7\xf2\xe5\xa3\x5c\xacR-\xa4\xa4\xed\xccJ5\xd6f\xe6\xb7\x08\x94\xe6\x02\xd6\xd5\xd5\xa1\xae\xaeN\x1c=I\x8bB\xa4\x91*z\xd01\xb0\xd4S\xf1\x9dw\xdeAOO\x0f\x06\x06\x06D\x7f\xdd\xe9\x8a\x02f\xb3Yd2\x19D\x22\x11\x11\xd9;\xed\xb4\xd3\x0a\xda\xaa\x01@WW\x17\xb6n\xdd*,b\x8a}\xff\x08\x9dNWP\xf9KQ\xf3\x99\xcc\x95e\xe17;!\x91v\xd7]w\x89\xb1\xd9\xd9\xd9\x89\xfb\xee\xbboR?O\xc6\xeb/\xbd\xf4R\x81\x0d\xd3\x9dw\xde\x89G\x1f}tR\xe2LZ\xa8u\xf9\xe5\x97\xa3\xbf\xbf\x1f\xcf?\xff\xf8\xa0\x98\x7f\xa4\xc7\xcb\xd2\x00\x85\xf4\xf7=\xf8\xe0\x83\xc2\xc7\xb4\xa1\xa1\x01\x9f\xf9\xccg\x8e\xeb:iuZ,jY\x84E\x8b\xdf\xb3D\x9b\x8dE \xc5\xc6\xd8\xcc\x1c\x15\x80\x93\xbd\xc0\xc5\xe2\xb08\x12\xc8\xdee\xccD\x22P\xab\xd5\xc2\xe1p\xa0\xa6\xa6\x06MMM\xa8\xae\xae\x16\xbez$\x04\xa9ZV\xba\x18\xa8\xd5j\xf1\xf3tL\x9aN\xa7\xd1\xda\xda\x8a\xae\xae.\x0c\x0e\x0e\x22\x1c\x0e\x1f3\x99\xfdx\x05 \xf5\xbc\x0e\x04\x02\x88F\xa3X\xbe|\xb9xm\xe4C\xb6\x7f\xff~\x1c8p\x00\x83\x83\x83\x08\x04\x02\xc2\xa7Lj\x94N\x0b\xcb\x19g\x9c\x01\xaf\xd7+r\xfff:\xfa\xc7\x85 \xb3_\x04\x9aL&l\xd9\xb2E\x88\xb0\xd6\xd6V<\xf5\xd4SSZ\xac/\xbc\xf0B<\xf2\xc8#B@\x01\xc0\xd5W_\x8d\x1f\xfd\xe8G\xc2~i2\xaf\x85\xbaw\xe4r98\x1c\x0e\x5c{\xed\xb5\xb8\xe7\x9e{\xf0\xe7?\xff\x19\x7f\xfd\xeb_\xf1\xdak\xaf\xe1\xb5\xd7^\xc33\xcf<\x83\xe6\xe6f\xa4\xd3\xe9I\x09\xc0\xe2\xc0\xc4x\xe2D&\x93\xe1\x8e;\xee\xc0}\xf7\xdd'N\xa9\xf4z=^\x7f\xfdux\xbd\xde1\xebSq\x852}|\xcf=\xf7\x08G\x81t:\x8d[o\xbd\xf5\xb87L2\xc8\xe0v\xbb\xd1\xd2\xd2\x02\x8b\xc52'\xc6\xdcd\x8d\xc7\x99Y&\x00'\xbb\xf8\x14W\xfe\x16G\x0a\xa7#\x02\xc3\xcc\x0d(\x11\x9ar\x01\xfd~?\x96/_.\xc4H,\x16\x13\xc7D\xa5\xa2\x80d\x8cM\x1fS\xc1\xc5\xe1\xc3\x87\xd1\xd5\xd5\x85\x91\x91\x91\x92\xfd\xab\xcb\x11\xfdK\xa5R\x18\x1d\x1d\xc5\xe0\xe0\xa08\xc6.\xe6\x9dw\xde\xc1\xf0\xf00\x06\x06\x06\x90L&\x0b\xbc2\xa5G_\x8d\x8d\x8dhnn\x86\xd3\xe9\x14\x95\xbf3\x19\xfdc\xe6\x0e\x9f\xfd\xecg\xe1\xf7\xfbE\xf5\xea\xf5\xd7_?\xa5{ \x97\xcb\xe1c\x1f\xfb\x18\xee\xb9\xe7\x1e\xc4\xe3qqzs\xdbm\xb7\xe1\x9b\xdf\xfc\xe6\x94\xac\x95J\xd9\xbe\x00\x80\xc9d\x82\xc7\xe3A]]\x1djkkE\x04p\xa2\xe78\x96\x07\xaa\xd4P:\x91H\xe0\xdf\xfe\xed\xdf\xb0u\xebV\xf17%\x12\x09\xec\xdf\xbf\x1fUUU\xc8d2c\x8c\xf8\xa5\xcfA\xddG\x8e\x1c9\x82{\xef\xbdW\xe40644\xe0\x92K.\x19\xf75\xcc\xb7\x0d\x07\xb5\x80e\xe6\xa0\x00\x9cj\xa8\xb7\x94\xad\x09G\xff\x98cA\x15\xc1v\xbb\x1d\xd5\xd5\xd5\x22\x12H\x96\x11\xa9T\x0a\xf1x\xbc\xa0\x070\x8d+*\x02!k!\xa5R\x89\xd1\xd1Q!\x00)\x0a(=J>Q\xa4\x95\xbf\xa1P\x08}}}\xd8\xb0a\xc3\x98{\xa1\xbd\xbd\x1d\x07\x0f\x1eDWW\x17\x82\xc1\xa0\xa8 \x94F\xff\xe8\xde\xa0j\xc6\xaa\xaa*q$\xc6>\x99\xcc\xf1\x8cM\x00\xf8\xc9O~\x22<5\xe5r9.\xb9\xe4\x12\xe1\x957\x99\x8d\x7f*\x95\xc2\xad\xb7\xde\x8a\x9f\xfe\xf4\xa7b\xbc*\x14\x0a|\xfd\xeb_\xc7\xd5W_\x8d\xde\xde\xde\xb2Dv\xa4y\xe1\x13\xdd\x9fr\xb9\x1cN\xa7SD\x09\xc7\xab@\xd5h4hmm\xc5\x96-[\xb0o\xdf>\xf1\xdcUUUhkk\x83\xd7\xeb\x1d\xb3NI\xc5\xa5\xf4y\xe5r9n\xb8\xe1\x06\x91\x8f\x0c\x00\x9f\xff\xfc\xe7\x11\x8f\xc7y\xa0\x15m\x18\x8aE!3\x07\x04`\xa9\x0b9\xd5s\x7f\xce\x13`&\xb3\xe0h4\x1aX,\x16x\xbd^\xd4\xd6\xd6b\xd1\xa2E\xc2\x0c\x9a,%\xa4\xc7\xc0R\xe3b\xaa2\xa7#aZ\x04\xba\xba\xba\xd0\xd9\xd9\x89@ \x80X,V\x96#N:\x9a\xa6\xe8_ww7\xbc^/\xbc^\xef\x98\xe7omm\x15\xbe\x84\xd1hTD\xff\xa4\x0b\x1d\xb5\x8c\xab\xaf\xaf\x87\xd7\xeb\x15\xb9\x7f\x95d\x974\xd5\xfb\x9d9\xb9Q\x99t:\x8d\x0f~\xf0\x83\xf8\xe8G?\x8aT*\x05\x85B\x81W_}\x15?\xfb\xd9\xcf&\xbd\xa9\xa0\x88\xdc\xe7>\xf79<\xf5\xd4Sb\xa3\xa6R\xa9\xf0\x87?\xfc\x01\xeb\xd7\xaf\xc7\xd0\xd0\xd0\x09\x8fQi\xca\xc4D\xbd\x80\x95J%\xea\xeb\xeb\xb1r\xe5J,]\xba\x146\x9bM|\x9d\x0c\xe35\x1a\x0d\x1ex\xe0\x01\xfc\xbf\xff\xf7\xff\x10\x0a\x85D\x8b\xb6\xfa\xfaz\xfc\xe3\x1f\xff\xc0\xc2\x85\x0b\x0b|\x0a\xa5\x05\x8a\xd2\x8fi\x1c?\xf4\xd0Cx\xe9\xa5\x97\xc4\xef\xd9\xb4i\x13\x1a\x1b\x1bgu\xe5n\xb9\x84^\xa9{\x9d\xd7\xfa9&\x00y\x82gfj\xd1\xa2\x8a\xe0\xaa\xaa*\xf8|>\xf8\xfd~444 \x95J\x15\xe4\x06\xa5R)1aK;\x04\xd0q\x17=\x12\x89\x04\xf6\xed\xdb'*\x82\xc3\xe1pY*\x82\xa9\xb0#\x16\x8baxx\x18\xbd\xbd\xbd8\xff\xfc\xf3\x0bz;\x03@OO\x0fv\xef\xde\x8d\xee\xeen\xf1\xbb\xa5\xa6\xcfR\xdf\xbfSN9\xa5 \xf7O\xab\xd5VD\xf4O\xfa^M\xf6\xbe\xe7h\xff\xc9\x87\xf2\xf4\xee\xbd\xf7^\xb8\xddnqDw\xe7\x9dw\xe2\xc8\x91#S\xda\x98\x01\xc0\xbf\xfc\xcb\xbf`\xcf\x9e=\xa2u\x1c\xf0\x9e\x0dJUUUY\xd6\x83\xc9\xf4\x02\xa6\xfb\xba\xb9\xb9\x19MMM\xa2\xea\x99ZF\xbe\xf2\xca+X\xb0`\x01\x9e|\xf2I1\x0e\xa9\x08\x85\x22\x7f\xc5\xbfS\x1a\x01\x94\x1e\xfd\xa6\xd3iD\xa3Q\xfc\xfb\xbf\xff\xbb\xf8\xba\xd3\xe9\xc4u\xd7]\x87\x5c.7'r\xf7\xca\xad\x07\x8a\xfdL\x999,\x00y\x92g\xca\x0d\xb5E2\x1a\x8dp\xbb\xdd\xa8\xaf\xafGCC\x03\xf4z=\xb2\xd9,\x92\xc9$\x92\xc9$r\xb9\x5c\x81Y4\x89@\x95JU\x109S\xa9ThkkCWW\x17\xfa\xfa\xfaDE\xf0\x89\x1c\x03\x93\xf8K&\x93\x08\x85B\xe8\xea\xeaBSS\x13\xecv\xfb\x98\x1c\xc5]\xbbv\xa1\xbd\xbd\x1d\xa3\xa3\xa3\x08\x87\xc3\x22\xff\xaf\xb8\xd2\xb1\xb1\xb1QTC\xda\xedv\xe1\x13V\xe9\x13~\xf1\xff)\xcf\xb7\x9c\xb9\x96\xcc\xf1\xa3T*QUU%z\xfa\xcad2\x0c\x0c\x0c\xe0\xfa\xeb\xaf/\xb0V\x9a\xec5onnF \x10\xc0\x86\x0d\x1b\xb0j\xd5*\xfc\xecg?C2\x99,\xcbZ0\x99\xcdN\xa9~\xc1\xf9|\x1e;w\xee\xc4\xe5\x97_\x8es\xce9\x07\xa3\xa3\xa3\x05\x91\xbc\x8f}\xeccx\xe4\x91GJ\xaeY\xb4i,6l\xa7t\x92\xdbo\xbf]\xf4\x1c\xcf\xe5r\xf8\xcew\xbe\x03\xaf\xd7\x0b\xbd^?\xa6\xc77\xc3\x81\xa2y#\x00\x19f: \xd1\xa6\xd7\xebEEpcc#\x5c.\x970L\x96V\xf3f\xb3\xd91\x91\x03\xa91\xb4L&C2\x99\xc4\x9e={\xd0\xd9\xd9\x89\xc1\xc1AD\x22\x91\x82<\xc2\xe3\x15\x80\x14\xfd\x0b\x87\xc38\xe5\x94S\xc6$\xba\x0f\x0c\x0c\xe0\xad\xb7\xde\xc2\xe0\xe0\xa0(B\xa1\xdf+\xcd\xfdS*\x95X\xb5j\x15jjj\xe0t:a0\x18\x0a|\x0dgj\x92&\x9fO\xfa\xdcd+1\xa5\xcf#=Zc*\x87\x8f}\xecc\xb8\xf4\xd2K\x85\x98y\xe5\x95Wp\xc3\x0d7L\xaa\xc3\x87\xf4\xba\xd3&\xeb\x85\x17^\xc0\xd6\xad[\x91\xcb\xe5\xa0\xd1h\xca\x22\x0a\xc6\x1b\xebTAL) 4\xbe\xe2\xf18\x1ey\xe4\x11\x5cq\xc5\x158\xf5\xd4S\xf1\xa7?\xfd\x09j\xb5Z\xe4\xd7.[\xb6\x0cw\xddu\x17\xae\xbb\xee:\xc4b1Q\x8d\x5c,\x00\x8b#\x80\xd4g\xfc\xdb\xdf\xfe6\xfa\xfa\xfa\xa0P(\x90N\xa7q\xc3\x0d7\xe0\x93\x9f\xfc$\x1a\x164\xe0\xb4\xd3N\x83V\xab\xe5\x81\xc5T\xe6\xc6\x8f\xdf\x02f\xd6\xee^\xde\xf7\xf5\xb3X,p\xbb\xdd\xa8\xa9\xa9\xc1\xa2E\x8b\xd0\xdb\xdb+\xaa\xf6\x92\xc9$\xf4z\xbd\x10-R?@Z\xa8(\x7f)\x93\xc9\xe0\xc8\x91#8|\xf80jkk\xe1r\xb9`2\x99\xc4\xf7L\x05\x8a8R\xf4\x8f*\x7fkjj\x0a\x16,\x99L\x86\xdd\xbbw\xa3\xa3\xa3\x03###\xe2\xf8\x97\x8e\xb1\xa5\xa2\xcb\xeb\xf5b\xc5\x8a\x15p\xb9\x5c\xb0Z\xad0\x18\x0c\xd3\xde\xf6\xedD\x16\xec\xf1\x16\xeab\x01\xc9T\x16\x7f\xfa\xd3\x9f\xe0\xf3\xf9088\x08\x85B\x81G\x1f}\x14\x8d\x8d\x8d\xf8\xc67\xbe1\xe5\x13\x9d\x135.\xa6\xfb\xa08\xe7N\xea\xfbIb/\x97\xcbA\xa9T\xc2`0\xc0b\xb1\xe0\x8d7\xde\xc0\xfd\xf7\xdf\x8f\xe7\x9f\x7f\x1e\x81@@\x8c5\x95J%\x8a4\xbe\xfc\xe5/\xe3\x8c3\xce\x80N\xa7\x13]H\xc6\x9bkJmZ\x1e{\xec1l\xdb\xb6M\xbc\xd63\xce8\x03\x0f<\xf0\x002\x99\x0cjkjy0\x1dC\xcc3'y\x0d\x9d\xc9\x8b\xcf\x83\x80)'\xd2(\xa0\xddn\x87\xdf\xefGcc#\xdcn\xb7\x88\x04\xe4r9Q\xd5+\xed\x17L\x22\x90\x1e\x9434::\x8a\xf6\xf6vtuuahh\xe8\xb8*\x82i\x9cSnP(\x14\xc2\xd0\xd0\x10V\xaf^=\xe6\xf5\xf7\xf7\xf7\xe3\xad\xb7\xdeB__\x1f\x82\xc1 \xb2\xd9\xac(^\xa1\xc8#5\xbe_\xb7n\x1d\x5c.\x17\x9cNg\xc5W\xfeR5i\xb1\x10\xa4\xbcF\x8e\xfcU.\xc9d\x12;v\xec\x10\xd7K&\x93\xe1[\xdf\xfa\x16\x9ex\xe2\x89\x19\xa9\xda\xa4\xe7\xdf\xb6m\x1b\xda\xda\xda\x10\x0a\x85\x10\x89D\x90H$\x84):U\xda\xc6\xe3qD\x22\x11\x8c\x8c\x8c\xe0\x9dw\xde\xc1SO=\x85\xaf}\xedk\xb0\xd9lX\xbf~=~\xf3\x9b\xdf`tt\xb4\xe0\xf9\xb5Z-n\xbe\xf9f\xf4\xf7\xf7\xe3\xa2\x8b.\x82B\xa1\x10=\xeaK\x8dY\xfa\xbf\xf4s*\x95\x0a\x7f\xfe\xf3\x9f\xf1\xa3\x1f\xfdH|\xde\xedv\xe3\xf1\xc7\x1f\x17Ef\x0cS\xe9\xcc\xd8(\xa5\x89\x84\xcdc\x99\xb2\xee`\xde\xaf\x08\xb6Z\xad\xf0z\xbd\xf0\xfb\xfdX\xb2d\x09zzz\xc4\x91\x0c\x1d\xf7\x90\x8b>E\x14\xb2\xd9\xac\x88\x06\x90\xe5E.\x97C{{;\x16-Z\x84\x9a\x9a\x1a\xb8\xddn\x98\xcd\xe61\x1d\x00hL\xd3sI\x05\x0dE\xee\x92\xc9$\xc2\xe10\x06\x06\x06\xa0\xd5j\xd1\xd4\xd44&\x82\xb2o\xdf>\xb4\xb5\xb5!\x12\x89 \x16\x8b\x89~\xc4\xd2$\xe9|>\x0f\xbf\xdf\x8f\xa5K\x97\x8a\xe8\x9f^\xaf\xaf\x88\xca\xdf\xf1\xaa0\xd5j5\x22\x91\xc8\x18\xa1G\x02\x90\xc49o\x0a+\x0f\x8dF\x03\x97\xcb\x85g\x9f}\x16\x17]t\x91\xe8\xdet\xf5\xd5W\xe3\xc9'\x9f\xc4\x87>\xf4!\xa4\xd3\xe9i\xebJ\x91\xcdf\xa1T*q\xcd5\xd7\xa0\xb3\xb3\x13\x00\xe0\xf1xPUU%r^GGGE\x8e\xef\xe8\xe8(\x8e\x1e=:\xe6o {\x1a\x8a\x10\xaeX\xb1\x02\x9b6m\xc2\xed\xb7\xdf\x0e\x87\xc3\x81\xe1\xe1a\xb1.QZ\xc2x\x1b\x13\x12\x80\xb9\x5c\x0ef\xb3\x19\x8f=\xf6\x18>\xfe\xf1\x8f\x8b{0\x95J\xe1\xe1\x87\x1fF}}=\x0f\xa0In\xde\x999.\x00yrgfb\x22!_@\x87\xc3\x01\x9f\xcf\x87\x86\x86\x06\xd4\xd6\xd6\xa2\xa3\xa3\x03\x0a\x85B\x08\xbcd2Y\xd0-\x83\x8eu(\xe7\x87\x04a(\x14B[[\x1b\xfc~?jjj`\xb5Z\xa1\xd5jE\x94\x90\xc6\xb64*X,\x00\xd3\xe94\xe2\xf18\x82\xc1 \x8e\x1c9\x82\xab\xaf\xbez\xcc\xc47<<\x8c\xad[\xb7bdd\x04\xc3\xc3\xc3\xa2\x0d]q\xee_>\x9f\xc7\xc6\x8d\x1b\xe1p8\xe0r\xb9`6\x9b\xc5\xeb\xa94\xb2\xd9,\xd4j5\xc2\xe1\xb0\xe8\x11K\x11L\x8a.\xa5\xd3\xe9\x13\xca\xaddf\x86\x0b/\xbc\x10w\xdf}7\xbe\xfa\xd5\xafB\xa3\xd1 \x99L\xe2\xaa\xab\xae\xc2\xa3\x8f>\x8a\x8f|\xe4#\xc8f\xb3\xd32\x06i\xdd\x18\x18\x18\x80\xd9lF4\x1aE\x7f\x7f?\xfa\xfb\xfb\xc7_\xc8\xde\xcf\xe5\xa5\xfb2\x99L\x8a\xcfo\xd9\xb2\x05\x9f\xf8\xc4'\xb0p\xe1B\xd8\xedv\xf1;hSW,\xf2&\x12,\x1a\x8d\x06\xbf\xf9\xcdo\xf0\xdd\xef~W|.\x95J\xe1\xf6\xdbo\xc7\x07>\xf0\x01.z<\x0em\xc0\xef\xd9\x1c\x13\x80\x5c\xe2\xcd\xcc$r\xb9\x1cj\xb5Z\xf8\x02RAHOO\x8f8*\xa2c^\xda\xf1\x17\xb7s\xa2\xa4u\x1a\xbf\xfb\xf6\xedCSS\x13:;;\x85\xd92\xe5\xdbI\xab\x00\x8b'/:\xbaM$\x12\x08\x06\x83\xe8\xee\xee\x86\xdb\xed\x86\xcf\xe7C.\x97+Xp:::p\xe8\xd0!\x04\x02\x01\xc4\xe3\xf11]?\xe8\xf9\x1a\x1a\x1aPWW\x87\xea\xea\xea\x93\xd2\xf3w\xbc\xfb\xbb\x18\x85B\x01\x93\xc9\x84\x91\x91\x11h4\x1ad2\x99\x82\xe35\xe0\xbdN-\xd4\xadE\x1a\x05\xe4\x05\xa0\xf2\xc8f\xb3\xf8\xcaW\xbe\x82X,\x86\xff\xfc\xcf\xff\x14\xd7\xe8\xba\xeb\xae\x83J\xa5\xc2\xe6\xcd\x9b\xa7\xe5\xf7\xaaT*Q\x80\xa5T*\x8f\xb9\x8e\xd0}H\xe3\xd2\xeb\xf5\xe2\x9ak\xae\xc1\x8d7\xde\x88%K\x96\x881{\xac\xce R\x93\xe9b\xb1\x22\x93\xc9`6\x9b\xf1\xf4\xd3O\xe3\xee\xbb\xef.\xd8\xe8}\xf1\x8b_\xc4E\x17]\xc4\x91\xad2\xcc!\xcc\x1c\x10\x80\x0c3\xd3\x02\x90\xfav\x92/`CC\x03\xf6\xed\xdb\x87\xbe\xbe>(\x95J\x11\x05\x94\xb6\x80#\xd1\xa2\xd1h\x84A4M\xe2\xa9T\x0a{\xf6\xec\x81\xcf\xe7\x83\xd3\xe9\x84\xd1h\x84Z\xad.\x10_$\x06\xa5\x93>\xf5!\x8eD\x22\x18\x1a\x1aB__\x1f\xae\xb8\xe2\x8a\x82D\xf6|>\x8fh4\x8a\xbf\xfc\xe5/\xa2\xf2\x97\x16<:V\x96.z\xcb\x97/GMM\x0d\xaa\xaa\xaaD\xf4o\xa6s\xe8\xc6\xeb\xa4 \xfd\x1aE[\x13\x89\x84\xe8\xb2\x22\xed\x05+\x93\xc9\xd0\xdd\xdd\x8dP($:\x9dX,\x16\x16\x80\x15\x0a\xf9c~\xe3\x1b\xdf\x80\x5c.\xc7\x9dw\xde)6P\x1f\xfe\xf0\x87\xf1\xc3\x1f\xfe\x10\xb7\xddv\xdb\x98\xd6\x9e\xe5\xc0h4\x22\x91H\xa0\xad\xad\x0dmmm8t\xe8\x10\xc2\xe10\xa2\xd1(\x86\x87\x87\xd1\xd9\xd9\x09\x9b\xcd\x86\xc5\x8b\x17\xc3\xe5ra\xc1\x82\x05\xa8\xaf\xaf\xc7\xc8\xc8\x08\x14\x0a\x05\x96.]\x0a\xa3\xd1\x88d2)\xa2\x83\xa5\xe6\x8d\x89\xc6x\xf1\xe7\x9f\x7f\xfey\xdc}\xf7\xdd\x22\xbf/\x93\xc9\xe0\x9e{\xee\xc1\xcd7\xdf\xcc\xe3\xf78`#\xe8y&\x00\xf9b3\xd36\x90\xdf\xaf\xd4\xb5X,\xa8\xae\xaeFCC\x03\x1a\x1b\x1bq\xf4\xe8Q\xd1\x98=\x93\xc9@\xaf\xd7\x8b\x89\x87\xf2\x00)\x82(\xb5\x83\x91\xc9d\xe8\xe9\xe9Akk+l6\x1b\x8cF#T*\x15\x9cN\xa78>\x92&\xc4S\x04\x22\x99L\x8a~\xbf]]]\xf0\xfb\xfd\xa8\xab\xab\x1b\x13M\xd8\xb9s'\xda\xda\xda\x84=\x0cY\xd6\x14\xe7\xc5\xb9\xddn\xac\x5c\xb9rL\x0e\xd4\xc9\x16\x80\xd2\x1d\x0c\xa3\xd1\x88\xcd\x9b7\xc3\xeb\xf5\x8eY$v\xef\xde\x8dW^y\x05\x07\x0e\x1c\xc0\xc8\xc8H\x81\xaf\x99\xb4\xf27\x97\xcb\xe1\x82\x0b.\x80\xddn\x87\xdb\xed\x86\xc5b\xa9\xa8\xe8_q\x15\x9f\xf4c\xb5Z-\x8cv\xe5r9\x0c\x06C\x81\xa7\xa1B\xa1\xc0\xe0\xe0 ^~\xf9e\xa4R)\xc4b1\xd4\xd5\xd5\x89\xc8\xeaLu6\x99/\xd0F&\x9f\xcfC\xa7\xd3\xc1h4\xc2h4\x8a\xb6\x89\x93\x8d\x04R\x84|\xe3\xc6\x8d\xd8\xb5k\x176m\xda\x84\xfd\xfb\xf7C\xab\xd5\x22\x9dN\xe3\xdak\xaf\xc5#\x8f<\x82\x9f\xfc\xe4'hjj\x12\xc2\xbf\xdc\x22\x89\xee\xdf1'J8~1A\xa6\xd2V\xab\x15o\xbf\xfd6n\xbd\xf5V\xbc\xf6\xdak\x05\x91\xeds\xce9GX\xbd\xb0\xf8cX\x00\x1ec\x91\xe0I\x9c\x99i\xe4r9\xb4Z\xad\xc8\x05\xf4\xfb\xfdX\xb4h\x11^}\xf5U$\x93I1iS^ y\x04\xd2\xcfR\x84M\xa3\xd1\xc0h4\xc2\xe7\xf3\xc1\xe3\xf1\xe0\x82\x0b.\xc0\x1bo\xbc\x81w\xdf}\x17\x89D\x02\xdd\xdd\xdd0\x18\x0cP(\x14H&\x93\x88F\xa3\xd0h48\xf3\xcc3\xb1|\xf9rq\x9c)]$R\xa9\x14v\xef\xde\x8d\xa3G\x8fb``@T\xfe\x16\x1f\xff\xe6\xf3y\xb8\xddn477\xa3\xba\xba\x1av\xbb\x1d:\x9dND\x1cO6\xd2\xfe\xc3\xc5]=\xe8\x08\x8e\x84\x9e4:H\xef\xb7\xc3\xe1@SS\x13\x94J%\xb6m\xdb\x86\xde\xde^,^\xbc\x18\x1e\x8fG\x1c\xad\xf3\xdcQ\x1e\xa1Dc0\x91H \x9b\xcd\xc2b\xb1\xc0\xe3\xf1\xa0\xba\xba\x1a2\x99\x0cz\xbd~JG\xee\xf4\xbd\x0b\x16,\xc0\xbb\xef\xbe\x8b\x9bn\xba\x09\xbf\xfe\xf5\xaf\xc5xx\xfe\xf9\xe7\xb1l\xd92\xdc|\xf3\xcd\xf8\xc1\x0f~ \x04h9\x8f\xf5e\xf2\x7f\x0a\xc0rT\x93\xe6r9\xe1\x12p\xe3\x8d7\xe2\xb7\xbf\xfd-\xe2\xf1\xb8x\x0fS\xa9\x14\xbe\xf0\x85/\xe0\xee\xbb\xef\x86V\xab-(\xe8b\x8e/0T\xfc13\xc7\x04`qd\x80afJ\x00R;\xa8\xaa\xaa*\xd4\xd4\xd4\xa0\xbe\xbe\x1emmm\x18\x1c\x1cD&\x93A\x22\x91\x00\xf0^Q\x02\xe5\x0dIs\x7f\x8cF\xa3\x10\x91\x16\x8b\x05MMMX\xb6l\x19\xd6\xacY\x03\x00\x18\x1c\x1cD2\x99DWW\x17\xd2\xe94\xccf3|>\x1f\x5c.\xd7\xb8\x13[6\x9b\xc5\x1f\xfe\xf0\x07\xbc\xfa\xea\xab8|\xf8\xb0\xb0\xa7!3\xdb\xe2\xe2\x8f\xb5k\xd7\xc2\xeb\xf5\xc2\xe5r\xc1b\xb1\x9c\x94\xca\xdf\xf1\x88\xc7\xe3\xa2x\xa6\xb8C\x8aF\xa3\x11\x06\xd0\x14\x0d\xccf\xb3\xd0\xe9t\xd0\xeb\xf50\x99L\xf0z\xbdhhh\xc0\xb9\xe7\x9e\x8b\xef}\xef{\xd8\xb3g\x0f\x8cF#\x02\x81\x00\x82\xc1 R\xa9\xd4\x94:\xaf0\x13\xcf\xbf===\xd8\xbbw/\x12\x89\x04t:\x1dZZZD\xd5<\x09\x9f\xe3\x19[r\xb9\x1c\x0f?\xfc06o\xde\x8cO}\xeaS\x18\x1c\x1c\x14\xe3\xff\x87?\xfc!~\xfc\xe3\x1f\xe3\x17\xbf\xf8\x05\xae\xbc\xf2JX,\x96\xf2\x09@\xc8D\xb1\xd0\x89\xe4\x95+\x95J(\x95J\xc4\xe3q\xbc\xf0\xc2\x0b\xf8\xf9\xcf\x7f.>O\xb8\x5c.\xfc\xfa\xd7\xbf\xc6\x05\x17\x5c \xc6$oN\xca?FY+\xb0\x00d\x98\xb2 \xcd\x05\xf4z\xbd\xa8\xaf\xafG}}\xbd\xa8\x08\xa6\x02\x8fT*U\x90\x0cO\x22\x8c\xaay\xa5\xf9m\xa9TJ\x1cu\x91\xd0\x93\xf6\xf5\xa5\x9f\x97V\x12RD,\x99L\xe2\xcd7\xdf\xc4\xae]\xbb088\x88H$\x82`0X\xb2\xf8\x03\x00\x9cN'\x16/^\x0c\xaf\xd7[q\xd1\xbf\xe2\x05Pz\x0cN\xef\x81\xb4\xad\x16u\x8c A\xa8\xd3\xe9\xa0\xd5jQ]]\x8d\xc6\xc6F\xa4\xd3iQ\xe5)\xad\x96f\xca\x0f\xb5s\xeb\xee\xee\x86\xcf\xe7\x83\xcdf\x83\xc1`8\xee\xe7#\x8b\x9fG\xa8\x91U\x00\x00\x1daIDAT\xcb/\xbf\x1c\xbbv\xed\xc2\xd7\xbe\xf65\xfc\xeaW\xbf\x12\xddwr\xb9\x1cn\xbc\xf1F<\xf7\xdcs\xf8\xfd\xef\x7f/\xa2\xee\xe5\x18\x7f\xc5\xde\x7f\x93]o\xa4'S\xad\xad\xadx\xec\xb1\xc7\xf0?\xff\xf3?\x18\x1a\x1a\x12v1d\x22}\xf3\xcd7\xe3\xfb\xdf\xff\xbe\xb8\xef\xa7\xab\xf3\xc9|\x15}\xac\x15* `2\x9dj\xbed\xa2.\xc3L\xf7\xa0~?\xbf\xcfl6\xc3\xe5r\x89c`\xb3\xd9,\xc4])\xcf=\x8aj%\x12\x09\xa4\xd3i\x84\xc3a\x0c\x0e\x0e\xe2\xc8\x91#hoo?\xe6\x22 5\x89\x96V\xc8\xbe\xf2\xca+x\xf6\xd9g\xd1\xde\xde\x8e\xfe\xfe~Q\xf8!\xb5O\xa1\x85I.\x97c\xc9\x92%\xa8\xab\xab\x83\xc7\xe3)\x88\xfeUZ\xe4\x81Z\xbeI\x0bSH\xe4Q!\x00\xe5VJ;\x81\x00\xefuA\x19\x1a\x1a\x82B\xa1\x18\x131\xa5k\xc2\x8f\xe3\x7f\xd0\xfbH\xe9\x0d\xdf\xf9\xcew\xf0\xc4\x13O\xa0\xab\xab\x0b\xb1XL\xe4\x9fR\xf4\xf9\xb8#\x08\xef\x1b5\xbb\x5c.<\xf4\xd0C\xd8\xbe};N?\xfdtQu\x0f\x00\x9f\xfd\xecg\x91\xcb\xe5\xcav\x0c,\x15\x80S][d2\x19^x\xe1\x05l\xdc\xb8\x11\x17^x!~\xf9\xcb_\x8aqH\xe3\xf3\xfc\xf3\xcf\xc7\x8e\x1d;p\xdf}\xf7\x89#_\x16\x7f\xe5\xd3\x08\x13\x8d7\xd6\x0a\xb38\x02X\xaep.\x87\xd8\x99rD\x01\xa5\xb9\x80\xb5\xb5\xb5hll\xc4;\xef\xbc\x03\x00\xa2\xe8\x22\x99L\x8a\xcaS\xa9\xb1s2\x99D*\x95B8\x1cF0\x18\xc4\xdbo\xbf\x0d\x97\xcb\x05\xb7\xdb=\xa9\xc5@.\x97c\xdf\xbe}\xd8\xb9s'^}\xf5U\xf4\xf7\xf7#\x18\x0c\x22\x12\x89\x08\xb3d\xa9\x08\xa5\xdfKy\x84.\x97\x0b6\x9b\xad\xa2*\x7fK\xdd\xef\x91HDD\x82\xa4\x22Pj\xa7Cy\x82$\x88\xbb\xba\xba`\xb5Z\xf1\xd2K/\xe1\xe2\x8b/\x86\xdb\xed\xe6\xfb\x7f\x9a\x04z<\x1e\xc7\xa3\x8f>\x8a'\x9f|\x12\x9d\x9d\x9d\x88\xc5bH$\x12H$\x12\x22\x1a^\x8e\xdfC\xd7}\xf5\xea\xd5x\xf3\xcd7\xf1\xfc\xf3\xcf\xe3\xe3\x1f\xff8\xd6\xacY\x833\xcf<\xb3\xac\xd7\x956Jt\x0fOE<<\xf1\xc4\x13\xb8\xea\xaa\xabJ\xf6\xa8\xf6\xfb\xfdx\xec\xb1\xc7\xb0a\xc3\x06\xf1\xdc\x5c\x90\x84\xb2\x8d\x0f\x16ys\x5c\x00N$\x0a\xa7\xb2\xcb\xe4\x1b\x8e)\xc7\xa4\xa3P(`6\x9bEw\x90%K\x96`\xdf\xbe}\x22\xc7,\x99L\x8a\x9d?\x1dO\xa5R)\xc8d2\xe8t:D\x22\x11a\x1f\x93L&\xf1\xe8\xa3\x8f\xe2\xd4SO\xc5\x8a\x15+\xa0\xd5ja6\x9b\x0b\xc6j6\x9bE(\x14B__\x1f\xda\xda\xda\xb0o\xdf>\xb4\xb6\xb6\x22\x12\x89`xx\x18\xa1P\x08\xb1XlL\xcf_\x22\x97\xcba\xfd\xfa\xf5p\xbb\xddp:\x9d\xa2\xebG\xa5\x0a@z\x9fI\xc8J\xefy\x8a\xa6\xd2\x91\x1fE^3\x99\x0cl6\x1bzzz\x10\x89D\x10\x8dF\xb1z\xf5j\xb4\xb4\xb4\xc0h4\x0a!\xce\x9c\xf8\xbc{\xe8\xd0!\xfc\xf1\x8f\x7f\xc43\xcf<\x83\xfe\xfe~\x91r maX\xceD|\xba\xe7R\xa9\x14>\xf0\x81\x0f`pp\x10\xdd\xdd\xdd\xd0j\xb5e\x1fs\xd2H\xfbT\xb8\xf8\xe2\x8b\xb1d\xc9\x12\xb4\xb6\xb6\xbe\xb7\x00*\x95X\xb2d\x096n\xdc\x88\x8f|\xe4#X\xb3f\x0d\x1f\xf7\xce\x80\x1e`\xe6\xa8\x00\x94\xde\x98\x94\x0fT\x5c\xe1\xc8032\xb8\x95Jh4\x1a8\x1c\x0e\xf8|>\xf8|>,_\xbe\x1c\xaf\xbf\xfe\xbaXD\xa8\xe2T\xaf\xd7\x8b\xf1I\xc6\xc5\xe9tZ\x88\xbfp8\x0c\x97\xcb\x85@ \x807\xdf|\x13~\xbf\x1f\x1e\x8fGT\x11SqIGG\x07\xfa\xfb\xfb\x0b\x04_8\x1c\x16\xc7\xceT\xf8!\xb5\xe6\xa0{\xc2d2a\xf5\xea\xd5p\xbb\xddp8\x1c\xc2\x1c\xb9\xd2\xc4\xdfxG\x8e\xd2\xaf\x93\xc8\xc8d2B\xd4\xc5b1h4\x1a\x04\x02\x01\xa4\xd3i\x04\x02\x01$\x93Itww\xc3\xe1p\xa0\xaa\xaa\x0a\xb5\xb5\xb5\xd0\xe9t\xc2\xf2\xc6h4B\xa7\xd3\x9d\x94\x85\xa7\x9c\xe2\x88\x9e\xebx\xaf\xe5d_\x7f2\x99D(\x14\xc2\xc1\x83\x07\xf1\xc6\x1bo`\xc7\x8e\x1dhooG(\x14\x12Q-i\xc7\x95\xe9\x18[j\xb5Z<\xb7\xdf\xef\x9f\x96MG\xa9\x8e1\x93y\x8fL&\x13\xd6\xad[\x87\xbe\xbe>|\xees\x9f\x13\xf3\x82Z\xadF$\x12A&\x93)\xbb`e\xfey}H\x0b\xb0G\xf0\x1c\x15\x80\xd2\x5c&\xbaY\xa5\xf9K\x1c\xddcf2:\xa5P(`4\x1a\x0b*\x82\xf7\xee\xdd\x8b\xd1\xd1QQ\xdc\xa1R\xa9D\xa7\x0a\xeap\x00\xbc\x17\xd1\xa3j]\x83\xc1\x80\x9e\x9e\x1etww\xc3\xe9t\xa2\xbd\xbd]\xfc\x0e\xb2\x8c\x19\x1c\x1cD.\x97C<\x1eG\x22\x91@4\x1aE4\x1a\x15Gm\xf1x\x5c\x88?\xa9\xef\x1fAG\xbf\x1e\x8f\x07V\xabU\x1cMWb\xee\x9f\xf4\xbe\x9e\xa8\xa7*E\x03\xb3\xd9,4\x1a\x8d\xb0\xcbI\xa7\xd3\xd0\xe9t\x18\x1a\x1a\x12\x1dN\xc8\x9f.\x16\x8b\x8d1\xd0\x9e\xca\x22_\x1cY\xad\xa4H\xc7tZ_P46\x12\x89 \x10\x08```\x00CCC\x22\xed\x80~7\xe5eR\x9e\xe6t\xbd\x96\xe9B*`\x8f\xe7\xfd\xbc\xf7\xde{\xf1\xb3\x9f\xfd\x0c\xb1X\x0c[\xb7n\x15\xc7\xe2F\xa3\x91\xd7\xa7i\x9e7\x8aSm\x989\x22\x00\xa5\x8b\xc2\xf0\xf00Z[[\x91\xc9d\x90N\xa7\xd1\xd1\xd1\x81\xd1\xd1Q\x84\xc3a$\x12\x09\x11\x0d\x91\xee\x06fb\xe2`\xe6\xe1\x00\x7f?\x0ah\xb7\xdb\xe1\xf5zQSS\x83\xc6\xc6F\xbc\xf5\xd6[\xa2Wm\x22\x91\x80\xc1`\x10BE\xea\x0bFQl\x12\x88z\xbd\x1e\xa1PHT\x19\x93P\xa4\xde\xb7\x99LF\x88<\xe9d\x97H$D\xe5\xb14\xfaG\xd8l6\xb4\xb4\xb4\xc0\xe7\xf3\x89\xca_\x8dFS1\xc7\xa1R\x93\xeaP(\x84}\xfb\xf6A\xa1P \x18\x0c\x8a\xc2\x96p8,\x22\xa7T|@\xa2'\x97\xcb!\x1a\x8d\x0a[\x18\x00\xc2k-\x14\x0a\x89B\x97t:\x0d\x83\xc1\x80\xae\xae\xaec\xce\x09\xb3\xcdG\xecD\xc5\xd6\xb1~>\x9b\xcd\x22\x95J!\x93\xc9 \x12\x89 \x16\x8batt\xb4\xc0V\x87\x22\x80\xd2\x0d\xfal\x9bs\xc7+\x02\x99lT\x89\xa2\xf6\xf4^p$jz\xaf\x15\xcdu}}}hmm\xc5\xc8\xc8\x08\x06\x06\x060<<\x8c\x91\x91\x91\x82\xd3\x96\xe2\x8d1\xfd\xcb}\xc2+\x5c\x00J/\x18%\xcf\xd3b\x11\x0e\x87\x0b\x92\xe0\xc73\xc0\x95F\x10\x19\xa6\x1c(\x14\x0a\x11\x05\xf4x<\xa8\xaf\xaf\xc7\xc2\x85\x0bq\xe0\xc0\x011Fe2\x99\xc8\x01\xa4\x1e\xb6T!IQ\x12\x95J\x05\x99L\x86H$\x02\x8dF\x83d2\x89`0\x88|>\x0f\xbd^/\x8e8\xa59\xaft\xdcKQ\xb0x<.\x04e\xf1D\xb6h\xd1\x22,X\xb0\x00.\x97\x0bV\xabU\x1c\x81V\xd2\xbd@\x133\xe5:\xaaT*\x84\xc3a\x0c\x0d\x0d\xa1\xbf\xbf_\xe4\xf4\xd1\x91x\xf1u\x00 r,\xa5\x0b\x84B\xa1@(\x14\x12\xdf\x17\x08\x04Dk\xbc\xa9F\x99\xa6k\x81\x98-\x0b\x0fy3R\x11\x13\xe5]J\xe7U\xe9b:[\xf3-O$\x02Hs\x02\xa5'\xb0\xb0\x98\xfey\x83\xdc\x15\x82\xc1 b\xb1\x18B\xa1\x10zzz\x10\x8dF1::\x8aX,&r\x89KE\xf0\xf9\xfa\xcc\x82\x08 M2\xc3\xc3\xc3\xd8\xb7o\x1f\x86\x87\x87E\x97\x04Z\x1c\xa4\x11\x02i\x04\x90&'\xee\x02\xc0\x94\x1b\x95J\x05\x8dF\x03\x9b\xcd\x06\xbf\xdf\x8f\xfa\xfaz\xd4\xd5\xd5a\xf7\xee\xddb\x13B\xd1\xbbX,V\x10!\xa1\xc8\x1e\x99\xe5*\x95J$\x93I\xa8T*\xf1 \xe3c\xe9B\x22\xb5\x96\xa1^\xc1\xd9lv\x8c\xfd\x0c\x15\x9c\xac]\xbb\x16n\xb7\x1bUUU\xd0\xeb\xf5\xa2%Z\xa5-\xba\x00\x10\x0e\x87\xd1\xd6\xd6&&n\x9a\xc4\xa3\xd1\xa8\x10\xbc\xc5\x02\xf0X\xd1\xbcb\x11CsA%L\xfc\xb3i\xf1\xa1\xb1%\x1dk\xd2\xb6\x9c\xd2\x08\xdal\xed\xc4p\x22>\x80\xe3\x8d5f\xfa\x85z \x10\xc0\x9e={\xd0\xdd\xdd-\xf4\xc0\xf0\xf0\xb0\x08\x08\xd1\xc6\xb1x\xbc\xd2ub\x11X\xc1\x02\x90\x8eyd2\x19FGG\x11\x0c\x06E\xd4\x83\x16\x06\xca\x85\x92\x1e\xb5\x95\xf2\x0ed\x01\xc8\x94;*\xa2R\xa9`4\x1a\xe1p8P[[\x8b\x95+Wb\xef\xde\xbdb\xa1$\x1b\x93\xe2\xb1'\x97\xcbE\xb50\xf5\x1a&\x93[\xaa\x10\xd6\xe9t\xc8d2\x05\x9b\x17\xb2\x92\xa1EY:\xfe\xa5c=\x9f\xcfc\xc9\x92%\xa8\xaf\xaf\x87\xcb\xe5\x82\xd9l\x86N\xa7\xab\xc8\x8d\x10\xbdn\xca1\xa3\xa3\xf1H$\x22\x8e\xb8\x8b\x0b[&\xbb8\x17\xe7\x0d\xce\xf5]\xfft\xe7\xdeI7\xd7\xf4\xb1TXOV\x8cW\xe2\xfb&\xed\x05|\x22\x22\xb6\xf8~\xe7ugz6$t\x8d\xfa\xfa\xfa\xd0\xdf\xdf\x8fd2)R\xc2h\xfe\x95\xce\x1d\xc5\x91\x7fi\xee S\xa1\x02\x90\x16K\x85B\x81H$\x82P($\xaa\x1e\xa59O\xc5\xea\x9e&)\xba\xa19A\x94\x99\x96\x81\xaeTB\xa7\xd3\x89\x5c@\x9f\xcf\x87U\xabV\xe1\xf5\xd7_\x1f\xd3\xcd\xa2xA\x90V\x0c\xd3\x18\xd6j\xb5H\xa5R\xa2\xa0A\xa5R\x15$\xd9\xd3\xc4GB\x90\xa2\x80\xd2\x85\x99\xee\x97\xf5\xeb\xd7\xc3\xe9t\x8a\xca_i\xff\xdcJ\x8b\xba(\x95JD\xa3Q\xd1U\x85v\xee\xe3\x1d\xdd0'O(\x8d'\xa4\xa9\x05\xdal\x8e*\x8d\x97\x03x\xbc\x82\x99\x05\xe0\xf4\x5c'\x9a\xc7\xe4r9\x22\x91HA\x0a\x98t\xde(\xbe~\xc5u\x01\xe5\xec%\xcdL\x83\x00\xa4\xe31\xbd^\x8fL&\x83\x91\x91\x91\x82\x04\xd0\xc9F\x05t:\x9d\xc8}\xe2\x1b\x92)\xe7\xf8\xa4\xee \x1e\x8f\x07uuu\xc2\x0f,\x18\x0cNj\xc1\x91\xf6\xec\xcd\xe7\xf30\x18\x0cB\x10R\xa7\x03\xfa\x7f\x22\x91\x80Z\xad\x169qT\x85Y\xdc\x0c\xfd\xcc3\xcfDuu5\xdcn\xb7\xe8\xfaA\xf9\x86\x95\x04\xfd}\x1a\x8d\x06\xf9|^\xe4?J\xff~\xa6\xb2\x04\xe0xc\xf9D\xfa\xffV\x8a\xb0(\xb7@\xa19\x82)\xffuR\xa9T\xd0\xeb\xf5H$\x12\x18\x1e\x1e\x16\xf3E\xa9`\xd0\xb1\xc6,U\xb03\x15$\x00I\xfc\xe9t:\x98\xcdf\x11\x15)\x95\x13u,\xccf\xb3h/\xc5\x17\x9a)'\x0a\x85\x02:\x9d\x0eV\xab\x15~\xbf\x1f\xb5\xb5\xb58\xe3\x8c3\xf0\xdcs\xcf\x8d\x9b\x08^*\xd2@\xc9\xe3\xd4\xf3T:V\xa9\xe2W\xa7\xd3\x09\xc3gz\x14\x1f\x8f\xeat:,_\xbe\x1c\x1e\x8f\x07UUU0\x1a\x8d\x15\x99\xfbG\x93\xb8F\xa3\x81\xd5j\x85V\xab\x15\xc7\xe2'\x9a\x83\xc5\xcc\xdcbLcY\xab\xd5Vt\x87\x99\xa9\xfc-'*\x109\x028\xbd\xd7I\xadVC\xa7\xd3\xc1b\xb1@\xa5R\x89\x22\xb0RQ\xbf\xf1\xae\x8b\x5c./\xf0\x06\xadDk\xacy+\x00)<\xabV\xaba2\x99\xe0\xf3\xf9D\x94`*\x0b\x02\x0d\x88\xaa\xaa*\x98L&\xa1\xf8yQa\xca\x05mT,\x16\x0b<\x1e\x0f\x9a\x9a\x9a\x10\x8b\xc5\xf0\x8f\x7f\xfc\x03}}}\xc7\x8c\xa4H\xad4\xc80Zz\xc4!\x15v\xe4\xf5G\xd1\xbfb\xdb\x17\x00X\xbat)\xea\xeb\xeb\x85\xef\x9f4\xfa]i\xc2\x99L\x99\xa9HE\xfa~0\x95\x8f4\xb7\xcal6\x8b\xcd\xc6l=Z\x1b/8 \xb5\x0d\x99\xaa\x00daQ\xfe\xeb#\x97\xcb\x85\x03\x834Mf2\xe3\x95H\xa5R\xb0Z\xad\x228\xc4\xd7\xa9\x82\x04 \xf5d\xd4\xe9t\x22\xc1~\xfd\xfa\xf5\x22G\xaa\xb8\x92\x87vo\xd2\xfc*\x95J\x05\xadV\x0b\x93\xc9\x04\xaf\xd7\x0b\x87\xc3!\x12\xe1\xf9\xec\x9f)\xe7\x8e\x94z\x04\xdb\xedv,X\xb0\x00\x00p\xd5UWa\xc7\x8e\x1d\xc2\x8bJ\x9a\xabJ\x95\x94\xa9TJT\xaee\xb3Ya\x19\xa3\xd5j\xa1\xd1h\xc4\xf8\xa6/\x04`:\x9d\xc6e\x97]\x86\xd3O?\x1d\xa3\xa3\xa3\xa20N\x1a\x18\xa2\xebI\xa2P\xa3\xd1\xc0`0\xc0n\xb7\x8f\x19\xb3\x9c\x1eVA\x02\x90&\x18\xab\xd5*\x12\xc5\xddn7\x12\x89\x84\x88\x10\x94*\xb9\x97VB\xaa\xd5j\x18\x0c\x06\x98L&q<\xc1\xa1^f:&&:\x8a\xa0vkf\xb3\x19\xe1p\x18\xb1XlL\xa1\x07\xf0\x9e\x17\xe0\xc8\xc8\x88hS\x16\x08\x04\xa0\xd7\xebQ[[+\xbe/\x9dNC\xaf\xd7\x17\xb4{\x93\x8e\x7fi\xe5/\x80\x82\xca_\x8a\xfeU\xea\xa4F\xef\x99\xd1h\x04\x00h\xb5Z8\x9dN\xd1\xd5G:\x91K==\xf9\xde\x9dY\x8a#`\xd2\xe3PZP\xb5Zm\xc1\x11\xf0l\xcd\x03,\xfe\x9b\x8b\xff\xfe\xc9\xfeM\x1c\x01\x9c>\xc8D\x9f\x8e\x80u:\x1d\x5c.\x17\x12\x89\x84h\xb7Y\xea:HuA\xf1\x98\xa5\x9ck\xa6\x82\x04 -\x10\x00`\xb1X\xa0\xd7\xeb\x0b\x8e\x87\x8e\xe58O\x17\x9bz\xb1\xd2\x83U>3]\x82\x86\x04\x17%\x18\xdbl\xb6\x92-\xda\xf2\xf9<\x22\x91\x08\x06\x07\x07\x11\x89Dp\xf0\xe0A\xa8\xd5j,^\xbcX\x08\xc8@ \x04$\xfd,\x89\xc0\xe2\xa3\xdf\x5c.\x87e\xcb\x96\xa1\xa9\xa9\x09n\xb7[\xecj+}!\xa6|\x5c\x12\x0e\x16\x8b\xa5\xe0\xfe\xe6hJ\xe5\x08\xa2R\xd11\x8a\xe2\xd2f[\xadV\xcf\xfaH\x0a\xb9L\x9c\xe8s\x14\x7f\xccc\xb7\xbc\x22\x10\x80\xf07\x95\xea\x82\xf1\xdeo\xe9\xc6E:fU*\xd5\xac\xdf\xb0\xccI\x01(\x15\x81$\xe4t:\xdd\xa4\x0bA\xa4G\x15\xec\x03\xc8\xcc\x04\x14\xb5V(\x14\xc8f\xb3\xd0\xeb\xf5\x05bFZ\xf0A\xad\xca\xe8hw\xf9\xf2\xe5\xb8\xfe\xfa\xeb\x11\x0e\x87q\xe4\xc8\x118\x1c\x0e\x1cW\xc5\x95\xbfk\xd7\xae\x85\xd7\xeb\x85\xd3\xe9\x84\xc1`\x98\x15\xb9\x7f\x0cS\x89\xd0=u\x22-\x08K9S0\xcc\xbc\xba\x8f\xf8-`\xe6;\x94\xaf\x07@\x18\xe3\xe6\xf3y$\x12\x09D\x22\x11\x0c\x0f\x0fC\xa3\xd1\xa0\xa5\xa5\xa5`\xb1\x88\xc7\xe3hkkC$\x12A,\x16C4\x1a\x15\xfe\x7f\xd2\xe8_>\x9fGuu5V\xacX\x01\xa7\xd39k*\x7f\x19\xa6R!\xcf\xccr\x1e\x01\xf3\xbd\xc8\xb0\x00d\x98y\xb8\x98\x90\xcd\x00\x1d\x07e\xb3Y\xe1\xff\xd7\xd5\xd5\x85\xb3\xce:k\xcc\xcfuvvb``\x00\xc1`\x10\x91H\xa4\xe0\xf8Wj\x7f\x94\xcf\xe7q\xe1\x85\x17\xc2n\xb7\xc3\xe9t\xce\x9a\xca_\x86\xa9\xd8\x85\xeb}\xe1&\xad\xd6\x97\x1e\xe5N\x16\x8e\x002,\x00\x19f\x1eS\xdc\xc1\x82L\x9cC\xa1\x10zzz`4\x1a\xd1\xd4\xd44\xe6\xb8\xa9\xad\xad\x0d}}}\x08\x87\xc3\xa2\x8b\x08\x89?\x22\x97\xcba\xe1\xc2\x85\xa8\xa9\xa9\x81\xd7\xeb\x85\xcdf\x83N\xa7\xab\xd8\xb6o\x0c3[6mr\xb9\x1c\xf1x\x5c\x08\xc0P($\x22\xf9\x93\x15\x91l\x04\xcd\xccg8\x07\x90a$bM*\xfe\x06\x07\x07\xd1\xd5\xd5\x85\xeb\xae\xbb\x0e\xb9\x5c\xae`\xb1\xe8\xed\xedEkk+\xba\xba\xbaD\xf4Oj\xfd\x22=\x8eZ\xb3f\x8d\xc8\xfd3\x99L\xa2\xcb\x0d\xc30'\xb6i\x8bF\xa3\xd8\xb3g\x0f\xd2\xe94\x06\x06\x06\xa6l\x0c\xcdG\xc0\x0c\x0b@\x86\x99\xc7\x90`\x93\x8a\xbf\xfe\xfe~\x1c\xe8gs\xb9\x1c\xd2\xe94\x12\x89\x04FGGq\xf4\xe8Q\x1c\x9f\xc7\xf0\xf00\x06\x07\x07100\x80\x03\x07\x0e\xe0\xc0\x81\x03\xe8\xec\xec\x14\x85&\xc1`P\x08?\x12\x7f\xd2\xc2\x0f\x85B\x81L&\x83\x8d\x1b7b\xc9\x92%\xa8\xa9\xa9\x81\xc3\xe1\x80\xc1`8\xa6Pe\x18\xe6\xf8\xe6\x84\xf16qS\xb9\xdf\x5c.\x17\x16/^\x0c\xadV\x0b\x8f\xd7\xc3o,\xc3\x02\x90a*\x09\x8a\xe6e\xb3YD\xa3Q\x0c\x0d\x0d!\x9dN\x17\xe4\x00\x92\xa9\xeb\xd0\xd0\x10\xe2\xf18\x86\x87\x87\x11\x0e\x87E\xa4 \x9f\xcfC\xa9T\xa2\xbf\xbf\x1f\x81@\x00n\xb7\x1b\xfd\xfd\xfd\x88\xc7\xe3\x08\x85B\x88\xc7\xe3\xe8\xed\xedE$\x12A\x7f\x7f?\xa2\xd1(b\xb1\x18\xc2\xe1\xb0\xb0\x9e\x91\xb6{\xa3\xe7\x94\xc9d\xc8f\xb3X\xbat)\xce>\xfbl\xf8\xfd~x\xbd^\x98L&\xf6\xfdc\x98\x93(\x10'\xb5\x00*\x95X\xb8p!\xbfa\x0c\x0b@\x86\xa9\xe4\xc9\x9c\xaam\xa9\x02W&\x93\xc1h4\xe2\xe8\xd1\xa3\xe8\xee\xee\xc6\x8b/\xbe\x88\xa3G\x8fb``\x00\x89DB\xe4\x0a\xd2sd\xb3Y\xb8\x5c.(\x95J\x8c\x8c\x8c \x91H@\xa1P\x08\x91\x17\x89D\x90\xcdf\x91J\xa5\x84\xc7 \x09?i\xab7\x12\x7fd\xf8\x5cUU\x85\x8b.\xba\x08\x0b\x17.Dmm\xad\xc8\xfd\xd3h4,\xfe\x18\xa6\x82\x05\xa0t#\xc70,\x00\x19\xa6\xc2\x90\x8a\xae\x5c.\x87@ \x80\xbf\xfd\xedop:\x9d\xc8\xe5r\xe8\xec\xecDww7\xf6\xee\xdd\x8b`0\x88t:\x8dT*U\x10\xa9\xcbd2\x00\x80\xde\xde^$\x12\x09qTL\xad\xe0\xf2\xf9\xe8\xf5zTUUA.\x97\xc3l6#\x1c\x0e\x0b\xc3\xe7l6[\xb0\x00\x94\x8a\x00\xd01\xb1\xb4\xb8C\xa1P\x88|\x22\x8dF#\xa2{Z\xadV|\xacR\xa9\xa0\xd1h\x0a*|9\xc2\xc00'\x17\xa3\xd1\x08\x83\xc1\x80\xba\xba:ttt\x08\xafO\x99\x9c\xab\x80\x19\x86\x05 3\xa7v\xfcj\xb5\x1af\xb3\x19\x1a\x8d\x06V\xabUt\xfc\x18\xaf\x95S\xa9*`\xea\xe0AB\x8e\xa2\x80$\x04\xe9\xb8Y\xadV\x8bJC\x85B\xc1G\xbd\x0cS\x81P\xe4_Z\x11,\xcb\xf3}\xca0,\x00\x999\x83L&\x83Z\xad\x16\x22M\xa7\xd3\x89N\x1d\xc5\x22o2\xcf%E*\xf0H\x0c\x96:6f\x18\xa6\xf2\xe6\x851~\x9f\xb2\xf7\x1f\x0c\xc3\xb0\x00d\xe6\xcedO\x11\xba\xe2N\x1f\xe5x\xeeR\xe2\x90a\x98\xcaG\x9a\x8f+\x93\xc9 \x97q~.\xc3\xb0\x00d\xe6\xfc\x84\xcf0\x0c\xc30\xcc\x14\xd7Q~\x0b\x18\x86a\x98\xd9\x0cG\xee\x19\x86\x05 \xc30\x0c3\x8f\x05 \xe7\xee2\x0c\x0b@\x86a\x18f\x1e\x8bA\x86aX\x002\x0c\xc30\xf3D\xf4\xb1\x08d\x18\x16\x80\x0c\xc30\xcc<\x13\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb8\xf8\xe3\x1c@\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe62\xd2N \x1c\x01d\x18\x16\x80\x0c\xc30\xcc<\x11\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb1\x18dA\xc80,\x00\x19\x86a\x98\xb9\xbe\x90qkH\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe60\xc560\x1c\x11d\x18\x16\x80\x0c\xc30\xcc<\x15\x83\x0c\xc3\xb0\x00d\x18\x86aX\xf41\x0c\xc3\x02\x90a\x18\x86a\x01\xc80,\x00\x19\x86a\x18\x86a\x18\x16\x80\x0c\xc30\x0cS\x99P\xe4\x8f#\x80\x0c\xc3\x02\x90a\x18\x86a\x18\x86a\x01\xc80\x0c\xc3\xcce8\x02\xc80,\x00\x19\x86a\x98\xf9\xb6\x90\xc9\xe5\xc8\xe7\xf3\xfcF0\x0c\x0b@\x86a\x18\x86a\x18\x86\x05 \xc30\x0c3'\xe1#`\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe6*2\x99\x8c#\x80\x0c\xc3\x02\x90a\x18\x86\x99\x8f\x22\x90a\x18\x16\x80\x0c\xc30\xcc<\x16\x83,\x08\x19\x86\x05 \xc30\x0c3\x0fD\x1f\xa1P(\xf8\x0da\x18\x16\x80\x0c\xc30\xcc\xbcZ\xd4\xe4\xbc\xac1\x0c\x0b@\x86a\x18f\xce#\x8d\x00\xf2\xf1/\xc3\xb0\x00d\x18\x86a\xe6\x99\x00\xe4\x08 \xc3\xb0\x00d\x18\x86aX\x002\x0c\xc3\x02\x90a\x18\x86\x99{\x0a\xb0\xb4\x18d\x18\x86\x05 \xc30\x0c3g\xf5\x1f\xe7\x002\x0c\x0b@\x86a\x18f~\x09@.\x02a\x18\x16\x80\x0c\xc30\xcc\xfc\x15\x80\x9c\x03\xc80,\x00\x19\x86a\x98y,\x06\x19\x86a\x01\xc80\x0c\xc3\xcc\x87EM\xc6\xcb\x1a\xc3\xb0\x00d\x18\x86a\xe6<\x05Q?^\xd5\x18\x86\x05 \xc30\x0c3\xbf\x04 G\x00\x19\x86\x05 \xc30\x0c3\x1f\x162\xb9\x1c\xf9|\x9e\xdf\x08\x86a\x01\xc80\x0c\xc3\xccGdr.\x02a\x18\x16\x80\x0c\xc30\xcc\xdc\x16|2Y\x81\xf5\x8bB\xae\xe07\x85aX\x002\x0c\xc30\xf3A\x042\x0c\xc3\x02\x90a\x18\x86\x99\xaf\x8b\x1a\x1bA3\x0c\x0b@\x86a\x18f~\xc1\xd1@\x86a\x01\xc80\x0c\xc3\xcc#\xf2\xf9<\x0b@\x86a\x01\xc80\x0c\xc3\xcc7X\x002\x0c\x0b@\x86a\x18\x86\x05 \xc30%\xf8\xff\x82\xe5f\x8a\x9e\x05\x9f\x09\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xbf\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\xaa\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05_IDATx\xdab\xfc\xff\xff?\x036\xc0\xc4\x80\x03`H\x18\xe4\xf1\x81\x8d`\x81\x09\xd8\xf5\x08\xff\xff\xf4\xe87\x03\x9f\x1c+\x98\xcf\x88\xcb\x0e\xb8\x0e\xe32\xfe\xff\x7f\x7f@\x14]\x98\xf4\x89\x11 \x80\x18)s\x15\xccEp;\xecz\x84\x80.\xfa\x83\xa9\xe3\xfb\xbb\xbfpK\xadZ\x05!\xba@\x96\x83\xb0Q)\xef\x7f\xfd\x5c\xde\xff0>@\x00\xe1t\x15\xd1AE\x08\xb0\xa0\x0b8N\x16\x81X\xc9\x08F`\x86\x82\x84\x08\xc3\xbc\x90\xeb\x8c(n\x05a\x8b&\x01\x14\xf7\xc2\xb0]\xaf\x10\x5c\x0c\xc5\x86\xef\xaf\xff2\xb0\xf131\x98\xd5\x0a\xfcg\x04\xda\xf3\x1fj\x0b\xb2/Q<\xcd\xc8\xc8\xc8\x0e\xa4\xe4\x818\x18\x88O\x00\xf1] \xfe\x04\xc4\xdf\x80\xea~\x81\xd4\x00\x04\x10\xedCi\x805\xa4l\xd0\xc6\xf0 \x8a\xa7\x1d\xa7\x88\x82\x02\x93\x01\x1e\xa4P\xa9w\xb7\x7f\x81\x13\x1a\x86\x0d\x7f\x7f\xfccx\x7f\xeb\x17X\x01\x88\xfe\xf3\xf3?\xc3\xbe\xdc7\x8c8\x93\xc6\x9f\xef\xff\xe0&\xc1\x80}\x9f\xf0\x7f\x9c~\xf8\xf6\xfa/\x86?\xfe\x00m\xc5i\x83\xba\x96\x10\x8390Y\x80\x8d\xfc\xcf\x00O\x17\xc8\xb6\xa2h\xb8y\xed\x1d\x033\x07#\x03,\x1b\xc3r\x15\x03\x9e\xb4\xc4\x0d\xa4\xf4\x80\xd8\x04\x88\x8f\x02\xf1#P\x9a\x04\xaa\xf9\x0aS\x03\x10@$\xa7%\x9a\xa7\x8c\xe1c\x01r!\x89\x0f\x10\x8c\x03|\x06\x09k\xb2\x83\xca(\x86\xb77~a\xa4\x06\x82\x16\x80\x0c\x16Pb\x03&\xbb\xff\x0c\xa8:\x19Q\x13%\x14|\xb8\xf7\x1b\xab%,\xb8\x5c\xc7#\xc9\x02\xce\x05_\x9e\xfd\xc1\xaa\x11\xe63\x90\x1c\x88-\xa8\xc2\x06\x16CW\x8b\xd3\x82?\xdf\xff\x83]\x8f\x0b \x1b\xc4#\xc3\xc2\xf0\x1b-K\x92\x14\x07\x1az\x22\x0c\x0f\x9f\xbdg\xf8\xfe\xe6/V5\x1c\xc2\xcc\xe0\xe0\xfa\xf1\xf6\x1f\xfe\x9c\x89\x0cL*\xf9\xff3\xc2\xc3\x1c\x16\xda\xb0:\x89\x11\x1a\x0b\xff\xe1\xb1\xf1\xfb\xeb\x7f\xacAITN\x06\xf9\x82\x11\xd8f8\xdf\x8b=\xa5P\x94L)\x05\x00\x014Z\x16\x8dZ0j\x01\x05\x16\x80\xdaM\xc4T:dW8\xc2\x1a\xec\xe0\xf6\x96\xa2\xb8\x18\xc3\xd9\x03\x8f\xc9\xabp\x04\xd5\xd8 \x0d7p\x13\x05\xa9z\xf9\x0f-\x02\x19\x11U\x8f\x82\x84(\xa2%NL\x10\xf1+\xb12\xfc\x07vC\xfe\xfd\xfd\xcf\xf0\xef\x0f2\x06\x89A\xf1\xef\xff\x105@|\xee\xd0S\xe2\xfa\x090\xd7\x83\xba\x5c \xdf}z\xfc\x1b\xabF^\xa0<\xc8\xb9\x9f\x1e\xfd\x82\xd7jD[\x00\x02 \xd7)H\x8b0,)\xbd\xc5\x88\xcb\x11\xb0pw\x9c$\xfc\xdf\xc0F\x9a\xb4T\xf4\x17h\xc1\xbf_\xff\x08V\x99 \x8b\xfe\x82\x83\xed\x1fi\x16\x80\xc2\xfb\xef\xdf\x7fD%\xd9\xff\xd0\xf8!\xc9\x02\x90\xa6\x9bW\xdf\xc0\x83\x03_\x9a\xff\xfb\x07\x12\xd9$\xc5\xc1\xa9\xb6\x8f\xe0 \x88\x98\xa3\xf4\x1fT\xa9\xffx\xfb\x17k\x9e\x00\xc9\xfd\xfb\x8d;\x88\xf0\xe6\x035-\x11\x86\x07O\xdf\xc1\x13;#J\xe5\xcf\x88\xe0\x03\x89_\x9f\xfe\x11_\xe9\x83\x0cg\xe1fDjA\xfc\xc7h\xd31\xa0uB\xcf\xb4\x7fd$\xb9\xa8\x00Y\xc4\xcc\x0e\x0d\xe7\x9f\x0cD5\xc2\xc8*\x8b\x98X\x19\xc0\xe1\x8c\xcf \xa2{\xfcH\xdd#N %\x08\xc4\x86@\xac\x0f\xec\xd8/\x00\x8a\xf9\x01\xd9\xa0\xa4\xf5\x1e\x88\xdf\x82Z\x98P\x0c\xeaz\xff\x01:\xf6\x0f\xdd\x9b-\x00\x01\xda\xb5\x9a\x95\x06b \x9c\xa8U(\x05Q\x16\x11\x17\xa1\x1e\xfc\xa1\x0aB\xef\xe2\xcb\xf8\x0e>\x96\x8f\xe0A\x8f\xeaE\x0f\x1e<\xb4hA\xa1\x94j\x11\xb5\xdb\xce:\x93MLv\xb7\xd8M\xeaV\x94\x0d\x0c\xcd\x12\x9a\xe4K6\x99\xf9\xbe\xd9\xdc\x07\xf8\xf3\x1e\xb3\x00P\x00(\x00\x14\x00\xdc\x1cA^\xc5\x8c\x18\x5c<\xd7\xaf\x03\xa0\xe2\xd5\x16D\x00qt\xb2+\xbc\xd0\xe5\xe9\xbd3\x18gG\xa6V\xb2~\xb8\x8e\x9d\x80\xd4|gd\x04\x03\xfa\xed\xe4\xc0\x92bP\xe3\xa9m\xe8)\xa4\x17sVE\xfe\xc2\xb0\x1f\x15\xfdg\x05d\x0d \xd2\xb4JB^\x13\x83\x1b\x9a\x1a\xe7\xba\xaf\x10\x11qn\x86gZ\xf6\xe1*`\xa3\x04\x03\xe7:|\x0b5\x7f\xea\xde\xf53\x81\xb0\x02 \xe8N\xb5\xc4R\xbd\x1aA\xa4\xaeF\xa8\xd4\xa4\xa26\x15\xc4\x86\xb1\xffR\xfbs#p:\x17\xd6g \x0c\xa2\xe1\x05m]\xf5\xd8\xf5\xc5\xa3\xd5\xa0\xb4\x08\x15\x7fNL\xba\xf70H1$\xb2\xfa\x81\xcf\xae\xceZ\x99\xfa\xb5\x06@\x14L\xa9\x800t;\xc4\xe7\xc7\x1d\x9e\x04EL\x99\xb4_\xda\x12\xc8\xc8\xd6\xdcv`\x00\x0c\x84\xc4\x09V\x03\x8d\xbb:\x896\xaa\x06\xa2\xfb\xb992\xc1\xf0\xa4\x860\x0c\xc0J1\xf9~a\xa8o\x90|9\xc73\xf0\xf1\x02\xa9\x95T\x13\xae\xed\xaf0WvAJ\x82:\xd5\x10d\xdf\xd9\x89\xfd\xc0\xf6\x9e\xc7\x9aH\x9d\x8d\x5c\x17\xab\xfaK\xb2F~\x01\xc6\xbe\x00\x8dV;\x96\xdd\xd8\xf0\x97\xd9\xedM;\x9fkts\xc7\x93\x83v\x8c\x8b1\xcd\xf7Y,G\x9c&\xeb<\xe6\x13\xf4\xc5J\xcf\xfd\x1e\xe4\xeb\xc8\xe8w\xb6\xcc\xbf\xb2\xad\xe9L\xb6tJ\xcay\xa9\x94z\x98h70Q\xfe\xc6\xc5\x0fL\xcc\x89\x85v1\xcf\xb5\x0a\x89\x15\xe8\x87?\x12\xa8M%\x98\x1bu\x98\xa7Y\xacw@~\xda0#m\x11\xad\x82VF#\x1df\x0bmM.L\x17\xad\x89\xd6\xa3\xcb\x0b\xed\x1d\xed\x0d\xedU\xd6!aCi\xe2y\x94F\xf3/e\x95O\x08\xef\x09\x03\x8d\x1a\xcd\xa5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" -qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x06\x07\xa7(\x98\x00s\x00p\x00l\x00a\x00s\x00h\x00\x0a\x08\x94\x19\x07\x00s\x00p\x00l\x00a\x00s\x00h\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x08\x00FX'\x00s\x00y\x00n\x00c\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" -qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x17\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x12\x00\x00\x00\x05\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x12C\x00\x00\x00z\x00\x00\x00\x00\x00\x01\x00\x00\xbc\xce\x00\x00\x01\xaa\x00\x00\x00\x00\x00\x01\x00\x01\xe9\x0a\x00\x00\x01F\x00\x00\x00\x00\x00\x01\x00\x01\x9dy\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x01r\x07\x00\x00\x00\xf4\x00\x00\x00\x00\x00\x01\x00\x01W\xe8\x00\x00\x00\xc2\x00\x00\x00\x00\x00\x01\x00\x016\x0e\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x01Hn\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x01\xd90\x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x00\xad&\x00\x00\x00\xaa\x00\x00\x00\x00\x00\x01\x00\x01%\xb3\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01\xc9J\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x02\x02a\x00\x00\x00J\x00\x00\x00\x00\x00\x01\x00\x00\x9e\x08\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x01\x16\x10\x00\x00\x01b\x00\x00\x00\x00\x00\x01\x00\x01\xb9Y\x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x01\xf1\xcc\x00\x00\x01.\x00\x00\x00\x00\x00\x01\x00\x01\x8d\xb6\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x18\x00\x00\x02\x08\x00\x00\x00\x00\x00\x01\x00\x02\x22\xf1" +qt_resource_data = "\x00\x00\x9e\x04\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x80\x00\x00\x01\x00\x08\x06\x00\x00\x00#\x95\xc7f\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x17\x12\x00\x00\x17\x12\x01g\x9f\xd2R\x00\x00\x00\x07tIME\x07\xdf\x07\x07\x090\x11\xbal\xcd}\x00\x00 \x00IDATx\xda\xec}w\x94\x5c\xe5y\xfe3w\xee\xf4\x99\x9d\xb2\xb3;\xdb\x9bV\xbb*+\xd1-\x0c\xa2\x83\xb0\x0d\x1cSbL\xe28v0v|lp\x0eq\x09\x04\x88c\x1cl\x9c\xe08\x80K\x82c\xe3\xb8!sL\x5c\xe4P\x02\x18\x04\x085TWm{/\xb3\xd3{\xbf\xf3\xfbC\xbf\xf7\xf3\x9d\xbb\xb3\xb3\xbb\xd2\xae\xb4\xda\xfd\x9est\xa4\x1d\xcd\xcc\xce|\xed}\xbe\xe7m\xaa|>\x9f\x07\x07\x07\x07\x07\x07\xc79\x8c|>\x8fT*\x85\xed\xdb\xb7#\x95J!\x97\xcb\xe1\xb6\xdbn\x83$I\x10\x04\x81\x0f\x10\x07\x87\x02|Wppppp\x9c\xf3P\xa9T\xc8\xe7\xf3\xc8\xe7\xf3P\xa9T\xc8\xe5r|P888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0\x0e-\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0* \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqppp\x02\xc8\xc1\xc1\xc1\xc1\xc1I \x07\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqpp\x02\xc8\xc1\xc1\xc1\xc1\xc1\xc1\x09 \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x9c\x00rppppp,%P!h\x0e\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\xe3\x9c\x07w\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xc1\x01\x95J\xc5\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\xe5\x0aI\x92\xb8\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7J\x05W\xfe888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83c\xb9A\xe9\xfe\x95$\x89\x0f\x0a\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\xcb\x9d\x00\xcaI\x1f\x8f\x07\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x10@\xe5\xcf<\x16\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\x02\xb9\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a \x7fr\xc5\x8f\x13@\x0e\x0eN\x0098888V\x08\x09\xe4\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\xfe\xe4\xa4\x8fg\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x08 A\xa5Rq\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95F\x029\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x91?N\x00988\x01\xe4\xe0\xe0\xe0\xe0X!\x04\x90\xc7\x00rpp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x09 \x95\x82\xe1\x0a \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0cR\x9e+\x80\x1c\x1c\x9c\x00rpppp\xac(p\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15F\xfc\xf2\x12'\x80\x1c\x1c\x9c\x00rpppp\xacH\x22\xc8\xc1\xc1\xc1\x09 \x07\x07\x07\x07\x07'\x80\x1c\x1c\x9c\x00\xf2!\xe0\xe0\xe0\xe0\xe0Xn\xe0\x9d@888\x01\xe4\xe0\xe0\xe0\xe0X\x81\xe0\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x86\x5c.\xc7\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eN\x0098888\x96-x+8\x0e\x0eN\x0098888V\x18x\x0c \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0c\x5c\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x85\x81+\x80\x1c\x1c\x9c\x00rpppp\xac \xf0:\x80\x1c\x1c\x9c\x00rpppp\xac@p\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a\x03\x8f\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\x13@\x0e\x0e\x0eN\x0098888\x963\xb8\x0b\x98\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x04\x95J\x85L&\xc3\x07\x82\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95\x04\xde\x0b\x98\x83cf\x88\x8b\xf5\xc6$\xbds\x09~\xf1o\xb9\xf2\xbf\xf3\xf9<\xfb7\x07\xc7b\xedm\xf9:+\xb5\xc7\xe9y\xf4\xb7r\xbdrpp\xfc\x09\x14\xb3\xc8K\xd8,\xdf\xb3S~n\xe6\xf3y\xa8\xd5\xeai\xf6\xfc\x9c$\x80\xf9|\x1e\xd9l\x16*\x95\x0a\x92$A\x92\xa4i\xc6\x82c\xe1\x17\x92\xdc\xb0\x0a\x82P\xf0X\xb1\xe7pp\x9c*\xe9\xa3}M\xc6\xaa\xd4\xfe\xce\xe7\xf3\xc8\xe5r\x10\x04\x81\xad?A\x10\xa0V\xab\xa1R\xa9\x0a\x0e>\x0e\x8e\x85\x14\x1e\x94\x84j\xa9\x93>I\x92\x98ZI{\x8a\xdb\xcd\xe5\xb76%I\x82J\xa5*8\x17\xe9L\x14\x04\x81\xfd9\xe7\x08`6\x9bE6\x9b\x85$I\xc8d2H\xa5R\xecg2\x14\x1c\x0b\x07Z\xb8$IB:\x9d\x86$Il\x7fg\xb3Y\xf6\xff\xc5@\x97\xc1|>\x0f\xadV\x0b\xbd^\x0f\xbd^\x0f\xadV\x0b\x00\x9c\x04r,\x0a\x01\xa4\xbf\x97\xba\x0b8\x9b\xcd\x02\x002\x99\x0c\xb2\xd9,2\x99\x0c\xd2\xe94r\xb9\x5c\xc1e\x8bcy\x81\xe6W\xa3\xd1@\x14E\x98\xcdfh\xb5Zh\xb5\xda3J\xfc\xc5\x85\xfa2\xf4'\x1e\x8f#\x1c\x0e#\x1c\x0e#\x16\x8b!\x99L\x22\x97\xcbq\x02\xb8\xc0 \x05EN\xfch1i\xb5Zh4\x1ah4\x1ah\xb5Z\xf6\x98(\x8aP\xab\xd5\x10E\x11*\x95\x0a\x1a\x8d\xa6@1\xe4\xe0(\xa5R\xa4\xd3i\xa4\xd3i\xb6\xbf#\x91\x08\x92\xc9$\xbb\xe8\x15\x83Z\xadfF\xd8h4\xc2n\xb7\xa3\xbc\xbc\x1cv\xbb\x1d\x1a\x8d\x86\x0f,\xc7\xa2\x13\xac\x5c.\xb7\xa4/\x1a\x99L\x06\x89D\x02\x89D\x02\xa1P\x08\xe1p\x18\x89D\x02\x99L\x86\x13\xc0eL\x00\xb3\xd9,DQ\x84\xd5jEyy9\x9cN',\x16\x0b4\x1a\xcd\x19[\xaf\xe2B~\xa1X,\x86`0\x88\xee\xeenx\xbd^\xf6E\xe4j\x15\xc7\xc2\x19\xe4h4\xca\x16\x92\x5ce\x15E\x11\x06\x83\x01&\x93\x09v\xbb\x1dF\xa3\x11F\xa3\x11z\xbd\x1e:\x9d\x0ez\xbd\x9e\x91C\xadV\x0bA\x10\x18)\xe4\xf3\xc4QLU\xc9\xe5rH\xa7\xd3\x08\x06\x83\xe8\xeb\xeb\xc3\xe8\xe8(\xbbP\x90\xb2\x5c\x0c\xf1x\x1c{\xf7\xeeE,\x16Css3\xd6\xaf_\x8f\x96\x96\x16\x08\x82\x00\xbb\xdd^\xf2\xb5\x1c\x1c\xa7z9\x96\x9f\x93K\x95\x00\x92r\x9eN\xa7\x11\x8dF1>>\x8e\xae\xae.\xe6\xa5!\xdb\xc9\xb1\xfc\xce\xd2\xfd\xfb\xf7cxx\x18v\xbb\x1d\xabW\xaf\xc6\xfa\xf5\xeb!\x08\x02\x13e\xce\x19\x02H_(\x9b\xcd\x22\x16\x8badd\x04\x95\x95\x95\xb8\xfa\xea\xab\x0bT*\x8e\x85_Hr\xf7\x1b\x11\xc0L&\x83\x89\x89\x09\xc4\xe3qLNN\xe2\xd8\xb1c\xc8f\xb30\x1a\x8dp:\x9d\xb0\xdb\xed0\x18\x0c\xd0\xeb\xf50\x99L0\x9b\xcd0\x18\x0c\xd0\xe9t\xd0h4|\xce8f\xdc\xe3\xf1x\x1c\x13\x13\x13p8\x1c\xd8\xb4i\x13[+\xa5\x0e\xabl6\x8b\x1bo\xbc\x11\xdb\xb7o\xc7\x0f~\xf0\x03\x08\x82\x00\xadV\x0b\x9b\xcd\x06\x8b\xc5\xc2\x14i\x0e\x8e\x85Z\xab\xca\x8b\xf2RU\xd1\xc8\x0d\x98L&\x11\x0c\x06\x11\x0e\x87q\xe7\x9dw2e\x88\x0b'\xcbw\x8dn\xd9\xb2\x05G\x8e\x1c\xc1\xd3O?\x8d#G\x8e \x16\x8b\xa1\xac\xac\x0cF\xa3\x11:\x9d\xee\x8c\xb9\x81O\xfb\xe4\xa5\xa0\xc6t:\x8dx<\x8e\xa9\xa9)\xe8\xf5z\x98\xcdf\xee\xf6]dP\x1c\x95r\x9c+++\x0b~\x0e\x06\x83p\xbb\xdd\xd8\xbf\x7f?\xba\xba\xba \x08\x02\x0c\x06\x03l6\x1b\x1c\x0e\x07\xacV+\xca\xca\xca`6\x9ba4\x1a\xd9\xe2#U\x90\x83\x1fX\x14\xd7\xeb\xf3\xf9PYY\x09\x8b\xc52\xeb\xfe\xa6\xb8\xbf\xae\xae.<\xf7\xdcs\xe8\xe9\xe9\xc1\xaaU\xabP__\x8fh4\x0a\xbb\xdd\xce\xd60\x07\xc7b`\xa9\xc7\xd1e\xb3Y$\x93I\x84\xc3a\xf8\xfd~\x18\x0c\x06fS9\x96'T*\x15\xa6\xa6\xa6\xf0\x9b\xdf\xfc\x06===\x98\x9a\x9a\x82\xdb\xedF0\x18\x84\xcb\xe5bb\xce9A\x00\x05A@:\x9dF2\x99D,\x16C\x22\x91\x80N\xa7c_\x94\xe3\xcc,\xa8b\xb7`\xca$\xb3\xd9l\xb0\xd9lhooG>\x9fGgg'zzz\xb0o\xdf>\x08\x82\x80\xd6\xd6VTTT\xc0\xe9t\xc2\xe1p\xb0\x9b\x08\xbd7w\xd3\xadl\xe4r9d2\x19\xc4\xe3q\xc4b1\xa4\xd3\xe9Y\xf7w>\x9f\x87 \x08\x18\x1d\x1d\xc57\xbf\xf9M\x9c8q\x02\x1e\x8f\x07\x91H\x04\xf1x\x1c\x99L\xa6d\xf2\x08\x07\xc7\xa9^V\x94?/U!\x82\x12&\x13\x89\x04\xdb[\xb4\xaf\xf8y\xbb<\xd7\xa5J\xa5B8\x1c\xc6/~\xf1\x0b\xec\xdc\xb9\x13###,|.\x1e\x8f\xb3\x98\xd53\x85\x05q\x01\x93\x8b\x88\xbe\xc4\x5c\x82\xbb\xb9:\xb8p\xe4Oy[\x98\xa9\xd6\x1a=o\xe3\xc6\x8d\xd8\xb8q#6o\xde\x8c\xb7\xdez\x0b/\xbe\xf8\x22\xea\xea\xea\xd0\xda\xda\x0a\x97\xcb\x85\xaa\xaa*8\x9dN\xe6\x1e\xa6\x0cb\xee\x16^\xb9kL\x92$\xc4\xe3qD\xa3\xd1Y\x89\x1f\x91\xbf}\xfb\xf6\xe1\xa7?\xfd)\x0e\x1e<\x08\xaf\xd7\x8bL&\xc3\xfePl\x167t\x1c\x8bmx\x97\xaa\xad!\x17o&\x93A \x10(i7\x17\xab\xae\xee|\xf6\xdfB\xfe\xee\x95\xb4\xef\xe5\x84\xde\xeb\xf5\xe2\xfb\xdf\xff>\xde|\xf3M\xf4\xf7\xf7#\x16\x8bA\xadV\xb3\x90\xae3\x9d0\xbb \x04\x90\x0cD4\x1a\xc5\xd8\xd8\x18\xbc^/_\x00gx\x81\x9d\xca\xf3\x5c.\x17\xee\xb8\xe3\x0e\xdcz\xeb\xad\xf8\xe7\x7f\xfeg\xbc\xf2\xca+hkkCss3\x9a\x9b\x9bQUU\x05\x87\xc3\x01\xb3\xd9\xcc\xe6\x9a\x97\xedX\xb9F4\x99Lbjj\x0aUUU%\xd7X<\x1e\xc7[o\xbd\x85\x17^x\x01\x87\x0f\x1f\x86\xdf\xefG<\x1e\x9fv\x18\xf23\x80c\xb1\x94\x16\xc2Rv\x01+\xe3\xe7\xa7\xa6\xa6f=\xbb\x17\xf2\xbb\x9c\xa9\xb1Q\xce\x89\xbc4\xd4\xd9\xba\xcc\x9e\xa95\xa1R\xa9\x90L&\x11\x8dF\xd1\xdd\xdd\x8d\xd7_\x7f\x1dG\x8f\x1eEWW\x17b\xb1\x18KP\x22q\xe5L\x8b,\xe2B.\xa6\x5c.\x87H$R\x92\x00F\x22\x11\xe4\xf3yLNN2\xe6;\xd3\xfbQ\x8d\xa4RFi)\xdd\xeeN\xf5\xf3\x9c\xcakT*\x15R\xa9\x14\xaa\xaa\xaa\x10\x0e\x87a\xb5Z!I\x12\xca\xca\xca\x90\xcb\xe5`6\x9b\xa1R\xa9\xa0\xd5jg,\xd2+7\xc4_\xfb\xda\xd7\xb0{\xf7n\xfc\xe4'?A\x7f\x7f?.\xb8\xe0\x02\xb4\xb6\xb6\xa2\xbe\xbe\x1e.\x97\x0b6\x9b\x8d\x05\xebs\x12\xb8\xf2@\xfb;\x1e\x8f\xc3\xe7\xf3\x15}N.\x97\xc3\xc0\xc0\x00^y\xe5\x15\xec\xd9\xb3\x07'N\x9c\xc0\xc8\xc8\x08b\xb1\x18;p)\xcb\x8d\x07\xb8s\xccv\x86.\xc4\x85s\xa9\x87\x19\x90\x1b8\x1e\x8fc||\xbc\xe8\xf9\x1c\x8b\xc50<<\x0c\xb7\xdb\x8d\xde\xde\xde\x19\xc9\xd3\x5c\xec\x88\xdc\x15y*\x9f\xf5T^W,\xc9\xebTm\xe5b\xb8H\xe7\xf3YJ=\xaf\x98\xc7M\x10\x04D\xa3Q\x04\x02\x01\x8c\x8f\x8fcdd\x04\x83\x83\x83\x88F\xa3,\x0c\x86\x88\xdf\xd9h\xda .\xd4\x00\x92\x01\x88\xc7\xe3,FH\x8e\xd1\xd1Q\xf4\xf7\xf7\xa3\xbb\xbb\x1b\xf1x\x1c\x1e\x8f\xa7(\xdb\xa5\x85\x9fJ\xa5\x90N\xa7Y\x11c\xe5\x00+[K-$\xb9:]\xe26\xd7\x00\xce\xf9\xc8\xfa\xb4\xf9\xe4\xef-\xaf\x01HcAn[\x8b\xc5\x82\xf2\xf2r\x18\x8dF\x94\x95\x95\xa1\xac\xac\x0cv\xbb\x1dv\xbb\x1d:\x9d\x8e\x1d\xac\xf4\x1e\xd9l\x16\x9b6m\xc2\xea\xd5\xab\xf1\xf4\xd3O\xe3\xc5\x17_\xc4\xfb\xdf\xff~\x04\x83A\xa4\xd3id\xb3Y\xf6ZN\x02W.\xb2\xd9,\x82\xc1`\x01\xe9K\xa7\xd3\x98\x9a\x9a\xc2\xfe\xfd\xfb\xb1\x7f\xff~\xf4\xf6\xf6bhh\x08n\xb7\x1b\x89D\xa2\xc0\xad!\xdf\xb3<\x0c\x84C\xde\x05\x83J\x9f(\xcf\xceP(\x04\xb7\xdb\x8d@ P\xd0UF\xaf\xd7\xa3\xbd\xbd\xbd\x80`\xccG\x01<\x1d2\xb4P \xfb\x96\xc9d\x0ab\x00\x01\xc0\xef\xf7\xa3\xbf\xbf\x1f;v\xec@\x7f\x7f?\xc2\xe10\x82\xc1`A\xfd\xd6S\xb5u\x0bE\x8a\xe5cX\xec\xf7SR\x8b\xb2\xc5\x9d\xb2\x1d\x9arLf\xfa^D\x98\x8b\xa9\x8a\xa5~.5N\x0b\xd5v\xaf\xd8{\xe4r9$\x12\x09V?\xd5\xe7\xf31\x8e$O\xf6\xa0j\x0ag\xfaL\x5c\xd0\xfa\x0b\xc4\xce'&&\xd8\x97\x0f\x06\x83\xe8\xe9\xe9\xc1\xc1\x83\x071::\x0a\x9f\xcf\x87d2\x09\xaf\xd7[@\xee\x94\x93F\x93\x12\x8f\xc7\xd9\x82 \xb9\x5c.\x87'\x93\xc9\x05'p\x8bI\x0eO\xf5\x90\xa0X<\xe5\xe3t\xf8i4\x1a\xe8t:\x18\x0c\x06$\x12\x098\x1c\x0eH\x92\x04\xb3\xd9\x0c\x93\xc9\xc4J\xc0TVV\xc2f\xb3\xc1n\xb7\xa3\xa2\xa2\x02&\x93\x09\x06\x83\x01\x82 \x97\xcb!\x95J\xe1\xc6\x1bo\x84\xc5b\xc1\x0b/\xbcPP\xec7\x97\xcb\xc1\xe1p\xc0h4\xb2R\x05\x1c+\x03tI\xc8f\xb3\xf0z\xbdH&\x93\xd0\xe9tH&\x93\xe8\xec\xec\xc4\xee\xdd\xbb\xd1\xdb\xdb\x8b\x91\x91\x11LNN\xc2\xe7\xf3!\x12\x89\xb0RE\xb4v\xc9\xc0s\xf2\xc7\x89\x1f)$\xa2(B\x14Ex<\x1e\xbc\xfd\xf6\xdbx\xeb\xad\xb7\xf0\xf6\xdboc``\xa0 ^T\xbefr\xb9\x1cZ[[\xf1\xd6[o\xc1\xe9t\xce\xe8:+\xe5j\xccI9\xa8p\xf6[\x12\x92]\x8bF\xa3\xe8\xec\xec\x84 \x08\x18\x1f\x1fGoo/\x06\x06\x06\xd0\xdd\xdd\x8d@ \x80d2\x89D\x22\x01\xe0d\xf1\xe8\xb3I\x5c\xf3\xf9<#\xecr\xb5\x8bHL>\x9f\x87\xc9d\x9aF\xc0\x95s\xa1\xd5j\x0b\xc6\x9fBMJ\xa9k\xc5\xbc\x834\x1er\x01\xe9L\x9d1\xf4{\x8b\xfd>\xba\xacd\xb3YV\xec;\x95JM[\xcf\xf2\x8bM\xb1\xb8\xfe%O\x00I\xae\xcf\xe7\xf3\xac\x04L:\x9df$.\x9dN#\x16\x8b\xc1\xe7\xf3\xb1\xfaG\xca~\x87$\x8bR\xab\xa9L&\x03\x8dF\xc3\xda\xe3H\x92\x04A\x10\x90J\xa5\xd8k\xa8v\xddb\xa8t\xa7\xb3\x80\x16r\xf2h,\x09T`\x9bz\x0aR\xc9\x16Q\x14!I\x12L&\x13<\x1e\x0f4\x1a\x0d\xab+\xd4\xd3\xd3\x03\x87\xc3\x01\xbd^\x8f\xf2\xf2r\xd4\xd7\xd7\xc3j\xb5\xa2\xb6\xb6\x16N\xa7\x13\xa2(\xb2[h:\x9d\x86F\xa3\xc1\xc5\x17_\x8c\xbd{\xf7\x16\x14\x9c&\xc5P\xa7\xd3-\xf9\x0a\xfb\x1c\x0b{\xc0\xc9\x0f)\xba|%\x12\x09$\x93I\xa6\x12+[X)\x0fF\xa5r\xcf\x13\x8bV\x06\x94\xe7m,\x16\x83\xc7\xe3\xc1;\xef\xbc\x83_\xfd\xeaWx\xe9\xa5\x97\x0a\xd6\x09u6\x92\xaf!\xe5:\x9a\xcb\x99-\xef\xb1+\x7fN\x22\x91\xc0\x1f\xff\xf8G\xd4\xd4\xd4\xa0\xa3\xa3cV;\xb2\xd8\xe3\x92\xcb\xe5\x10\x8dF\xf1\xf2\xcb/#\x16\x8b!\x93\xc9\xa0\xaf\xaf\x0f\x1e\x8f\x07^\xaf\x97u\x06\xc9d2KfNi\xaf\x13\x91#\x11\x81:R%\x12\x09&\x14P\xe7)R\x04\xe9\x5c\xa1\xccW\xf9x(\xc3\x96H\x80\x90\x17\xa4\xa7\x0eC\xc4\x17\xe8w\x93\xa7\xecL\xf7\xd4-\xc5\x15\xe8<$\x1e\x93\xc9d\x0a\xfa>\xcb/\xc6g\x1a\x0bF\x00\xc9(\x00@\x7f\x7f?\xf6\xec\xd9\x03I\x92\xe0\xf3\xf9\xd0\xdf\xdf\x8f\xfe\xfe~LNN\x22\x1c\x0e#\x9b\xcd\xb2\xa0pb\xef\xc4\xe0\x05A`\x841\x97\xcb\xb1Vr\xc4\xa2\xcf\x04\xab_\xca\xea\x04\xa9n\xb4\x80\xa8`(\x19g\xb5Z\x8dX,\x06\x95J\xc5\x5c\xbd\x1a\x8d\x06\x16\x8b\x05\x81@\x00eee\x98\x9c\x9cD__\x1f\xecv;\x1c\x0e\x07\x0c\x06\x03V\xaf^\xcdnc\xe1p\x18\xa2(B\xaf\xd7\xa3\xb5\xb5\x15G\x8f\x1ee\xad\xfeh\xa1\x92;\x98\xc7r\xad\x1c\xd0\x9as\xbb\xdd8r\xe4\x08\x04A\xc0\xd4\xd4\x14\x06\x07\x071<<\x8c\xf1\xf1q\xf8|>D\xa3\xd1\x02\xd5X\x0ey\xb8\xc2R\xdfk\x1c\x0b\xb3fH-\x02\x80\xd7_\x7f\x1d\xdb\xb6m\xc3\xee\xdd\xbb\xb1k\xd7\xae\x82K-\x11\x05\xf9e\xa2\x14\xc6\xc6\xc6fue\x16S\xa0T*\x15N\x9c8\x81t:\x8d\xf1\xf1q477\xc3n\xb7\x9fUrL\xf5\x00\xdf|\xf3M\x18\x0c\x06\xa6\xa2\xc7b1\xa4R)\xa4R\xa9\xb3\x9681\x1b\x04A`\xaa\x1d\xa9\x80\x94\xd8\xa0\xd5j\x19\x09$\xe2M\x82\x05\x915\xa5\xfd \x92K]R\xe89\xd4\x8aR\x9e<#\x9fc\xb9r\xb6XY\xd3\xf3\x15|\xe8\xf7\xcb\x9b5\x14\x9b\xc7\xb3U\xfaG\x5c\x8cA\x10E\x91)D\xd4aB\xde\xda\x86\x9e\xa7V\xab\x11\x8f\xc7\xd9\xe4\x91\x9f\x0f\xe5\xe5\xe5\xf0\xf9|0\x9b\xcd\xa8\xa8\xa8\x00\x00x<\x1ev\x135\x9b\xcd\xe8\xec\xecd]\x1bh.m6\x1b\xdb\xcc\x1c+\x07t\xa9 %\x98\x0es\xba\x8c\xc8\xd7\xecL5*9\x96\xbf\xe2\x97\xcdf\x99\x8a\xf7\xf4\xd3O\xe3{\xdf\xfb\x1e\x06\x07\x07\x91J\xa5\xd8\xda\xa0\xb3C\xaenY\xadV\x5cs\xcd5hkkCCC\x03\xfbc\xb5Z\x0b\xde_\xa3\xd1\xa0\xa2\xa2\xa2\xc0\x16\xccu}%\x93I\xf6\xbaR\xc9\x86gb\x9c\x88\x10\xe4r9\xf4\xf5\xf5!\x99L\x22\x95J!\x99L2\xb5\x88D\x10\xb9z\xb6\x14m\x14\x8d)\x9d\x11\xe9t\x1a:\x9d\x0e\xd9l\x96\xf5\xa8'\x8f\x16u\xbc(f\xd3\x88\xf0\xd2\xbf3\x99\x0c\xb2\xd9,\xe3\x04t\xb1P\xd6\xbc\x95\x7f\x9e\xa5r\xd6(\x95k%9%\xe1\xe6L\xb9}\x17\x8d\x00\xcewP\x22\x91\x08\x93ri\xc1\xd3m\x87\xfc\xe4rW\x92\xfc\xef3Y,q\xa9\x93Be\xf6\x10-(\x22\xe3Z\xad\x96\xf5\x07\xa6q\xa6\x9f\xa9\x83\x8b\xc9d\x82\xcb\xe5B(\x14b\xad\xe2\xd4j5\x8cF#\xdb|\x07\x0f\x1edq\x86\xd4K\x98nv\x5c\x05\xe4\x06\x9f\x83C~&\x85\xc3a<\xf5\xd4Sx\xf4\xd1G\xd9Y\xa44\x86z\xbd\x1e\x95\x95\x95\xb8\xf3\xce;q\xc3\x0d7\xe0\xf2\xcb/\x9f\xd6\x1d\x86\xc8Q\xb15\xb6\x10Y\xc2\x14^s6\xce1y\x22\x9f \x08\x18\x1b\x1b\x83Z\xadf\x1e0y\xa8\xd4R\xdc\xefJ;$'\xaa\xe4\x0e&\xb2N\xa4\x8cD\x84L&3\xcdU+O\xf2 \xc50\x9b\xcd2AH9&\xa7\x9b\x88y6\xc6l\xa6X\xfe\xb3\xa1\x02\x8a\x8b\xb5\xf9U*\x15\xeb\x0c\x92L&\xd9d\xc9\xcbI\xe8t:\xa4\xd3\xe9\x82\xe0H\x8au(\x15GT,\x93h\xa5\xa9\x7f\xf2\x9b\xa0|\x1c\xe4A\xa7\x14\x17\x91\xc9d \x8a\x22\x0b\xdc\xcff\xb3\xcc}\xeb\xf5z\xa1\xd7\xeb\xd9\x1cP\x1b\xbfd2\x09\x8dF\x83p8\xcc$\xfd`0\x88}\xfb\xf6\x15\xf4\x10\xa6x\x0d\xde6n\xf9\x13f\xf2\x18\x8b\x5c.\xc7\x08!\x05\xd2\x92{\x97\xdc\xec\x14\x9bID\x906\xb3Z\xadF&\x93\x81Z\xad\xc6\xd0\xd0\x10\x8e\x1e=\xcaJ\xcb\x18\x8dFF\xfe\xb8+\x98+\x7f\x1c+\x0ft~h\xb5Z<\xf2\xc8#\xf8\xe1\x0f\x7f\x08\xb7\xdb\x0d\x00,\x06L\x92$\xacY\xb3\x06\x8f<\xf2\x086o\xde\x8c\x86\x86\x86\xa2kh\xb1\xcf\x10eAe\x95J\x05)/\xcd\xba\xc6\xe5\xee\xec\x85T\xff\xc8\xcbb\xb5ZY\xf8\x8d\xd2\xce\xcd&\x06\x9cm\x05\xb0X\xfb=\xa5r)\x0f\xfb\x22\xc5\x8b\xc2Fh.\x94\xe4\x8f\x04!\xb2M\xc5\xde\x93\x88\xe5\xb9$>(\xe7\xd4`0\xc0f\xb3\xc1h4N\xcb\x8a>\xa7\x08\xa0\xbc\xae\x8d\xdb\xedFUU\x15\xe2\xf18K@ \xd7$\x11\x0br\xf5\xc6b\xb1\x82x\x07\xb9\xaa\xa0\xcc>,\xa64\xae$\x05p\xa6\x1aF\xc5T\x18\xf9\xd8\x90\x22H\xb7(\x9dN\xc7\xe4v\x8b\xc5\x02\x95J\x85H$\x02\xbd^\xcfn\xc5\x92$\xb1\x22\xbe\xf2\xf2\x1d\x9d\x9d\x9dp:\x9d\xacw0\x91@\xee\x0a\xe6d\x90c\xe5\x81\x5c\x97\xd7]w\x1d\x06\x06\x06\x0a\x5cz\xe9t\x1a\x97]v\x19\xbe\xff\xfd\xef\xe3\xbc\xf3\xce\x9b\xb1T\xc6\x99\x5c\x83rwr>\x9fG^*}\xc9>r\xe4\x08\xc6\xc6\xc6\xb0a\xc3\x06\xd4\xd6\xd6.\xd89G\x99\xb1F\xa3\x11.\x97\x0b\xf5\xf5\xf5\x05\x04j&\x22\xb8\x14I\x8c2\x16Oi\x93\xc8\x0bEv%\x95J\xb1\xd2f\xa4\xd6\x91:H\xc9\x1f\x14\x06&O\x08*V6\xee\x5c%\x7f\x04\xb5Z\x0d\x97\xcb\x05\x8b\xc5rn\x13@\x22\x81\x82 \xa0\xa1\xa1\x81\x05\xd7\xca\xbf4e\xf1\xa4R)\xa6\x14\x92\x8f_\x1e\xf3\x07\x00z\xbd\x1e\xf5\xf5\xf5X\xb5j\x15+[B\x81\xe6\xf2E\xb7\x9cI\x87|<\xe8\xdf\xd9l\x16\xa1P\x88e[\xd2f\x19\x1d\x1dE4\x1aE8\x1cf\x8f\xd1\xa6Q\xb6\xdf\xa1C\x862\xb8r\xb9\x1cL&\x13{\x1e5)'\xd7\xb1^\xafg\x85J\x83\xc1 \x0e\x1f>\x8c\xea\xeajTWW\xc3b\xb1\xb0\x98A^\x16fe+A\x8be\xd89\x96\x16\xc8\xdd\x9bJ\xa5\xf0\x9d\xef|\x07\x0f>\xf8 \x04A`e\xbb\x00\xe0\xaa\xab\xae\xc2?\xfc\xc3?`\xcb\x96-,\xd6\xeel\x94[Q\xaeKe\xd8B)\x050\x99Lb||\x9c\x950\xab\xad\xad]P2 \x08\x02\xccf3jkkq\xe9\xa5\x97\xe2\xa7?\xfd)\xc6\xc7\xc7\x0bB\xa1\xe4b\xc7R =r\xd7\xab<\xb35\x93\xc9 \x99Lbxx\x18\x03\x03\x03\x18\x19\x19A$\x12a\x17\x82L&\xc3\x1a\x09P9\x18\x22\x85\xc0\xc9Lp*\x1cO|\x80\x94?9\xf9\xd3h4\x8c\x17\xd4\xd5\xd5\xa1\xbc\xbc\x9c%\xa5\x15k\x16q\xb6/\xc5\xca\x905\xb9\xdb\xdfn\xb7\xa3\xaa\xaa\x0av\xbb\x1dV\xab\x95y\xde\xcei\x17\xb0\xb2\xb1\xb1\xcdf\xc3\xc0\xc0\x00#\x17\x82 0\xb7/\xc9\xbdJ\xe5\xcf\xe5ra\xf3\xe6\xcdX\xb7n\x1d\x9cN'\x1b\x1cr7\xcao\x99+A\x01T\xc6\xaa\xd0x\x11y\xa6\x04\x9ah4\x0a\xb7\xdb\x8d`0\xc8Z\xcf\xf4\xf5\xf5\x15,D\xb9\x9cN\x8a \x118yk\x1aJ\xceQ\xab\xd5,k\x8e2\x81\xfb\xfa\xfa\xd0\xd5\xd5\x85\x9a\x9a\x1a8\x9dN\x94\x95\x95\xb1ZP\xdc\xf0\xaf\x1c\x92W\x8a\xf0qEpy\x93\xbf\xa3G\x8f\xe2\xe3\x1f\xff8\x0e\x1c8p\xd2\x98\x88\x22\xd2\xe94\xd6\xacY\x83'\x9f|\x127\xdcpCA\xf8\x892\xb9c1\x8cm1\xa3\xaf\xfcY^/\x0e\xc0\x8c\x0a\xa0\xbc$\x8d \x08\xec\x02\xbc\x90g\xbbZ\xadf\x09xZ\xad\x16eee\xa8\xac\xacd\xd51\x8ay\xc0\x96\xc2\xbeR\xc6\xda\x11YK\xa7\xd3X\xb7n\x1d&''\xd1\xdd\xdd\x8d\xfd\xfb\xf7\xa3\xbf\xbf\x9f}\x07\xaa\xe8\x91\xcdf\x99`@\xff&\xce@\xb1\x7fD\x80\xe5j\xa2\xcdf\xc3\xd5W_\x8d\x8e\x8e\x0eTVV\xc2j\xb5\xb2J#d\xb7\x96\xd2\x19\xa4\x8c\xcf\x97\xab~T-\xc5j\xb5\x16\x10\xc0sZ\x01\x94\x7fqb\xf8\xb4\x89\x08\xf2\xac\x1e\xba9\xc8\x0d\x8b\xddn\xc7\x07>\xf0\x01\xac[\xb7\x0e---p\xb9\x5c\x05\x04C\x9e\xf6\x7f\xb6\xea\xe7\x9c\xe9\x03\x97\xc6\x94\xfe-'p\x14\xcfG1\x13\xd1h\x14\xd1h\x14\xc1`\x10>\x9f\x0f\x13\x13\x138|\xf80:;;Y\x11m\xda\xb4\x82 \xb0\xc2\x9a\xc9d\xb2\xe0\xa6I%d\xe8\x00\x94\xbb\x81s\xb9\x1c\x0e\x1f>\xcc\xfa\x05;\x9dN\xe6\x0a\xe6*\xe0\xf2\xc5\xd9l\xe4\xceq\xf6A1\xc1/\xbf\xfc2\xfe\xec\xcf\xfe\x8c\x91\x22:\xe7\xef\xbf\xff~<\xf1\xc4\x13\x05\x86\xf8L\x9f\x07\xb3%&)\xeb\xc9\x96Z\xcbD\x16\x17\x83\x00\xd2\xb8Q\xd7\x0c\xadV\x0b\xab\xd5\xcaD\x11%i\x90\x17d_*kAN\x02\xc9\xae\x93\x08A\x84&\x9f\xcfctt\x94\xa9w\xe4\xe6\xa4\xf8P\xbd^\xcf:w\x10\x1f\x90\x8b\x14\xf4\xddM&\x13n\xbf\xfdv\xb4\xb7\xb7\xa3\xb9\xb9\x99\xb9M\xa9\xde\xadR%]*c\xa4\xbc\x84\xd0\xe5\x83l%%\x80P\x99\x9c39\xbf\x8b\x1aqK\x0bV\xde\xdaE\xb9`\xc8%,\x1f\xa4\xcd\x9b7c\xcd\x9a5X\xbf~=\x93x\x0d\x06\x03\x0b\x10V\xca\xe0\xcb\xd5\x18\xcd\x14`+\x1f/\xda(tsR\x12A\x9f\xcf\x07\x8f\xc7\x83\xa6\xa6&\x9cw\xdeyx\xe7\x9dw\xd0\xd7\xd7\xc7Z\xea\x91\xbb\x86\x12v\xe8\xbd\x89\x94S\x89\x04y\x0b>\xfa{jj\x0a===hllDuu5/\x0e\xcd\xc1\xb1\x8c\x89\x1f\x19\xd7\x1f\xfc\xe0\x07\xf8\xdc\xe7>W@\xec\x9a\x9b\x9b\xf1\xb3\x9f\xfd\x0c\x9b6mb\xc1\xfcg\x03\x92$\xa1\xaf\xaf\x0f555\x05IjJ\xdbS\xaa=Y\xb1\xf3V\x10\x04\xd6\x86m!]\x8bt\xd1&\x85\x94b\xb3g\x22\x0f\xf2\xdf\x7f\xb6\xec\x9e\xd2\xcd*\xff\x9b*|P\xd7)I\x92\x10\x0a\x85\xe0\xf5z\x0b\x08\x9a^\xaf\x07\x00\x18\x8d\xc6\x82\xf6v\xca,_\xb9\xbd\xdb\xb2e\x0bZZZ\xb0f\xcd\x1a455\xb1\xdfA\xea\xdfR\xb49J\x05P\xe9\x0a&[)o\xe8p&!.\xf6\xa1Q,iA\x1e\xd8)\x7f\x8e$I\xa8\xae\xae\xc6\xda\xb5k\xd1\xd8\xd8\x88\xba\xba:TUU\xc1b\xb1\xb0\xf6g\xf2\xc5\xb6\x12\xdaH\xcd\xa5\x06\x94\xfc@\xa3MDD\xb0\xba\xba\x1a\xc1`\x10\x1e\x8f\x07UUU\xa8\xae\xae\xc6\xae]\xbb\xb0w\xef^\x84B!6\x1f\xf4|Z\x94\xb4A\xe5E*S\xa9T\x81\x0b>\x16\x8b\xa1\xbf\xbf\x1f\xa3\xa3\xa3hjj\x82\xcb\xe5\x82\xc9d\xe2\xb1\x80+\x84\x10p\xac\x1c\x90\xd1\xfa\xd4\xa7>\x85\x1f\xff\xf8\xc7\xac\x96[.\x97\xc3=\xf7\xdc\x83g\x9ey\x86]H\xcf\x06\xf9#\x92v\xe4\xc8\x11<\xf0\xc0\x03\xa8\xab\xabCmm-V\xb5\xb4\xe0\xaf?\xf5)\x5c\x7f\xddu\x8c\xfc\xc9E\x07\x22-\xb3\xa9\x89\xe4)Y\xac\xb1%5H\xab\xd5\x16\xcdt]\xaa(\x16_Iud\xa9\x8e\xdf\xf8\xf88\xea\xea\xea055\x05\xa3\xd1XP9\x82TARe\x95\x8d\x1f\x08\xabW\xafFSS\x13\x9a\x9b\x9b\xd1\xd8\xd8\x88\xca\xcaJ\xa6\xfe)\xc3\xc1\xce\x15^\xa0$\x83g\x03\xe2\x99\xfe\xc2\xa4\x08*\xb3Wi\x00\xd6\xae]\x0b\xbb\xdd\x8e\xca\xcaJ\xd8l6\x98\xcd\xe6\x82\xfa8+\xf5\xe0-\x059\xd9\xd2j\xb5\x8c\x10f\xb3YX,\x16X\xadV\xd8l6\x94\x97\x97\xc3f\xb3A\xab\xd5\xa2\xbc\xbc\x1c\xff\xf7\x7f\xffW@\x02\xd3\xe94+\xd0I2}>\x9f/\x88\xd1\xa4\xc3\x8a\xc8\xe0\xe0\xe0 \xc6\xc7\xc7\xe1v\xbbY\xa2\x8e\x9c\xacs,_,\x85:[\x1cgf\x9eU*\x15\xee\xba\xeb.<\xff\xfc\xf3\xec\x02\x98L&\xf1\xcc3\xcf\xe0\xd3\x9f\xfe\xf4\x9f\x0c\xcaY*\x05E\xc6\xf4\x8f\x7f\xfc#\x00`tt\x14\xa3\xa3\xa3\xd8\xbd{7~\xfb\xbb\xdf\xa1\xa9\xa9\x09\x9f\xfb\xdc\xe7\xf0\xf9\xcf\x7f\x9e\x95\xb5\x9a\xcbE\x86\xce\xd23q\x9e\x9d\x8bY\xad\xcaq\xa1\xd86\xe0d(\x97\xcb\xe5b\x7f\x02\x81\x00\xb3\xe7\x0e\x87\x83%0R\xbc#\x91\xf1b\xed\xd2jjj\xe0r\xb9PUU\x05\x87\xc3\x01\x8b\xc5\x02\x83\xc1pN\xf3\x82\xa50\xcf\xe2\x99>H\xe80Q\xa6\xe2\x13(\xde\xcfl6\xc3d2\x9d\xb5&\xdd\xe7\xf2\xa2\x22\x05\x8e\xba\x80h4\x1a\xe8\xf5z\x18\x8dF\xe8\xf5z\xd6\xbe+\x9f\xcf\xe3\xa5\x97^b}\x99\x95-\x87\xe4\xe4\x9c$jy\xcb\x1aA\x10\x10\x0e\x87\xe1v\xbb\xe1\xf1x\x10\x0a\x85PQQ\xc1:\x85ppp\x9c\xdb\xa0K\xfa\xddw\xdf\xcd\xc8\x1f\xa9,o\xbf\xfd66o\xde\xccz\xbd.\x05\xec\xd8\xb1\xa3\xa0\xf6,p\xb2\xf2DOO\x0f\xee\xbd\xf7^\xdc\x7f\xff\xfdx\xf8\xe1\x87\xb1v\xedZ\xe8t\xbaY\x0b\x06\xcf\xd4\xb7\xb5\x94\x9a\xc3m\xd0I{a2\x99`\xb1X`\xb3\xd9\xd8\xdfv\xbb\x9d\xf5]\xa6\xb2dF\xa3\x91\xcd\x13\xcd\x87\xbc\xc4\x9bV\xab\x85\xc3\xe1`\xbc@\xa7\xd3\xb1\xe2\xe2\x1cK\x90\x00\xca'Q\xf98\xc5\xac\xc9\xff\xc8AA\xa2D\xfcVB\x92\xc7b\x93A\xea\xd8!\xef\x0dL\x99\xd8\xf1x\x1c\xaf\xbe\xfa*#\x7f\xca\xc0^j\xe5C\x89 D\x02\xe57\xfe\xa3G\x8f\xe2\xfc\xf3\xcfG0\x18D\x22\x91@YY\x19\x0b\x9c\xe6X\xfe\x04a>\x8fs\x9c{g\xc8_\xfe\xe5_b\xeb\xd6\xad,h\xbf\xac\xac\x0c\xef\xbe\xfb.\xd6\xad[\xc7\x12\xfdf[\x1f\x8b}\x86Sm\xd9\xef~\xf7\xbbx\xf1\xc5\x17\xd1\xdf\xdf\x8f\xde\xde^\x1c9r\x84\x91A\xbd^\x8fl6\x8b\xaf~\xf5\xab0\x9b\xcd\xb8\xe9\xa6\x9bp\xfb\xed\xb7\xb3\xb8\xbbb%D\x94\xa5\xc9\x94\xe7\x1a\xfdL\xf1\xd4\xfc\xe2\xfb\xa7\xf9 \x976u9\xb1\xdb\xed\xa8\xad\xad\x85\xc1``%\xc6\xa8\x12\x88<\xa9\xb1Xm[\x12/\xc8\x8eq^\xb0\x04\x09\xa0r\x03\x15K`\xa0\x85A\x05\xa1\x95\xc5\x9c\xc9}H$\x83O\xf2\xc2\x80\xb2\x8d\xe8\xdf\xf9|\x1e\xf1x\x1c\xe1p\x18\x93\x93\x938t\xe8\x10\x80\xc2\x12\x09\xf2lk\x22{\xf1x\xbc\xe0\xff\x04A\xc0\xe4\xe4$B\xa1\x10\x82\xc1 \xe2\xf18#\xf9\x9c\x00.op\x17\xf0\xf2\xc7'>\xf1\x09l\xdd\xba\x95\x15\xe9\xd5\xe9t8t\xe8\x10\x1a\x1b\x1bg\x8d\xb7\x92\xb7\x06;S\xeb\xb1\xaa\xaa\x0a\x97]v\x19\xae\xb9\xe6\x1aD\x22\x11\x04\x83A\x04\x02\x01\xfc\xe2\x17\xbf\xc0\x81\x03\x07\xd8%8\x1a\x8d\xe2W\xbf\xfa\x15^{\xed5<\xf0\xc0\x03\xf8\xd2\x97\xbe\xc4\xea\x9d\xceDdU*\x15|>\x1f\x92\xc9$\x02\x81\x00\x9a\x9b\x9ba\xb1X\xe0\xf1x\xf0\xf5\xaf\x7f\x1dO=\xf5\xd4\x92RC\x97\x0a\x11$\x8f\x13\x95\xbb\xa1\xf8Q\xaf\xd7\x8bL&\x03\xb7\xdb\xcdb3\x8b\xad\x17y\x1b9\xce\x07\x16x~\x16\xe3\xc6(\x872\xd1CN\x0e\x8b\xc9\xebt\xa8\xcc\xd4\xfd\x83\xe3\xf4H\xa0V\xab\x85^\xafGEE\x05\x1a\x1b\x1b\xb1j\xd5*\xb4\xb7\xb7\xa3\xbc\xbc\xbc\xb06\xd6\xff\x9f\x17\xbau\xa9\xd5j\xe4r\xb9\xa2\x1d?(\xf3.\x12\x890\x02\xc8\x15\xa0\xe5\x0f>\xc7\xcb{^\xbf\xfb\xdd\xef\xe2\xa7?\xfd);\xc7\x8dF#zzzPSS3\xa7\xcb\xb9 \x08x\xf5\xd5W\xb1k\xd7\xae3\xb6^\xa8\xb2D\x22\x91\x80Z\xad\x86\xd3\xe9\xc4\x1dw\xdc\x81\xfd\xfb\xf7\xe3\xbd\xf7\xde\xc3\xc6\x8d\x1bY\xfc\x1fp\xb2\xa8\xfd\x97\xbf\xfcel\xd9\xb2\xa5\xa4\x1d#\xbb\xb4o\xdf>\x1c?~\x1c}}}\x18\x1e\x1e\xc6\x81\x03\x07\xd0\xd6\xd6\x86\xef}\xef{x\xf2\xc9'9\xf9S\xcc?\x09=\x82 \xa0\xae\xae\x0e\xabV\xadB[[\x1b\xf3\xf0%\x12\x09X,\x16&P\xc8\xcb\xc5\x15\xe3\x16\xe4\xd5\xe2\xbc`\x89\x12\xc0\xb9lt\xb9\xebW9\x91\x9c\xf8-.\xa8\xf8\xa4\xd9lf$\xb0\xb9\xb9\x19\xf5\xf5\xf5Lu\xa59\xd0\xeb\xf5H&\x93\xac\x04\x0c\x91\xbfb\xca\xad\xc7\xe3A8\x1cF4\x1ae\xdd]8\x96\x0ff\xda\x8fs\xc9R\xe78w@1\xc0\xaf\xbf\xfe:\xee\xbb\xef>f\xbc\x8dF#v\xef\xde\x8d\xda\xda\xda\x92nN\xf9Zx\xea\xa9\xa7\xb0e\xcb\x16\x5cv\xd9ep\xbb\xddg\xa5t\x09)J\xd9l\x16\x17]t\x11\xf6\xed\xdb\x87\xe7\x9e{\x0e\xeb\xd7\xafg\xdfW\x14E\xbc\xfa\xea\xabhoo\xc7\xdbo\xbf\x0d\x00\x05$Q\x0e*\xb1%I\x12|~\x1fn\xbc\xf1F\x04\x83A\xe4\xf3y|\xe5+_\xc1\xce\x9d;\xf9\x22\x92\xd9y9\x014\x99L\xa8\xa9\xa9\x81\xd1hD}}=DQdmH\x95\xf6\xbf\xd8:\x91W\x0b\xe1g\xce\x12$\x80r\xf5h.\x13T\xcc\xa8PM;>\xc1\x8b\x07J\x10\xb1Z\xadp:\x9d\xa8\xad\xadEcccAp-\xb9J,\x16\x0b\x8cF#s\xf5\xd0\x86Vf\x7fMLL \x1e\x8f#\x91H\xb02\x0b|\x0e\x97\x8f\x224\x9f\xb9\xe4\xf3~n\x9f\x0d]]]\xb8\xf9\xe6\x9b\x99\xfbN\x92$\xfc\xcf\xff\xfc\x0f\xd6\xacY3\xeb\xdcR\xec\xf0\x87?\xfca\xfc\xed\xdf\xfe-T*\x15t:\x1d.\xbd\xf4R$\x12\x89E\xbf\xd8\x17\xf3(E\xa3\xd1\x82f\x04W\x5cq\x05\xbe\xf1\x8do\xe0\x8b_\xfc\x22\x8b\x0b\x14E\x11\xc3\xc3\xc3\xd8\xb2e\x0b\xfe\xe5_\xfe\x05\x1a\x8dfZ\xfc\x9f\xfc;\xaa\xd5j\xd8]\xe1/\x0f\x00\x00 \x00IDAT\xac6<\xf5\xd4S\xec2\x9cN\xa7q\xd3M7!\x1c\x0e\x17\xd4\xbe]\xa9{\x83\x92<\x88\xd4\x95\x97\x97\xa3\xa6\xa6\x06\xf9|\x1e\xc1`\xb0\xa0\x86\x9f\x92\x03\xcc\xa4\xc0r,a\x02(\xdf \xf2\x94\xf6\x996\xd1L\x87\x01/$\xbc\xf8j\x8e(\x8a\xd0\xeb\xf5(//\x87\xcb\xe5Buu5\x1c\x0e\x07{\xdcl6\x17\xa4\xe6\xd3\xebf:\xc0\xd2\xe94\xc2\xe10\xe2\xf18S\x009\x11X>\xeb\x85\xf6%\xc7\xf2\xc7G>\xf2\x11\x96\x91\x99H$\xf0\xd4SO\xe1\xc6\x1bo,j\x98\x8b\x91\xad\xcd\x9b7\xe3\xf7\xbf\xff=#\x94\xa9T\x0a\x7f\xfd\xd7\x7f\x0d\x83\xc1\xb0\xe8g\x82\xf2\xfd%I\xc2\xe0\xe0 \xdez\xeb-\xec\xdd\xbb\x97\xd53\x15\x04\x01W]u\x15~\xf2\x93\x9f\xa0\xbd\xbd\x9dy9R\xa9\x14\xfe\xfe\xef\xff\x1e\x0f=\xf4\x10\x8b{\x9ciO\xa4\xd3i\xdcu\xd7]\xb8\xef\xbe\xfb\x98\xda\x15\x08\x04p\xe5\x95W2b\xa9\xfc,+)n\x96\xba\xc6\xd0w\x0e\x85B\x08\x85B\xa8\xaa\xaaBCC\x03\xcb\xfe-v\xc9,\x96@\xca\xed\xc99B\x00\xe5\x13z*\x0b\xbeTaN\x8e\x85\xbb\x9dQ\x86\xb0\xddnGEE\x05\xacV+\xabF/\x8a\x22l6\x1bk\xe7C\xedvH\xceW\xcek<\x1e\x87\xcf\xe7\x9b\xd6\xf6o\xb6\xf5!/\x05\xa4|Ly\xa0\xf3\x03\xe0\xd4\xf7a\xb11\x9ei\xbc\x8b\xa9(3\xdd\xcc9\x96\xcf:\x01\x80\xc7\x1e{\x0c\x9d\x9d\x9d\xcc\x13\xf3\xc9O~\x12\xf7\xddw\x1f\x0b\xd0/\xf5Z\xaf\xd7\x8b\xcb.\xbb\x0c;w\xeed\xcf\xb5Z\xadx\xeb\xad\xb7\xf0\x8f\xff\xf8\x8f\x05\xdd\x84\xce\x04\xf9\x93?\x9e\xc9dX\xa9*\xf9z\x06\x80\x17^x\x01\x0f?\xfcpA\xd7\x8fo|\xe3\x1b\xf8\xf2\x97\xbf\xccb\x9fg\xda\x0f\x99L\x06O>\xf9$\xb6l\xd9\x82\x5c.\x07\x8dF\x83C\x87\x0e\xe1[\xdf\xfa\x16\x0b\x99\x01N\xba\x8e\xfb\xfa\xfap\xe8\xd0!\xd6\xf9b%\xd8\x18\xea\xf3\xabR\xa9PSS\x83\xb6\xb66\x96\x8c8\x1fn\xc0\x15\xc0s\x8c\x00\x16c\xf1\x9c\xc9/-UG\x1e\x0fh\xb3\xd9PQQ\x81|>\xcfZ(\xe9\xf5z\x16#Cs\xa9\x8c\x13\x94oPR\xff(\xad\x9f68\x15\xa5\xa6^\x91\xd4\x09F\xfe\x98\xfcg\x0a\xe4\xa6\xee$\xd45F\xdeA\x86\xa3\xf8!)o\xcc>\xdb\x18\xd3\xf8R_n\xf98+\xc30\x94\x8a\xfdL$\x91\xe3\xdcD&\x93A__\x1f\x1e~\xf8a\xf6X{{;\xfe\xf3?\xff\x93\xd5b+u\x96D\xa3Q\x5c}\xf5\xd5\xe8\xec\xecd\xc5\xe8\xdb\xda\xdap\xf4\xe8Q\x5cq\xc5\x15\xec\x1c9\x955M\xeb9\x9dN\x97\xdc\xfbs\x89S\x0d\x06\x83\xcc;A\xeei\xbf\xdf\x8f\xaf\x7f\xfd\xeb\xf8\xcdo~\x03\x00\xac+\xc7\x13O<\x81G\x1ey\x04v\xbb\xbd\xe8\xa5\x96\x08_:\x9d\xc6\xb6m\xdbPSS\x83L&\x03A\x10\xf0\xe8\xa3\x8f\xa2\xab\xab\x8b}\xa6@ \x80\xae\xae.\x0c\x0f\x0f#\x14\x0a\xad(;#o\x137\xd7\x02\xdc\xa5\xe6\x90c\xe1pFR\x96x\xb9\x88\xa5M\x02\x0d\x06\x03,\x16\x0b\xccf3\xacV+\xacV+\xeb\xb3\x98N\xa7\xe1\xf3\xf9\x10\x8b\xc5\xd8\x0d\xac\xd8A\x9b\xcb\xe5\x90L&\x19\xf9\x937\xf3&2A\x07@\xa9,\xf0b\x87\x87<\xfbK\xde\xa1\x84g\xdc\x15\xee1\x1ag\x22x\xf2\xdaZ\xb3\x1d\xac\xf22L\x1a\x8d\x06:\x9d\x8e\x198\xf9\xf3h.\xe4e~\xf2\xf9\xfcY\xeb\xfd\xca\xb10\xe4O\xab\xd5\xe2\xe6\x9bofk@\x92$|\xf7\xbb\xdf-I\xfc\x08\xa9T\x0a\x1f\xf8\xc0\x07p\xec\xd81\xd62\xed\xa2\x8b.\xc2\xae]\xbb\xd8\x1e\x9do'\x0d\xba@\x16\x8b7>U\x15\x10\x00\xa2\xd1\xe84%\x89>\xe3\xad\xb7\xde\x8a\x83\x07\x0f\xe2\xfc\xf3\xcfg$\xf6\xbf\xff\xfb\xbf\x99\x0b[\xa9\xdc\xd1\xfe\xa21\xfa\xe1\x0f\x7f\x88\x9bn\xba\x09\x92$!\x1e\x8f\xe3\xcb_\xfe2~\xfd\xeb_\xb3\xf2Y\xa4bq{\xc89\xc1\x8a\x22\x80\x1cK\x9b\x00\xeat:\x98L&\x98\xcdf\x88\xa2\x08\xbb\xdd^P\x0f\x8bj8\xe5r9v\x80)\x89C.\x97C,\x16c7u\xf9\xe3\x92$\xb1\xe4\x10\xca\xa2\xa3\xdb|\xa9\x9b\x9d\xbc96e\x22\xd2\xe7\xa4 m\xder\xeeO*\x09\x91py2\x0e\x11\xee\x99\x0e\x5c*\x08\x9e\xc9d\x0a\xaa\xf7\x93\x0a,\x8f\xff$\xe2\xa7\xd7\xeb\x0b\x94`y\xed\xb3\xd9\xdc4\xdc\x03\xb0\xf4\xd6\x8eF\xa3\xc1\x13O<\x81\x13'N@\xab\xd5\x22\x9dN\xe3\x9e{\xee\xc1\xf5\xd7__r\x8f\xd1\xff\xdds\xcf=\xd8\xb1c\x073\xec\x97\x5cr\x09\xf6\xec\xd9\xc3\x8a\xfd\x9e\x0ab\xb1\x18\x02\x81\x00\x8e\x1c9\x82\x1d;v`\xdf\xbe}\xe8\xea\xea\xc2\xc7?\xfeq<\xfa\xe8\xa3\xa7\xdcy#\x93\xc9L\xfb>\xf2\xb3j\xfd\xfa\xf5\xe8\xea\xeaB{{;[\xaf[\xb7nE}}=\xae\xbc\xf2\xca\x0276\x9d\x83\xf4\xf3\x87>\xf4!\xdcz\xeb\xad\xf8\xedo\x7f\x0b\xb5Z\x8dm\xdb\xb6\xe1\xd5W_\xc5\x87>\xf4!\xd6\xee\x8c\x8aK\xaf4;\xc3\xc3GV \x01\xe4\xedq\x96>\x04A`\xad\xe2L&\x13$IbE^EQD(\x14B.\x97c\x07\x98\xbc\xf2\xbfrn\xe5\xaa\x13\x1d\x90*\x95\x0a\xc9d\x12\xd1h\x14\xe1p\x18^\xaf\x17>\x9f\x0f\xa1P\x08\xc9drNY\x85\x00\xa0\xd3\xe9X\xb2\x0a\xf5\x89\xd6\xe9t+~\x8d\xc9\xdd\xbe\xd1h\x14\x81@\x00~\xbf\x1f\x13\x13\x13\xac3K)\xd0\xeb\xf2\xf9<\xccf3\xea\xea\xeaP[[\xcb\x02\xb8\xa9\xd9\xba\x9c\xf4'\x93I\xb6\x0e(K4\x95J1e\x96\xe3\xdc2\xce\xb1X\x0c\x8f?\xfe8#\x7f\x8d\x8d\x8d\xf8\xe1\x0f\x7fX\xb202\x91\xfe\xa7\x9f~\x1a?\xff\xf9\xcfY\xb0\x7fKK\x0b^{\xed5\xe4\xf3y\x18\x0c\x869}\x06\xf9\x05b\xe7\xce\x9d\xd8\xb6m\x1b^{\xed5\xec\xdd\xbbw\xdas\x87\x87\x87g,!6\x177a)\x22B%c\xda\xda\xda\xf0\xea\xab\xaf\xe2\x86\x1bn`\x17\x96'\x9f|\x12k\xd7\xae\x85\xd5je\x04R\xe9\xc1\x08\x06\x83\xb8\xfb\xee\xbb\xf1\xca+\xaf0\x97\xf1_\xfd\xd5_\xc1\xe7\xf3!\x1a\x8d\xb2D\x11^\x22\x8bcE\x10@~\xdb_\xfa\x87\xbf\xbc\xca:\xb9=jjj`\xb7\xdb\x11\x0c\x06\x11\x8b\xc5\x98\xb2D\x07d\xb1\x04\x0e:\x10)\xbe\x86\x0eiR\x04\x03\x81\x00\x04A\x80V\xab\x85\xc1`\x80\xc3\xe1`\x8d\xc1\x8b)\x0c\xa4j\xa5\xd3itww#\x14\x0a\xe1\xbd\xf7\xdeCkk+#\x1f\xd4\xd2n\xa5\x13\xc0l6\x8bD\x22\x01\x9f\xcf\x87\xb1\xb11\x0c\x0f\x0f\xa3\xbe\xbe\x9e5M'\xb7y1\x8c\x8f\x8f\xa3\xb7\xb7\x17{\xf6\xecA<\x1e\x87V\xab\xc5\x95W^\x09\xa3\xd1\x08\xb3\xd9\x5c\xe0\x02T\xb6f\xcc\xe7\xf3\x88D\x22\x10E\x11f\xb3\x99\xb9\x9e\xe7c\x909\xce>\xbe\xf5\xado\xc1\xe7\xf3\xb1y}\xf6\xd9ggu\xeb\x8b\xa2\x88]\xbbv\xe1\x0b_\xf8\x02\xdb\x8b\x00\xb0g\xcf\x1e\x94\x95\x95\xcd\xf9w\xa7\xd3ih\xb5Z\xec\xda\xb5\x0b\xf7\xde{/\xba\xbb\xbb\x11\x89D\x0a.\xa7\x94l\x01\x00###L\xad>\x15\xcc\xd4\x83^N\x02\x01\xe0\xfa\xeb\xaf\xc73\xcf<\x83\xcf|\xe63,\xce\xef\xa1\x87\x1e\xc2\x8f\x7f\xfcc\xc4\xe3q\x88\xa2X\xe0\xc1\xc8\xe5r\xd8\xb9s'4\x1a\x0d>\xfb\xd9\xcf\xe2;\xdf\xf9\x0e\xb4Z-\xfc~?\xfe\xf5_\xff\x15\xb7\xdcr\x0b\xc6\xc7\xc7\x91\xcf\xe7\xe1\xf1xPYY\x09\xa3\xd1\xb8b\xce..\x04-s\x02\xa8\xec!;\xdbm\x8bci\x90\x07r\xb3R\xdd?\x8dF\x83\xf7\xbd\xef}\xe8\xea\xeab\xaa[(\x14\x82\xc3\xe1\xc0\xc8\xc8H\xc1\x5c\x17K\x04\x90\xf7\xfe\xa4\xf5\x90N\xa7\x91L&\xd1\xdd\xdd\x8d\x8f}\xecc\xf3\xfe\x9c\x17^x!\xa2\xd1(\xee\xb9\xe7\x1e\xa6J\x10y\x5c\xc9\xbd7i\xbc\xd3\xe94\xa2\xd1(\xbc^/\xba\xbb\xbbq\xf3\xcd7\xa3\xae\xaenN\xef\xb1f\xcd\x1a\xc4b1x\xbd^tvv\xc2\xe7\xf3\xe1\xf2\xcb/G8\x1cFee%\xfb=\xf2\xf9T\x1e\xec\x82 \x14\x0a\xf1,\xbds\x0c\x92$abb\x02?\xfe\xf1\x8f\xd9<_{\xed\xb5\xb8\xf4\xd2Kg5\xda\x99L\x06w\xddu\x17DQ\x84$IH&\x93\xd8\xbbw/l6\xdb\xbcT\xf9h4\x8a\xbf\xf8\x8b\xbf\xc0+\xaf\xbc2MA\xa6z\xa5uuu\xd8\xbcy3\x1a\x1b\x1bq\xe1\x85\x17\xb2\x8b\xdfL{\xa2\xd4\xef\x9ek\x11\xe1\x5c.\x87O\x7f\xfa\xd3x\xf1\xc5\x17\x99Kwrr\x12\xff\xfe\xef\xff\x8e\xaf|\xe5+\x88D\x22\x05\x1e\x91\x91\x91\x11\xd6&\xf3\xba\xeb\xae\xc3\xd6\xad[111\x01Q\x14\xf1\xf8\xe3\x8f\xe3\xa2\x8b.b\x97\xb0\x89\x89\x09\xf8|>\xb4\xb5\xb5a\xd5\xaaU\xcb\xde\x8b\xc1\xfb\x85\xaf\x10\x05P\xbe\x88\x95\xea\x1f\x9f\xec\xa5y+#\xb7\x04\x05\x5c\x9b\xcdf\xb4\xb5\xb5\xa1\xae\xae\x0e\xdd\xdd\xdd\x88\xc7\xe3\xd0\xeb\xf5\xd3\x0e\xdc\x992\xbc\x8b)r\x94!\xec\xf3\xf9\xf0\xf6\xdbo\xe3\x8a+\xae\x98\xf3\xe5\x80n\xd7\xdb\xb6m\x83\xdf\xefG4\x1a\x85\xc3\xe1@EE\x05\xca\xca\xcaX\xab\xba\x95z\xd1\xc8f\xb3H\xa5R\x08\x04\x02\x18\x1c\x1c\x84\xddnG]]\x1d+\xbfP\x8a\xf8'\x93I\xfc\xe4'?\xc1\xb3\xcf>\x8b\xde\xde^\x04\x02\x01\x5cs\xcd5\xacN\x9a\xbc\xdb\x8b8\x96>\xf9S\xa9Tx\xe1\x85\x17066\xc6\xce\x80\xcf\x7f\xfe\xf3sr\xdd>\xfe\xf8\xe3\x18\x1a\x1ab??\xf6\xd8c\xb8\xf8\xe2\x8bY\x9c\xf0l{Z\xa5R\xe1\xd9g\x9f\xc5\xa7?\xfdi\xe4r9\x18\x0c\x06\x16\xae\xd0\xda\xda\x8a\x1bn\xb8\x017\xddt\x13\xae\xbf\xfe\xfa9'\x18\xcd\xc5\xc6\x90\x87b\xb6\xe7\xaa\xd5j\xc4b1|\xeb[\xdf\xc2\xde\xbd{166\x06A\x10\xf0\xc6\x1bo\xe0\xc6\x1boD{{{\xc1\xfb\xd0\x05\x88\x92`>\xf3\x99\xcf\xe0k_\xfb\x1a\xb2\xd9,\xfc~?\x9e}\xf6Y|\xecc\x1fC*\x95\x82J\xa5bY\xd7\xabV\xad\xe2\x8b\x91\xe3\xac\xe1\x8c\x97\x81\xe1\xd9?K\xebfFA\xc9j\xb5\x1a\xe9t\x9ae\xc0\xe9t:\xe6\xa6\xb0Z\xad\x88\xc7\xe3\xecfL%E\x94\x07)\x95O(X`\xff_]\xa4\xf7\xdf\xb6m\x1b\xb2\xd9\xec\xb4l8\xb9{Q\x99iz\xf0\xe0A\xec\xd8\xb1\x03^\xaf\x17\xbd\xbd\xbd\x18\x1c\x1c\xc4\xf8\xf88\x02\x81\x00\x8bG[\x89\xa0\xb9\x88D\x22\xf0x<\xf0x<\xb8\xe5\x96[\x0aJ\xf7(\xdd\xb64']]]x\xe0\x81\x07\xf0o\xff\xf6o8~\xfc8\xc2\xe10\x0c\x06\x03\x9a\x9b\x9ba\xb5Za4\x1a\xa7\xc5z*/x\x84p8<\xef\x0b^\xb1\xf7\xe48s\xc8d2P\xa9Tx\xe4\x91G\xd8\xfa\xb8\xf0\xc2\x0bq\xfb\xed\xb7\x97\x9c\xcbl6\x8b\xf1\xf1q<\xfa\xe8\xa3\xec\xb1\xf7\xbd\xef}x\xf0\xc1\x07Y\xdch)P7\x90?\xff\xf3?\xc7\xddw\xdf\x8d\x5c.\x07\xadV\x8bD\x22\x81\xc6\xc6F<\xf7\xdcs\xd8\xb1c\x07\xbe\xff\xfd\xef\xe3\xa6\x9bnb1\xa6\xf2\x18\xd4\xd3!\x81\xa9T\x8a%\xab\xcd\x16\xa2\xa4\xd3\xe9\x90\xc9d\xf0\x85/|\x81\x85\xc7\xa4\xd3i\xfc\xf2\x97\xbfD2\x99d\x09V\xf2\xefE\xfb\xeb\xe2\x8b/\xc6\xa6M\x9b\xd8{\xed\xd8\xb1\x03~\xbf\xbf\xa0\xd6 \x9d\xa9\x00\x10\x89D\xd0\xdf\xdf\x0f\xaf\xd7\xcb\xc5\x12\x8e\xe5A\x00y\x0c\xd0\xd2W\x00Ia\xa3@\xfe\x8d\x1b7\x02\x00\x0c\x06\x03k\x17\xe7\xf5z\x91\xcdf\xa1\xd7\xeb\x0b\x5c\xbcJ\x90\x0b\x99\x0a\x7f\xca{\x0aS\xad\xc1\x81\x81\x01\xfc\xd3?\xfd\x13N\x9c81k\xdfgA\x10p\xe4\xc8\x11\xec\xde\xbd\x1b]]]\x88D\x22\xf0\xfb\xfd\xe8\xe9\xe9\xc1\xd8\xd8\x18|>\x1f\xe2\xf1x\xc1A\xbc\x92\xc8;eW\x87\xc3a\x0c\x0f\x0fc\xc3\x86\x0d\x05m\xfb\x8a\xcd\xb7\xdf\xef\xc7\xcf\x7f\xfes<\xf8\xe0\x83\xf8\xedo\x7f\x8b\xd1\xd1Qf\x10\x9b\x9a\x9a\xd0\xd4\xd4\x04\xbb\xdd\x0e\x93\xc9\xc4\xe6\xb2\xd8\xd8\xcac\xfd\x94*!\xc7\xd2_;:\x9d\x0e\xbf\xff\xfd\xef\x11\x0e\x87\xa1\xd3\xe9\x90\xcdf\xf1\xd5\xaf~uV\xf5L\x14E|\xf1\x8b_d\xff\xd6h4x\xec\xb1\xc7\xe6\xd4\xbf]\x92$\x18\x0c\x06\x9c\x7f\xfe\xf9\xd8\xbau+{~:\x9d\xc6\xc3\x0f?\x8c\x81\x81\x01|\xf4\xa3\x1fe\xa1\x07\xf2s`.=\xe2\xe7\x12s.I\x12FGG\xe7\x1c\x9f.I\x12:::p\xf5\xd5W\xb3\xdf\xf1\xde{\xef\xa1\xab\xabkZ\xc2\x9b\x1ceee\xf8\xc8G>\xc2>\xfb\xc0\xc0\x00\xc6\xc6\xc6\x8a\xc6\x1f\xe6\xf3y\x0c\x0c\x0c\xe0\xc4\x89\x13\xe8\xef\xefG:\x9d\xe6\x8b\x94\xe3\x8c\x80\x97\x81Y\xe1\x86\x80n\xd7\xa4&Q\x00\xb7$I\xa8\xa8\xa8@6\x9b\x85\xd9l\x86Z\xadF&\x93\x99V\xdbO~\x90\xe9\xf5z\x88\xa2\xc8\x12\x07\xa8\xcc\x8cV\xab\x85\xc5bAee%\x9a\x9b\x9b\xf1\xc6\x1bo`\xff\xfe\xfdhoo\xc7\xb5\xd7^\x8b\x86\x86\x06TUU\xc1h4\x22\x9dN#\x9dN#\x91H`hh\x08\xef\xbd\xf7\x1e\x8e\x1f?\x8e@ \x80x<\x8et:\x8d\xfe\xfe~\x8c\x8c\x8c\xa0\xa9\xa9\x09\x15\x15\x15\xac|\xcdJ\xaa\x0bH\xf3E\xf1{\xa9T\x0a\x1b6l(J\xce\xf3\xf9<\xbc^/\x8e\x1d;\x86\xe7\x9f\x7f\x1e{\xf6\xec\xc1\xd0\xd0\x10\x22\x91\x08S\x82DQ\xc4E\x17]\x84\xea\xeaj\xb8\x5c.F\x00\xe5FWi\xe4\x94\xe5,\xb8\x9awn\x80\xc2>\x1ey\xe4\x11\xb6\xaf\xadV+n\xb9\xe5\x16\x96\x94Q\x0cj\xb5\x1a\x13\x13\x13\xd8\xbau+\xf4z=\x92\xc9$n\xbb\xed\xb6Y\xcb\xc5\xc8\x09\xe4e\x97]\x86\xc3\x87\x0fC\x14Ed\xb3Y\xb4\xb4\xb4\xe0\x97\xbf\xfc%6m\xda\x84t:}Z1\xbdsq\xeb\x0e\x0e\x0e\xe2\xfe\xfb\xefGMM\x0d\xbe\xf9\xcdo\x96\xfc}*\x95\x8a\x85X\xdc}\xf7\xdd\xd8\xb1c\x07+L\xff\xcc3\xcf\xe0G?\xfa\xd1\x8c%]\xe2\xf18>\xfa\xd1\x8f\xe2\xf1\xc7\x1fG(\x14\x82$I\xf8\xfd\xef\x7f\x8f\x07\x1ex\x80e\xcc\xcb?w8\x1cF6\x9bE0\x18D2\x99\x5c\xb6u5\x8b\x85\x85\xf1K\xe32!\x80|\x22\xcf]\x02H\x99u\xf2\xfaW\xf9|\x1eF\xa3\x91\xd5\x0a\xa3\x8e\x11\xf2b\xce\xf4>\x1a\x8d\x06\x16\x8b\xa5\xa0e\x1c\xbd\x8fF\xa3AYY\x19\xaa\xab\xab\xd1\xdc\xdc\x8c\x9e\x9e\x1e\x1c\x8f\x91\x91\x11\x84B!LNN\x22\x99L\xb2\xcf499\x89\xbe\xbe>444\xc0\xe5r\xc1f\xb3\x15(\x8f+\xc1\x80Sfv(\x14\xc2\xd4\xd4\x14\xaa\xaa\xaa\xa6)'4?\x07\x0f\x1e\xc4K/\xbd\x84\x1d;v\xa0\xaf\xaf\x0f^\xaf\x17\x91H\xa4\xa0\xd9}[[\x1b:::P]]\x0d\x9b\xcd\x06\x83\xc1\xc0\xc63\x97\xcbM\xeb\xef]l\xbf\xf3\xf0\x8es\x03j\xb5\x1a\xfb\xf7\xef\xc7\xe1\xc3\x87\xa1\xd3\xe9\x90J\xa5\xf0\xf8\xe3\x8f\x9f4\x083\x5c\xa2(\x1c\xe3S\x9f\xfaTA\xf8\xc6\xcf~\xf63d2\x99\x92D\x8aH\xe5\xad\xb7\xde\x8a}\xfb\xf61bu\xfd\xf5\xd7\xe3\xb9\xe7\x9e\x83\xd3\xe9D6\x9b\x9dS\xd1\xe9\xd3\xd93^\xaf\x17\xf7\xde{/\xd2\xe94zzz\xf0\xd8c\x8f\x15t>Q\x92\xd5\xae\xae.LLL\x00\x00\xca\xcb\xcbq\xe7\x9dw\xe2\xd9g\x9f\x85V\xab\xc5\xe0\xe0 v\xef\xde\x8d\xcb/\xbf|\xc6\xd7\xd7\xd5\xd5\xe1\xd6[o\xc5\x7f\xfd\xd7\x7fA\x14E\xbc\xf3\xce;\x88D\x22\xd0j\xb5E\xdb/R\x9f\xe2\x85\xaa\x13\xc8\xcb\xb0q\xcc\x86\x05s\x01\x173\x06|\xf1-}\x15I\xde\xb5C\xa3\xd1\xa0\xad\xad\x8d\x110jh\x1e\x8f\xc7Y\xbc\x9d\xb2\x06\x96<\x19\xc0`0L\xeb\x19L\x06\xc7h4\xc2\xe1p\xa0\xbe\xbe\x1e\xad\xad\xad\xb0\xd9l\x98\x9a\x9a\xc2\xf0\xf00\x0e\x1e<\x887\xdex\x03\xbbw\xef\xc6\x9e={\xb0\x7f\xff~\xec\xdc\xb9\x13\x03\x03\x03\x08\x87\xc3\x88\xc5b\x05\xbf+\x9dN\xa3\xb7\xb7\x17\xa3\xa3\xa3p\xbb\xdd\x08\x85B\xb3\xb6\x89Z\x8e\xea\x1f\xd5\xfd\xf3z\xbd\xb8\xe2\x8a+\x8a\x1a\xde]\xbbv\xe1\xe7?\xff9\xdex\xe3\x0d\xf4\xf6\xf6\xc2\xe3\xf1 \x12\x89\xb09'R}\xfd\xf5\xd7\xc3\xe9t\xa2\xb2\xb2\x92\x95\x8eQ\xba\x92\x95Y\xfe3\xa9&\x1cK\x1f?\xfb\xd9\xcf\x00\x9c\x8c\x89\xd3\xe9t\xb8\xe3\x8e;X\x1b\xb3\xa2\x86B\x10p\xfc\xf8ql\xdf\xbe\x9d\xc5\xf3\xde{\xef\xbd0\x99L%\xe7\x9cb\xfc\xbe\xf4\xa5/\xe1\xa5\x97^b\xcf\xbd\xf2\xca+\xf1\xd2K/\xc1\xe9t\x96$\x9e\x0b)BX\xadV\xac_\xbf\x9e\xfd|\xe0\xc0\x01<\xff\xfc\xf3E_322\x82\xbe\xbe>6&\xa1P\x08w\xdcq\x07\x8b\x0bT\xab\xd5\xf8\x8f\xff\xf8\x0fx\xbd^$\x12\x09\xa6\xa4\xcb\xf7\xc1\xa4{\x12\xdf\xf8\xc67\x0a\xbe\xdfK/\xbd\xc4.\xd5r[\xa9\x8c\x87>\x1d\xc4\xe3qvy^\x0a\xe0g\xc2\x0a \x80\xf31\x12\xa5\x16\x87\xbc\x84\x0c\xc7\xe2\xaa\x7fD\x12R\xa9\x14\xfc~?\xee\xb8\xe3\x0eF\xd8\x08\xe1p\x98\xa9x\x94\xe1V\xac\x9f\xa3N\xa7\x83\xddng\xc9\x03r\x02(W\x01].\x17\x9a\x9b\x9b\xd1\xde\xde\xce\x8a\x10\x87\xc3a\xf8\xfd~x\xbd^\x8c\x8f\x8fcbb\x02\xb1X\x8c\xd5\x0fL&\x93\xec}(\xeehhh\x08\xc3\xc3\xc3\x18\x1d\x1den\x13e\xff\xda\xe5:oT\x1f1\x1c\x0ec||\x1c\xd5\xd5\xd5(//\x9f\xb6gz{{\xf1\xe2\x8b/\xe2\xe0\xc1\x83\x18\x19\x19\x81\xc7\xe3A\x22\x91(p\xdfK\x92\x84\x0b/\xbc\x10UUU\xa8\xaa\xaabsH\xf3]l\xaf\x16#\x86\x1c\xe7\x0e$I\xc2\x9e={\xd8\xcf\x1f\xfe\xf0\x87a\xb3\xd9fu\xe1n\xdd\xba\x95\xc5\xdc\x02\xc0\xfd\xf7\xdf_\xb2\xc7/%\x85l\xdf\xbe\x1d\xdf\xfe\xf6\xb7Y\x0cq[[\x1b\xb6o\xdf~F3\xc7\xa9\x06\xe9\x83\x0f>\x88\xd6\xd6Vv\xce\xfd\xeaW\xbf\xc2\xb1c\xc7\xa6\xadc\xea\x19L \xd2{\xf7\xddw\xb3\xe7MMM\xe1\xd7\xbf\xfe5\x0e\x1c8P\x90\xd0A\xc8\xa43\xa8\xa8\xa8\xc0\xa6M\x9b\xd8\xe5\xf9\xcd7\xdf\x84\xc1`(8\x7f\x95\x04\xf0t\xf7S\x7f\x7f?\xf6\xee\xdd\x8b\xce\xce\xce%aG\xb9\xabw\x05\x11@9\xa1S.>\xf9\x02\x98)\xb0w\xa6zc\x1c\x0bo\x04\x88H\x84B!\xc4\xe3q\xd4\xd7\xd7\x17(z\xc1`\x10\xe9t\x1a\xc1`\x10\xe1p\x98e\xff\xca\xfb\xce\x12\x0c\x06\x03*++\xa1\xd5j\xa7\xa9G\xd4\xc7\xd7h4\xa2\xbc\xbc\x1c\xf5\xf5\xf5hllDKK\x0b\xe2\xf18#\x81\xe1p\x18\xd1h\x94\x156\xa6vfT\x80\x9a\x0aVS\xfb\xb2c\xc7\x8eatt\x14\xe3\xe3\xe3\x88D\x22\xec0]\x09\xf3\x16\x8dFY\xe1\xe7\x0f~\xf0\x83\x05\xbdy\x01`ll\x0c\xbf\xfb\xdd\xef\xb0\x7f\xff~\xf8\xfd~6>\xcan-j\xb5\x1a\x17_|1jkk\x0b\xd4?\xb9\xd2[\xea`\xa7B\xd4\x1c\xe7\x0e\x8e\x1f?\xceH\x0f\x00\x5c}\xf5\xd5E\x09\xbf\x1c\x89D\x02\xaf\xbf\xfe:\xfb\xf9\xb6\xdbnCccc\xc9\xd7\xa8T*V\xe7O\xab\xd5\x22\x99L\xc2j\xb5\xe2\xddw\xdfea\x05\x8bE6f\xfa?\xa3\xd1\x88\x87\x1ez\x88\x95.\x12E\x11_\xfa\xd2\x97\x90\xcf\xe7\x91\xc9d\xe0\xf7\xfb155\x85X,V4\xeb\xfd\xe2\x8b/\x86^\xafg!\x18;v\xec@8\x1c\x9e\x96\xb8!\xb7a\x9f\xf8\xc4'\xd8\x99:66\x86\x91\x91\x11F\x9a\xc9s1W\x028[\xa2\x15\x95\x9d\x11E\x11\xd1h\x14\xd1htI]^g\xeb\xfd\xce\xb1\x0c\x14\xc0\xb9L\xba\xbc\x91|\xb1\xd7s\x17\xf2\xe2\x92\x08\xca\xfa\x0d\x87\xc3\x98\x9a\x9aBEE\x05\xaa\xab\xab\x0b6\x22\x95|\xa0\x96mD\xb0\x8a\x19\xfc\x8a\x8a\x0a\x18\x8dFV\x97\x8f\x12\x08\xe4\xf3-\x8a\x22\xca\xca\xcaPUU\x85\x86\x86\x06\xacZ\xb5\x0aF\xa3\x11\x99L\x86\xf5\x08\xa6^\xb6Tf\x81\x0eF\xeaTB\x17\x07\x9dN\x87\xc1\xc1A\x0c\x0d\x0da||\x9c\xb9b\x96\xb3\x0aH\xea\x1f\xc5\xfe\x0d\x0f\x0fc\xe3\xc6\x8d0\x99L\xd3\x9e\xb3\x7f\xff~\xf4\xf7\xf7#\x14\x0a1\x82\x9f\xcdf\xa7\x8dOGG\x07\x9a\x9a\x9a\x98\xfaG\x89<\xc5\x0c\xd9L\xfbu\xa6va\x1cK\x13\x87\x0f\x1fF0\x18\x84J\xa5\x82\xcdfc\x85\x9fK\xad\xbb\x9e\x9e\x1e\xd6\xef\x17\x00n\xbc\xf1\xc69\xc5\xdc>\xf1\xc4\x13\x18\x1f\x1fg\x04\xe9\x97\xbf\xfc%\x1c\x0e\xc7Y\xeb\xe0\x93\xc9\x9cT\xe5\x1e~\xf8a\xe6\xb6\x0d\x87\xc3\xb8\xef\xbe\xfb\xa0\xd1h\xb0w\xef^\xbc\xf7\xde{\xacs\x91\x92\xa0\x94\x97\x97\xe3\x82\x0b.`\x8f\x1dD\x22\x11f\x8c\xe4c\xe4\xf7\xfbq\xf8\xf0aLLL \x10\x080w\xba\xb2\x0b\x82N\xa7\xc3\xc6\x8d\x1bQ]]\x8d\x8a\x8a\x0a\x98L&F\xb2g2\xce\xc5B6\xf8E\xed\xdc\x00\xed\xa7W^y\x85\xcdeMMM\x01\xa1)\xb6\xe6T*\x15\xdez\xeb-\x00'c\xd9\xca\xcb\xcb\xf1\xa1\x0f}h\xd6\xdf511\x81\xef}\xef{\xec\xb1\xbb\xee\xba\x8b\xbdn\xb1\xd4\xbf\xb9\xf4\x16O\xa7\xd3\xe8\xe8\xe8\xc0u\xd7]\xc7\xe2\xf9\x9e\x7f\xfey\xbc\xfa\xea\xab\xac\x9da1\x12\xa6R\xa9\xa0\xd3\xe9p\xd1E\x17\xb1\xc7\xfa\xfb\xfb\xe1\xf7\xfbg\xfc<*\x95\x0a\x06\x83\x01UUUl\xff\x1d\x04\x83A\xdcv\xdbm,\xf1C\x8eP(\x84\xb1\xb11\x0c\x0e\x0e\x22\x1c\x0e#\x91H\xb0\xd8\x18\xb9\x92D\xf3q\xfe\xf9\xe7\xc3b\xb1\xc0f\xb317m\xa9\x182\xa5\x0aX[[\x8b\xe6\xe6f\xf4\xf6\xf6B\x14EV\x9a\x86\xe6]\x14E\xe6N\xa1\x9e\x9c\xf4\xb7(\x8a\x08\x87\xc3\xe8\xef\xefg\xad\xcc\x9cN'+G\xb3\x1c\x08\x09\x19\x85x<\x8e`0\x88\xc1\xc1A\x96Y(\x1f\xe7\xc9\xc9Itww#\x16\x8b\xb1\xb8\xa0T*UP\xb3\x91\x08\xde\xe5\x97_\x8e\x8a\x8a\x0a8\x9dN\x94\x95\x95\xb1q.\xa6\xd8\x92\xa23\x9b\xd2R,\xa4\x83c\xe9\xac!A\x10\x0a2y\x01`\xdd\xbau3v\xf1\xa0\xd7\xec\xdc\xb9\xb3\xe0\xf1\x8b.\xbah\xc6\xe2\xcf\xb4\xc6\x9ey\xe6\x19\x00'=:\xf5\xf5\xf5X\xb7n]\xc9.#\xb3|\xf8\xb90\x89y\xbfm*\x95\xc2e\x97]\x06\x8b\xc5\x82H$\x02\x00\xd8\xb9s'ZZZ\x98\x9d*v~\x85\xc3a\xdc\x7f\xff\xfd\xd0j\xb5\x88\xc7\xe3,\xfe\xae\x94\x22J\x9d\x8e(<\xc5\xe3\xf1\xa0\xbd\xbd\x9d\xb9h\xe5\xe5\xd3\x02\x81\x00\xd4j5\x12\x89\x04\xecv;\x9cN';\xc7i\x8c}>\x1f***\x10\x89DX8\xd5\xd8\xd8\xd8\xb4\xb9\xa40\xaa\x85\xdc\x97\xd4\x13Y\xa7\xd3!\x97\xcbadd\x04\x83\x83\x83\x00\x00\x8b\xc5\x82\xb6\xb66\xbe\xe18\x01\x9cy\xf1\x10\x01\xd4h4\xc8\xe5r0\x18\x0c,K\xd4\xedvcdd\x04\xfd\xfd\xfdp8\x1c,\xa1@\xd9\xe0\x9e\x03\xd3T\x1d\xfa\x9b\x5c\xa0\x99L\x86e\xd5\x86\xc3ax\xbd^\xf4\xf4\xf4\xc0n\xb7\xe3\xf6\xdboGUU\xd5\xb4\xb9\xc9d2\xd8\xb1c\x07v\xef\xde\x8d@ \xc0\x0a1\xa7R\xa9\x82Vp\xf4\xfb\xd7\xad[WP@X\x19\xffW\x0cD\xe4\xacV+\xaa\xab\xab\xd1\xd0\xd0\x80\xd5\xabWchh\x88\xa9}Db\xe5\xb1\x80\x14\x9fCa\x04\xf4\x1d\x0d\x06\x03:;;\xd1\xde\xde\x8e\x91\x91\x11\xe6\xd2\x9c\xcbg9W\xd4?r\xfb\x8c\x8c\x8c\xa0\xbe\xbe\x1eN\xa7\xb3\xe0`\x97$\x09\xfb\xf6\xedCww7+\xfbB\xea\x822\xcbp\xed\xda\xb5hjjBuu5\xecv;t:]\xc9\xd6Ss\xddo\xbc\x17\xf0\xd2?+\x0e\x1e<\xc8\xc8\x0f\xc5\xa5\xcd6g\x87\x0e\x1d\x02p2\xc1\xa0\xae\xaenN\x8a\xce\x1f\xfe\xf0\x07\xd6e\xe4o\xfe\xe6o\x0aT\xc7y\xe3\x14\xd6\xdf|\x5c\xc1\x9b7o\xc6\xb6m\xdb\x00\x00o\xbf\xfd6>\xf9\xc9O\xb2\xf2S3\xbd&\x1e\x8f\xb3R1\xb3\x91\xabL&\x83\xea\xeaj\x98\xcdf\x84\xc3a\xd6\xfb\xf7\xf2\xcb/G4\x1a-\x88\xe3U\xa9T\x18\x19\x19\xc1\xf0\xf00\xb2\xd9,\x9cN'6m\xda\xc4\xcef\x82\xd7\xebE\x7f\x7f?\xe2\xf18\xb3\x89>\x9fo\xda\xe7!\xaf\xcdlc_\xac\xc7{\xb1\xc7(\x16qdd\x04\xeb\xd6\xadCYY\x19\x02\x81\x00\xfb\x0cn\xb7\x9b\xd5YT\x8eY\xb19\xe1\xe7\xc5\x0a#\x80\xe4\x02\xa4\xd6?z\xbd\x1e\xa1P\x08\xa2(\xb2\x1e\x89\xc7\x8f\x1fg\xeeD\xca\x94\xa2 \xf5\xb3U>`)\x1e\xe8\xf2\x929D\xfa\xc8\xddKeU\xa2\xd1(\x82\xc1 <\x1e\x0fB\xa1\x10.\xbf\xfcr\x5ct\xd1EE\x8b\xf9\x0a\x82\x80\xbe\xbe>\xbc\xf3\xce;\x18\x1b\x1bC0\x18D4\x1ae\x99eJW\x82 \x08\xd8\xb0a\x03\xaa\xaa\xaa\xe0p8X\x06\xb0\xb2\x06\xa0r\x93\xd3!e4\x1aQQQ\x81\x86\x86\x06455\xa1\xae\xae\x0e\x03\x03\x03\x05\x87\x18)}r\x12(\xbfDP}\xb1L&\x83\xc3\x87\x0f\xa3\xa9\xa9\x09\xf5\xf5\xf5p8\x1c\xcc\xady.\xabR\xf2\xd8?\xaaQv\xfb\xed\xb7O\xcb\x8e\x9e\x9c\x9c\xc4\xd1\xa3G\xe1\xf1x\x98\xfb\xb7X\xe6\xaf(\x8a\xe8\xe8\xe8@MM\x0d*++QVV\x06\x9dN\xc7\x0e\xf0\xd3q\x17\x15\xdb\x97|\xaf.-\x02\xd8\xdf\xdf\xcf\xce\x0cy\xdb\xc7R\xaf9|\xf80\xbb\x84o\xdc\xb8\xb1\xe4kT*\x15\xc6\xc7\xc7Y\xf7\x1e\x00\xb8\xf9\xe6\x9b\xd9\xc5n\xde\x18\x0d\x03\xbf\xef\x06D\x01@\x91\xdfI\x0f]\xdb\x044\x9b\xe7E(T*\x15b\xb1\x18\xae\xb9\xe6\x1al\xdb\xb6\x0d:\x9d\x0e\xe3\xe3\xe3s\xda\x03\xf3\x11\x22\xb2\xd9,\xacVkA\xb9$\x9f\xcf\x07\x00\xac\x16k\xb1q\xa7\x18\xe7t:=\xed\x1c\xcdd2\x18\x19\x19\x99V[\xb7\x18\xf9\x94\xf2\x12\x90/>|\xf3\xdd\xbb\xb1X\x0c\x83\x83\x83\x88\xc7\xe3\xe8\xee\xeeFmm-s\xfb\x92::\x97\xb9^\x8c\xe4\x0f^?x\x09\x11@r\xdb\xc9'\x99\x8c~,\x16c\x1d\x1d\xa2\xd1(\xecv{A?Z\x8dF\x83\x13'N0\x22\x13\x8f\xc7QSS\xc32\xaf\xe6\xbb\x01\x97\x0b\x94\x07\x13);D\xfc\xa8TK,\x16+\x88\xa3\xd4h4hnn\xc6\x95W^9-XY\xbe\xd9\x8f\x1f?\x8em\xdb\xb6\xb1\xae\x11\x81@\xa0@IR\x06+\xd7\xd5\xd5a\xed\xda\xb5,\x8e\x8c\xdc\xbf\xc5H\xbf\x12\x94\xf9Ku\x01\xa90\xf4\xd8\xd8\xd84\xd7\xb5\xc1`(\xd8\xe0\x82 \xb0\x18R*\xe3@1<\xc3\xc3\xc3hhh`I)\xa2(\x9e\xb3\x8d\xd5\xe5\xb1\x7f\x91H\x04\xe3\xe3\xe3\xacc\x87RQ\xa7\xde\xca\xa4\xda\x16+\xfa\x0c\x00UUUX\xbf~=\x5c.\x17S\xffHe?\x9dCw9\x97\xdfYN\x04p``\x00Z\xad\x16\xe9t\x1a\x8d\x8d\x8dszMgg'\x8b\xdd\xa6Vj\xa5\xd6Kgg'K\xe42\x18\x0c(//?\xf5\x0f\x9e\xce\x01\x13Q@3\x03\x01\x14p\x92\xdc$\xb2\xa7\xe4\x06N\xa7\xd3\xd8\xb0aC\xc1c===hnn>\xed\xf5L\xaf\xa7s\xd3f\xb3\xb1\xc7\x02\x81@\xc1\xe5n&2\x94\xcb\xe5\x10\x8b\xc5X\x17\x95R\xf6`\xa6\xef'\xc3E\x87\xb0\x00\x00 \x00IDATe%\xe4uy\xa80s\xa2\xcf\xc0\xc0\x00\xb4:-\xaa\xab\xaaY\xd8\xc7\xc4\xc4\x04&''\xb1f\xcd\x1aX\xadVF\x5c\x93\xc9$k\x8fG\xb1\x88d\x8f\xb3\xd9,\x92\xc9$\xccf\xf3\xbc/\x80s\x1doyMZ\xb2gT\xab\x96B\x7ff\xb2\x9f\xc41\x96k\xac2\xc5\x89\xce\xf5\xb2%\x9e\xce\x22\x97\xdfJ\xe4\x7fK\x92\x84\xf2\xf2r\x8c\x8d\x8dM{>%#\x10,\x16\x0b3\xea:\x9d\x0e\xe5\xe5\xe50\x9b\xcdp:\x9d\x08\x87\xc3x\xf3\xcd7\x11\x08\x04P[[\x0b\xb3\xd9\xcc\x12\x00V2\x01\xa4\x80`\x22eJ\xa5\xc7\xe1p\xa0\xac\xac\x0c\xcd\xcd\xcd\xa8\xaa\xaaBmm-s\x9f*\x95#\x8a\x19\xe9\xed\xed\xc5\xb6m\xdbp\xe4\xc8\x11\x8c\x8c\x8c\xc0\xef\xf7#\x1a\x8d\xb2\xd2\x02\xf2\xa4\x0a*\x7f\xf0\xfe\xf7\xbf\x1f\x0d\x0d\x0d\xa8\xac\xac\x84\xd9l.(\xcf2\x97uE\xae\xdc\xf2\xf2r\xd4\xd5\xd5\xa1\xad\xad\x0dG\x8f\x1e\x85\xdf\xefg\xc9&\xa4l\xca\xe3\xcb\xe41\xa4\xa4&\x93[\xe6\xe8\xd1\xa3hhh@MM\x0dS\x8d\x89 \x9e\x8b\xf3M\xfb\xc5\xef\xf7#\x10\x08\xe0\xca+\xaf,\xd8\xdc*\x95\x0a^\xaf\x17\x9d\x9d\x9d\x98\x9a\x9ab\x074\x112\xf9\x9cK\x92\x84M\x9b6\xb1\xba\x7ff\xb3\xb9@\x95P\x92E\xa5\xda\x5c\xaa\xec\x07\xc5\xf0\xce\xe7f?\xdb\xfbr,<\x01\x9c\x9a\x9ab\x17\xaa\xe6\xe6\xe6\x92\xc6\x97\x1e\x0b\x87\xc3,\xde\xaf\xb6\xb6\xb6`\xee\x8a\xd9\x84\xd1\xd1QFz\xea\xeb\xeb\x0b.p\xf32\xf6*\x15TR\xfed\x0c\xa0<\xd9\xa9\xe0C\xaaNK\x05\x92$\x09f\xb3\x19f\xb3\x99\xd5\xcc\x1b\x1e\x1eF[[\x1b\x8bC\x9e\x8f=\xd4\xeb\xf5\xd3\xceK\xba\xa8;\x9d\xcei\x040\x91H`rr\xb2d\xdd\x5cj\xc7y*H\xa5R\xc8J\xd9\x821\x97'z\x09\x82\x80\x13'N\xa0\xab\xab\x0bZ\xad\x16\xa3#\xa30\x9b\xcd\x88F\xa3\xec\x1cN&\x93\xb8\xe4\x92K`4\x1a\xa7\x95\x95\x917\x05\xa0\xc7\xe3\x898\xca\xca\xca\x98m\x9a\xc9\xfd{\xaa\x9c\x83\x0a\xff\x93\xfd\xa0nV\xd9l\x16\xb5\xb5\xb5X\xbf~=\xcb\x19\xa0\xf5@\x9d\xa3B\xa1\x10\xf4z=.\xb8\xe0\x82\x82\x18\xd6\xb9\xac\x9bd2\xc9<\x5ct\xf1\x96$\x09\xb1X\x0c\x16\x8b\xa5\xe0}\x8a\xbd\x9f\xfc\xff\xe8\xbbP\x85\x0d\x9b\xcd6\xe3\xe5z\xa6\xcf\xa6t\xd1\x07\x02\x01\xf4\xf4\xf4@\x92$\xd4\xd4\xd4\xa0\xa1\xa1\x81\xbdvrr\x92\x09it\xe6\xe7\xf3y\x88\xa7K\xa2\xe4\x85\x9ciQi\xb5ZLNN\xa2\xa2\xa2\x02\xa9T\xaa\x80\x14P\xe0\xbe|Ah\xb5ZVC\xce\xe1p`\xcd\x9a5\xb8\xfd\xf6\xdbQYY\xc9\xcaY\xe4\xf3y\x84B!\x04\x83\xc1eY\xe6c\xae\xaa*\x15T\xb6\xdb\xed\x8c\x04\x91+\xddf\xb3\xc1d21uL9\xb7\xca\xf8\x0e\xbaA\xed\xde\xbd\x1b/\xbe\xf8\x22\xba\xbb\xbbY/\xdeh4\xca\x02\x9c)+\x8d\x16S.\x97\xc3\x86\x0d\x1bp\xc9%\x970\xf7/\x91\xf3\xf9\xb8[)\x09\xc8f\xb3\xb1X\xc0\xf3\xce;\x0f/\xbf\xfc2\x04A@*\x95b\xea\x94\xbc\x9c\x01\xb9\x82\xa9\x86\x17e\x03\xe7r9\x0c\x0e\x0ebpp\x10\xf5\xf5\xf5\xac\xbb\xc5\xe9(\x5cg\x1b\xe9t\x1a\x91H\x04>\x9f\x0f:\x9d\x0ek\xd7\xae\x9dv@P1l\x8f\xc7\xc3\x92?h\x8f\xc8\xc3\x04jjjX\xe1g\x87\xc31\xad\xed\xdbL\x87\x8d\xb2m\xa3\x92(\xca]\xf4\xb3\x19w\xba\xec\xc9K@q\xb7\xcd\xe2\x83\x92/.\xbc\xf0Bx\xbd^\x0c\x0f\x0f3\x028\xd3\xe5\xa8X\x92\x07)A\xa5\x14\x06\xb7\xdb\xcd.\xab.\x97\x0bf\xb3y\xda\xf3\xe7<\xe7\x1a\x0d \x08\x80\xa0\xa6E\xaf\xf8\xfb\xf4\xc6\x85\xca\xd4\xd4\xd4\xd4\xa0\xbb\xbb\x1b\xc0\xc9,\xddS\x09\x1bQ\xab\xd5\xa8\xa8\xa8\xc0\xd4\xd4T\x81}\xa2\xcb\xac\x5c\x09\x0d\x04\x02\x8c\xa0\x95\xb2e\x92$\xc1\xef\xf7\x17\x9d\x8b\xb9z\x10\x90GQ[\x90\xcf\xe7Y\xb9/\xba\xd4{<\x1ex<\x9e\x82\xe7\x06\x83A\x9c8q\x02k\xd7\xae\x85\xdb\xed\x9e\x95\x90\x0c\x0f\x0d#\x93>\x19\x92\x13\x08\x04\x10\x8dF\x17d\x8f\xabT*\x04\x83A\xb8\xddnv\x86\xc8\xc7\x98\xe2'\xa9\xbe%\xd9\xb8\x9e\x9e\x1e\x8c\x8e\x8e\x16(\xad\xe1p\x18v\xbb\x1d\xa2(\xa2\xb5\xb5\x15&\x93\x09\xbd\xbd\xbd\xe8\xe9\xe9\x81\xd1h\xc4\x05\x17\x5c\x80\xb2\xb22\xf6\x9d\x8e\x1f?\x8e\xc1\xc1A\xf6\xfc\x96\x96\x16\xf4\xf4\xf4```\x00\xd9l\x16:\x9d\x0e\x97^z)L&\x13\xb3\x95\x9d\x9d\x9d\x98\x98\x98\x80\xc1`@{{;\x5c.W\xc1\xda\x9f\x9c\x9c\xc4\x81\x03\x07X7\xa5\xf5\xeb\xd7#\x9b\xcd\xc2\xeb\xf52QDN`'''\x11\x89D \x8a\x22\xf3p\x112\x99\x0c:;;\xd9\xba\x9a\x9a\x9a\x82N\xa7\x83\xcb\xe5BWW\x17\xba\xbb\xbb\x91\xcf\xe71::\x8a\x0b/\xbc\x10f\xb3\xf9$\x97\x90W\x15\x9f\xaf\x14K\x03\x9f\xc9d\xe0v\xbb\x11\x0c\x06\x99\xf4O\x8bK\xe96\xa4/\x1b\x8f\xc7\x19Y$\x03m2\x99\x98q\x22bSYY\x89\x9a\x9a\x1a~\x82\xcfC!\x9cOl\xca\xeb\xaf\xbf\x8e\xed\xdb\xb7\xb3\x96jtS\xa0^\xbc\xf2\xac_\x9a?\x83\xc1\x80K/\xbd\x94\xd5\xa4\xa26l\xf3%\x80\xf4\x19\x92\xc9$\xa2\xd1(\x04A`\x89!\x1e\x8f\x07\x82 0\xb5\xd8h4N\xcb2%7\xb2N\xa7c\xaen\xbf\xdf\x8f\xee\xeenX\xadVh4\x1a\x84\xc3a\xd8l\xb6\x19\x13BJ\x8dU\xa9[9\xfb7\xf2,\xbeF\xe9bQ\x12ny(\x84\xf2&^p0#\x8f\x5c\xf6O1\x9d~\xbf\x1fCCC\xac\xa5\x94\xfc\xb9\xd1h\x14\x87\x0e\x1d\xc2\xe4\xe4$\x02\x81\x00\x8b\x87T\x96\xeb\xc9\xe7\xf3\xd8\xb8q#r\xb9\x1c\x22\x91\x08\x9bgRm\xe5\xdd\x08\xe4\xddz\xe8p\x09\x85B\x98\x9c\x9c,X\x17\xc5\xc2\x8f<\xf20\x8c%Q;\xe0A^#\x14\x84~X\xad\xd6\xe9$\xf4\x14\xd5\xe4\x5c.\x07\xbb\xdd\xce~\x8e\xc5b\xa7D\x00\xc9\x86)\xd73\xd9C\xa7\xd3\xc9\x88\x22e\x1d\xcf\xe5\xf3Ro\xf4\xf9|7\xaa\xd9(I\x12\xeb\x92\x14\x0e\x87Q^^\x0e\xadV\x8b\x9e\x9e\x1e\x0c\x0f\x0f\xcf\xd9;2<<\x8c\x91\x91\x919y\xde\xc6\xc7\xc7\xd9s\xb3\xd9,+F_\xcc^\x95R\xb8\x8a)p\xa4.[,\x16\x88\xa2\x88L&\x03\x87\xc3\x81\x96\x96\x16D\x22\x11\xf4\xf5\xf5\xb1\x8b\xb0\xd9d\xc6\xbb;\xdf\x85\xd7sR-t8\x1c\xec\xfc\xa2$\x1eI\x92011\x01\x8b\xc5\x02\x8f\xc7\x03\xb5Z\x8dP(\x84\xae\xae.ttt\xc0`0\xa0\xaf\xaf\x0f'N\x9c`cz\xf8\xf0atvv\x16x\xd3\xb2\xd9,v\xee\xdc\x89\xf6\xf6v\xf6\x9a\x89\x89\x09\xa8\xd5j\xa4R)\x1c8p\x00\xe7\x9dw\x1etz\x1db\xd1\x18&''\x0bzBG\xa3Q\xd6jQ\xadV\xa3\xaf\xaf\x0fn\xb7\x1b\xf5\xf5\xf5\x08\x87\xc38z\xf4h\x81\x08\xd2\xd9\xd9\x09\xb3\xd9\x8c\xc6\xc6F\xb8\x5c.\x0c\x0e\x0e\xb2\x5c\x0a\xaa\xa4\xf1\xce;\xef\xa0\xba\xba\x1a~\xbf\x9f\xed\xdf@ \x80\xbe\xbe>\xb4\xb4\xb4`jj\x0a\xe2{\xef\xbdw\xda\x12:\x19\x15\xb7\xdb] \x9b\x0b\x82\x80d29\xad\x03\x01\xb9\xecHB\xcdf\xb3L\xb1\x92+\x18\xf2N\x10\xa7BN\x973\x8a\xcdQ\xa9\x22\xbe\xf2\xc5\x9aH$\xd0\xdd\xdd\x8d\xd7^{\x0d]]]\xac\xf4\x8e\xcf\xe7C<\x1eg\xca\x1f\x91\x08e\xa0\xf1%\x97\x5c\xc2\xfaL\xba\xddnD\xa3\xd1\x02\xf7\xef|n\xfatK\x8dF\xa3H&\x93\x10E\x11\xb5\xb5\xb5\x98\x9c\x9cd\xe4\x93.\x15\xa4\x80\x12\x11$\x05\x93n\x83d\xb0\x8e\x1d;\x06\x9b\xcd\xc6n\x8c\x0e\x87\x03:\x9d\xee\xd4\xb3\x10K\xcd\xc3I\xe6\x87\x93\xb6?\xbf\xa0sKk?\x12\x89\xc0`0`\xcd\x9a5\xd3J:\xec\xdb\xb7\x0f\x03\x03\x03\xacc\x0b\x91?e1Y\xa7\xd3\x09\x8b\xc5\x82d2\x09\x9f\xcfW\x90}/\xff]\xc5\xe6-\x97\xcb!\x1e\x8f\xc3\xe3\xf1 \x91H\x14\xb8W(\x0bq\xae\xc9#\xd1h\x14\x1e\x8f\x07###\x88\xc5b\xacn\xe4B\xee\x83\xd39\xcf\x94\xef9/\x17 \xad\x85\x05\xfc\xfc\xf3\xfd\x0c\xb3\xc1\xe3\xf1\xb0\xf3\xa0T\xc1v\xb9\xaa%_GtY\xcbKy\xa8\x04\xd5\x8cdj\xcd\x9a5\xc8d2\xa8\xab\xabC2\x99\x9cV\x1ajN\xdfI\xa5\x82\x86B]\x00H\xb9?\x15\xa0\xcff\xb3\xa8\xac\xac<\xf9\x19\x16`\xdb\xc9\x09\xca\x5c\xd6\xa3<\xfb^\xfe\x98^\xaf\x9f\xf6\x7fd\xcb\xe4*\xde|\xe64\x9dN\xb3\xec\xe1\xb9\x90\x7f\xbd^\x0f\xab\xd5\x0a\xaf\xd7\x0bI\x92p\xe2\xc4\x09v\x8eR\xa1\xfeD\x22\xc1\xe2\x0a\xe5\x7f\x94\x97V\xf2\x10\xcc\xa7&/}\x7f:\xbf)1\xb1\x14\xf1\x9fK\x0b\xbfP(\x84d2\x09\xa7\xd3\x89\xd6\xd6V8\x1c\x0e\xc4b1\xb8\x5c.V\xcagdd\x04\x89D\x02CCC'\xd5\x5c\x9f\x1fz\xbd\x1eMMMhnn\x86\xd7\xeb\xc5\x91#G\x90H$\x98-\xa1\x84I\xb5Z\x8dL&\x03A\x10\xe0\xf3\xf90>>\x0e\xa3\xd1\x88\xbe\xbe>\x08\x82\x00\x83\xc1\xc0B\x8dr\xb9\x1c4\x1a\x0d\xca\xcb\xcbQQQ\x81\xf1\xf1q\xf8\xfd~\xec\xdb\xb7\x8fy\xc9\x8cF#\xf4z=\xc2\x91\x931\xd9\x9d\x9d\x9d\x90$\x09\xf1x\x9c\x952\xa3P\x1c\x9f\xcf\x87h4\xca\x08\x5c,\x16\xc3\xe8\xe8(\xc6\xc7\xc7Y\xc2lee%\x9cN'2\x99\x0c\xb3\xd7\xc7\x8f\x1f\xc7\xe8\xe8(\xa2\xd1(\xab\xefZQQ\xc1jwNMM1\xd2N\x89\xb6\x14\xe2\x15\x0a\x85 RP\xef)\x1dx*\x15rR\x0e\xb9l\x0eSSS\xac\xce\xd3L\x86DyP\x90A\xa6\xbaB\xe1p\x18Z\xad\x16\x1e\x8f\x07\xef\xbd\xf7\x1e\x1c\x0e\x07\x93T\xe9\xbd\xb8\xbbhn\xd2?\xfd-w\xdd\x05\x83Atuu\xe1\xd0\xa1Cl\xe1P\xcd\xc5X,\xc6b\xfe\xe4\x0a\x92R\xb5\xda\xbcy3n\xb8\xe1\x06\xac^\xbd\x1a\x8d\x8d\x8dp:\x9d\xd0\xeb\xf5,\x93t\x0eV\xb2P9P\x01\xd9\xcc\xc9\x1b\xae\xdb\xed\x86\xd3\xe9\x84(\x8a\x98\x9a\x9a\xc2\xc8\xc8\x08ssS\xbc#\x1d\xa2D\x00I)\xa6D\x0f\x8aY\x09\x04\x020\x9b\xcd\xac\xc4\x0c}Nf\xb4\xf2s$\x0bE\x94\xbd\x85\x9c\xa7bcF\xb13\xd1h\x94\xd5c\xbc\xed\xb6\xdb\xa6\xed\xa7P(\x84\xc3\x87\x0fchh\x08\xe1p\x98\xa9\x7fr\x97=\xe1\xfd\xef\x7f?.\xbd\xf4Rttt\xa0\xb1\xb1\x91%\xed(U\x19e\x8c(\x19 \x9f\xcf\x87\xee\xeen\x8c\x8d\x8d!\x14\x0a\xb1\xf7&W\xc2\x5c\xe1r\xb9\xd0\xde\xde\x8eK.\xb9\x04\xd5\xd5\xd5\xc5\x95\x9c%~\xd1Z\xca\x9f\xf5t\x09\xafr\x1d\x96\x95\x95\xb1\x90\x0b\x9a\xbf\xab\xae\xbaj\xc6:y\xc0\xc9\xba\xa0\x0f?\xfc0S\x92\xf5z\xfd\xa9\x8d\xa3J\x05\xa1?\x08\xf1\xf8\x11\xe4E\x81\xb9C#\x91\xc8\x9fTh\xb5l\xfd\x9e\xa2@@FY\xeeY\x98M5\xa63'\x10\x08L\xebU\xae\x0c\x85 \xc1CIv\xe6J\xfe\xe9RN\xaf\xb5\xdb\xed\xac\xf4J1\x98L&TTT0\xe5\x87~\x9fF\xa3a\x15\x1d\xe4kE\xadV\xa3\xb1\xb1\x11\x91H\x84\x95\xb6\xa1\xef\xb2f\xcd\x1a$\x93I\x1c:th\xd6\xd0\x0e\xf9\x19B\xef-\x0f\xe1QzB\x8a\x89\x143\x8d7u\xaf\xaa\xac\xacD}}=\xda\xdb\xdb\xa7\x9d\x1b&\x93\x09eeeH$\x12\x18\x1c\x1cd\x8aYMM\x0d\xd6\xacY\x03\x95J\x85\xba\xba:H\x92\xc4H]:\x9df\xe4O\x14E\xb8\x5c.VP\xbb\xab\xab\x8b\x11D\x97\xcb\x85\xf5\xeb\xd7C\x14E\xd6q\x8a\xcahi\xb5Z\xd4\xd7\xd7chh\x08\x13\x13\x13\xc8\xe5r\xb0Z\xadhii\x81\xc9d\xc2\xe0\xd0 \xfaz\xfb\x18\xc1s\xb9\x5c(++\x83\xd3\xe9Dyy94\x1a\x0d+\xd7F\xbd\xee\xa9\xd4N*\x95\x82^\xafG\xc7\x86\x0eT\xb9\xaa\x98\xfdkhh`\xe1N\xe1p\x18\x82 \xa0\xbc\xbc\x1ck\xd7\xaee\xe3B\xed\x01\x05A\xc0\xaaU\xabP^^\x8e\xa3G\x8f\x22\x1a\x8d\x22\x14\x0a\x9d\xf4\x04\x9cn\xd1F2V\x1a\x8d\x06n\xb7{\xc6x\xab\x99\xfa\x1b\x92\x8cK\xb7\x8c`0\x88\xc9\xc9I\xe6\xdaknnF{{;/0{\x0a\x86@\x92$x\xbd^\x8c\x8d\x8d\xe1\xd8\xb1c\x18\x18\x18`\x01\xb4>\x9f\x0fSSS\x88\xc5bl\xf1Q\xf0\xb2\x92@\xd0|vtt\xe0\xe6\x9bo\xc6\xea\xd5\xab\xd1\xd2\xd2\x02\x97\xcb\x05\xab\xd5zJ\xae_\xe5:\xa0x\xbfx<\x0e\x9f\xcf\x87\x81\x81\x01\x16B@EG)\xf1\x85bH)\x03\x8cjJR'\x0a\xb5Z\x8d\x9e\x9e\x1el\xdc\xb8\x91\xb5\x86\xd3h40\x99Lls\xcc\xd9 -\xa0\xb27\xd3<\xc9\x7f&E\x9dZ\xbey\xbd^\xb8\x5c.vp\x91\xbb\x96b\xffN\x9c8\x81P(\x84P(TP\xaeG\xfe\xbe\xb5\xb5\xb5\xe8\xe8\xe8@ss3\xea\xea\xeaX\xd2\x8e\xbc\xf3\x87\xf2\x10\x97\x7f&*(-/\xf2]\xcc}=\x97\x84\x0e\x0a\xdc\xa6\xc3\x9a\x5c9\xcbI\x89_*\x17\xc0\xd3\xfd\x0e\xa4\x8a\xe9\xf5z\xf6oR\xa3g*\x1e=\xd3\xef?\xe5q2\xa4N\xc6\x01jN\xfe.\xaa\x22!wK\x9f\xcew'\x8c\x8d\x8d\xb1KeEE\xc5\xac1\xe6r\xaf\x83\xfc;\x16ss+\x09\xe0|\xc7C\x1e\xca!I\x12\xaa\xab\xabYu\x8cb\xef\xa3\xd3\xe9`\xb7\xdba0\x18\xa6%\x90\x14K\xc8\xb0Z\xad\xd8\xb0a\x03+\x1bF\xcf\xd7\xe9tLY\x93\x87m\xd1z\x90\x8f\x11\x85\xd8P\x22\x0d\xbd\xbe\xa6\xa6\xa6\xe0s\x9e\xca\xfcP\xfc\x7f*\x95BYY\x19\xea\xea\xeaf\xf4\x80\x99\xcdf\x16\xa7Hjnuuu\xc1\xf3\xeb\xeb\xeba\xb5Z\x99+\xfe\xe8\xd1\xa3H\xa7\xd3hmmEkk+K\xaa#\x95\xb0\xa9\xa9\x09\xabV\xadb\x99\xcd---\xd3~\xb7V\xab\xc5\xea\xd5\xabQSS\x83L&Sp\xae556\xc1l2#\x10\x08\xc0d2\xa1\xaa\xaa\xaa \xf9\x0e\x00\x0c\x06\x03K\x96\x02N\x16hw8\x1c\x88\xc7\xe3\xb0X,'\xd5n\xd9w\xd0\xeb\xf5X\xb5j\x15\xacV+&&&\xa0\xd5j\xd1\xd0\xd0\xc0~gee%6n\xdc\x08\xaf\xd7\x0b\x8b\xc5\x82\xda\xdaZh\xb5Zd2\x19\x9c8q\x82er\x8b\x0b\x91\xea\xae\xcc\xe6\x9bO:7\x19t*2,\x08\x02\xdcn7#$\xc7\x8e\x1d\xc3\xf8\xf88+5B\x81\xeb\x92$\xcd\xe8v\x5c\x8c\x83\xf3l\xba\x9e\x95\xf5\x9f\x8cF#\x8b\x9d\x8b\xc7\xe30\x99L\xcc\xfd\x97H$X\xaf\xd8\xbe\xbe>\x04\x02\x01&gS-\xb9P(\xc4\xdc\xbc\x941ZL\xf5\x93+m\xe7\x9f\x7f>\xee\xbc\xf3N444\xa0\xa1\xa1\x01\xe5\xe5\xe5\x8cP\x9d\xae\x11$y\xdah4\xb2\x1b^[[\x1b\xba\xba\xbaX&\x9a\x5c\xa2\x97\xc7\xa8\xd1\xcd\x8d\x94\x86L&\xc3n\xcd\xc7\x8e\x1dCSS\x13jkkY]@r\x19\xcf\xc3\xaf\xb7h\x0a`1C@.\xf1D\x22\x81@ \x00\xaf\xd7\x8b\xff\xd7\xde\x99G\xc7]\x97\xfb\xff=\xfb\xbeg\xb6\xccdk\xd2\xa4{KiYZ\x0ae\x97\x02r\xa5\x02z\x11\x11\x5c\xa8\x82\x0a\xcaQ\xef\xfdq\xc5\x8b\xc7\x85\xe3\x82\x22\x02\xea\x11\xf1\x82\xe0\x02\x17/(\xdb\xbd*{i\x11*]\xd26M\x97\xecK\x93\xc9Lf\xdf\x7f\x7f\xc0\xf3\xf1;\x93I\x9a\xb4\x93t\x92<\xafs\xe64\xcd2\x99\xcc\xf7\xf3\xfd|\xde\x9f\xe7\xf3<\xefg\xfd\xfa\xf5cDR4\x1aE[[\x1b\xfa\xfa\xfaD\xee\x1f\x89?\xba\x86R\xe1N\xe6\xd1&\x93I\xf4\xe2\x96\x1e5O\xf4\x9a\xc6\xfb\xbe\xf9\x18e\x9bO\xa2\x94\x9e\xc7\xe9t\xa2\xbb\xbb\x1b\xc0{\x89\xeb3~=\xb3y@\x9e\x07d\x80R&\x87\x1c2\xe42YdSi@\xa5\xfeg\x95\xf0qN\xd1\xc9d\x12\xa3\xa3\xa3\xc2\xb8\xda\xe7\xf3\xa1\xaa\xaa\x0a}}}\xe3\xce\xfb\xc5E\x08\xf4\xb9RF\xca'*\x00\x8b\x85\x86\xd3\xe9D,\x16\xc3\x91#G\xa0V\xaba\xb5ZEZ\x07U\xe3Ses0\x18,x\x9d4g\x93\xe0\x90\xcb\xe5\xa2\x1bT\xb1\x08!\xf4z=\xccf\xb38\xa1kiiA\xfb\x81v\xc4\xe2\xff\x8c\x9a\x9a\xcdf\x18\x0c\x86\x02oB\xeabB\xa7\x0d':6\xb4Z-jjj\x84\x10+\x85\xcb\xe5\x12\x913\xaa\xbc.\xae\xb0\x95\xc9d\xa2\x98\xc9`4\x88\xe0\x82\xddn\x87\x5c.\x87\xd3\xe9\xc4\xa9\xa7\x9e\x8aX,\x06\x95J%r\xc8\x8b\xafk)aM\xaf\xad\xb8\xc8\xb5\xba\xba\xba\xa0\x96\xe1X\x91z\xb9\x5c.*\xee'\xdaL\x93\xa3Cq~\xbc\x5c.\x87\xdf\xef\x87\xd7\xeb-\xb8\xfe\xd5\xd5\xd5\xd0\xe9t\x22\xe0\xa2,\xc7\xe2}\x22\x17\x96r\x13h\x80P\xdfYz\xc8\xe5rtww\x8b\x81^UU\x85T*\x05\x85B\x01\x9b\xcdV\xf6\xc8`q\xa9\xf6T\x05\xe0t\x08E\xa9\xb0\xa6|,\xda\x0d%\x12\x09$\x12\x09\x1c=zT\x88\xc2@ \xc4!\xe5\xec\xd0\xcf\x90Ap:\x9d\x16\xc7\x85R\xe1Wl\x1a\x9c\xc9d\xb0n\xdd:\x5cv\xd9eX\xb0`\x01\x1a\x1b\x1bE\x05R\xb9\x8c\xb9\xe9\x18B\xa3\xd1\xc0b\xb1\xc0\xe7\xf3\xa1\xb6\xb6\x16---x\xe3\x8d7Dd\x8f\xc6\x09]#\xda\x11K\x13\x99)\x12\x98\xc9dp\xe8\xd0!\x1c:t\x08~\xbf\x1fN\xa7S\xb4<\xab\xd4h2\xbd\xff\xe4\xfbw\xf4\xe8Q1\xe1\x16\x8f\xcfC\x87\x0ea\xfb\xf6\xed\x08\x04\x02\xc2\xf7\x8f\xc4\xb1t\x822\x9b\xcdX\xb3f\x0d\xdcn\xb7H\xa9(\xe7{p\xa2\xe3\x9d\xc5_\xe5\xb3l\xd92\x1c>|X\x1c+\xcd\xe8\x86X\xad\x00\xbcFa\x04-3\x00\xe9L\x10\xb9l\x0ei\x87\x06:\x8b\xfe}u\xa0\x9c\xb2\xa5\x10u$\xda\xb3gO\xc1\xe7\xcf<\xf3L477#\x18\x0c\x8e\xdb\xdb\xb6T\xbb\xcb\xe2B\x17:^\xa4 \x07\x15~\xe4r9q\xb4>U\x9f<\x8dF\x03\x83\xc1\x80\x85\x0b\x17\xc2h4\xc2d2\xc1b\xb1`\xfb\xf6\xed\xe28Z\xab\xd5\x8a#j\xe9\xdaa\xb3\xd9\xb0j\xd5\xaa)\x1b\xe4\xd3\x111u&\xa9\xaf\xaf\x7f/H\x13\x8f\x89\xb5\xd2\xe9t\xc2\xe5r\x89\x9c\xb5|>/,\xde\xa4]\x9cJ5\x05\x98\xec\xdf\xeev\xbbE\xd7\x94\xf1\xae\xb3\xc3\xe1@KK\x0b\x0e\x1e<(\xe6\xce\x89\xda\x0f\xca \x83\xddn\x1f\xf3\x9a\xacVkIk\x96\x89^\xf3Tr\x5cO\xf4\xeb\xc5\xe3n\xa2kW\xfc\x7f*F\x02\xcal\x04]\x5c\xb41\x99\xc4pir1\x0d^24\x8eD\x220\x99L\xa2\xa4\x99\xbe\xa6R\xa9`4\x1a\x0b\x0c \xcb-\xb4*\x19\xba\x91\xc8u]zdC\xc7\xb8\x94\xd7Bm\xc4\xa4Q>\x12H\xd2hQ\xf1\xce\x86\x8eW\xaf\xb8\xe2\x0a\xac[\xb7\x0e\x8d\x8d\x8d\xa2}\x98\xd4\xf3\xaf\x5c\x0b8M\xc6\x06\x83A\x94\xc0755a\xef\xde\xbd\xc2\xe3\x89Z\xdb\x19\x8dF!\x0a\xe9\xef&s[\xaap\xa4\xe3\xef\xc3\x87\x0f\xa3\xae\xae\x0e~\xbf_\xb4\x16\x9cr\x14p\x86\xa0\xe2\x8dD\x22\x81P(\x84\x81\x81\x01\xacZ\xb5j\xcc\x0d\x9eN\xa7\xf1\xf2\xcb/chh\x08###\x22\xf2K\xc7\xc7\xd2\xe3\xa2u\xeb\xd6\x89]\xa2\xd9l.HR?Q\x91Wj\x93\xc4\x82n\xeeE\x12\xd7\xae]+:e\xbc\xf5\xd6[\x93\x9e\xdb\xcbB\x8d\x19\xb8e-\xbd \xa4GF\xd0\xb7#\x8bx\x22\x01\xcb\xcaf\x98\xab\xdf\x8f\x92\xc8e\x80\xe4\xf8q\xb2\x0b'\xd9`\xd1}e\xb7\xdb\xe1\xf5z\xa1V\xab'\xb4^1\x18\x0c0\x1a\x8d\x18\x1c\x1c\x14\xe3\x9e\x12\xf8\x09\xca\xd1\xa2H\xd4\xc0\xc0\x80\xf8ZUU\xd5q\xcdA4\xe7\x1a\x0c\x86\x82\x96k\x14\xed\xa3\xaf\xc9d2a\x15&\x15\x80\x14\xf9\x9a*\x1e\x8f\xa7\xc0\xc6\xc4f\xb3\x89\x82\x22\x99L\x06\xbd^\x0f\xab\xd5\x0a\xbb\xdd\x8e\xfe\xfe~\xe8t:8\x9dND\x22\x91\x82\xf9\xedx\x03+\x1e\x8f\x07\x0e\x87\xe3\x98\xf3\x8bB\xa1\xc0\x82\x05\x0b\x84\xc5\x11\xcfG\xc7\xb8\x07\xa6S\xa0LdGPj'\x90\xcb\xe5D\xb5\x93B\xa1\x10\xc9\xfc4\xf0\xa5\x09\xeat|\x5c*\x17\xe3d\x89\xb2\x99BZ>/\x15qd\x15@\xd1=iO`\x8a\x1eJm]J\x09?\xfa\xbc\xcdf\xc3\x87>\xf4!,^\xbc\x18\x0b\x16,(0|\xa6\x1dd\xb9o.2R\xb5\xd9l\xf0z\xbd\xa8\xa9\xa9\xc1\xe2\xc5\x8b\xf1\xf2\xcb/\x8b\x1cQJ\x98\xd5\xeb\xf5\x22e@\xab\xd5\x22\x99LB\xa5R\x89\xf4\x00\xaa:;p\xe0\x00\x9a\x9b\x9b\xd1\xdb\xdb+\xfa\x15Wb{8\xba\x06\xc9d\x12\x91H\x04CCCH$\x12X\xb3fM\x81\xb0\x92\xc9d\xd8\xbf\x7f?\xda\xdb\xdb\x11\x08\x04D\x82xq\xbf_\x00\xb0Z\xadX\xb5j\x15\x9cNgEF\xff\x98\xd9!\x00\xd7\xacY#\x16W\xdaLN5o\xf3\xb87\x07y\x14\x1c\xed\xcae\xef\x07\x19\xb2\xef\x15\x1fJ\xbf6U\xc3a\x12\x80/\xbf\xfc\xb28\xed8\xed\xb4\xd3\x0a\x1c(J\xfd\x1dF\xa3\x11\x8f\xff\xf6ql\xdf\xb6\x1d+V\xac@ss3\x1a\x1a\x1aD\xb4\xab\xb8\x15&\xbdw\x03\x03\x03\xe25R$k\xaa\x11@i\xe4Q\xfa\x9e\xfa|>\x04\x02\x01q2F\x22\xd3h4\x0a[\x1b\x83\xc1P\xb6\xb1Q\xaa;\x13\x00\xb4\xb4\xb4 \x99L\x8a<\xbbR\x1e\x80S\x9d7hS?U\xfd\xc1\xe2\xef$\x09\xc0\xa9F\xd3\xc8\xe8\x90\x84\x0a\x09\x1c\x8aZ\x91\xe8!\xf1\x22\xcdq:\x9e\xc5l\xb6-\x5c\xd2\x1b^\xba\xd0\x17w\x06\xa1\xef\xa1(\x90T\xec\x15G\xfa\x8a\xf3Q\xa8\xfd\x8fV\xab\xc5\xda\xb5kq\xce9\xe7\xa0\xa1\xa1\x01\xb5\xb5\xb5\xf0\xf9|\xb0\xd9l\xa2sD\xa9\xe3\x8frE\x01U*\x15t:\x9d\xc8\x05\x5c\xb0`\x01\xf6\xef\xdf\x8f\xa1\xa1!\xf1\x1a\xa9j\x9c*\xces\xb9\x9c8\xfaU\xab\xd5\x05\xbd(\xfb\xfa\xfap\xf0\xe0A\xd4\xd4\xd4\xc0\xe7\xf3\x15\xb4\xac\xab$\x11H\x1b\x1a\xca\xe1\xff\x22\xf6\xb4\xee\xc1\xeb\xaf\xbf\x8e\x05\x0b\x16\xe0G?\xfa\xd1\x98(`\xf1<\x1b\x08\x04\xc4\xef^\xb0`AAq\xcdT#\x80\xc5\xe2\x91\x0a\xf2\xe8$\x05x/wo\xcd\x9a5\xd8\xb9s'\xacV+\x1a\x1b\x1b\xcb\xb2\x0e\x01\xff\xec\xe0\x05@x3\x02\xef\xe5\xfd\x9ds\xce9\x00PP\x10R<&\xa6{\xdc\xb2\xf8\xab \x01x\xac\x9b\x92\xbaM\xd0\xc7\x94\x04O\xc7\x95\xc59j\xf3I\xdd\x97\xaa\xb4,\xf5\xb5R\x1f\x97\x12\xe1\xa5\xaa\xd6H|\xacX\xb1\x02\x1b6l@SS\x13jkk\xe1\xf5zE\xc9\xbaN\xa7\x13\xc7\x87\xd3\xf9\xdeS. u\x07\xa9\xa9\xa9Acc\xa3HrN&\x93B\x80\xd2$-\xf5\xb2\xa3\x9e\xa5\x1a\x8dF\x08\xc3\xdd\xbbw\xa3\xb9\xb9\x19\xfd\xfd\xfd\x22\x8aYi\xb9\x80T\xedL&\xcdj\xb5\x1a+W\xae\x1c\xb3h\xbe\xf3\xce;x\xfb\xed\xb7E\xcf_\xa9\xf1\xb3\xf4\xda\xba\x5c.\xb4\xb4\xb4\x08\xf1\xae\xd7\xebO\xb8Z{*\x0b\x043w\xe6\x1f\xb3\xd9\x8c\xfa\xfaz\x1c9r\x042\x99\x0c\x7f\xf8\xc3\x1f\xb0y\xf3\xe6I\xfd,\xdd\x9fO<\xf1\x04>\xfc\xe1\x0f\x9f\xf0\xdc=\x9e\x00:\x9e\x0d\x88F\xa3\xc1\x8b/\xbe(\xd6\x1e\xb7\xdb-\x92\xe6\xc7\x13\x11r\xb9\x1c\xfd\xfd\xfd\xe8\xe9\xed\x11Q\xc3\xb3\xce:K8ZH+\xe4\xa5~\xb8\xc3\xc3\xc3\x05\x82h\xd1\xa2E\xa2\xe5\x1a\xcd{\xe4\x91;\xd9\x08`1\xc5\x95\xa5\xc0{\xa7\x00\xeb\xd6\xad+{\xb1\xa4\xd4\xdaG\xa5R\x8d\xe9\xb7~\xac*j\xde,\xceq\x01(\xbd\xd0\xe3%G\x92\xc5\x04E\xfeb\xb1\x98\xc8c\xa3\xe8\x9f4\xf27\xdf\x0c\xa1K\xd9s\x90`\x9b(\x97b\xa2\xf7\xa9\xf8\x98D\xadV\xa3\xa1\xa1\x01\xe7\x9cs\x0e\x1a\x1b\x1b\xe1t:\xe1\xf3\xf9\xe0r\xb9`\xb7\xdb\xa1\xd7\xebE\x83\xed\x99h\xdbE\xbbh\x83\xc1\x00\xb7\xdb]\x10\x05\xa4\x8a6\x8a\x02R\xc4O.\x97\x17t\xa6\xa0\xb6p\xf4\xbd###\xd8\xbf\x7f\xbf\xa8\x086\x9b\xcd\x22\x87\xb1\x12D \x8dq\x8a\xfeuwwc\xed\xda\xb5b\xf1\xa0\xeb\x18\x8b\xc5\xb0s\xe7ND\x22\x911\x95\xbf\xc5cb\xf1\xe2\xc5\xa8\xad\xadEUU\x15,\x16\xcb\xb4\x19a3s\x1b\xda|\x5cv\xd9e\xb8\xef\xbe\xfb\xa0T*\xf1\xc6\x1bo\x1cS\xf0\xd3\xa6\xf2\xe7?\xff9\xbe\xf4\xa5/!\x9f\xcfc\xfd\xfa\xf5\xa2\xe2\xf4D^\xcfD\xfd\xc6\xa7\xb2.\xf4\xf4\xf4`\xfb\xf6\xed\xe2\xff\xeb\xd7\xaf/\xa8\xf8,57(\x95J\xec\xda\xb5K\xa4]\x00\xc0\xd2\xa5K\x0b\xc4Y\xa9\xea\xe0\x9e\x9e\x9e\xf7\x0c\xb4\xdf\x7f}\xabV\xad\x12\x96id\xca\xadR\xa9\x84?\xdbd\x04\xf0T\xafa9Q\xa9T0\x99L\x18\x1e\x1e\x86V\xab-Y=<\xde\x5cW\xaa\xb9\x03o\x1e\xe7\xa8\x00\x9c\xe8\xe6\xa4.!T\xb0@\xc9\xec\xa9TJ,\xf2\xc5\x1d\x0d\xe6\xd3\xce\xa1\xd8\x5c\xb3\xd4\xc7\xe3\x09;i\x84Oz\xf3\xd1\xee\xcd\xe5r\xa1\xb1\xb1\x11+V\xac@CC\x03\xecv;\x9cN'\xdcn\xb78\xee\xd5\xe9t\xc2\x98r&#<\xd4#\xd8b\xb1\xa0\xba\xba\x1a\x0d\x0d\x0dhhh\xc0\xbb\xef\xbe+\xf2\xfb\xd2\xe94t:\x9d8\xf2\x966\xe8V\xa9T\xe2\x98\x98\xc4\xe1\xfe\xfd\xfb\xb1h\xd1\x22\xf8\xfd~8\x1c\x0e\x98\xcd\xe6\x8a\xc9\x05\xa4<\xcdp8\xfc^{\x1e\xa5\x12---cr}\xde}\xf7]\x1c:t\x08}}}\xa2\xf2\x97\x22\xe4\xd2\xcd\x82\xd1h\xc4\x9a5k\xe0\xf5zQUUU\xf6\xdc?\xde\xcd\xcf\x1fh\xc3y\xc9%\x97\xe0\xbe\xfb\xeeC6\x9b\xc5\xf0\xf00^y\xe5\x15\x9c}\xf6\xd9\x13.\xf4\x1d\x1d\x1d\xb8\xe5\x96[\xc4\xbdy\xd7]w\xe1\x81\x07\x1e(\xcb\x9c(\x9d\xcf\x8a=\xe6\x8e5O\xd1\x91\xf43\xcf<#\x04\x97Z\xad\xc6y\xe7\x9dW \xe4J=\x8fL&\xc3[o\xbd%:c\xd8\xedv\xd1\xe3\x976\x94\xc5\xf7\x99\x5c.Goo\xaf\x10\x80J\xa5\x12\xd5\xd5\xd50\x9b\xcd\xe8\xe8\xe8\x80B\xa1\x10\xc5\x19\xc1`\xf0\x98bn\xaa\x86\xdf\xd3\x91\xab\xadT*\xd1\xdc\xdc\x8c\xe1\xe1aaf<\x99y\x82\xe7\x8ay*\x00\x8b/<\xe5\xb2\xa5\xd3i\x11\x05\x0c\x87\xc3\xa2B\xb5T\xb1\xc2|c\xb2;\x5c\xe9Q\x83t\xd2\x96\xe6\x0b\xaa\xd5j\xd4\xd7\xd7\xa3\xa9\xa9I\xf4\x0f\xa4\xbe\xcbv\xbb\x1dV\xabU\x1c\x15R\x0b\x9b\x89&\xc2\xe9^t\xc8\xcb\xca\xe3\xf1\xc0\xef\xf7c\xc9\x92%8p\xe0\x80\xb0\x06J\xa7\xd3\x88\xc5b\xd0j\xb5\x05\x93\x0fM\xe6\xd9lV\xfcK\x15\xc1\xed\xed\xed\x22\xa7\x91\xda\xc3\x9d\xec\x5c\xc0\xe2\xe8\xdf\xe0\xe0 \x9a\x9a\x9a\xc6x]\xa5R)\xbc\xf3\xce;\xa2Y{<\x1eG\x22\x91(\xc8\xfb\xa3\xeb\xb4|\xf9r\xd4\xd5\xd5\xc1\xe9t\xc2b\xb1@\xab\xd5\x96=\xfa'mIW\xca\xfb\x8c\x99;\x9b\xd0|>\x8f\xe5\xcb\x97\xc3\xeb\xf5\xa2\xaf\xaf\x0f\xb1X\x0cO?\xfd\xf4\x84\x02P.\x97\xa3\xa9\xa9\x09_\xf8\xc2\x17p\xef\xbd\xf7\x02\x00\x1e|\xf0A|\xeaS\x9f\xc2\xca\x95+\xcb2\x1eK\x1d3N6\xbf\xec\xd0\xa1C\xf8\xe5/\x7f)\x22j\xa7\x9f~:\x96/_\x8e\xc1\xc1A1\xef\x15\xcf\x0b\xf9|\x1e\x81@\x00;w\xee\x14\x9f[\xbat)<\x1e\x8f(H,NI!\xa1\xd6\xd3\xd3#^oMM\x0d\xdcn\xb7\xe88\x22\x97\xcb\xe1v\xbb\x91\xcdf'\xf4Y,\xd5\xa5\xe7d\xe2\xf1x\xe0\xf1x\xf8&a\x018y!C\xc2$\x91H\x88\x9eyT\xf9H\xbet\xd2\x82\x06fr\x93\xb4T\xa8)\x14\x0a\xd1\x9e\xa6\xaa\xaa\x0a---\xc2\x10\x93|\xa3l6\x9b\xf8\xbf\xc9d\x12\xa2\x8f\xec\x0fNv\x22-\x19;\x9b\xcdfTWW\xc3\xe7\xf3a\xe1\xc2\x85x\xe7\x9dw\x0a\x8e\x81\xa5cJj\x05Cm\x8f\xe8H&\x16\x8b\xa1\xbd\xbd\x1dMMM\xf0\xfb\xfd\x222v\xb2\xa3\x80\xb4\x09\xa2c\xddH$\x823\xce8\xa3\xe0~\x91\xc9dhmmE__\x1fz{{\x11\x0e\x87E\xf4\x5c\xbaQ\xa2\x8a\xf8\x0d\x1b6\xc0\xe1p\x88#\xfcJ\xf6>df\xc7\xfcRSS\x83\xb3\xcf>\x1b\xbf\xfb\xdd\xef\x00\x00/\xbc\xf0\x02\xee\xb8\xe3\x0e\x98\xcd\xe6\x09{\x80\xdfv\xdbmx\xec\xb1\xc7022\x82l6\x8b\x0f\x7f\xf8\xc38p\xe0@A\xa1\xd6\x89\xdc;\xe3E\xc2&\xaa\x08V(\x14\xb8\xe3\x8e;DG\xa1l6\x8b/\x7f\xf9\xcb\x18\x1d\x1d-\x88\xb2\x95\xca1\x1c\x18\x18\xc0\xe1\xc3\x87\xc5\xd7\xd7\xae]+r\x01\xa5U\xc0\xd2S\x9aT*\x85\xce\xceN\xf1<>\x9f\x0fn\xb7[XY\xa9\xd5j\x98\xcdf\xe1i:\x91\x88\xe5\xfb\x98\xa9x\x01(5\xb4\xa5\x85J\x0ay\xfeQ'\x0aJd\xa7\x9c\xbfb\x8cF#\xf4z\xbdh\xc6,\xbd\xc1\xe8f\x9f1_\xaacL\x94\xd3\xf5\x9c\xc5\xff\xd2b\xafV\xaba\xb1XD\x95'\xb5\xca!\xaf>\x12vz\xbd\x1eF\xa3\x11\x16\x8bE\x1c\xef\xd2\xd7(\xff\xa4\x12\x84\x9ft\xa2#\xbfG\xb7\xdb-\x8c\xa1)\x0aH\xa6\xd7\xe4AE\x16\x15\xf4\xbeP$\x90\x0a$\xb4Z-:;;\xd1\xd5\xd5\x85\xbe\xbe>\xd4\xd6\xd6\xc2j\xb5\x8a\xca\xd8\x93\xf17\xd315\xf9\xfeuuua\xf1\xe2\xc5\xa2I;]\x8bl6\x8b\xed\xdb\xb7\xa3\xb3\xb3S$\x93K;\xb7H\xef\x81s\xcf=\x17UUU\xa2\xf2w:\xa2\x7f\xcc\xfc\xe4\x8a+\xae\xc0\x13O<\x01\x00\xd8\xbd{7\xb6n\xdd\x8a\x8b.\xbah\x5c\x11\x96\xcf\xe7Q__\x8fo}\xeb[\xb8\xe9\xa6\x9b\xa0\xd5jq\xe4\xc8\x11|\xf0\x83\x1f\xc4\xb3\xcf>{B\x9bC\x12n\xa5N\x96(\xa7\x8e\x9c$\xa4si2\x99\xc47\xbf\xf9MaO\x92\xcb\xe5\xf0\x95\xaf|\x05Z\xad\x16\x89DB<\x7f\xb1\xd8\xcaf\xb3\xd0\xe9tB\x00+\x14\x0a\xe8t:\x9cw\xdeyH&\x93\xef}\xbfB>&\x07\x90\xd6\xc1\xbd{\xf7\x8a\xcf577\x0b\xf3\xfaK/\xbd\x14\x89d\x02&\xa3\x09\xd1h\x14\x0a\x85\x02\xd9lv\xdc\xf9h.\x09\xc0\xf9\x96\xd3?g\x05`\xb1MI>\x9f\x177\x98\xd4z\x82nX\xda\x9d\xd1\xf1/U\xfe\x16[\x95\xb8\x5c.\xac\x5c\xb9\x12\xd5\xd5\xd5\x22\x99]\x9a\xa0+\xad\x08\x9e\xe8\xa6\x99\xe9\x08\xdct\xdc$RGu\x12\xc1\xd4\x06\x8d\x1eT\x8dE\xff\xeat:!\x02\xe9c\xfa:y\xe6I\x05S\xa5\xa1P(\xa0\xd7\xeba\xb3\xd9\xe0\xf3\xf9\xe0\xf3\xf9\xd0\xd2\xd2\x82m\xdb\xb6\x89\xd7Lm|T*\x95h)(\xad\x10V\xa9T\x05\x1b\x8b={\xf6\x08c\xe8\xaa\xaa*\xe8t:\xd1\xc7r\xa6\xa1\x08\x1e\xf9\xfe\x85B!\x9cw\xdeyc*\x7f[[[\xd1\xd9\xd9\x89\xbe\xbe>D\xa3Q\x912Q\x5c\x1ce4\x1a\xb1b\xc5\x0ax\xbd^\xd1\xaaO\xa3\xd1L\xeb}q\xacj?fv,\xc4\x93\x19#\x1f\xfd\xe8G\xf1\xa5/}I\xb4g\xdc\xb2e\x0b:::D\xd5}\xa9y1\x93\xc9\xe03\x9f\xf9\x0c\xb6n\xdd\x8a\x87\x1f~\x18j\xb5\x1a\xcf=\xf7\x1cn\xb8\xe1\x06\xfc\xeaW\xbf\x9a\xb25\x8cT`\x8d'\x1ah\xdd\xc9\xe7\xf3H&\x93\x05\x7f[$\x12AGG\x87\xc8\x0d>\xf7\xdcs\xb1a\xc3\x06qZ \x15\x80\xd2M\xb7L&CGG\x07\xb6o\xdf.\xe6\x94k\xae\xb9\x06N\xa7S\xf4\x11V\xc8\x15\xc2\xa7P\x1a\x08\x19\x1a\x1aBww\xb7\xd8\x88m\xda\xb4I\xbc\x1e\x9a\x8b)7\xf0X\xf3\xf7\xa2\xfd=\x00\x00 \x00IDAT\xf0\x5c\x12J\x5c\xf81\xc7\x22\x80\xd2\x1085D\xa6\xddQ\xf1\xe0%\xdb\x8b\xe2\xfe\xa5\xf4=K\x96,\xc1Yg\x9d\x85\xfa\xfazQ\x98@\xc7\x93\xf31\x0c^\x9coE\xb9&\xb4\xd3%\x11#\x8d\xe6\xd1C*\x14\xe9{\xe89*\xf9&$qj4\x1a\xe1r\xb9\xd0\xd0\xd0\x80\xce\xceN\x1c|\x18g\x9ey\xe6\x98\xdc\xa3d2\x89\xdd\xbbw\xe3\xc0\x81\x03\x88F\xa3B\x00\x16wq\x01\x80SN9\x05>\x9f\x0f\x1e\x8f\x07V\xabU\xb4\xeac\x98\x13]\x88I$~\xe3\x1b\xdf\xc0\x96-[\xa0\xd1h\xd0\xd9\xd9\x89\xff\xfe\xef\xff\xc6\x95W^9\xae\x88\xa4{\xf3\xfe\xfb\xef\xc7\xae]\xbb\xf0\xf6\xdbo\x03\x00\x1e~\xf8a\xc8\xe5r\xfc\xe2\x17\xbf\x98\xb2\x08\x9c(\x02H\x7f\x8f^\xafG<\x1e\x1f\x93\x9f\xeap8\xf0\xf3\x9f\xff\x1c\xb7\xddv\x1b\xb4Z->\xfb\xd9\xcf\x8a\xcaT\xe9\xf3K\xdf\x97\x5c.\x07\x9b\xcd\x86;\xef\xbc\xb3`\xa3\xff\xe3\x1f\xff\x18\x7f\xfb\xdb\xdf\x0aOgd\x85\x01\x11\xb5Z\x8d\xd7^{M<\xaf\x5c.\xc7\xe5\x97_>\xee|\xa7\xd1hD$r\xbc\xf5u\xael:\xb8(d\x8e\x08\xc0R\x17\xd1n\xb7\x0b\x13g\xe9\xf7Q\x7fZi\xd4\xaf8\xfaWWW\x87\xf3\xcf?\x1f\x8b\x16-B}}=<\x1e\x0f\xf4z\xbd\x88fHo\x84\xc9V~\xcd\x85\x1bF*\x02\xa5\x0f\x12\x83R\xab\x96b\xaf<\xcaw\x99m\x93\x085.\xb7Z\xad\xf0x<\xa8\xaf\xaf\x87\xdf\xefG0\x18\x14\xd5\xb3\x14\xfd\x94&]K{\x03\x03\x10f\xc9\x0a\x85\x02\xbbv\xed\x12\xc50\xe4\x96?\xd3Q@\xca\xfd\x0b\x87\xc3\x18\x1a\x1aB.\x97\xc3\xb2e\xcb\x0a\x22\xe42\x99\x0c\x87\x0f\x1f\xc6\x8e\x1d;\x10\x0c\x06122\x22<3\xa9\xdf&a4\x1a\xb1t\xe9R\x11\xfd#\xdf?6Ee&\x8a\xf8\xb5\xb7\xb7\xe3\xd0\xa1C8\xf7\xdcs'\xec\xeeC\xc7\xa97\xddt\x13\xbe\xfa\xd5\xafbtt\x14j\xb5\x1a[\xb6l\xc1\x95W^9\xe1\x18\xa3\xcd\xe7\xd3O?\x8d\x0d\x1b6\xe0\xd0\xa1CP\xab\xd5x\xe8\xa1\x87p\xf0\xe0A<\xfe\xf8\xe3\xf0z\xbd\x93\x9a\xcb\xa5\x1b\xa4\x89\xc4\x03m\xfe\x8a\x85i.\x97\x83\xc1`\xc0\xb6m\xdb\xd0\xd6\xd6\x86D\x22!\xee%:U\xa2y\xc0j\xb3bxx\x18.\x97\x0b\xaf\xbe\xfa*v\xef\xde-:\x12\xdd~\xfb\xed\xc2\xfa\xa4\xb8\x08O\xda\xf7V\xa3\xd1\xe0/\x7f\xf9\x8b0\xd5^\xb7n\x9d(N+e\x17c2\x99D.\xa2\xc1`\x80\x5c.\x17=\x84I 2LEF\x00\xa5\x13\x0cE(\x8aof:\x9e\x8cD\x22\x05v/4\xc0\x95J%\xce>\xfbl466\x0a\xdb\x0e\xab\xd5Z\x90\xcb4\xdfv\x0d\xc5\x13\xccx\xbbA\xe9\xce\xb5\x94Y\xeal\x14\x024&\xccf\xb3\xe8\x0e\xb2h\xd1\x22\x1c\x9f\x18\x1fmmmH\xa7\xd3\xa2\x18\x86\xf2\xff\xc8\x08\x9a\xc6\x1e\x1d}\xd3\x04\x1a\x8f\xc7\xd1\xde\xde\x8e\xc6\xc6Faz\xad\xd7\xeb\xcbR\x998\x19\xa4\xbe\x7f###\x88\xc5b8\xe5\x94S\xc6\x5c\xdf\x9e\x9e\x1e\xbc\xf9\xe6\x9b\x08\x85B\x08\x85B\xc2$]\x9a\xfbJ\x1ekg\x9f}6\x1c\x0e\x87\xb8_\xa6\xbb\xeb\x874RY\x09\x05X\xcc\xd4\xb0X,x\xec\xb1\xc7\x84\xe0{\xe0\x81\x07p\xf7\xddwOx\x1cK\xd7\xfb\xd2K/\xc5\x05\x17\x5c\x80\xff\xfb\xbf\xff\x03\x00\xdc{\xef\xbd\xb8\xfe\xfa\xeb\xb1l\xd9\xb2\x09E[>\x9f\x87\xcf\xe7\xc3\xce\x9d;q\xd9e\x97\xe1\xe5\x97_\x86\x5c.\xc7\xd0\xd0\x10>\xfd\xe9O\xe3\x87?\xfc!\x1e~\xf8a\xac]\xbb\xb6 \x80P<\x8e\xa5\x11\xb6\xf1\x8e\x80K\xad\x17t2@\x1e\xa0\xa5\x02\x14\x14\x01\xa4\x8a\x5c\xbb\xdd\x8e\xfb\xef\xbf\x1f;v\xec\x10\xdf\xf3\xaf\xff\xfa\xafp\xbb\xddc\xde'\xe9\x06<\x9f\xcf\xc3b\xb1\xe0\xbf\xfe\xeb\xbf\x0a:|\x5c{\xed\xb5\x22-\xa5\xd4f\xd7b\xb1\x88cs\xa7\xd3YpoI\xd3w\xe6\x0a\xbcy<\xc9\x01\x96\x99\xbe\xd8T\xf9H\x85\x1b\xc5a\xffE\x8b\x16\xc1l6\xc3j\xb5\x8aH\x86\xb4R\x93\x1f\xf3\xefHO\x1a\x05\xa4N%\xcb\x96-\x13\x13<\xd9\x0aIm\x84h\xb2\x97\x16\xc5\x90(R\xab\xd5\xa2\x22\xb8\xb7\xb7\x17\xc3\xc3\xc3\xa2\x0b\xcdtOH\x94\xea\x90H$D\xdb\xb7\xea\xeajX\xad\xd61\x11\xb5m\xdb\xb6\xa1\xb7\xb7W\x1c\xfd\xc6b1Q8\x22\x15\x93k\xd6\xac\x11\xb9\x7f\x16\x8b\x05:\x9dnZ+\x9b+\xa5\xea\x9e9\xfe\x0d\x88\xdf\xef\xc7\xc6\x8d\x1b\x91N\xa7\xa1R\xa9\xf0\xbd\xef}\xef\x98Q5\xba\x17\xd5j5\xfe\xe3?\xfeC\x88,*\x88\xa0\xfb\xefX\x9bV\xadV\x8b\x97^z\x09\x9f\xfb\xdc\xe7\x0a*n\x0f\x1c8\x80\xd3N;\x0d\xcb\x97/\xc7\x1f\xff\xf8G\xf4\xf4\xf4\x8c\xc9\x87S*\x95\xa2\x8bOq\x0el\xf1\xeb,\xfe\xdd\xe4*`\xb3\xd9D\xbe\x1ed\xe3\xdf\xa72\x99\x0c;w\xee\xc4-\xb7\xdc\x22\xee\xa7\xea\xeaj\x5c~\xf9\xe5\x13\xfe\x9d\xb4\xd9\xe3LD\xffJEP\x98\xd9\xb1\xa1\xca\xe7\xf3\xf8\xc1\x0f~P\x10Q{\xf0\xc1\x07E\xca\xc4\xb1\xae\xff\xd9g\x9f\x8dO\x7f\xfa\xd3\xc2\x7fs\xef\xde\xbd\xf8\xc4'>!*\xf1\x8fu/g\xb3Y\xfc\xf4\xa7?\xc53\xcf<\x83u\xeb\xd6\x89\xa8\xb8N\xa7\xc3\xee\xdd\xbb\xb1y\xf3f477\xe3\xd2K/\xc5-\xb7\xdc\x82{\xef\xbd\x17\xbf\xfd\xedo\xf1\xdcs\xcf\xe1\xa5\x97^\xc2\xc8\xc8\xc81\xffF\xe9\xd8\xac\xad\xad\xc5\xc6\x8d\x1bq\xd6Yg\xc1\xe1p\xfcs3\x8d\xd2\xfd\x84\x95J%\xc2\xe1\xb0\xc8\x8d\xa4\xf7\xe8\x96[n\x81\xc9d\x9a\x94H\xfe\xdb\xdf\xfe&6\x96\x00p\xc3\x0d7\x1c\xb3\xe0E\xa1P`\xd1\xa2EX\xb6l\x19\x00\x14\xa4\xa6\xd0I\xc7l\xe5D{73\x15.\x00'\xb3\x18H\x8f\x8dJ-\xb6dUB\xbbK\x1e$\x0cM\xaa\xd4\x7f\xd2\xe5r\xa1\xb6\xb6\x16\x0b\x16,\x80\xc9d\x12\xd5\xe4\xe4+Y||D\x132E\x12\xc9^f\xef\xde\xbd\xe8\xed\xedEww\xb7\xc8\xb1\x9b\xce(`q\xee\xdf\xe1\xc3\x87q\xca)\xa7\x88\x02\x1d\xe9&\xe9\xed\xb7\xdfF[[\x1b\x82\xc1`A\xee\x9f\xf4\xbe\xc9\xe7\xf3\x22\xfa\xe7\xf5za\xb5ZO\xda\xa6\x89\xf3\x01g\xd7B\x9cN\xa7\xb1z\xf5j,\x5c\xb8\x10\x89D\x02J\xa5\x12\xdf\xf9\xcew&5\x8f\xd3\xe9\xcd\xfd\xf7\xdf\x8fe\xcb\x96!\x95JA\xa9T\xe2\xf1\xc7\x1f\xc7]w\xdd5)\x11I\x82\xea\xe2\x8b/\xc6_\xff\xfaW<\xf5\xd4Sp\xbb\xdd\x88\xc7\xe3P*\x95\xc2\xd4\xfd\xa5\x97^\xc2O\x7f\xfaS|\xf1\x8b_\xc4\xb5\xd7^\x8b\xcd\x9b7c\xcb\x96-hkk\x9b\xd0\xec\xb9\x18\xaa\x8a\xa7\x1c;\x00%\xc5\x1f\x09\xacd2\x89K.\xb9\x04###\xc2\xcc\xf9\xa6\x9bn\xc2\xca\x95+\x85\xdf\xe8D\x1c=z\x14/\xbf\xfc\xb2\xf8\xffE\x17]\x84\x85\x0b\x17Njc&\xcd\xdd\xa5>\xec\xf4\xda\xf4z\xfd\xac\x1dw<7\xccq\x01H\x15\xbd\xc7\xf2\xe5\xa3\x5c\xacR-\xa4\xa4\xed\xccJ5\xd6f\xe6\xb7\x08\x94\xe6\x02\xd6\xd5\xd5\xa1\xae\xaeN\x1c=I\x8bB\xa4\x91*z\xd01\xb0\xd4S\xf1\x9dw\xdeAOO\x0f\x06\x06\x06D\x7f\xdd\xe9\x8a\x02f\xb3Yd2\x19D\x22\x11\x11\xd9;\xed\xb4\xd3\x0a\xda\xaa\x01@WW\x17\xb6n\xdd*,b\x8a}\xff\x08\x9dNWP\xf9KQ\xf3\x99\xcc\x95e\xe17;!\x91v\xd7]w\x89\xb1\xd9\xd9\xd9\x89\xfb\xee\xbboR?O\xc6\xeb/\xbd\xf4R\x81\x0d\xd3\x9dw\xde\x89G\x1f}tR\xe2LZ\xa8u\xf9\xe5\x97\xa3\xbf\xbf\x1f\xcf?\xff\xf8\xa0\x98\x7f\xa4\xc7\xcb\xd2\x00\x85\xf4\xf7=\xf8\xe0\x83\xc2\xc7\xb4\xa1\xa1\x01\x9f\xf9\xccg\x8e\xeb:iuZ,jY\x84E\x8b\xdf\xb3D\x9b\x8dE \xc5\xc6\xd8\xcc\x1c\x15\x80\x93\xbd\xc0\xc5\xe2\xb08\x12\xc8\xdee\xccD\x22P\xab\xd5\xc2\xe1p\xa0\xa6\xa6\x06MMM\xa8\xae\xae\x16\xbez$\x04\xa9ZV\xba\x18\xa8\xd5j\xf1\xf3tL\x9aN\xa7\xd1\xda\xda\x8a\xae\xae.\x0c\x0e\x0e\x22\x1c\x0e\x1f3\x99\xfdx\x05 \xf5\xbc\x0e\x04\x02\x88F\xa3X\xbe|\xb9xm\xe4C\xb6\x7f\xff~\x1c8p\x00\x83\x83\x83\x08\x04\x02\xc2\xa7Lj\x94N\x0b\xcb\x19g\x9c\x01\xaf\xd7+r\xfff:\xfa\xc7\x85 \xb3_\x04\x9aL&l\xd9\xb2E\x88\xb0\xd6\xd6V<\xf5\xd4SSZ\xac/\xbc\xf0B<\xf2\xc8#B@\x01\xc0\xd5W_\x8d\x1f\xfd\xe8G\xc2~i2\xaf\x85\xbaw\xe4r98\x1c\x0e\x5c{\xed\xb5\xb8\xe7\x9e{\xf0\xe7?\xff\x19\x7f\xfd\xeb_\xf1\xdak\xaf\xe1\xb5\xd7^\xc33\xcf<\x83\xe6\xe6f\xa4\xd3\xe9I\x09\xc0\xe2\xc0\xc4x\xe2D&\x93\xe1\x8e;\xee\xc0}\xf7\xdd'N\xa9\xf4z=^\x7f\xfdux\xbd\xde1\xebSq\x852}|\xcf=\xf7\x08G\x81t:\x8d[o\xbd\xf5\xb87L2\xc8\xe0v\xbb\xd1\xd2\xd2\x02\x8b\xc52'\xc6\xdcd\x8d\xc7\x99Y&\x00'\xbb\xf8\x14W\xfe\x16G\x0a\xa7#\x02\xc3\xcc\x0d(\x11\x9ar\x01\xfd~?\x96/_.\xc4H,\x16\x13\xc7D\xa5\xa2\x80d\x8cM\x1fS\xc1\xc5\xe1\xc3\x87\xd1\xd5\xd5\x85\x91\x91\x91\x92\xfd\xab\xcb\x11\xfdK\xa5R\x18\x1d\x1d\xc5\xe0\xe0\xa08\xc6.\xe6\x9dw\xde\xc1\xf0\xf00\x06\x06\x06\x90L&\x0b\xbc2\xa5G_\x8d\x8d\x8dhnn\x86\xd3\xe9\x14\x95\xbf3\x19\xfdc\xe6\x0e\x9f\xfd\xecg\xe1\xf7\xfbE\xf5\xea\xf5\xd7_?\xa5{ \x97\xcb\xe1c\x1f\xfb\x18\xee\xb9\xe7\x1e\xc4\xe3qqzs\xdbm\xb7\xe1\x9b\xdf\xfc\xe6\x94\xac\x95J\xd9\xbe\x00\x80\xc9d\x82\xc7\xe3A]]\x1djkkE\x04p\xa2\xe78\x96\x07\xaa\xd4P:\x91H\xe0\xdf\xfe\xed\xdf\xb0u\xebV\xf17%\x12\x09\xec\xdf\xbf\x1fUUU\xc8d2c\x8c\xf8\xa5\xcfA\xddG\x8e\x1c9\x82{\xef\xbdW\xe40644\xe0\x92K.\x19\xf75\xcc\xb7\x0d\x07\xb5\x80e\xe6\xa0\x00\x9cj\xa8\xb7\x94\xad\x09G\xff\x98cA\x15\xc1v\xbb\x1d\xd5\xd5\xd5\x22\x12H\x96\x11\xa9T\x0a\xf1x\xbc\xa0\x070\x8d+*\x02!k!\xa5R\x89\xd1\xd1Q!\x00)\x0a(=J>Q\xa4\x95\xbf\xa1P\x08}}}\xd8\xb0a\xc3\x98{\xa1\xbd\xbd\x1d\x07\x0f\x1eDWW\x17\x82\xc1\xa0\xa8 \x94F\xff\xe8\xde\xa0j\xc6\xaa\xaa*q$\xc6>\x99\xcc\xf1\x8cM\x00\xf8\xc9O~\x22<5\xe5r9.\xb9\xe4\x12\xe1\x957\x99\x8d\x7f*\x95\xc2\xad\xb7\xde\x8a\x9f\xfe\xf4\xa7b\xbc*\x14\x0a|\xfd\xeb_\xc7\xd5W_\x8d\xde\xde\xde\xb2Dv\xa4y\xe1\x13\xdd\x9fr\xb9\x1cN\xa7SD\x09\xc7\xab@\xd5h4hmm\xc5\x96-[\xb0o\xdf>\xf1\xdcUUUhkk\x83\xd7\xeb\x1d\xb3NI\xc5\xa5\xf4y\xe5r9n\xb8\xe1\x06\x91\x8f\x0c\x00\x9f\xff\xfc\xe7\x11\x8f\xc7y\xa0\x15m\x18\x8aE!3\x07\x04`\xa9\x0b9\xd5s\x7f\xce\x13`&\xb3\xe0h4\x1aX,\x16x\xbd^\xd4\xd6\xd6b\xd1\xa2E\xc2\x0c\x9a,%\xa4\xc7\xc0R\xe3b\xaa2\xa7#aZ\x04\xba\xba\xba\xd0\xd9\xd9\x89@ \x80X,V\x96#N:\x9a\xa6\xe8_ww7\xbc^/\xbc^\xef\x98\xe7omm\x15\xbe\x84\xd1hTD\xff\xa4\x0b\x1d\xb5\x8c\xab\xaf\xaf\x87\xd7\xeb\x15\xb9\x7f\x95d\x974\xd5\xfb\x9d9\xb9Q\x99t:\x8d\x0f~\xf0\x83\xf8\xe8G?\x8aT*\x05\x85B\x81W_}\x15?\xfb\xd9\xcf&\xbd\xa9\xa0\x88\xdc\xe7>\xf79<\xf5\xd4Sb\xa3\xa6R\xa9\xf0\x87?\xfc\x01\xeb\xd7\xaf\xc7\xd0\xd0\xd0\x09\x8fQi\xca\xc4D\xbd\x80\x95J%\xea\xeb\xeb\xb1r\xe5J,]\xba\x146\x9bM|\x9d\x0c\xe35\x1a\x0d\x1ex\xe0\x01\xfc\xbf\xff\xf7\xff\x10\x0a\x85D\x8b\xb6\xfa\xfaz\xfc\xe3\x1f\xff\xc0\xc2\x85\x0b\x0b|\x0a\xa5\x05\x8a\xd2\x8fi\x1c?\xf4\xd0Cx\xe9\xa5\x97\xc4\xef\xd9\xb4i\x13\x1a\x1b\x1bgu\xe5n\xb9\x84^\xa9{\x9d\xd7\xfa9&\x00y\x82gfj\xd1\xa2\x8a\xe0\xaa\xaa*\xf8|>\xf8\xfd~444 \x95J\x15\xe4\x06\xa5R)1aK;\x04\xd0q\x17=\x12\x89\x04\xf6\xed\xdb'*\x82\xc3\xe1pY*\x82\xa9\xb0#\x16\x8baxx\x18\xbd\xbd\xbd8\xff\xfc\xf3\x0bz;\x03@OO\x0fv\xef\xde\x8d\xee\xeen\xf1\xbb\xa5\xa6\xcfR\xdf\xbfSN9\xa5 \xf7O\xab\xd5VD\xf4O\xfa^M\xf6\xbe\xe7h\xff\xc9\x87\xf2\xf4\xee\xbd\xf7^\xb8\xddnqDw\xe7\x9dw\xe2\xc8\x91#S\xda\x98\x01\xc0\xbf\xfc\xcb\xbf`\xcf\x9e=\xa2u\x1c\xf0\x9e\x0dJUUUY\xd6\x83\xc9\xf4\x02\xa6\xfb\xba\xb9\xb9\x19MMM\xa2\xea\x99ZF\xbe\xf2\xca+X\xb0`\x01\x9e|\xf2I1\x0e\xa9\x08\x85\x22\x7f\xc5\xbfS\x1a\x01\x94\x1e\xfd\xa6\xd3iD\xa3Q\xfc\xfb\xbf\xff\xbb\xf8\xba\xd3\xe9\xc4u\xd7]\x87\x5c.7'r\xf7\xca\xad\x07\x8a\xfdL\x999,\x00y\x92g\xca\x0d\xb5E2\x1a\x8dp\xbb\xdd\xa8\xaf\xafGCC\x03\xf4z=\xb2\xd9,\x92\xc9$\x92\xc9$r\xb9\x5c\x81Y4\x89@\x95JU\x109S\xa9ThkkCWW\x17\xfa\xfa\xfaDE\xf0\x89\x1c\x03\x93\xf8K&\x93\x08\x85B\xe8\xea\xeaBSS\x13\xecv\xfb\x98\x1c\xc5]\xbbv\xa1\xbd\xbd\x1d\xa3\xa3\xa3\x08\x87\xc3\x22\xff\xaf\xb8\xd2\xb1\xb1\xb1QTC\xda\xedv\xe1\x13V\xe9\x13~\xf1\xff)\xcf\xb7\x9c\xb9\x96\xcc\xf1\xa3T*QUU%z\xfa\xcad2\x0c\x0c\x0c\xe0\xfa\xeb\xaf/\xb0V\x9a\xec5onnF \x10\xc0\x86\x0d\x1b\xb0j\xd5*\xfc\xecg?C2\x99,\xcbZ0\x99\xcdN\xa9~\xc1\xf9|\x1e;w\xee\xc4\xe5\x97_\x8es\xce9\x07\xa3\xa3\xa3\x05\x91\xbc\x8f}\xeccx\xe4\x91GJ\xaeY\xb4i,6l\xa7t\x92\xdbo\xbf]\xf4\x1c\xcf\xe5r\xf8\xcew\xbe\x03\xaf\xd7\x0b\xbd^?\xa6\xc77\xc3\x81\xa2y#\x00\x19f: \xd1\xa6\xd7\xebEEpcc#\x5c.\x970L\x96V\xf3f\xb3\xd91\x91\x03\xa91\xb4L&C2\x99\xc4\x9e={\xd0\xd9\xd9\x89\xc1\xc1AD\x22\x91\x82<\xc2\xe3\x15\x80\x14\xfd\x0b\x87\xc38\xe5\x94S\xc6$\xba\x0f\x0c\x0c\xe0\xad\xb7\xde\xc2\xe0\xe0\xa0(B\xa1\xdf+\xcd\xfdS*\x95X\xb5j\x15jjj\xe0t:a0\x18\x0a|\x0dgj\x92&\x9fO\xfa\xdcd+1\xa5\xcf#=Zc*\x87\x8f}\xecc\xb8\xf4\xd2K\x85\x98y\xe5\x95Wp\xc3\x0d7L\xaa\xc3\x87\xf4\xba\xd3&\xeb\x85\x17^\xc0\xd6\xad[\x91\xcb\xe5\xa0\xd1h\xca\x22\x0a\xc6\x1b\xebTAL) 4\xbe\xe2\xf18\x1ey\xe4\x11\x5cq\xc5\x158\xf5\xd4S\xf1\xa7?\xfd\x09j\xb5Z\xe4\xd7.[\xb6\x0cw\xddu\x17\xae\xbb\xee:\xc4b1Q\x8d\x5c,\x00\x8b#\x80\xd4g\xfc\xdb\xdf\xfe6\xfa\xfa\xfa\xa0P(\x90N\xa7q\xc3\x0d7\xe0\x93\x9f\xfc$\x1a\x164\xe0\xb4\xd3N\x83V\xab\xe5\x81\xc5T\xe6\xc6\x8f\xdf\x02f\xd6\xee^\xde\xf7\xf5\xb3X,p\xbb\xdd\xa8\xa9\xa9\xc1\xa2E\x8b\xd0\xdb\xdb+\xaa\xf6\x92\xc9$\xf4z\xbd\x10-R?@Z\xa8(\x7f)\x93\xc9\xe0\xc8\x91#8|\xf80jkk\xe1r\xb9`2\x99\xc4\xf7L\x05\x8a8R\xf4\x8f*\x7fkjj\x0a\x16,\x99L\x86\xdd\xbbw\xa3\xa3\xa3\x03###\xe2\xf8\x97\x8e\xb1\xa5\xa2\xcb\xeb\xf5b\xc5\x8a\x15p\xb9\x5c\xb0Z\xad0\x18\x0c\xd3\xde\xf6\xedD\x16\xec\xf1\x16\xeab\x01\xc9T\x16\x7f\xfa\xd3\x9f\xe0\xf3\xf9088\x08\x85B\x81G\x1f}\x14\x8d\x8d\x8d\xf8\xc67\xbe1\xe5\x13\x9d\x135.\xa6\xfb\xa08\xe7N\xea\xfbIb/\x97\xcbA\xa9T\xc2`0\xc0b\xb1\xe0\x8d7\xde\xc0\xfd\xf7\xdf\x8f\xe7\x9f\x7f\x1e\x81@@\x8c5\x95J%\x8a4\xbe\xfc\xe5/\xe3\x8c3\xce\x80N\xa7\x13]H\xc6\x9bkJmZ\x1e{\xec1l\xdb\xb6M\xbc\xd63\xce8\x03\x0f<\xf0\x002\x99\x0cjkjy0\x1dC\xcc3'y\x0d\x9d\xc9\x8b\xcf\x83\x80)'\xd2(\xa0\xddn\x87\xdf\xefGcc#\xdcn\xb7\x88\x04\xe4r9Q\xd5+\xed\x17L\x22\x90\x1e\x9434::\x8a\xf6\xf6vtuuahh\xe8\xb8*\x82i\x9cSnP(\x14\xc2\xd0\xd0\x10V\xaf^=\xe6\xf5\xf7\xf7\xf7\xe3\xad\xb7\xdeB__\x1f\x82\xc1 \xb2\xd9\xac(^\xa1\xc8#5\xbe_\xb7n\x1d\x5c.\x17\x9cNg\xc5W\xfeR5i\xb1\x10\xa4\xbcF\x8e\xfcU.\xc9d\x12;v\xec\x10\xd7K&\x93\xe1[\xdf\xfa\x16\x9ex\xe2\x89\x19\xa9\xda\xa4\xe7\xdf\xb6m\x1b\xda\xda\xda\x10\x0a\x85\x10\x89D\x90H$\x84):U\xda\xc6\xe3qD\x22\x11\x8c\x8c\x8c\xe0\x9dw\xde\xc1SO=\x85\xaf}\xedk\xb0\xd9lX\xbf~=~\xf3\x9b\xdf`tt\xb4\xe0\xf9\xb5Z-n\xbe\xf9f\xf4\xf7\xf7\xe3\xa2\x8b.\x82B\xa1\x10=\xeaK\x8dY\xfa\xbf\xf4s*\x95\x0a\x7f\xfe\xf3\x9f\xf1\xa3\x1f\xfdH|\xde\xedv\xe3\xf1\xc7\x1f\x17Ef\x0cS\xe9\xcc\xd8(\xa5\x89\x84\xcdc\x99\xb2\xee`\xde\xaf\x08\xb6Z\xad\xf0z\xbd\xf0\xfb\xfdX\xb2d\x09zzz\xc4\x91\x0c\x1d\xf7\x90\x8b>E\x14\xb2\xd9\xac\x88\x06\x90\xe5E.\x97C{{;\x16-Z\x84\x9a\x9a\x1a\xb8\xddn\x98\xcd\xe61\x1d\x00hL\xd3sI\x05\x0dE\xee\x92\xc9$\xc2\xe10\x06\x06\x06\xa0\xd5j\xd1\xd4\xd44&\x82\xb2o\xdf>\xb4\xb5\xb5!\x12\x89 \x16\x8b\x89~\xc4\xd2$\xe9|>\x0f\xbf\xdf\x8f\xa5K\x97\x8a\xe8\x9f^\xaf\xaf\x88\xca\xdf\xf1\xaa0\xd5j5\x22\x91\xc8\x18\xa1G\x02\x90\xc49o\x0a+\x0f\x8dF\x03\x97\xcb\x85g\x9f}\x16\x17]t\x91\xe8\xdet\xf5\xd5W\xe3\xc9'\x9f\xc4\x87>\xf4!\xa4\xd3\xe9i\xebJ\x91\xcdf\xa1T*q\xcd5\xd7\xa0\xb3\xb3\x13\x00\xe0\xf1xPUU%r^GGGE\x8e\xef\xe8\xe8(\x8e\x1e=:\xe6o {\x1a\x8a\x10\xaeX\xb1\x02\x9b6m\xc2\xed\xb7\xdf\x0e\x87\xc3\x81\xe1\xe1a\xb1.QZ\xc2x\x1b\x13\x12\x80\xb9\x5c\x0ef\xb3\x19\x8f=\xf6\x18>\xfe\xf1\x8f\x8b{0\x95J\xe1\xe1\x87\x1fF}}=\x0f\xa0In\xde\x999.\x00yrgfb\x22!_@\x87\xc3\x01\x9f\xcf\x87\x86\x86\x06\xd4\xd6\xd6\xa2\xa3\xa3\x03\x0a\x85B\x08\xbcd2Y\xd0-\x83\x8eu(\xe7\x87\x04a(\x14B[[\x1b\xfc~?jjj`\xb5Z\xa1\xd5jE\x94\x90\xc6\xb64*X,\x00\xd3\xe94\xe2\xf18\x82\xc1 \x8e\x1c9\x82\xab\xaf\xbez\xcc\xc47<<\x8c\xad[\xb7bdd\x04\xc3\xc3\xc3\xa2\x0d]q\xee_>\x9f\xc7\xc6\x8d\x1b\xe1p8\xe0r\xb9`6\x9b\xc5\xeb\xa94\xb2\xd9,\xd4j5\xc2\xe1\xb0\xe8\x11K\x11L\x8a.\xa5\xd3\xe9\x13\xca\xaddf\x86\x0b/\xbc\x10w\xdf}7\xbe\xfa\xd5\xafB\xa3\xd1 \x99L\xe2\xaa\xab\xae\xc2\xa3\x8f>\x8a\x8f|\xe4#\xc8f\xb3\xd32\x06i\xdd\x18\x18\x18\x80\xd9lF4\x1aE\x7f\x7f?\xfa\xfb\xfb\xc7_\xc8\xde\xcf\xe5\xa5\xfb2\x99L\x8a\xcfo\xd9\xb2\x05\x9f\xf8\xc4'\xb0p\xe1B\xd8\xedv\xf1;hSW,\xf2&\x12,\x1a\x8d\x06\xbf\xf9\xcdo\xf0\xdd\xef~W|.\x95J\xe1\xf6\xdbo\xc7\x07>\xf0\x01.z<\x0em\xc0\xef\xd9\x1c\x13\x80\x5c\xe2\xcd\xcc$r\xb9\x1cj\xb5Z\xf8\x02RAHOO\x8f8*\xa2c^\xda\xf1\x17\xb7s\xa2\xa4u\x1a\xbf\xfb\xf6\xedCSS\x13:;;\x85\xd92\xe5\xdbI\xab\x00\x8b'/:\xbaM$\x12\x08\x06\x83\xe8\xee\xee\x86\xdb\xed\x86\xcf\xe7C.\x97+Xp:::p\xe8\xd0!\x04\x02\x01\xc4\xe3\xf11]?\xe8\xf9\x1a\x1a\x1aPWW\x87\xea\xea\xea\x93\xd2\xf3w\xbc\xfb\xbb\x18\x85B\x01\x93\xc9\x84\x91\x91\x11h4\x1ad2\x99\x82\xe35\xe0\xbdN-\xd4\xadE\x1a\x05\xe4\x05\xa0\xf2\xc8f\xb3\xf8\xcaW\xbe\x82X,\x86\xff\xfc\xcf\xff\x14\xd7\xe8\xba\xeb\xae\x83J\xa5\xc2\xe6\xcd\x9b\xa7\xe5\xf7\xaaT*Q\x80\xa5T*\x8f\xb9\x8e\xd0}H\xe3\xd2\xeb\xf5\xe2\x9ak\xae\xc1\x8d7\xde\x88%K\x96\x881{\xac\xce R\x93\xe9b\xb1\x22\x93\xc9`6\x9b\xf1\xf4\xd3O\xe3\xee\xbb\xef.\xd8\xe8}\xf1\x8b_\xc4E\x17]\xc4\x91\xad2\xcc!\xcc\x1c\x10\x80\x0c3\xd3\x02\x90\xfav\x92/`CC\x03\xf6\xed\xdb\x87\xbe\xbe>(\x95J\x11\x05\x94\xb6\x80#\xd1\xa2\xd1h\x84A4M\xe2\xa9T\x0a{\xf6\xec\x81\xcf\xe7\x83\xd3\xe9\x84\xd1h\x84Z\xad.\x10_$\x06\xa5\x93>\xf5!\x8eD\x22\x18\x1a\x1aB__\x1f\xae\xb8\xe2\x8a\x82D\xf6|>\x8fh4\x8a\xbf\xfc\xe5/\xa2\xf2\x97\x16<:V\x96.z\xcb\x97/GMM\x0d\xaa\xaa\xaaD\xf4o\xa6s\xe8\xc6\xeb\xa4 \xfd\x1aE[\x13\x89\x84\xe8\xb2\x22\xed\x05+\x93\xc9\xd0\xdd\xdd\x8dP($:\x9dX,\x16\x16\x80\x15\x0a\xf9c~\xe3\x1b\xdf\x80\x5c.\xc7\x9dw\xde)6P\x1f\xfe\xf0\x87\xf1\xc3\x1f\xfe\x10\xb7\xddv\xdb\x98\xd6\x9e\xe5\xc0h4\x22\x91H\xa0\xad\xad\x0dmmm8t\xe8\x10\xc2\xe10\xa2\xd1(\x86\x87\x87\xd1\xd9\xd9\x09\x9b\xcd\x86\xc5\x8b\x17\xc3\xe5ra\xc1\x82\x05\xa8\xaf\xaf\xc7\xc8\xc8\x08\x14\x0a\x05\x96.]\x0a\xa3\xd1\x88d2)\xa2\x83\xa5\xe6\x8d\x89\xc6x\xf1\xe7\x9f\x7f\xfey\xdc}\xf7\xdd\x22\xbf/\x93\xc9\xe0\x9e{\xee\xc1\xcd7\xdf\xcc\xe3\xf78`#\xe8y&\x00\xf9b3\xd36\x90\xdf\xaf\xd4\xb5X,\xa8\xae\xaeFCC\x03\x1a\x1b\x1bq\xf4\xe8Q\xd1\x98=\x93\xc9@\xaf\xd7\x8b\x89\x87\xf2\x00)\x82(\xb5\x83\x91\xc9d\xe8\xe9\xe9Akk+l6\x1b\x8cF#T*\x15\x9cN\xa78>\x92&\xc4S\x04\x22\x99L\x8a~\xbf]]]\xf0\xfb\xfd\xa8\xab\xab\x1b\x13M\xd8\xb9s'\xda\xda\xda\x84=\x0cY\xd6\x14\xe7\xc5\xb9\xddn\xac\x5c\xb9rL\x0e\xd4\xc9\x16\x80\xd2\x1d\x0c\xa3\xd1\x88\xcd\x9b7\xc3\xeb\xf5\x8eY$v\xef\xde\x8dW^y\x05\x07\x0e\x1c\xc0\xc8\xc8H\x81\xaf\x99\xb4\xf27\x97\xcb\xe1\x82\x0b.\x80\xddn\x87\xdb\xed\x86\xc5b\xa9\xa8\xe8_q\x15\x9f\xf4c\xb5Z-\x8cv\xe5r9\x0c\x06C\x81\xa7\xa1B\xa1\xc0\xe0\xe0 ^~\xf9e\xa4R)\xc4b1\xd4\xd5\xd5\x89\xc8\xeaLu6\x99/\xd0F&\x9f\xcfC\xa7\xd3\xc1h4\xc2h4\x8a\xb6\x89\x93\x8d\x04R\x84|\xe3\xc6\x8d\xd8\xb5k\x176m\xda\x84\xfd\xfb\xf7C\xab\xd5\x22\x9dN\xe3\xdak\xaf\xc5#\x8f<\x82\x9f\xfc\xe4'hjj\x12\xc2\xbf\xdc\x22\x89\xee\xdf1'J8~1A\xa6\xd2V\xab\x15o\xbf\xfd6n\xbd\xf5V\xbc\xf6\xdak\x05\x91\xeds\xce9GX\xbd\xb0\xf8cX\x00\x1ec\x91\xe0I\x9c\x99i\xe4r9\xb4Z\xad\xc8\x05\xf4\xfb\xfdX\xb4h\x11^}\xf5U$\x93I1iS^ y\x04\xd2\xcfR\x84M\xa3\xd1\xc0h4\xc2\xe7\xf3\xc1\xe3\xf1\xe0\x82\x0b.\xc0\x1bo\xbc\x81w\xdf}\x17\x89D\x02\xdd\xdd\xdd0\x18\x0cP(\x14H&\x93\x88F\xa3\xd0h48\xf3\xcc3\xb1|\xf9rq\x9c)]$R\xa9\x14v\xef\xde\x8d\xa3G\x8fb``@T\xfe\x16\x1f\xff\xe6\xf3y\xb8\xddn477\xa3\xba\xba\x1av\xbb\x1d:\x9dND\x1cO6\xd2\xfe\xc3\xc5]=\xe8\x08\x8e\x84\x9e4:H\xef\xb7\xc3\xe1@SS\x13\x94J%\xb6m\xdb\x86\xde\xde^,^\xbc\x18\x1e\x8fG\x1c\xad\xf3\xdcQ\x1e\xa1Dc0\x91H \x9b\xcd\xc2b\xb1\xc0\xe3\xf1\xa0\xba\xba\x1a2\x99\x0cz\xbd~JG\xee\xf4\xbd\x0b\x16,\xc0\xbb\xef\xbe\x8b\x9bn\xba\x09\xbf\xfe\xf5\xaf\xc5xx\xfe\xf9\xe7\xb1l\xd92\xdc|\xf3\xcd\xf8\xc1\x0f~ \x04h9\x8f\xf5e\xf2\x7f\x0a\xc0rT\x93\xe6r9\xe1\x12p\xe3\x8d7\xe2\xb7\xbf\xfd-\xe2\xf1\xb8x\x0fS\xa9\x14\xbe\xf0\x85/\xe0\xee\xbb\xef\x86V\xab-(\xe8b\x8e/0T\xfc13\xc7\x04`qd\x80afJ\x00R;\xa8\xaa\xaa*\xd4\xd4\xd4\xa0\xbe\xbe\x1emmm\x18\x1c\x1cD&\x93A\x22\x91\x00\xf0^Q\x02\xe5\x0dIs\x7f\x8cF\xa3\x10\x91\x16\x8b\x05MMMX\xb6l\x19\xd6\xacY\x03\x00\x18\x1c\x1cD2\x99DWW\x17\xd2\xe94\xccf3|>\x1f\x5c.\xd7\xb8\x13[6\x9b\xc5\x1f\xfe\xf0\x07\xbc\xfa\xea\xab8|\xf8\xb0\xb0\xa7!3\xdb\xe2\xe2\x8f\xb5k\xd7\xc2\xeb\xf5\xc2\xe5r\xc1b\xb1\x9c\x94\xca\xdf\xf1\x88\xc7\xe3\xa2x\xa6\xb8C\x8aF\xa3\x11\x06\xd0\x14\x0d\xccf\xb3\xd0\xe9t\xd0\xeb\xf50\x99L\xf0z\xbdhhh\xc0\xb9\xe7\x9e\x8b\xef}\xef{\xd8\xb3g\x0f\x8cF#\x02\x81\x00\x82\xc1 R\xa9\xd4\x94:\xaf0\x13\xcf\xbf===\xd8\xbbw/\x12\x89\x04t:\x1dZZZD\xd5<\x09\x9f\xe3\x19[r\xb9\x1c\x0f?\xfc06o\xde\x8cO}\xeaS\x18\x1c\x1c\x14\xe3\xff\x87?\xfc!~\xfc\xe3\x1f\xe3\x17\xbf\xf8\x05\xae\xbc\xf2JX,\x96\xf2\x09@\xc8D\xb1\xd0\x89\xe4\x95+\x95J(\x95J\xc4\xe3q\xbc\xf0\xc2\x0b\xf8\xf9\xcf\x7f.>O\xb8\x5c.\xfc\xfa\xd7\xbf\xc6\x05\x17\x5c \xc6$oN\xca?FY+\xb0\x00d\x98\xb2 \xcd\x05\xf4z\xbd\xa8\xaf\xafG}}\xbd\xa8\x08\xa6\x02\x8fT*U\x90\x0cO\x22\x8c\xaay\xa5\xf9m\xa9TJ\x1cu\x91\xd0\x93\xf6\xf5\xa5\x9f\x97V\x12RD,\x99L\xe2\xcd7\xdf\xc4\xae]\xbb088\x88H$\x82`0X\xb2\xf8\x03\x00\x9cN'\x16/^\x0c\xaf\xd7[q\xd1\xbf\xe2\x05Pz\x0cN\xef\x81\xb4\xad\x16u\x8c A\xa8\xd3\xe9\xa0\xd5jQ]]\x8d\xc6\xc6F\xa4\xd3iQ\xe5)\xad\x96f\xca\x0f\xb5s\xeb\xee\xee\x86\xcf\xe7\x83\xcdf\x83\xc1`8\xee\xe7#\x8b\x9fG\xa8\x91U\x00\x00\x1daIDAT\xcb/\xbf\x1c\xbbv\xed\xc2\xd7\xbe\xf65\xfc\xeaW\xbf\x12\xddwr\xb9\x1cn\xbc\xf1F<\xf7\xdcs\xf8\xfd\xef\x7f/\xa2\xee\xe5\x18\x7f\xc5\xde\x7f\x93]o\xa4'S\xad\xad\xadx\xec\xb1\xc7\xf0?\xff\xf3?\x18\x1a\x1a\x12v1d\x22}\xf3\xcd7\xe3\xfb\xdf\xff\xbe\xb8\xef\xa7\xab\xf3\xc9|\x15}\xac\x15* `2\x9dj\xbed\xa2.\xc3L\xf7\xa0~?\xbf\xcfl6\xc3\xe5r\x89c`\xb3\xd9,\xc4])\xcf=\x8aj%\x12\x09\xa4\xd3i\x84\xc3a\x0c\x0e\x0e\xe2\xc8\x91#hoo?\xe6\x22 5\x89\x96V\xc8\xbe\xf2\xca+x\xf6\xd9g\xd1\xde\xde\x8e\xfe\xfe~Q\xf8!\xb5O\xa1\x85I.\x97c\xc9\x92%\xa8\xab\xab\x83\xc7\xe3)\x88\xfeUZ\xe4\x81Z\xbeI\x0bSH\xe4Q!\x00\xe5VJ;\x81\x00\xefuA\x19\x1a\x1a\x82B\xa1\x18\x131\xa5k\xc2\x8f\xe3\x7f\xd0\xfbH\xe9\x0d\xdf\xf9\xcew\xf0\xc4\x13O\xa0\xab\xab\x0b\xb1XL\xe4\x9fR\xf4\xf9\xb8#\x08\xef\x1b5\xbb\x5c.<\xf4\xd0C\xd8\xbe};N?\xfdtQu\x0f\x00\x9f\xfd\xecg\x91\xcb\xe5\xcav\x0c,\x15\x80S][d2\x19^x\xe1\x05l\xdc\xb8\x11\x17^x!~\xf9\xcb_\x8aqH\xe3\xf3\xfc\xf3\xcf\xc7\x8e\x1d;p\xdf}\xf7\x89#_\x16\x7f\xe5\xd3\x08\x13\x8d7\xd6\x0a\xb38\x02X\xaep.\x87\xd8\x99rD\x01\xa5\xb9\x80\xb5\xb5\xb5hll\xc4;\xef\xbc\x03\x00\xa2\xe8\x22\x99L\x8a\xcaS\xa9\xb1s2\x99D*\x95B8\x1cF0\x18\xc4\xdbo\xbf\x0d\x97\xcb\x05\xb7\xdb=\xa9\xc5@.\x97c\xdf\xbe}\xd8\xb9s'^}\xf5U\xf4\xf7\xf7#\x18\x0c\x22\x12\x89\x08\xb3d\xa9\x08\xa5\xdfKy\x84.\x97\x0b6\x9b\xad\xa2*\x7fK\xdd\xef\x91HDD\x82\xa4\x22Pj\xa7Cy\x82$\x88\xbb\xba\xba`\xb5Z\xf1\xd2K/\xe1\xe2\x8b/\x86\xdb\xed\xe6\xfb\x7f\x9a\x04z<\x1e\xc7\xa3\x8f>\x8a'\x9f|\x12\x9d\x9d\x9d\x88\xc5bH$\x12H$\x12\x22\x1a^\x8e\xdfC\xd7}\xf5\xea\xd5x\xf3\xcd7\xf1\xfc\xf3\xcf\xe3\xe3\x1f\xff8\xd6\xacY\x833\xcf<\xb3\xac\xd7\x956Jt\x0fOE<<\xf1\xc4\x13\xb8\xea\xaa\xabJ\xf6\xa8\xf6\xfb\xfdx\xec\xb1\xc7\xb0a\xc3\x06\xf1\xdc\x5c\x90\x84\xb2\x8d\x0f\x16ys\x5c\x00N$\x0a\xa7\xb2\xcb\xe4\x1b\x8e)\xc7\xa4\xa3P(`6\x9bEw\x90%K\x96`\xdf\xbe}\x22\xc7,\x99L\x8a\x9d?\x1dO\xa5R)\xc8d2\xe8t:D\x22\x11a\x1f\x93L&\xf1\xe8\xa3\x8f\xe2\xd4SO\xc5\x8a\x15+\xa0\xd5ja6\x9b\x0b\xc6j6\x9bE(\x14B__\x1f\xda\xda\xda\xb0o\xdf>\xb4\xb6\xb6\x22\x12\x89`xx\x18\xa1P\x08\xb1XlL\xcf_\x22\x97\xcba\xfd\xfa\xf5p\xbb\xddp:\x9d\xa2\xebG\xa5\x0a@z\x9fI\xc8J\xefy\x8a\xa6\xd2\x91\x1fE^3\x99\x0cl6\x1bzzz\x10\x89D\x10\x8dF\xb1z\xf5j\xb4\xb4\xb4\xc0h4\x0a!\xce\x9c\xf8\xbc{\xe8\xd0!\xfc\xf1\x8f\x7f\xc43\xcf<\x83\xfe\xfe~\x91r maX\xceD|\xba\xe7R\xa9\x14>\xf0\x81\x0f`pp\x10\xdd\xdd\xdd\xd0j\xb5e\x1fs\xd2H\xfbT\xb8\xf8\xe2\x8b\xb1d\xc9\x12\xb4\xb6\xb6\xbe\xb7\x00*\x95X\xb2d\x096n\xdc\x88\x8f|\xe4#X\xb3f\x0d\x1f\xf7\xce\x80\x1e`\xe6\xa8\x00\x94\xde\x98\x94\x0fT\x5c\xe1\xc8032\xb8\x95Jh4\x1a8\x1c\x0e\xf8|>\xf8|>,_\xbe\x1c\xaf\xbf\xfe\xbaXD\xa8\xe2T\xaf\xd7\x8b\xf1I\xc6\xc5\xe9tZ\x88\xbfp8\x0c\x97\xcb\x85@ \x807\xdf|\x13~\xbf\x1f\x1e\x8fGT\x11SqIGG\x07\xfa\xfb\xfb\x0b\x04_8\x1c\x16\xc7\xceT\xf8!\xb5\xe6\xa0{\xc2d2a\xf5\xea\xd5p\xbb\xddp8\x1c\xc2\x1c\xb9\xd2\xc4\xdfxG\x8e\xd2\xaf\x93\xc8\xc8d2B\xd4\xc5b1h4\x1a\x04\x02\x01\xa4\xd3i\x04\x02\x01$\x93Itww\xc3\xe1p\xa0\xaa\xaa\x0a\xb5\xb5\xb5\xd0\xe9t\xc2\xf2\xc6h4B\xa7\xd3\x9d\x94\x85\xa7\x9c\xe2\x88\x9e\xebx\xaf\xe5d_\x7f2\x99D(\x14\xc2\xc1\x83\x07\xf1\xc6\x1bo`\xc7\x8e\x1dhooG(\x14\x12Q-i\xc7\x95\xe9\x18[j\xb5Z<\xb7\xdf\xef\x9f\x96MG\xa9\x8e1\x93y\x8fL&\x13\xd6\xad[\x87\xbe\xbe>|\xees\x9f\x13\xf3\x82Z\xadF$\x12A&\x93)\xbb`e\xfey}H\x0b\xb0G\xf0\x1c\x15\x80\xd2\x5c&\xbaY\xa5\xf9K\x1c\xddcf2:\xa5P(`4\x1a\x0b*\x82\xf7\xee\xdd\x8b\xd1\xd1QQ\xdc\xa1R\xa9D\xa7\x0a\xeap\x00\xbc\x17\xd1\xa3j]\x83\xc1\x80\x9e\x9e\x1etww\xc3\xe9t\xa2\xbd\xbd]\xfc\x0e\xb2\x8c\x19\x1c\x1cD.\x97C<\x1eG\x22\x91@4\x1aE4\x1a\x15Gm\xf1x\x5c\x88?\xa9\xef\x1fAG\xbf\x1e\x8f\x07V\xabU\x1cMWb\xee\x9f\xf4\xbe\x9e\xa8\xa7*E\x03\xb3\xd9,4\x1a\x8d\xb0\xcbI\xa7\xd3\xd0\xe9t\x18\x1a\x1a\x12\x1dN\xc8\x9f.\x16\x8b\x8d1\xd0\x9e\xca\x22_\x1cY\xad\xa4H\xc7tZ_P46\x12\x89 \x10\x08```\x00CCC\x22\xed\x80~7\xe5eR\x9e\xe6t\xbd\x96\xe9B*`\x8f\xe7\xfd\xbc\xf7\xde{\xf1\xb3\x9f\xfd\x0c\xb1X\x0c[\xb7n\x15\xc7\xe2F\xa3\x91\xd7\xa7i\x9e7\x8aSm\x989\x22\x00\xa5\x8b\xc2\xf0\xf00Z[[\x91\xc9d\x90N\xa7\xd1\xd1\xd1\x81\xd1\xd1Q\x84\xc3a$\x12\x09\x11\x0d\x91\xee\x06fb\xe2`\xe6\xe1\x00\x7f?\x0ah\xb7\xdb\xe1\xf5zQSS\x83\xc6\xc6F\xbc\xf5\xd6[\xa2Wm\x22\x91\x80\xc1`\x10BE\xea\x0bFQl\x12\x88z\xbd\x1e\xa1PHT\x19\x93P\xa4\xde\xb7\x99LF\x88<\xe9d\x97H$D\xe5\xb14\xfaG\xd8l6\xb4\xb4\xb4\xc0\xe7\xf3\x89\xca_\x8dFS1\xc7\xa1R\x93\xeaP(\x84}\xfb\xf6A\xa1P \x18\x0c\x8a\xc2\x96p8,\x22\xa7T|@\xa2'\x97\xcb!\x1a\x8d\x0a[\x18\x00\xc2k-\x14\x0a\x89B\x97t:\x0d\x83\xc1\x80\xae\xae\xaec\xce\x09\xb3\xcdG\xecD\xc5\xd6\xb1~>\x9b\xcd\x22\x95J!\x93\xc9 \x12\x89 \x16\x8batt\xb4\xc0V\x87\x22\x80\xd2\x0d\xfal\x9bs\xc7+\x02\x99lT\x89\xa2\xf6\xf4^p$jz\xaf\x15\xcdu}}}hmm\xc5\xc8\xc8\x08\x06\x06\x060<<\x8c\x91\x91\x91\x82\xd3\x96\xe2\x8d1\xfd\xcb}\xc2+\x5c\x00J/\x18%\xcf\xd3b\x11\x0e\x87\x0b\x92\xe0\xc73\xc0\x95F\x10\x19\xa6\x1c(\x14\x0a\x11\x05\xf4x<\xa8\xaf\xaf\xc7\xc2\x85\x0bq\xe0\xc0\x011Fe2\x99\xc8\x01\xa4\x1e\xb6T!IQ\x12\x95J\x05\x99L\x86H$\x02\x8dF\x83d2\x89`0\x88|>\x0f\xbd^/\x8e8\xa59\xaft\xdcKQ\xb0x<.\x04e\xf1D\xb6h\xd1\x22,X\xb0\x00.\x97\x0bV\xabU\x1c\x81V\xd2\xbd@\x133\xe5:\xaaT*\x84\xc3a\x0c\x0d\x0d\xa1\xbf\xbf_\xe4\xf4\xd1\x91x\xf1u\x00 r,\xa5\x0b\x84B\xa1@(\x14\x12\xdf\x17\x08\x04Dk\xbc\xa9F\x99\xa6k\x81\x98-\x0b\x0fy3R\x11\x13\xe5]J\xe7U\xe9b:[\xf3-O$\x02Hs\x02\xa5'\xb0\xb0\x98\xfey\x83\xdc\x15\x82\xc1 b\xb1\x18B\xa1\x10zzz\x10\x8dF1::\x8aX,&r\x89KE\xf0\xf9\xfa\xcc\x82\x08 M2\xc3\xc3\xc3\xd8\xb7o\x1f\x86\x87\x87E\x97\x04Z\x1c\xa4\x11\x02i\x04\x90&'\xee\x02\xc0\x94\x1b\x95J\x05\x8dF\x03\x9b\xcd\x06\xbf\xdf\x8f\xfa\xfaz\xd4\xd5\xd5a\xf7\xee\xddb\x13B\xd1\xbbX,V\x10!\xa1\xc8\x1e\x99\xe5*\x95J$\x93I\xa8T*\xf1 \xe3c\xe9B\x22\xb5\x96\xa1^\xc1\xd9lv\x8c\xfd\x0c\x15\x9c\xac]\xbb\x16n\xb7\x1bUUU\xd0\xeb\xf5\xa2%Z\xa5-\xba\x00\x10\x0e\x87\xd1\xd6\xd6&&n\x9a\xc4\xa3\xd1\xa8\x10\xbc\xc5\x02\xf0X\xd1\xbcb\x11CsA%L\xfc\xb3i\xf1\xa1\xb1%\x1dk\xd2\xb6\x9c\xd2\x08\xdal\xed\xc4p\x22>\x80\xe3\x8d5f\xfa\x85z \x10\xc0\x9e={\xd0\xdd\xdd-\xf4\xc0\xf0\xf0\xb0\x08\x08\xd1\xc6\xb1x\xbc\xd2ub\x11X\xc1\x02\x90\x8eyd2\x19FGG\x11\x0c\x06E\xd4\x83\x16\x06\xca\x85\x92\x1e\xb5\x95\xf2\x0ed\x01\xc8\x94;*\xa2R\xa9`4\x1a\xe1p8P[[\x8b\x95+Wb\xef\xde\xbdb\xa1$\x1b\x93\xe2\xb1'\x97\xcbE\xb50\xf5\x1a&\x93[\xaa\x10\xd6\xe9t\xc8d2\x05\x9b\x17\xb2\x92\xa1EY:\xfe\xa5c=\x9f\xcfc\xc9\x92%\xa8\xaf\xaf\x87\xcb\xe5\x82\xd9l\x86N\xa7\xab\xc8\x8d\x10\xbdn\xca1\xa3\xa3\xf1H$\x22\x8e\xb8\x8b\x0b[&\xbb8\x17\xe7\x0d\xce\xf5]\xfft\xe7\xdeI7\xd7\xf4\xb1TXOV\x8cW\xe2\xfb&\xed\x05|\x22\x22\xb6\xf8~\xe7ugz6$t\x8d\xfa\xfa\xfa\xd0\xdf\xdf\x8fd2)R\xc2h\xfe\x95\xce\x1d\xc5\x91\x7fi\xee S\xa1\x02\x90\x16K\x85B\x81H$\x82P($\xaa\x1e\xa59O\xc5\xea\x9e&)\xba\xa19A\x94\x99\x96\x81\xaeTB\xa7\xd3\x89\x5c@\x9f\xcf\x87U\xabV\xe1\xf5\xd7_\x1f\xd3\xcd\xa2xA\x90V\x0c\xd3\x18\xd6j\xb5H\xa5R\xa2\xa0A\xa5R\x15$\xd9\xd3\xc4GB\x90\xa2\x80\xd2\x85\x99\xee\x97\xf5\xeb\xd7\xc3\xe9t\x8a\xca_i\xff\xdcJ\x8b\xba(\x95JD\xa3Q\xd1U\x85v\xee\xe3\x1d\xdd0'O(\x8d'\xa4\xa9\x05\xdal\x8e*\x8d\x97\x03x\xbc\x82\x99\x05\xe0\xf4\x5c'\x9a\xc7\xe4r9\x22\x91HA\x0a\x98t\xde(\xbe~\xc5u\x01\xe5\xec%\xcdL\x83\x00\xa4\xe31\xbd^\x8fL&\x83\x91\x91\x91\x82\x04\xd0\xc9F\x05t:\x9d\xc8}\xe2\x1b\x92)\xe7\xf8\xa4\xee \x1e\x8f\x07uuu\xc2\x0f,\x18\x0cNj\xc1\x91\xf6\xec\xcd\xe7\xf30\x18\x0cB\x10R\xa7\x03\xfa\x7f\x22\x91\x80Z\xad\x169qT\x85Y\xdc\x0c\xfd\xcc3\xcfDuu5\xdcn\xb7\xe8\xfaA\xf9\x86\x95\x04\xfd}\x1a\x8d\x06\xf9|^\xe4?J\xff~\xa6\xb2\x04\xe0xc\xf9D\xfa\xffV\x8a\xb0(\xb7@\xa19\x82)\xffuR\xa9T\xd0\xeb\xf5H$\x12\x18\x1e\x1e\x16\xf3E\xa9`\xd0\xb1\xc6,U\xb03\x15$\x00I\xfc\xe9t:\x98\xcdf\x11\x15)\x95\x13u,\xccf\xb3h/\xc5\x17\x9a)'\x0a\x85\x02:\x9d\x0eV\xab\x15~\xbf\x1f\xb5\xb5\xb58\xe3\x8c3\xf0\xdcs\xcf\x8d\x9b\x08^*\xd2@\xc9\xe3\xd4\xf3T:V\xa9\xe2W\xa7\xd3\x09\xc3gz\x14\x1f\x8f\xeat:,_\xbe\x1c\x1e\x8f\x07UUU0\x1a\x8d\x15\x99\xfbG\x93\xb8F\xa3\x81\xd5j\x85V\xab\x15\xc7\xe2'\x9a\x83\xc5\xcc\xdcbLcY\xab\xd5Vt\x87\x99\xa9\xfc-'*\x109\x028\xbd\xd7I\xadVC\xa7\xd3\xc1b\xb1@\xa5R\x89\x22\xb0RQ\xbf\xf1\xae\x8b\x5c./\xf0\x06\xadDk\xacy+\x00)<\xabV\xaba2\x99\xe0\xf3\xf9D\x94`*\x0b\x02\x0d\x88\xaa\xaa*\x98L&\xa1\xf8yQa\xca\x05mT,\x16\x0b<\x1e\x0f\x9a\x9a\x9a\x10\x8b\xc5\xf0\x8f\x7f\xfc\x03}}}\xc7\x8c\xa4H\xad4\xc80Zz\xc4!\x15v\xe4\xf5G\xd1\xbfb\xdb\x17\x00X\xbat)\xea\xeb\xeb\x85\xef\x9f4\xfa]i\xc2\x99L\x99\xa9HE\xfa~0\x95\x8f4\xb7\xcal6\x8b\xcd\xc6l=Z\x1b/8 \xb5\x0d\x99\xaa\x00daQ\xfe\xeb#\x97\xcb\x85\x03\x834Mf2\xe3\x95H\xa5R\xb0Z\xad\x228\xc4\xd7\xa9\x82\x04 \xf5d\xd4\xe9t\x22\xc1~\xfd\xfa\xf5\x22G\xaa\xb8\x92\x87vo\xd2\xfc*\x95J\x05\xadV\x0b\x93\xc9\x04\xaf\xd7\x0b\x87\xc3!\x12\xe1\xf9\xec\x9f)\xe7\x8e\x94z\x04\xdb\xedv,X\xb0\x00\x00p\xd5UWa\xc7\x8e\x1d\xc2\x8bJ\x9a\xabJ\x95\x94\xa9TJT\xaee\xb3Ya\x19\xa3\xd5j\xa1\xd1h\xc4\xf8\xa6/\x04`:\x9d\xc6e\x97]\x86\xd3O?\x1d\xa3\xa3\xa3\xa20N\x1a\x18\xa2\xebI\xa2P\xa3\xd1\xc0`0\xc0n\xb7\x8f\x19\xb3\x9c\x1eVA\x02\x90&\x18\xab\xd5*\x12\xc5\xddn7\x12\x89\x84\x88\x10\x94*\xb9\x97VB\xaa\xd5j\x18\x0c\x06\x98L&q<\xc1\xa1^f:&&:\x8a\xa0vkf\xb3\x19\xe1p\x18\xb1XlL\xa1\x07\xf0\x9e\x17\xe0\xc8\xc8\x88hS\x16\x08\x04\xa0\xd7\xebQ[[+\xbe/\x9dNC\xaf\xd7\x17\xb4{\x93\x8e\x7fi\xe5/\x80\x82\xca_\x8a\xfeU\xea\xa4F\xef\x99\xd1h\x04\x00h\xb5Z8\x9dN\xd1\xd5G:\x91K==\xf9\xde\x9dY\x8a#`\xd2\xe3PZP\xb5Zm\xc1\x11\xf0l\xcd\x03,\xfe\x9b\x8b\xff\xfe\xc9\xfeM\x1c\x01\x9c>\xc8D\x9f\x8e\x80u:\x1d\x5c.\x17\x12\x89\x84h\xb7Y\xea:HuA\xf1\x98\xa5\x9ck\xa6\x82\x04 -\x10\x00`\xb1X\xa0\xd7\xeb\x0b\x8e\x87\x8e\xe58O\x17\x9bz\xb1\xd2\x83U>3]\x82\x86\x04\x17%\x18\xdbl\xb6\x92-\xda\xf2\xf9<\x22\x91\x08\x06\x07\x07\x11\x89Dp\xf0\xe0A\xa8\xd5j,^\xbcX\x08\xc8@ \x04$\xfd,\x89\xc0\xe2\xa3\xdf\x5c.\x87e\xcb\x96\xa1\xa9\xa9\x09n\xb7[\xecj+}!\xa6|\x5c\x12\x0e\x16\x8b\xa5\xe0\xfe\xe6hJ\xe5\x08\xa2R\xd11\x8a\xe2\xd2f[\xadV\xcf\xfaH\x0a\xb9L\x9c\xe8s\x14\x7f\xccc\xb7\xbc\x22\x10\x80\xf07\x95\xea\x82\xf1\xdeo\xe9\xc6E:fU*\xd5\xac\xdf\xb0\xccI\x01(\x15\x81$\xe4t:\xdd\xa4\x0bA\xa4G\x15\xec\x03\xc8\xcc\x04\x14\xb5V(\x14\xc8f\xb3\xd0\xeb\xf5\x05bFZ\xf0A\xad\xca\xe8hw\xf9\xf2\xe5\xb8\xfe\xfa\xeb\x11\x0e\x87q\xe4\xc8\x118\x1c\x0e\x1cW\xc5\x95\xbfk\xd7\xae\x85\xd7\xeb\x85\xd3\xe9\x84\xc1`\x98\x15\xb9\x7f\x0cS\x89\xd0=u\x22-\x08K9S0\xcc\xbc\xba\x8f\xf8-`\xe6;\x94\xaf\x07@\x18\xe3\xe6\xf3y$\x12\x09D\x22\x11\x0c\x0f\x0fC\xa3\xd1\xa0\xa5\xa5\xa5`\xb1\x88\xc7\xe3hkkC$\x12A,\x16C4\x1a\x15\xfe\x7f\xd2\xe8_>\x9fGuu5V\xacX\x01\xa7\xd39k*\x7f\x19\xa6R!\xcf\xccr\x1e\x01\xf3\xbd\xc8\xb0\x00d\x98y\xb8\x98\x90\xcd\x00\x1d\x07e\xb3Y\xe1\xff\xd7\xd5\xd5\x85\xb3\xce:k\xcc\xcfuvvb``\x00\xc1`\x10\x91H\xa4\xe0\xf8Wj\x7f\x94\xcf\xe7q\xe1\x85\x17\xc2n\xb7\xc3\xe9t\xce\x9a\xca_\x86\xa9\xd8\x85\xeb}\xe1&\xad\xd6\x97\x1e\xe5N\x16\x8e\x002,\x00\x19f\x1eS\xdc\xc1\x82L\x9cC\xa1\x10zzz`4\x1a\xd1\xd4\xd44\xe6\xb8\xa9\xad\xad\x0d}}}\x08\x87\xc3\xa2\x8b\x08\x89?\x22\x97\xcba\xe1\xc2\x85\xa8\xa9\xa9\x81\xd7\xeb\x85\xcdf\x83N\xa7\xab\xd8\xb6o\x0c3[6mr\xb9\x1c\xf1x\x5c\x08\xc0P($\x22\xf9\x93\x15\x91l\x04\xcd\xccg8\x07\x90a$bM*\xfe\x06\x07\x07\xd1\xd5\xd5\x85\xeb\xae\xbb\x0e\xb9\x5c\xae`\xb1\xe8\xed\xedEkk+\xba\xba\xbaD\xf4Oj\xfd\x22=\x8eZ\xb3f\x8d\xc8\xfd3\x99L\xa2\xcb\x0d\xc30'\xb6i\x8bF\xa3\xd8\xb3g\x0f\xd2\xe94\x06\x06\x06\xa6l\x0c\xcdG\xc0\x0c\x0b@\x86\x99\xc7\x90`\x93\x8a\xbf\xfe\xfe~\x1c\xe8gs\xb9\x1c\xd2\xe94\x12\x89\x04FGGq\xf4\xe8Q\x1c\x9f\xc7\xf0\xf00\x06\x07\x07100\x80\x03\x07\x0e\xe0\xc0\x81\x03\xe8\xec\xec\x14\x85&\xc1`P\x08?\x12\x7f\xd2\xc2\x0f\x85B\x81L&\x83\x8d\x1b7b\xc9\x92%\xa8\xa9\xa9\x81\xc3\xe1\x80\xc1`8\xa6Pe\x18\xe6\xf8\xe6\x84\xf16qS\xb9\xdf\x5c.\x17\x16/^\x0c\xadV\x0b\x8f\xd7\xc3o,\xc3\x02\x90a*\x09\x8a\xe6e\xb3YD\xa3Q\x0c\x0d\x0d!\x9dN\x17\xe4\x00\x92\xa9\xeb\xd0\xd0\x10\xe2\xf18\x86\x87\x87\x11\x0e\x87E\xa4 \x9f\xcfC\xa9T\xa2\xbf\xbf\x1f\x81@\x00n\xb7\x1b\xfd\xfd\xfd\x88\xc7\xe3\x08\x85B\x88\xc7\xe3\xe8\xed\xedE$\x12A\x7f\x7f?\xa2\xd1(b\xb1\x18\xc2\xe1\xb0\xb0\x9e\x91\xb6{\xa3\xe7\x94\xc9d\xc8f\xb3X\xbat)\xce>\xfbl\xf8\xfd~x\xbd^\x98L&\xf6\xfdc\x98\x93(\x10'\xb5\x00*\x95X\xb8p!\xbfa\x0c\x0b@\x86\xa9\xe4\xc9\x9c\xaam\xa9\x02W&\x93\xc1h4\xe2\xe8\xd1\xa3\xe8\xee\xee\xc6\x8b/\xbe\x88\xa3G\x8fb``\x00\x89DB\xe4\x0a\xd2sd\xb3Y\xb8\x5c.(\x95J\x8c\x8c\x8c \x91H@\xa1P\x08\x91\x17\x89D\x90\xcdf\x91J\xa5\x84\xc7 \x09?i\xab7\x12\x7fd\xf8\x5cUU\x85\x8b.\xba\x08\x0b\x17.Dmm\xad\xc8\xfd\xd3h4,\xfe\x18\xa6\x82\x05\xa0t#\xc70,\x00\x19\xa6\xc2\x90\x8a\xae\x5c.\x87@ \x80\xbf\xfd\xedop:\x9d\xc8\xe5r\xe8\xec\xecDww7\xf6\xee\xdd\x8b`0\x88t:\x8dT*U\x10\xa9\xcbd2\x00\x80\xde\xde^$\x12\x09qTL\xad\xe0\xf2\xf9\xe8\xf5zTUUA.\x97\xc3l6#\x1c\x0e\x0b\xc3\xe7l6[\xb0\x00\x94\x8a\x00\xd01\xb1\xb4\xb8C\xa1P\x88|\x22\x8dF#\xa2{Z\xadV|\xacR\xa9\xa0\xd1h\x0a*|9\xc2\xc00'\x17\xa3\xd1\x08\x83\xc1\x80\xba\xba:ttt\x08\xafO\x99\x9c\xab\x80\x19\x86\x05 3\xa7v\xfcj\xb5\x1af\xb3\x19\x1a\x8d\x06V\xabUt\xfc\x18\xaf\x95S\xa9*`\xea\xe0AB\x8e\xa2\x80$\x04\xe9\xb8Y\xadV\x8bJC\x85B\xc1G\xbd\x0cS\x81P\xe4_Z\x11,\xcb\xf3}\xca0,\x00\x999\x83L&\x83Z\xad\x16\x22M\xa7\xd3\x89N\x1d\xc5\x22o2\xcf%E*\xf0H\x0c\x96:6f\x18\xa6\xf2\xe6\x851~\x9f\xb2\xf7\x1f\x0c\xc3\xb0\x00d\xe6\xcedO\x11\xba\xe2N\x1f\xe5x\xeeR\xe2\x90a\x98\xcaG\x9a\x8f+\x93\xc9 \x97q~.\xc3\xb0\x00d\xe6\xfc\x84\xcf0\x0c\xc30\xcc\x14\xd7Q~\x0b\x18\x86a\x98\xd9\x0cG\xee\x19\x86\x05 \xc30\x0c3\x8f\x05 \xe7\xee2\x0c\x0b@\x86a\x18f\x1e\x8bA\x86aX\x002\x0c\xc30\xf3D\xf4\xb1\x08d\x18\x16\x80\x0c\xc30\xcc<\x13\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb8\xf8\xe3\x1c@\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe62\xd2N \x1c\x01d\x18\x16\x80\x0c\xc30\xcc<\x11\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb1\x18dA\xc80,\x00\x19\x86a\x98\xb9\xbe\x90qkH\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe60\xc560\x1c\x11d\x18\x16\x80\x0c\xc30\xcc<\x15\x83\x0c\xc3\xb0\x00d\x18\x86aX\xf41\x0c\xc3\x02\x90a\x18\x86a\x01\xc80,\x00\x19\x86a\x18\x86a\x18\x16\x80\x0c\xc30\x0cS\x99P\xe4\x8f#\x80\x0c\xc3\x02\x90a\x18\x86a\x18\x86a\x01\xc80\x0c\xc3\xcce8\x02\xc80,\x00\x19\x86a\x98\xf9\xb6\x90\xc9\xe5\xc8\xe7\xf3\xfcF0\x0c\x0b@\x86a\x18\x86a\x18\x86\x05 \xc30\x0c3'\xe1#`\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe6*2\x99\x8c#\x80\x0c\xc3\x02\x90a\x18\x86\x99\x8f\x22\x90a\x18\x16\x80\x0c\xc30\xcc<\x16\x83,\x08\x19\x86\x05 \xc30\x0c3\x0fD\x1f\xa1P(\xf8\x0da\x18\x16\x80\x0c\xc30\xcc\xbcZ\xd4\xe4\xbc\xac1\x0c\x0b@\x86a\x18f\xce#\x8d\x00\xf2\xf1/\xc3\xb0\x00d\x18\x86a\xe6\x99\x00\xe4\x08 \xc3\xb0\x00d\x18\x86aX\x002\x0c\xc3\x02\x90a\x18\x86\x99{\x0a\xb0\xb4\x18d\x18\x86\x05 \xc30\x0c3g\xf5\x1f\xe7\x002\x0c\x0b@\x86a\x18f~\x09@.\x02a\x18\x16\x80\x0c\xc30\xcc\xfc\x15\x80\x9c\x03\xc80,\x00\x19\x86a\x98y,\x06\x19\x86a\x01\xc80\x0c\xc3\xcc\x87EM\xc6\xcb\x1a\xc3\xb0\x00d\x18\x86a\xe6<\x05Q?^\xd5\x18\x86\x05 \xc30\x0c3\xbf\x04 G\x00\x19\x86\x05 \xc30\x0c3\x1f\x162\xb9\x1c\xf9|\x9e\xdf\x08\x86a\x01\xc80\x0c\xc3\xccGdr.\x02a\x18\x16\x80\x0c\xc30\xcc\xdc\x16|2Y\x81\xf5\x8bB\xae\xe07\x85aX\x002\x0c\xc30\xf3A\x042\x0c\xc3\x02\x90a\x18\x86\x99\xaf\x8b\x1a\x1bA3\x0c\x0b@\x86a\x18f~\xc1\xd1@\x86a\x01\xc80\x0c\xc3\xcc#\xf2\xf9<\x0b@\x86a\x01\xc80\x0c\xc3\xcc7X\x002\x0c\x0b@\x86a\x18\x86\x05 \xc30%\xf8\xff\x82\xe5f\x8a\x9e\x05\x9f\x09\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xbf\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xa9\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10^IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfe\x07\xeb\xb8q\xe9\xb0\x5c\xdf6\x06\x86\xf5\x14\x83\x82\x8e\xcb\xae\xdb'W\xde\xde\xbau\xab\x15\x0b''\x97q\xed*\x06\xc6\xbcH3\x86wW\x97\x9e:\xf8\x7f\x05\x03\x83\x0c\x837\xb2\xa5L\xe6\xe6\xe6\xb1\xc6\xc6\xc6\xb7@|\x0c\xcbMLL\xb2\x81T\x0d@\x00\xa1H\xcc\x9b7\xef?+++\xc3\x97/_\x18~\xfc\xfa\xafY\x98\x9fu\x03\xdd\xb5`\x0dS\xa6L\xf9\xf3D \xc1\x5c_\x89\x87A_\x9e\x81AQ\x94\x81a\xfb\xf1\xc7\x0c\xff?^\x9f\x1c\xec\xe7f\x85\xe1=NNN\xe6\x9a\x10\x1e\x06C\xe9\x8f\x0ci\x13\x1f0\xf4ne`\xf8\xc5*\xcb\xc0\xcb'\xb8\x88\x91\x91Qy\xc7\xfd\xa5\xffMm\x8c\xfe\x83h\xb8\x93f\xcc\x9c}\x8d\x9b\x87\xff\x83\x8aIP>\xeb\xd7k\xc6\x0f\xef\xdf\x9e~\xe2\xdc\x05\x86KgN\xfd}\xf9\xf2e\x8c\xbe\xbe~\xfe\xa2E\x8b\x82\xb0\x0692\x06\x86\x82+(\x0a\x80\xf4e\x948\xc2\x11\x1bB@\xac\x02\xc4\xb6\x0a\x0a\x0a=@Z\x0c \x80p\xc6\x1d.\xc0\x02c\xf4O\x99\xa3.\xcc\xc7v\xe3\xcf\x9f?\x0c\x9f>}b(((`\xc4\xa6\x01l\xc3\x8c\x99\xb3\x0e\xbd\xe3\xb4\xea\xff\xc6\xaa\xf0HO\x91\x87AO\x8e\x81\xe1\xca\xb15gBBB\x18\xb1\xda\xc0/(\xce\xf2\x9d[\xfd\x91\xad2+\x83\x9a$\x03\x83\x00\x17\x03C\xc2>I\x93/_\x16\xfcIHH`\xc1\x88\x87\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\xdd\xf2\xcc\x17\x8d\xb5eLN\xbb\x96\x9c`\xd05\xb0`\xf01d`P\x93``\xb8\xff\xfc7\xc3\xf6++\x1f\x14\xc5\xd4\xc0\x92\xceK\xb0\x1f\xb6o\xdf\xfe\xbf\xeb\x98\x91\x89\xbd\x818\x83\x020YhJ10p\xff\xb9'\xb7{\xfb\xc6\x07\xd7\x7f\x9e\xd8\xaak\xad*\x09\xd2\xb0\xa9\xe9\xe4T\xb0\x0d\x9e\x9e\x9e\x8cO\x9f\xce\xf9/\xc4\xa5\x97\xa9\xaddt\xf6\xf1\x8d\x03\x13\x9f~\xfad\xf9\xe2\xed{\x86\xd9\xad\xabX\x83\x83\x837\x7f\xfc\xf8\xf1\xee\xee\xdd\xbbsP\xe2\xe1\xc2C\x06Vmi\x06vV\x16\x86/ \xbe\xbf\x9f\x1f0\xa132<}\xfa\x94\xff\xcc\x993\x9f\xe0\x9e\x86\x01\x03y\x86\xdf0\xc5 \xb0q\xd3&F`2`\x90\x96\x02\xa6J\xe4P\xc2\x07@\x9a\x80I\x94\xc1\xcf\xdf\xef?JL\x03\x83\x0c]-'4-\xf1\x83\x5c\x07\xc4\xaf@\x82\x00\x01\x843-\xd9U>fdgad\xe0\xe1ba\x13\x13`\xff+/\xc1\xf9\xaf*\x90\xe3\x1f\x03\x89\x00\xc3\x829s\xe7]\x14\x14\xe0\xd7cf\xe3=\xfe\xe2\xbb\xe0\xa2\xfb\xdfU\xce\x8a\x09\xb22H3]3\xfe\xf7\xf9A\xdc\xb7\xaf\x9f,\xff\xfd\xfbw)--M\x9f$\x0b\xbaz'\x18IK\x88\x9e}\xf2O?\xe8\xe5_\xf9G\xc2|\xac\x0c\x92B\xec\x0c\x12\x02\x8c@\xcc\xc0 \xc2\x03\xf4?\x10\xf3p00\xec\x06\x06q`h\xf2\x01\xd3\xb9\x107\x03\xc3\xc7\xaf\xbf\x19\xb2\xfb.2<\xf8(\xc4\xa0\xa6\xaa\xf5\xc8\x5c\xf0[&\xe3\x9fg\xe7A\x8e\x84\xc6\x09P\x15\x030\xbd2\xb0\x03\xf1[ ~\x0dt\xfc?\xb8\x05,\xc0T\xae\xa6mrVL\x80\x19\xecZP\x06\xfa\xf2\xe3\x0f\xc3\xe2\xadW\x19\x16\x1e\xf8\xc1\xa0\xaci\xce\xa0\xa2b\xc2\xa0\x0b\x14\xe7\x07\x1a%\xca\xa3\x7f\xf6\xf5\xfb\x0b`\x83\x81E\xf5\xab\x03O\xd7q!\xbb\xfc\xdf\x0fF\x86\xe2\xe2\xe2jd\x0b\xfe~\x7f\xbc\x7f\xa2\xb2\x9akn\xcf\xaa\xfb\x0c;N\xbeb\x90T\xb1\x00\x96\x98\xfa\x0cA\xc0\xfaD\x04\xe8#an\x88\xaf@Au\xf3\xcc\xf6\x89?~\xfc\xf8\x0b\xcaz\xe79\xb7\xb1]8\xcb\xc0\xb0\xa2z\x0f\xdc\x82\x88V\x17\x867o\x98}Q\x22y\xd1\xe2\xa5\xc7\xb98\xd9-\x1e\xff\xd5\x0d\xfa\xc1\xa1\xfeH\x00h\xa0\x08/\xd0`\xa0\x81\xa0x\x10\x04\xba\xf1\xef\xf7\x17\x0e\xa7O\x1e\xefy\xf4\xe4\x09\x83\x99\x89\xc9O+++.{{\xfb\x06\x19\x7f\xb6\x92\x7f\x1c\xbf8af\xb1\x9dT8\xb5p\xe1\xc2\x00\xac\xc9\xb4\x7f\xea\x22]\xc6\xdf\xef.\xb1\xb3\xb33\x800\x0b\xa4\x90`\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab5\xa6\xad2\x0c\xbf\x9cCO\xdbs\xdaA{zg=]\xd0-Fp8\xe7\x80\xf0c\xa8\x01o\x08\xd1\xc4\xf1c1!\xb8\xc4\x82n\x89+\xc9\xe2t&\xc61\x89a\x8a\xcb\xf8\xb3\xb0\x18/Y2\xc4\xc4\xe1d:\xc7\x0c\xa0eNS\xb5:1A\xb2r\x0e\xc85PF/\xd0\xbb\xef\xd7\xd6\xe2\x22\xd9\xc5t_r\xd2\xe6$\xe7}\xde\xf7\xfd\xde\xefy\x9e\xef\x8e\x03\xdc\x90N\x1f\x7fcJ\xf6\x5c\xc7\xbc2\xab\x15\x10\x16\xdd\xbd\xe9|7\xabdv\x11JFe\x83H$\x02\xa1P\x08B\xe1HO\x8bc\x7f\xfd\xff\x06x\xf7x\x97\xc5\xc4s\x7f\xa9\xd5j\xf0\xc56t\x8e\xfb-}1\xb9qa#\xb7\xc8+W\xff\xa8Y]\x9e\xddK\xc0\xf0\x5c\x14466N\xdd\x16@GG\x07\xa3\xd7\xebCr\xb5\xe9L\xef\xd5\x92V\xa3F\x0e\x84QMZ\x05X4)F%\x14\xf1\xdb\xf7\xe7\x0e\xf9\xfd\xbe\xa7\xbd\xfe\x98\xa2i\xcf\xee\xd0-{\x114aA9\xab\xf9\xf1\xe2\xf4\x03\xad\x82\x81\x01\x13\xaf\x00}^.XQ\xa3\xf4\xc8C\x1a.\xc5E\xfa\xea'Z\x9d\xdf\x0e\x16P\xd4|\xe0\xdf\xdf\xdf\xb0\x82\xb6\xf6\xf7\xacf}\xbe\xf4s\xb4\xae\xd4\xa0e\xe3\xba<\x19Xy\x1a\xf4\xea\x14\xc9\x91\xe0\x84]\xbd\xfe(<\x7fd\x18|\xb9\x9b(\xfb\xd6\xcb?,x\x97uM/\xecY\xb8i\x05\x0c\x15?N\xf8\xc6\xca\xab\xe2F\xa2^\xd8\x12\x925\x9f\x16\x19\x85\x0c\xe0\xd4\xd7\x1ex\xff\xfc<\xd0\xba\x0a\xb8_\xc8\x8d\xc7b\xc3\x10\x0d\xaf\x9c\xc0\xcf\x9f\xc5a \xd3\x88\x96\x04x|H\xdbf0\xf1@\x06\x80\xa6\xa9\x87\x91\xc4\xe0n3\x93\xec5\xc9\x9c\x04\xce\xc3\x01\x9d\xbb\x16\x81\xa3\xdd\x1e\x18p/\xc2\xb6\xed\xe5`\xceO\xbd\x8f_\x8b\x03Jg5\xf1KmmmgJ\x9f\xbc\xafFi\xc8\x81\xe8*\xc0\xc4\xf0R\x10\xdf\xdf\x8b b\x12\x80\x04'K\xd0\xa1\xa5\xdc\x90\x0a\xa0\xc6gd\xdc\x07\x8ec\x97!\xaa\xad\x80\xf2\xb2-`B-\xc8cS\x14\x9e@g\x82\x00\x09\x87\xc3q\xa0\xac\xfe\x9e\x1aJ\x11\x860\xb1\x04\x0c@\xc1CJ\x16Eh\x04A\x0c\xe9\x0ar\x07\x10\xa4n\xa36%\x8f\xb94\xc0\xe1\x0f~\x87\xfe\x11\x06tB\x15\x14[q\x08\x18\xc8\xb4\x8d$\xe1\xf1$\xc8\x19\xb9P\x5c\x5c\xdc\x12\xca\x09\xc0\xeb\xf5\xefd\xfa~\xf8\xe3\x16\xd0\x96\xc8\x88\xc2\x99\x92\x00\x9cJ\xb5\x8f\xa6\xe9:-\x1b\xa3\xaeN\x87\xe3G\xbb\xc7`d\x86\x85\x92\xa2\xbb\x92\xedJN\x90*\x05@\x82\xe7+c\xd4(\x9e\x07\xd4\x07\xfb\xc0\xc0@\x9fQ\x17+\x9f]^;\x16\xdf\x5c\xf9\x12\x12Q \x94-\xcf\x9c\x83\x9e\x9e\x9e(\xaf3\xfct\xe0T\xa8\x99\xdf\xfc(l\xb5\xa6\x14\x8c\xa8\x9a!\x9d9\x99(\x02\xe8r\x9e;=99Qh\xb7\xdb\xe9\xea\xea\xea\xce\xc2\x06\xc5K\xc5\xb65\x17sEtCb\x92\x9d\xee:\xf8\xc9\xb6\xcc\x1c\xb3*\x8dz~n&\xb8\xff1\xf5\xa1?)hU+\xd6\x02f\xaa\xc0\xdf_]Cg\xbd\xdeE\xf3\x83;J\xa1\xb9\xb9\xb9\xa1\xbf\xbf\xffHCA\xc3\x8eKe\xdf\x95f\x08n\x95Y\x99\xfc\xcaw\x928\x8b\xeb\xa8\xa2\xbd\xf3#[\xbe<4N\xeeSr\xb5\xb1Sc.\xea3\x9a-\x0bLt\x8e_\x9c\xf5\xd4,y\x17\xf7\xfa|>(\xdc\xbc\x05\xde|\xebmx\xf9E;\xbcz\xf0\x95\x06\x97\xcbu\xa1\xaa\xaa\xea5\x8b\xc5\xb2=\x10\x08,\x8d\x8d\x8d}\xe8v\xbb?\xc5\xd8\xd1\xff\x90]\xfb\x17@S\xa3\xc7>\x933t-\xd1c\x02\x86\xfb\x93\xbc\xdb \x0f\xf5:\x9d\xce]\xb5\xb5u!-\xaf\xcd9\xd9\xd5\x05\xa2$B0\x10\xdc\x87\x0a\xd6y[zpi\x14(\xdc\xd4\x1c\xb4)JN\x01\x11*\x07\xae\xe3\x9d\xca\xca\x9daA\x10d\xc4wI\x92\x04\x98y\x13\x82\x9c\xc8\xaa\xe0T\xee\xac\x94\x04\x9b\xd5J@DQ\x8a\x07\x83\x81g\x10\xe4\xf3[\x16\x9c\x9b\xad\xc1\xa1AA\x12\xa5\xa47\xb5\xd9\x04\x8a\xe3\xd8^\xd4\xe5G\xb2\x06@V \x18\xac\x90D\xf1\x17r\xf3\x10\x04\x1bp\x1cw\x11A\x9e\xca\x1a\x00\xb6d\x05A*Eib\x924\x1b\xf7\x05X\x8e=\x9b5\x804\xc82\xf6_\x98\x10%R\x08\xd8\xac\xb6\xa2u7y\x9dk\xd1zK\x96\xbe.\xfd\xf3(\x08\xdb\xa4\xa9Ze\xb6\x98\xf9\xe9\xa9\xe9!\xfc?\x85\xb1}w\xdc\xb6\xfc-@wV\x1e\x1bE\x19\xc5\xdf\xce\xec\xce\xec\xce\xcc\xeel)\xa5\xdb\x16H\x01)\x14I\xb8\x84?\x00mbPK+\x876\xf5V\xee\xa8\x1c\xfdCb4\x9a@D4\x1c\x16\x94\x16B \x08\xe1P9S\x82\x5c!\x04J\xc2!\xc8e\xb1\xa5\x14\xcae\xd9\x1e\x94\xbd\xba;\xdbmw\xd7\xf7\xe6\xc0\x82\xd5\x80\x8a6\xee\xe6\xe5\x9b\xec\xec\xec\xfe~\xf3\xbd\xf7}\xbf\xdf\x9bG\xfe\x07\x8f\xfa\xf5\xd0\x04\xe6/Y\xdfE\xb2\x84sXS4\x8fa\x99n\xf1X4\x19\xe21W4\x16\xaf\xc5\xed\xa7\x1e\xf7\xa0\x1b\xd1\xb8i\xbb\xc9\x22\xedy\xbf`J\xfd\x7fN`n\xe1wR\x8a=\xb4\xd0\xc6\xb3\xd38\x8e\xb3\x904\xa00\xf2\x0d\x01\xab\xcb \xe9?\xb2'\xc6H\xf2\x8c\x02\xcf\xb7\xa0\x22Xew8?\x98\xf0\xd6\xeb\xc1\x7f\x8d@QQQ\x92M\x10O\x88\x82\xad'*2\xdc\xa5\x85S\x9efq\xd7\xe1\x9b\xbd\xf6q\x1c\x1f\x13x\x06l\x18\xa2\xd5\x0c\x92\x8d\xd5\xc2\xca\xaa&\x9a\x04\x14\xcfF\x99\xda\xaa\xd2l\xa5\xe9\xce\x98\xd6\x96\xe6\xa1D\x0c\xff\xab:\x16g\x86O\x9a\xf8f\xdd#%P\xbc|\xc52I\x14fI\x92\x041\xb3\x5cr\xa6\xa1g\xf1\xedH'/g!\xc0\x8c\x0e\xd8\x0cDB\x03nRe\x8e\x80\xf6[\xc4\x108M\xa8\xd11\x8d\x8d\x8d\xf5\xce\x8b\x15\x17f*\xa1\xd0x\x9a\x15%\x1c)~\xed\xd5\x97g=\x12\x02K\xbf\xfc\xaa\xc4a\x97\xc6\x11\xf8&\x93k\xce9\xff\xc0=\xda\xddf\x11 \x0b\x22\x02w\x08,\x90\x1e \x80\xb8\xd1\x81\xc4k\xed\x0b\x1b\x82\xe5,\x1ah\x0a\xca\xb2HK\x0c\xca\xaa}\xb0tk5<\xdbO\xc9q\x89M\xf3\xa8u\x8a\x9b_\xc9\x84\x09\x13^\xf8G\x09,^\x5c8C\x92\x84b\x02\x1f\xe7\x93\x8b+[\x87\xaf\xb3\x0b\x16\xb0\x0bf\x95\x00\x81&\xa0\x0e\x9b6\xf2fM\xebQ\x10\x19\x22@\xfa\x13\xcb\x02\x0e\x9e\xbd\x03\x17\xaa\xbdp\xbe\xea\x0e\x94]\x0f\x83\x9c\xd2\x1f\xba$:\xe1\xf9\xd4\xc3\x13\xb9h\xfd\xccP(\x04\xa1\x90\xf2\xc6\xf4\xe9\xefn\xfa;\x04\xeeq\x0ef33\x83\xb4\x09\xa9\xe0\x90\xad\xf7\xee\x1e\x92\x88\xea\xd7\xa4\x02'\x15,\xb6\x01K\xc1#`\x0b\xab\x8d\x01\x94\xd3?\xe1\x9d.\xbb\xdc\x08\x1b\xf7W\x83'\xe2\x00\xde\x91\x06\x9d\x93\x9e\x80'Gh3E\xb3\x12`z\xeevF\xdc3\xa9&b\xb1\xe8\xc7E\xfb\xe1\x9bY\xcf\x81\xda\xce$\xed\x8f*zRZZ\xda\xf8\x84\x84\x84^\xb2,;\x08\x97\xcf\xe7\xf3{<\x9e+555%K\x96,Y\xab\xf7\x88Z\x7fG\x00\xc1g\x12xZaRS\xbb6J\x82I\x05L\xf9\xcd\xebiA\xa3M\x1f\xe3\xfa*\xb4\xf0\xdb\xabp\xe0l\x00\xa2\xc0C\xabI\x80\x84\xae\xa3`\xb0S\xd3\xb3T\x0f4SV\x83\xbc%\xa51\x1e\xd4\x9a\xd3xm\xe6\xa6\x05\xf9LA\xf6\xb6.\xe8-V\xf7\xe9\xd3';q\xb0\x85e\xef3\xcc\xc9\x90D\x06\xaeST\xc9\x18:r\xe4\xc8y\x95\x95\x95\xfb\x10\xe34\xbbR~,\xa7\xce\xfd\xcb<2\x16\xb8\x0a\xed,((\x18O\x8fW\xf2\xf2\xf2\x8a]\xd9\xec\x08F\x8e\xda\x09\xc7\xfe\x05\xa7\xdb%\xf0\xdc\x87C\xb4\xcd\xd3\xc7\x06j\xf7E\x8fn\xdf\xbe}f\xbb\xfb\xc0\x96-[V\xa2\xd9x[}Z\x141\x97\xec\xab\xeaZ\xdc,\xf6\xf7\x92}r\xe8\xf5`\xac@\x94&m\x8b\xdb8o\x90\xa3\xcf=\x0d7R\xce\xfcxb\xa1\x99e\xfa\xd1\x12jbL\xd1\xe3\xc7\x8e\xcfG\x99\xf9\x05:\xa2\xf8\x80\x01\x03\xc6dff\xbec\x1d\xe1\x1f\x16\xb7\xb5\xfci\xcb\xc8\xa4X\x94\xf0Q\xc7\xc9\x8a\x8a\x8a\x95\xe8\x9cv\xfd\xe1N\xbcb\xf5F\x97\xcd\x12\xfd\x81eL\xddUo\xceXO\xb5\x9a;\xed\xf2\xdb\xb3p'fc\xc6\xda/\xe8\xe0\xd5ML'\xc1\xa9\x04\xd1\xdd\x96\x9f\xcd\x0e\x05\x9b\xa6y\xbd\xden\x8a\xa2\x80\xcf\x1f\x80\xdd\x07\x0eB\xdf\x8c\xde\xf0J~\x1e464x\xf0.~v\xe8\xd0\xa15UUUrVV\xd6\x94\x94\x94\x94\xa7\x98\xc7\x9a2Z\xc5\x90\x14\xe3\x9bU2L3\xaf\x98\x83BS\xec\xb2t\xc9\xedv\x1f)--]\x83\x1f\xd7\xb4k\xfb\xee\x7f-Z\xb5\xdfa\x8d\x5c]\x04\xb1\xc8T\xac\x0d\x96f\x85\xb4\x90a\x07\x8d \x92F\x13\x82~\x93\xf4\x10E$\x12\x89\xe2\x12x\x10e\xc9\xb0\xa9S&;\xcf\x97]\x80\xcd[w@E\xe5E\xb0\x98bp\xbb\xae\x1emK\x90:\xac\x1f\x91?\xd55\xba\xa4\x07\xaf\xc3 G\xd8\xa4\x87Bm\xf6\xbf\xa4Fw\x9c\x04\xd3\xads\x9b\xd2Z\xc3\xbe\xdcx\xb4e,\x8bj\x14\xe1\xba\x10x\x12F\x03\x86\x1b\xbfv\x13\x09\xee\xc25\xfc\xfb\xfc\xfc\xfc\x9a\xb6\xd7\xe7\xe4\xe4\xbe8z\xf4\xe8eee\xe7S\x15%lBqH\x04qf\xfc\xe0\xf3z\xa9..\xe3\xd7\xa6cj\x1d\xe8\xb0~\x00\x0d!\x15\xeaKh\x0e\xdf\x93\x9dr?\xd9.\x03\xc7s\xd0\x8cD\xfc\xbf\x11\x09\xe0w&#\x91m\x1d\xd6\xd0 \x11j\x1fMA\x22\xf3\x90\x88\xd3 rwF<^J-\xf5\x118\x12Y\xd5\xa1\x1d\x19\x92\x99$\x08\xe2\xd7\xce\x04\x19d\xbb\x03\x89\xf0*\x11\xbf\xdf\x07\x1e\x8f\x0f5S\xb0\x96\xbc\x14=\x92A2\x81\x0ek)\x91\xc8X$\xb2\xd6\xe9\x94;a\x0d\x81Q#~\x1f\x12\xf1\xf9\xe2\xa1`\xb0\x8c\x1e\x1c\x22\x89\xa2\x0e\xed\x89\x91\xc83\x82(,\xc3\xcc\xea{\x97H\xb3\x9aZu>\x9fgTi\xe9\x91\x0b\x1d\xde\xd4#\x09Z\x93G\x8b\x82\xf8\xa9\x9c \xa7\xca\x0e\xf9\xe9\x0d\x1b6\x94?T\x0d<`\x9f\xe5A\x1e\xd8\xb0\xba\xf2\xb5\xb4\x19\x8d0\xce\xd1\x9f\x09z\x8fF\xd0\xfb5f\xab\xd5\xca\x87\xc3a/\x1e{0\xee\xe8A{A\xe0\x7f\xd1V\xf9\x15N\x0d\x19H\xb5\x03\xcb\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\xaa\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05_IDATx\xdab\xfc\xff\xff?\x036\xc0\xc4\x80\x03`H\x18\xe4\xf1\x81\x8d`\x81\x09\xd8\xf5\x08\xff\xff\xf4\xe87\x03\x9f\x1c+\x98\xcf\x88\xcb\x0e\xb8\x0e\xe32\xfe\xff\x7f\x7f@\x14]\x98\xf4\x89\x11 \x80\x18)s\x15\xccEp;\xecz\x84\x80.\xfa\x83\xa9\xe3\xfb\xbb\xbfpK\xadZ\x05!\xba@\x96\x83\xb0Q)\xef\x7f\xfd\x5c\xde\xff0>@\x00\xe1t\x15\xd1AE\x08\xb0\xa0\x0b8N\x16\x81X\xc9\x08F`\x86\x82\x84\x08\xc3\xbc\x90\xeb\x8c(n\x05a\x8b&\x01\x14\xf7\xc2\xb0]\xaf\x10\x5c\x0c\xc5\x86\xef\xaf\xff2\xb0\xf131\x98\xd5\x0a\xfcg\x04\xda\xf3\x1fj\x0b\xb2/Q<\xcd\xc8\xc8\xc8\x0e\xa4\xe4\x818\x18\x88O\x00\xf1] \xfe\x04\xc4\xdf\x80\xea~\x81\xd4\x00\x04\x10\xedCi\x805\xa4l\xd0\xc6\xf0 \x8a\xa7\x1d\xa7\x88\x82\x02\x93\x01\x1e\xa4P\xa9w\xb7\x7f\x81\x13\x1a\x86\x0d\x7f\x7f\xfccx\x7f\xeb\x17X\x01\x88\xfe\xf3\xf3?\xc3\xbe\xdc7\x8c8\x93\xc6\x9f\xef\xff\xe0&\xc1\x80}\x9f\xf0\x7f\x9c~\xf8\xf6\xfa/\x86?\xfe\x00m\xc5i\x83\xba\x96\x10\x8390Y\x80\x8d\xfc\xcf\x00O\x17\xc8\xb6\xa2h\xb8y\xed\x1d\x033\x07#\x03,\x1b\xc3r\x15\x03\x9e\xb4\xc4\x0d\xa4\xf4\x80\xd8\x04\x88\x8f\x02\xf1#P\x9a\x04\xaa\xf9\x0aS\x03\x10@$\xa7%\x9a\xa7\x8c\xe1c\x01r!\x89\x0f\x10\x8c\x03|\x06\x09k\xb2\x83\xca(\x86\xb77~a\xa4\x06\x82\x16\x80\x0c\x16Pb\x03&\xbb\xff\x0c\xa8:\x19Q\x13%\x14|\xb8\xf7\x1b\xab%,\xb8\x5c\xc7#\xc9\x02\xce\x05_\x9e\xfd\xc1\xaa\x11\xe63\x90\x1c\x88-\xa8\xc2\x06\x16CW\x8b\xd3\x82?\xdf\xff\x83]\x8f\x0b \x1b\xc4#\xc3\xc2\xf0\x1b-K\x92\x14\x07\x1az\x22\x0c\x0f\x9f\xbdg\xf8\xfe\xe6/V5\x1c\xc2\xcc\xe0\xe0\xfa\xf1\xf6\x1f\xfe\x9c\x89\x0cL*\xf9\xff3\xc2\xc3\x1c\x16\xda\xb0:\x89\x11\x1a\x0b\xff\xe1\xb1\xf1\xfb\xeb\x7f\xacAITN\x06\xf9\x82\x11\xd8f8\xdf\x8b=\xa5P\x94L)\x05\x00\x014Z\x16\x8dZ0j\x01\x05\x16\x80\xdaM\xc4T:dW8\xc2\x1a\xec\xe0\xf6\x96\xa2\xb8\x18\xc3\xd9\x03\x8f\xc9\xabp\x04\xd5\xd8 \x0d7p\x13\x05\xa9z\xf9\x0f-\x02\x19\x11U\x8f\x82\x84(\xa2%NL\x10\xf1+\xb12\xfc\x07vC\xfe\xfd\xfd\xcf\xf0\xef\x0f2\x06\x89A\xf1\xef\xff\x105@|\xee\xd0S\xe2\xfa\x090\xd7\x83\xba\x5c \xdf}z\xfc\x1b\xabF^\xa0<\xc8\xb9\x9f\x1e\xfd\x82\xd7jD[\x00\x02 \xd7)H\x8b0,)\xbd\xc5\x88\xcb\x11\xb0pw\x9c$\xfc\xdf\xc0F\x9a\xb4T\xf4\x17h\xc1\xbf_\xff\x08V\x99 \x8b\xfe\x82\x83\xed\x1fi\x16\x80\xc2\xfb\xef\xdf\x7fD%\xd9\xff\xd0\xf8!\xc9\x02\x90\xa6\x9bW\xdf\xc0\x83\x03_\x9a\xff\xfb\x07\x12\xd9$\xc5\xc1\xa9\xb6\x8f\xe0 \x88\x98\xa3\xf4\x1fT\xa9\xffx\xfb\x17k\x9e\x00\xc9\xfd\xfb\x8d;\x88\xf0\xe6\x035-\x11\x86\x07O\xdf\xc1\x13;#J\xe5\xcf\x88\xe0\x03\x89_\x9f\xfe\x11_\xe9\x83\x0cg\xe1fDjA\xfc\xc7h\xd31\xa0uB\xcf\xb4\x7fd$\xb9\xa8\x00Y\xc4\xcc\x0e\x0d\xe7\x9f\x0cD5\xc2\xc8*\x8b\x98X\x19\xc0\xe1\x8c\xcf \xa2{\xfcH\xdd#N %\x08\xc4\x86@\xac\x0f\xec\xd8/\x00\x8a\xf9\x01\xd9\xa0\xa4\xf5\x1e\x88\xdf\x82Z\x98P\x0c\xeaz\xff\x01:\xf6\x0f\xdd\x9b-\x00\x01\xda\xb5\x9a\x95\x06b \x9c\xa8U(\x05Q\x16\x11\x17\xa1\x1e\xfc\xa1\x0aB\xef\xe2\xcb\xf8\x0e>\x96\x8f\xe0A\x8f\xeaE\x0f\x1e<\xb4hA\xa1\x94j\x11\xb5\xdb\xce:\x93MLv\xb7\xd8M\xeaV\x94\x0d\x0c\xcd\x12\x9a\xe4K6\x99\xf9\xbe\xd9\xdc\x07\xf8\xf3\x1e\xb3\x00P\x00(\x00\x14\x00\xdc\x1cA^\xc5\x8c\x18\x5c<\xd7\xaf\x03\xa0\xe2\xd5\x16D\x00qt\xb2+\xbc\xd0\xe5\xe9\xbd3\x18gG\xa6V\xb2~\xb8\x8e\x9d\x80\xd4|gd\x04\x03\xfa\xed\xe4\xc0\x92bP\xe3\xa9m\xe8)\xa4\x17sVE\xfe\xc2\xb0\x1f\x15\xfdg\x05d\x0d \xd2\xb4JB^\x13\x83\x1b\x9a\x1a\xe7\xba\xaf\x10\x11qn\x86gZ\xf6\xe1*`\xa3\x04\x03\xe7:|\x0b5\x7f\xea\xde\xf53\x81\xb0\x02 \xe8N\xb5\xc4R\xbd\x1aA\xa4\xaeF\xa8\xd4\xa4\xa26\x15\xc4\x86\xb1\xffR\xfbs#p:\x17\xd6g \x0c\xa2\xe1\x05m]\xf5\xd8\xf5\xc5\xa3\xd5\xa0\xb4\x08\x15\x7fNL\xba\xf70H1$\xb2\xfa\x81\xcf\xae\xceZ\x99\xfa\xb5\x06@\x14L\xa9\x800t;\xc4\xe7\xc7\x1d\x9e\x04EL\x99\xb4_\xda\x12\xc8\xc8\xd6\xdcv`\x00\x0c\x84\xc4\x09V\x03\x8d\xbb:\x896\xaa\x06\xa2\xfb\xb992\xc1\xf0\xa4\x860\x0c\xc0J1\xf9~a\xa8o\x90|9\xc73\xf0\xf1\x02\xa9\x95T\x13\xae\xed\xaf0WvAJ\x82:\xd5\x10d\xdf\xd9\x89\xfd\xc0\xf6\x9e\xc7\x9aH\x9d\x8d\x5c\x17\xab\xfaK\xb2F~\x01\xc6\xbe\x00\x8dV;\x96\xdd\xd8\xf0\x97\xd9\xedM;\x9fkts\xc7\x93\x83v\x8c\x8b1\xcd\xf7Y,G\x9c&\xeb<\xe6\x13\xf4\xc5J\xcf\xfd\x1e\xe4\xeb\xc8\xe8w\xb6\xcc\xbf\xb2\xad\xe9L\xb6tJ\xcay\xa9\x94z\x98h70Q\xfe\xc6\xc5\x0fL\xcc\x89\x85v1\xcf\xb5\x0a\x89\x15\xe8\x87?\x12\xa8M%\x98\x1bu\x98\xa7Y\xacw@~\xda0#m\x11\xad\x82VF#\x1df\x0bmM.L\x17\xad\x89\xd6\xa3\xcb\x0b\xed\x1d\xed\x0d\xedU\xd6!aCi\xe2y\x94F\xf3/e\x95O\x08\xef\x09\x03\x8d\x1a\xcd\xa5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" +qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x06\x07\xa7(\x98\x00s\x00p\x00l\x00a\x00s\x00h\x00\x0a\x08\x94\x19\x07\x00s\x00p\x00l\x00a\x00s\x00h\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x0a\x033{\x87\x00z\x00o\x00o\x00m\x00_\x000\x00.\x00p\x00n\x00g\x00\x08\x00FX'\x00s\x00y\x00n\x00c\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" +qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x18\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x13\x00\x00\x00\x05\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02\x0c\x00\x00\x00\x00\x00\x01\x00\x02-\xf0\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x12C\x00\x00\x00z\x00\x00\x00\x00\x00\x01\x00\x00\xbc\xce\x00\x00\x01\xaa\x00\x00\x00\x00\x00\x01\x00\x01\xe9\x0a\x00\x00\x01F\x00\x00\x00\x00\x00\x01\x00\x01\x9dy\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x01r\x07\x00\x00\x00\xf4\x00\x00\x00\x00\x00\x01\x00\x01W\xe8\x00\x00\x00\xc2\x00\x00\x00\x00\x00\x01\x00\x016\x0e\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x01Hn\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x01\xd90\x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x00\xad&\x00\x00\x00\xaa\x00\x00\x00\x00\x00\x01\x00\x01%\xb3\x00\x00\x01z\x00\x00\x00\x00\x00\x01\x00\x01\xc9J\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x02\x02a\x00\x00\x00J\x00\x00\x00\x00\x00\x01\x00\x00\x9e\x08\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x01\x16\x10\x00\x00\x01b\x00\x00\x00\x00\x00\x01\x00\x01\xb9Y\x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x01\xf1\xcc\x00\x00\x01.\x00\x00\x00\x00\x00\x01\x00\x01\x8d\xb6\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x02\x22\x00\x00\x00\x00\x00\x01\x00\x02>\x9e" def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 496928d4..08ef0779 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -237,6 +237,8 @@ class PickDlg(QDialog): filter_icon.addPixmap(QPixmap(':/icons/filter.png')) zoom_icon = QIcon() zoom_icon.addPixmap(QPixmap(':/icons/zoom_in.png')) + home_icon = QIcon() + home_icon.addPixmap(QPixmap(':/icons/zoom_0.png')) # create actions self.filterAction = createAction(parent=self, text='Filter', @@ -249,6 +251,9 @@ class PickDlg(QDialog): slot=self.zoom, icon=zoom_icon, tip='Zoom into waveform', checkable=True) + self.resetAction = createAction(parent=self, text='Home', + slot=self.resetZoom, icon=home_icon, + tip='Reset zoom to original limits') # create other widget elements self.selectPhase = QComboBox() @@ -263,6 +268,8 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.filterAction) _dialtoolbar.addWidget(self.selectPhase) _dialtoolbar.addAction(self.zoomAction) + _dialtoolbar.addSeparator() + _dialtoolbar.addAction(self.resetAction) # layout the innermost widget _innerlayout = QVBoxLayout() @@ -724,6 +731,11 @@ class PickDlg(QDialog): self.getPlotWidget().setYLims(new_ylim) self.draw() + def resetZoom(self): + self.getPlotWidget().setXLims(self.getGlobalLimits('x')) + self.getPlotWidget().setYLims(self.getGlobalLimits('y')) + self.draw() + def draw(self): self.getPlotWidget().draw() From 4f634cc43a679e1a8fb58ea6892cfb8767e1403d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 12 Aug 2015 16:48:55 +0200 Subject: [PATCH 0511/1144] Debuged getDataArray: Reliable cutting of waveforms independetn from actual waveform lengths. --- pylot/core/pick/CharFuns.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index b6fb2f41..b3ad1f07 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -15,6 +15,8 @@ autoregressive prediction: application ot local and regional distances, Geophys. :author: MAGS2 EP3 working group """ + +import matplotlib.pyplot as plt import numpy as np from obspy.core import Stream @@ -162,10 +164,12 @@ class CharacteristicFunction(object): stop = min([len(self.orig_data[0]), len(self.orig_data[1])]) elif self.cut[0] == 0 and self.cut[1] is not 0: start = 0 - stop = self.cut[1] / self.dt + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ + len(self.orig_data[1])]) else: - start = self.cut[0] / self.dt - stop = self.cut[1] / self.dt + start = max([0, self.cut[0] / self.dt]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ + len(self.orig_data[1])]) hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() @@ -176,13 +180,15 @@ class CharacteristicFunction(object): elif len(self.orig_data) == 3: if self.cut[0] == 0 and self.cut[1] == 0: start = 0 - stop = min([len(self.orig_data[0]), len(self.orig_data[1]), len(self.orig_data[2])]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ + len(self.orig_data[1]), len(self.orig_data[2])]) elif self.cut[0] == 0 and self.cut[1] is not 0: start = 0 stop = self.cut[1] / self.dt else: - start = self.cut[0] / self.dt - stop = self.cut[1] / self.dt + start = max([0, self.cut[0] / self.dt]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ + len(self.orig_data[1]), len(self.orig_data[2])]) hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() From 2668f4435c21ec5132ae375c252027d3a24135a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 13 Aug 2015 09:01:47 +0200 Subject: [PATCH 0512/1144] Removed unsused import-pdb command. --- pylot/core/pick/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 807fda4c..35520f8c 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -13,7 +13,6 @@ import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings -import pdb def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): From fba2aad6647cf2d0d55d47b28cd9c2f9c2134b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 13 Aug 2015 10:52:07 +0200 Subject: [PATCH 0513/1144] Added more detailed information for user about skipped picks. --- pylot/core/pick/autopick.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 31fc1da9..abb5a7c1 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -39,9 +39,9 @@ def autopickevent(data, param): # quality control # median check and jackknife on P-onset times - jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, iplot) + jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, 2) # check S-P times (Wadati) - return wadaticheck(jk_checked_onsets, wdttolerance, iplot) + return wadaticheck(jk_checked_onsets, wdttolerance, 2) def autopickstation(wfstream, pickparam): """ @@ -230,7 +230,7 @@ def autopickstation(wfstream, pickparam): aicpick.getSNR() >= minAICPSNR and Pflag == 1): aicPflag = 1 - print 'AIC P-pick passes quality control: Slope: %f, SNR: %f' % \ + print 'AIC P-pick passes quality control: Slope: %f counts/s, SNR: %f' % \ (aicpick.getSlope(), aicpick.getSNR()) print 'Go on with refined picking ...' # re-filter waveform with larger bandpass @@ -298,7 +298,8 @@ def autopickstation(wfstream, pickparam): else: print 'Bad initial (AIC) P-pick, skip this onset!' - print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope() + print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope(), 'counts/s' + print '(min. AIC-SNR=', minAICPSNR, ', min. AIC-Slope=', minAICPslope, ')' Sflag = 0 else: @@ -387,7 +388,7 @@ def autopickstation(wfstream, pickparam): aicarhpick.getSNR() >= minAICSSNR and aicarhpick.getpick() is not None): aicSflag = 1 - print 'AIC S-pick passes quality control: Slope: %f, SNR: %f' \ + print 'AIC S-pick passes quality control: Slope: %f counts/s, SNR: %f' \ % (aicarhpick.getSlope(), aicarhpick.getSNR()) print 'Go on with refined picking ...' # re-calculate CF from re-filtered trace in vicinity of initial @@ -504,7 +505,9 @@ def autopickstation(wfstream, pickparam): else: print 'Bad initial (AIC) S-pick, skip this onset!' print 'AIC-SNR=', aicarhpick.getSNR(), \ - 'AIC-Slope=', aicarhpick.getSlope() + 'AIC-Slope=', aicarhpick.getSlope(), 'counts/s' + print '(min. AIC-SNR=', minAICSSNR, ', min. AIC-Slope=', \ + minAICSslope, ')' else: print 'autopickstation: No horizontal component data available or ' \ From 64b8e1a9e8b5c27af89137cedb2ec2254697ed11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 13 Aug 2015 10:54:17 +0200 Subject: [PATCH 0514/1144] checksignallength: Added more detailed information for user. --- pylot/core/pick/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 35520f8c..161942cb 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -618,6 +618,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): else: print 'checksignallength: Signal shorter than required minimum signal length!' print 'Presumably picked noise peak, pick is rejected!' + print '(min. signal length required:', minsiglength, 's)' returnflag = 0 if iplot == 2: From ceed663a7719fde42efa435e6c3cd891f8373f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 13 Aug 2015 10:57:46 +0200 Subject: [PATCH 0515/1144] AICPicker: if iplot > 1, figure with waveform and CF is raised, even when slope is calculation failed. --- pylot/core/pick/Picker.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index ae41a82e..d8a224e6 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -223,6 +223,18 @@ class AICPicker(AutoPicking): if imax == 0: print 'AICPicker: Maximum for slope determination right at the beginning of the window!' print 'Choose longer slope determination window!' + if self.iplot > 1: + p = plt.figure(self.iplot) + x = self.Data[0].data + p1, = plt.plot(self.Tcf, x / max(x), 'k') + p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') + plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(p) return islope = islope[0][0 :imax] dataslope = self.Data[0].data[islope] From def9760f4b06c7c2e5ac8ac952bf4a3d0d500eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 13 Aug 2015 11:02:07 +0200 Subject: [PATCH 0516/1144] Cosmetics. --- pylot/core/pick/autopick.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index abb5a7c1..6c235f18 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -299,7 +299,7 @@ def autopickstation(wfstream, pickparam): else: print 'Bad initial (AIC) P-pick, skip this onset!' print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope(), 'counts/s' - print '(min. AIC-SNR=', minAICPSNR, ', min. AIC-Slope=', minAICPslope, ')' + print '(min. AIC-SNR=', minAICPSNR, ', min. AIC-Slope=', minAICPslope, 'counts/s)' Sflag = 0 else: @@ -507,7 +507,7 @@ def autopickstation(wfstream, pickparam): print 'AIC-SNR=', aicarhpick.getSNR(), \ 'AIC-Slope=', aicarhpick.getSlope(), 'counts/s' print '(min. AIC-SNR=', minAICSSNR, ', min. AIC-Slope=', \ - minAICSslope, ')' + minAICSslope, 'counts/s)' else: print 'autopickstation: No horizontal component data available or ' \ From 8c8ef7bb2a5c3b8fdb25f257443ece0d671ee199 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 18 Aug 2015 10:39:58 +0200 Subject: [PATCH 0517/1144] [closes #164] test --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index c5452495..87058826 100644 --- a/README +++ b/README @@ -31,5 +31,6 @@ release notes: + October 2013 From f844f7b316f4ef18f90c503005e24304ea3ca066 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 18 Aug 2015 11:09:34 +0200 Subject: [PATCH 0518/1144] [closes #164] test --- README | 2 -- 1 file changed, 2 deletions(-) diff --git a/README b/README index 87058826..751b6264 100644 --- a/README +++ b/README @@ -30,7 +30,5 @@ release notes: ============== - - October 2013 From 70d9389f4b4051868243be5f814ecdf2cb32aa6e Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 18 Aug 2015 11:13:16 +0200 Subject: [PATCH 0519/1144] [closes #164] test --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index 751b6264..87058826 100644 --- a/README +++ b/README @@ -30,5 +30,7 @@ release notes: ============== + + October 2013 From b1736358b75e4c14c6a6e3b2e27db51b25b1e771 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 18 Aug 2015 11:23:04 +0200 Subject: [PATCH 0520/1144] [closes #164] test --- README | 2 -- 1 file changed, 2 deletions(-) diff --git a/README b/README index 87058826..751b6264 100644 --- a/README +++ b/README @@ -30,7 +30,5 @@ release notes: ============== - - October 2013 From 5d970fde1e4e7e570f7b604d1779800e411dc742 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 18 Aug 2015 11:28:38 +0200 Subject: [PATCH 0521/1144] [closes #164] test --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index 751b6264..c5452495 100644 --- a/README +++ b/README @@ -30,5 +30,6 @@ release notes: ============== + October 2013 From 6719939a664652d382f783f51c4681014a14206a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 26 Aug 2015 16:45:19 +0200 Subject: [PATCH 0522/1144] New parmater prefilt for filtering traces before restitution. --- autoPyLoT_local.in | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 22d90771..dbe9bb77 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -9,6 +9,8 @@ EVENT_DATA/LOCAL #datapath# %data path 2013.02_Insheim #database# %name of data base e0019.048.13 #eventID# %event ID for single event processing +/DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file +0.5 0.9 190.0 195.0 #prefilt# %corner frequencies for pre-filtering traces before restitution PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file @@ -29,8 +31,8 @@ HYPOSAT #locrt# %location routine used ("HYPO 300 #Qp# %quality factor for P waves 100 #Qs# %quality factor for S waves #common settings picker# -15 #pstart# %start time [s] for calculating CF for P-picking -40 #pstop# %end time [s] for calculating CF for P-picking +20 #pstart# %start time [s] for calculating CF for P-picking +80 #pstop# %end time [s] for calculating CF for P-picking -1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking 7 #sstop# %end time [s] after P-onset for calculating CF for S-picking 2 20 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] @@ -49,7 +51,7 @@ HOS #algoP# %choose algorithm for P-onset 0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick 0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick 0.001 #addnoise# %add noise to seismogram for stable AR prediction -3 0.1 0.5 0.1 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] +3 0.1 0.5 0.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] 3 #pickwinP# %for initial AIC pick, length of P-pick window [s] 8 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) 0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) @@ -80,9 +82,9 @@ ARH #algoS# %choose algorithm for S-onset #inital AIC onset# 0.01 0.02 0.04 0.08 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P 0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S -80 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +50 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected 1.2 #minAICPSNR# %below this SNR the initial P pick is rejected -50 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +6 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# 2.5 #minsiglength# %minimum required length of signal [s] From 8af4f8ad1ed6087acd279ef30e4cb4a69f858f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 26 Aug 2015 17:00:17 +0200 Subject: [PATCH 0523/1144] Finished (temporary!) new function restitueWFData. --- pylot/core/read/data.py | 52 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index dad3bf56..7723988a 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -1,8 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import pdb import os - +import glob +import matplotlib.pyplot as plt +from obspy.xseed import Parser from obspy.core import read, Stream, UTCDateTime from obspy import readEvents, read_inventory from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID @@ -143,13 +146,50 @@ class Data(object): self.wfdata = self.getOriginalWFData().copy() self.dirty = False - def restituteWFData(self, fninventory): + def restituteWFData(self, invdlpath, prefilt): st = self.getWFData() - inv = read_inventory(fninventory) - st.attach_response(inv) - pre_filt = (0.005, 0.006, 30.0, 35.0) # set in autoPyLoT.in - st.remove_response(output='VEL', pre_filt=pre_filt) + for tr in st: + if tr.stats.station[3] == '_': # this is for some ugly station naming + tr.stats.station = tr.stats.station[0:3] + dlp = '%s/*.dataless' % invdlpath + invp = '%s/*.inv' % invdlpath + respp = '%s/*.resp' % invdlpath + dlfile = glob.glob(dlp) + invfile = glob.glob(invp) + respfile = glob.glob(respp) + if len(dlfile) >= 1: + print "Found dataless-SEED file!" + print "Reading meta data information ..." + print dlfile[0] + parser = Parser('%s' % dlfile[0]) + + try: + print "Correcting for instrument response ..." + st.simulate(pre_filt=prefilt, seedresp={'filename': parser, \ + 'date': st[0].stats.starttime, 'units': "VEL"}) + except ValueError, e: + vmsg = '{0}'.format(e) + print vmsg + + elif len(invfile) >= 1: + print "Found inventory-xml file!" + print "Reading meta data information ..." + inv = read_inventory(invfile, format="STATIONXML") + print "Applying instrument correction ..." + st.attach_response(inv) + st.remove_response(output='VEL', pre_filt=prefilt) + elif len(respfile) >= 1: + print "Found response file!" + print respfile + seedresp={'filename': respfile[0], 'date': st[0].stats.starttime, \ + 'units': "VEL"} + st.simulate(paz_remove=None, pre_filt=prefilt, seedresp=seedresp) + else: + print "No dataless-SEED file,inventory-xml file nor RESP-file found!" + print "Go on processing data without source parameter determination!" + + return st def getEvtData(self): return self.evtdata From 2dc7819a84dc13b2b71a15c96b0ecb6c7931dfa4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Aug 2015 05:51:03 +0200 Subject: [PATCH 0524/1144] restitute seismometer response --- autoPyLoT.py | 9 +++++++++ 1 file changed, 9 insertions(+) mode change 100755 => 100644 autoPyLoT.py diff --git a/autoPyLoT.py b/autoPyLoT.py old mode 100755 new mode 100644 index 1b377f07..c4e941ea --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -68,6 +68,11 @@ def autoPyLoT(inputfile): datastructure.modifyFields(**dsfields) datastructure.setExpandFields(exf) + # get path to inventory or dataless-seed file with station meta data + invdir = parameter.getParam('invdir') + # get corner frequencies for pre-filtering traces + prefilt = parameter.getParam('prefilt') + # multiple event processing # read each event in database datapath = datastructure.expandDataPath() @@ -78,6 +83,8 @@ def autoPyLoT(inputfile): print data wfdat = data.getWFData() # all available streams + # restitute waveform data getting responses from inventory-file + wfdat = data.restituteWFData(invdir, prefilt) ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) @@ -93,6 +100,8 @@ def autoPyLoT(inputfile): print data wfdat = data.getWFData() # all available streams + # restitute waveform data getting responses from inventory-file + wfdat = data.restituteWFData(invdir, prefilt) ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) From 0f4d13b8a5a2b64afd20570a4a5674d78df2eae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 27 Aug 2015 09:46:35 +0200 Subject: [PATCH 0525/1144] Changed permissions. --- autoPyLoT.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 autoPyLoT.py diff --git a/autoPyLoT.py b/autoPyLoT.py old mode 100644 new mode 100755 From 4bfe4ce0474f4097e7a637eaa152f1b8c72ea6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 27 Aug 2015 09:53:05 +0200 Subject: [PATCH 0526/1144] Marginal changes. --- pylot/core/pick/autopick.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 6c235f18..3c074a7d 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -163,7 +163,7 @@ def autopickstation(wfstream, pickparam): if Ldiff < 0: print 'autopickstation: Cutting times are too large for actual ' \ 'waveform!' - print 'Use entire waveform instead!' + print 'Using entire waveform instead!' pstart = 0 pstop = len(zdat[0].data) * zdat[0].stats.delta cuttimes = [pstart, pstop] @@ -200,7 +200,7 @@ def autopickstation(wfstream, pickparam): # both horizontal traces needed if len(ndat) == 0 or len(edat) == 0: print 'One or more horizontal components missing!' - print 'Skip control function checkZ4S.' + print 'Skipping control function checkZ4S.' else: # filter and taper horizontal traces trH1_filt = edat.copy() @@ -297,14 +297,14 @@ def autopickstation(wfstream, pickparam): Sflag = 1 else: - print 'Bad initial (AIC) P-pick, skip this onset!' + print 'Bad initial (AIC) P-pick, skipping this onset!' print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope(), 'counts/s' print '(min. AIC-SNR=', minAICPSNR, ', min. AIC-Slope=', minAICPslope, 'counts/s)' Sflag = 0 else: - print 'autopickstation: No vertical component data availabler!, ' \ - 'Skip station!' + print 'autopickstation: No vertical component data available!, ' \ + 'Skipping station!' if edat is not None and ndat is not None and len(edat) > 0 and len( ndat) > 0 and Pweight < 4: @@ -503,7 +503,7 @@ def autopickstation(wfstream, pickparam): Sweight, SNRS, SNRSdB) else: - print 'Bad initial (AIC) S-pick, skip this onset!' + print 'Bad initial (AIC) S-pick, skipping this onset!' print 'AIC-SNR=', aicarhpick.getSNR(), \ 'AIC-Slope=', aicarhpick.getSlope(), 'counts/s' print '(min. AIC-SNR=', minAICSSNR, ', min. AIC-Slope=', \ From 834e836a3d7a55285d88ff2bf2cb8e00bc7d1a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 27 Aug 2015 11:01:59 +0200 Subject: [PATCH 0527/1144] Removed parameter prefilt, no more necessary. --- autoPyLoT_local.in | 1 - 1 file changed, 1 deletion(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index dbe9bb77..18849c2e 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -10,7 +10,6 @@ EVENT_DATA/LOCAL #datapath# %data path 2013.02_Insheim #database# %name of data base e0019.048.13 #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file -0.5 0.9 190.0 195.0 #prefilt# %corner frequencies for pre-filtering traces before restitution PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file From 845fd6a7b3c0a0c74461b8ef7939fcbf1735b028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 27 Aug 2015 11:04:46 +0200 Subject: [PATCH 0528/1144] restituteWFData: able to handle several dataless-, inventory- or resp-files, calculates prefiltering from sampling rate of trace. Processing with inventory- or resp-file has yet not been checked! --- pylot/core/read/data.py | 104 +++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 7723988a..78dd1c7c 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import pdb import os import glob import matplotlib.pyplot as plt @@ -146,10 +145,11 @@ class Data(object): self.wfdata = self.getOriginalWFData().copy() self.dirty = False - def restituteWFData(self, invdlpath, prefilt): + def restituteWFData(self, invdlpath): st = self.getWFData() for tr in st: - if tr.stats.station[3] == '_': # this is for some ugly station naming + # remove underscores + if tr.stats.station[3] == '_': tr.stats.station = tr.stats.station[0:3] dlp = '%s/*.dataless' % invdlpath invp = '%s/*.inv' % invdlpath @@ -158,38 +158,88 @@ class Data(object): invfile = glob.glob(invp) respfile = glob.glob(respp) + # check for dataless-SEED file if len(dlfile) >= 1: - print "Found dataless-SEED file!" + print "Found dataless-SEED file(s)!" print "Reading meta data information ..." - print dlfile[0] - parser = Parser('%s' % dlfile[0]) + for j in range(len(dlfile)): + print dlfile[j] + parser = Parser('%s' % dlfile[j]) + for i in range(len(st)): + # check, whether this trace has already been corrected + if len(st[i].stats) < 13: + try: + print "Correcting %s, %s for instrument response ..." \ + % (st[i].stats.station, st[i].stats.channel) + # get corner frequencies for pre-filtering + fny = st[i].stats.sampling_rate / 2 + fc21 = fny - (fny * 0.05) + fc22 = fny - (fny * 0.02) + prefilt = [0.5, 0.9, fc21, fc22] + # instrument correction + st[i].simulate(pre_filt=prefilt, seedresp={'filename': parser, \ + 'date': st[i].stats.starttime, 'units': "VEL"}) + except ValueError, e: + vmsg = '{0}'.format(e) + print vmsg + else: + print "Trace has already been corrected!" - try: - print "Correcting for instrument response ..." - st.simulate(pre_filt=prefilt, seedresp={'filename': parser, \ - 'date': st[0].stats.starttime, 'units': "VEL"}) - except ValueError, e: - vmsg = '{0}'.format(e) - print vmsg - - elif len(invfile) >= 1: - print "Found inventory-xml file!" + # check for inventory-xml file + if len(invfile) >= 1: + print "Found inventory-xml file(s)!" print "Reading meta data information ..." - inv = read_inventory(invfile, format="STATIONXML") - print "Applying instrument correction ..." - st.attach_response(inv) - st.remove_response(output='VEL', pre_filt=prefilt) - elif len(respfile) >= 1: - print "Found response file!" - print respfile - seedresp={'filename': respfile[0], 'date': st[0].stats.starttime, \ - 'units': "VEL"} - st.simulate(paz_remove=None, pre_filt=prefilt, seedresp=seedresp) - else: + for j in range(len(invfile)): + print invfile[j] + inv = read_inventory(invfile[j], format="STATIONXML") + for i in range(len(st)): + # check, whether this trace has already been corrected + if len(st[i].stats) < 13: + try: + print "Correcting %s, %s for instrument response ..." \ + % (st[i].stats.station, st[i].stats.channel) + # get corner frequencies for pre-filtering + fny = st[i].stats.sampling_rate / 2 + fc21 = fny - (fny * 0.05) + fc22 = fny - (fny * 0.02) + prefilt = [0.5, 0.9, fc21, fc22] + # instrument correction + st[i].attach_response(inv) + st[i].remove_response(output='VEL', pre_filt=prefilt) + except ValueError, e: + vmsg = '{0}'.format(e) + print vmsg + # check for RESP-file + if len(respfile) >= 1: + print "Found response file(s)!" + print "Reading meta data information ..." + for j in range(len(respfile)): + print respfile[j] + for i in range(len(st)): + # check, whether this trace has already been corrected + if len(st[i].stats) < 13: + try: + print "Correcting %s, %s for instrument response ..." \ + % (st[i].stats.station, st[i].stats.channel) + # get corner frequencies for pre-filtering + fny = st[i].stats.sampling_rate / 2 + fc21 = fny - (fny * 0.05) + fc22 = fny - (fny * 0.02) + prefilt = [0.5, 0.9, fc21, fc22] + # instrument correction + seedresp={'filename': respfile[0], 'date': st[0].stats.starttime, \ + 'units': "VEL"} + st[i].simulate(paz_remove=None, pre_filt=prefilt, seedresp=seedresp) + except ValueError, e: + vmsg = '{0}'.format(e) + print vmsg + + if len(respfile) < 1 and len(invfile) < 1 and len(dlfile) < 1: print "No dataless-SEED file,inventory-xml file nor RESP-file found!" print "Go on processing data without source parameter determination!" return st + def getEvtData(self): return self.evtdata From 96395f6a1abdf295113b1f494d1982e53e2ebb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 27 Aug 2015 11:40:42 +0200 Subject: [PATCH 0529/1144] restituteWFData: searches now for keyword processing to check, whether this trace has already been corrected or not. --- pylot/core/read/data.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 78dd1c7c..47a0fb1a 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import pdb import os import glob import matplotlib.pyplot as plt @@ -151,8 +152,8 @@ class Data(object): # remove underscores if tr.stats.station[3] == '_': tr.stats.station = tr.stats.station[0:3] - dlp = '%s/*.dataless' % invdlpath - invp = '%s/*.inv' % invdlpath + dlp = '%s/*.dless' % invdlpath + invp = '%s/*.xml' % invdlpath respp = '%s/*.resp' % invdlpath dlfile = glob.glob(dlp) invfile = glob.glob(invp) @@ -167,7 +168,9 @@ class Data(object): parser = Parser('%s' % dlfile[j]) for i in range(len(st)): # check, whether this trace has already been corrected - if len(st[i].stats) < 13: + try: + st[i].stats.processing + except: try: print "Correcting %s, %s for instrument response ..." \ % (st[i].stats.station, st[i].stats.channel) @@ -184,7 +187,6 @@ class Data(object): print vmsg else: print "Trace has already been corrected!" - # check for inventory-xml file if len(invfile) >= 1: print "Found inventory-xml file(s)!" @@ -194,7 +196,9 @@ class Data(object): inv = read_inventory(invfile[j], format="STATIONXML") for i in range(len(st)): # check, whether this trace has already been corrected - if len(st[i].stats) < 13: + try: + st[i].stats.processing + except: try: print "Correcting %s, %s for instrument response ..." \ % (st[i].stats.station, st[i].stats.channel) @@ -209,6 +213,8 @@ class Data(object): except ValueError, e: vmsg = '{0}'.format(e) print vmsg + else: + print "Trace has already been corrected!" # check for RESP-file if len(respfile) >= 1: print "Found response file(s)!" @@ -217,7 +223,9 @@ class Data(object): print respfile[j] for i in range(len(st)): # check, whether this trace has already been corrected - if len(st[i].stats) < 13: + try: + st[i].stats.processing + except: try: print "Correcting %s, %s for instrument response ..." \ % (st[i].stats.station, st[i].stats.channel) @@ -233,6 +241,8 @@ class Data(object): except ValueError, e: vmsg = '{0}'.format(e) print vmsg + else: + print "Trace has already been corrected!" if len(respfile) < 1 and len(invfile) < 1 and len(dlfile) < 1: print "No dataless-SEED file,inventory-xml file nor RESP-file found!" From 3dd65b9cc3c2dfb9cc70372b7079a6065ccd6c8d Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 27 Aug 2015 12:55:34 +0200 Subject: [PATCH 0530/1144] [addresses #167] started fixing the multiple phase saving issue --- QtPyLoT.py | 18 +++++++++++++++--- pylot/core/read/data.py | 7 ++++++- pylot/core/util/errors.py | 4 ++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 3d34ac8d..254e88f7 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -42,7 +42,8 @@ from pylot.core.read.data import Data from pylot.core.read.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent from pylot.core.util.defaults import FILTERDEFAULTS -from pylot.core.util.errors import FormatError, DatastructureError +from pylot.core.util.errors import FormatError, DatastructureError,\ + OverwriteError from pylot.core.util.connection import checkurl from pylot.core.util.utils import fnConstructor, createEvent, getLogin,\ createCreationInfo, getGlobalTimes @@ -399,7 +400,18 @@ class MainWindow(QMainWindow): def saveData(self): settings = QSettings() exform = settings.value('data/exportFormat', 'QUAKEML') - self.getData().applyEVTData(self.getPicks()) + try: + self.getData().applyEVTData(self.getPicks()) + except OverwriteError: + msgBox = QMessageBox() + msgBox.setText("Picks have been modified!") + msgBox.setInformativeText("Do you want to overwrite the picks and save?") + msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | + QMessageBox.Cancel) + msgBox.setDefaultButton(QMessageBox.Save) + ret = msgBox.exec_() + if ret == QMessageBox.Save: + print('Overwrite and Save') try: self.getData().exportEvent(self.fname, exform) except FormatError: @@ -407,7 +419,7 @@ class MainWindow(QMainWindow): except AttributeError, e: print 'warning: {0}'.format(e) directory = os.path.join(self.getRoot(), self.getEventFileName()) - file_filter = "Seismic observation files (*.cnv *.obs *.xml)" + file_filter = "QuakeML file (*.xml);;VELEST observation file format (*.cnv);;NonLinLoc observation file (*.obs)" fname = QFileDialog.getSaveFileName(self, 'Save event data ...', directory, file_filter) fbasename, exform = os.path.splitext(fname[0]) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index dad3bf56..84142b09 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -9,7 +9,7 @@ from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID from pylot.core.read.io import readPILOTEvent from pylot.core.util.utils import fnConstructor, getGlobalTimes -from pylot.core.util.errors import FormatError +from pylot.core.util.errors import FormatError, OverwriteError class Data(object): @@ -143,6 +143,9 @@ class Data(object): self.wfdata = self.getOriginalWFData().copy() self.dirty = False + def resetPicks(self): + self.getEvtData().picks = [] + def restituteWFData(self, fninventory): st = self.getWFData() inv = read_inventory(fninventory) @@ -157,6 +160,8 @@ class Data(object): def applyPicks(picks): firstonset = None + if self.getEvtData().picks: + raise OverwriteError('Actual picks would be overwritten!') for station, onsets in picks.items(): print 'Reading picks on station %s' % station for label, phase in onsets.items(): diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py index 6239a512..daf21d46 100644 --- a/pylot/core/util/errors.py +++ b/pylot/core/util/errors.py @@ -16,3 +16,7 @@ class FormatError(Exception): class DatastructureError(Exception): pass + + +class OverwriteError(IOError): + pass From 3e164fd0c6e73ea264cfcaa45885bbd96ff14542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 27 Aug 2015 14:24:52 +0200 Subject: [PATCH 0531/1144] restituteWFData: More output for user. --- pylot/core/read/data.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 47a0fb1a..18e2cabd 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -146,8 +146,12 @@ class Data(object): self.wfdata = self.getOriginalWFData().copy() self.dirty = False - def restituteWFData(self, invdlpath): - st = self.getWFData() + def restituteWFData(self, invdlpath, streams=None): + if streams == None: + st = self.getWFData() + else: + st = streams + for tr in st: # remove underscores if tr.stats.station[3] == '_': @@ -164,7 +168,7 @@ class Data(object): print "Found dataless-SEED file(s)!" print "Reading meta data information ..." for j in range(len(dlfile)): - print dlfile[j] + print "Found dataless-SEED file %s" % dlfile[j] parser = Parser('%s' % dlfile[j]) for i in range(len(st)): # check, whether this trace has already been corrected @@ -192,7 +196,7 @@ class Data(object): print "Found inventory-xml file(s)!" print "Reading meta data information ..." for j in range(len(invfile)): - print invfile[j] + print "Found inventory-xml file %s" % invfile[j] inv = read_inventory(invfile[j], format="STATIONXML") for i in range(len(st)): # check, whether this trace has already been corrected @@ -220,7 +224,7 @@ class Data(object): print "Found response file(s)!" print "Reading meta data information ..." for j in range(len(respfile)): - print respfile[j] + print "Found RESP-file %s" % respfile[j] for i in range(len(st)): # check, whether this trace has already been corrected try: From ee777b4beb114b6b1f4450105100976598ee525f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 27 Aug 2015 14:26:15 +0200 Subject: [PATCH 0532/1144] Removed import pdb. --- pylot/core/read/data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 18e2cabd..5af2473a 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import pdb import os import glob import matplotlib.pyplot as plt From 6597c881a6ae7cec3204a8a6809805c9b1c23b28 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 27 Aug 2015 15:42:51 +0200 Subject: [PATCH 0533/1144] reformatting code --- pylot/core/read/data.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 5c9df06e..35332e9f 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -145,7 +145,7 @@ class Data(object): self.wfdata = self.getOriginalWFData().copy() self.dirty = False -def resetPicks(self): + def resetPicks(self): self.getEvtData().picks = [] def restituteWFData(self, invdlpath, streams=None): @@ -156,7 +156,7 @@ def resetPicks(self): for tr in st: # remove underscores - if tr.stats.station[3] == '_': + if tr.stats.station[3] == '_': tr.stats.station = tr.stats.station[0:3] dlp = '%s/*.dless' % invdlpath invp = '%s/*.xml' % invdlpath @@ -192,7 +192,7 @@ def resetPicks(self): vmsg = '{0}'.format(e) print vmsg else: - print "Trace has already been corrected!" + print "Trace has already been corrected!" # check for inventory-xml file if len(invfile) >= 1: print "Found inventory-xml file(s)!" @@ -220,7 +220,7 @@ def resetPicks(self): vmsg = '{0}'.format(e) print vmsg else: - print "Trace has already been corrected!" + print "Trace has already been corrected!" # check for RESP-file if len(respfile) >= 1: print "Found response file(s)!" @@ -248,7 +248,7 @@ def resetPicks(self): vmsg = '{0}'.format(e) print vmsg else: - print "Trace has already been corrected!" + print "Trace has already been corrected!" if len(respfile) < 1 and len(invfile) < 1 and len(dlfile) < 1: print "No dataless-SEED file,inventory-xml file nor RESP-file found!" From 038caf4d54ad9536baea857b9f2eaca18f769f87 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Aug 2015 09:05:50 +0200 Subject: [PATCH 0534/1144] reformat code --- pylot/core/read/data.py | 311 ++++++++++++++++++++++++++++++++-------- 1 file changed, 254 insertions(+), 57 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 35332e9f..d5962b92 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -3,7 +3,6 @@ import os import glob -import matplotlib.pyplot as plt from obspy.xseed import Parser from obspy.core import read, Stream, UTCDateTime from obspy import readEvents, read_inventory @@ -57,31 +56,61 @@ class Data(object): return str(self.wfdata) def getParent(self): + """ + + + :return: + """ return self._parent def isNew(self): + """ + + + :return: + """ return self.newevent def getCutTimes(self): + """ + + + :return: + """ if self.cuttimes is None: self.updateCutTimes() return self.cuttimes def updateCutTimes(self): + """ + + + """ self.cuttimes = getGlobalTimes(self.getWFData()) def getEventFileName(self): + """ + + + :return: + """ ID = self.getID() # handle forbidden filenames especially on windows systems return fnConstructor(str(ID)) def exportEvent(self, fnout, fnext='.xml'): + """ + + :param fnout: + :param fnext: + :raise KeyError: + """ from pylot.core.util.defaults import OUTPUTFORMATS try: evtformat = OUTPUTFORMATS[fnext] - except KeyError, e: + except KeyError as e: errmsg = '{0}; selected file extension {1} not ' \ 'supported'.format(e, fnext) raise FormatError(errmsg) @@ -89,24 +118,42 @@ class Data(object): # try exporting event via ObsPy try: self.getEvtData().write(fnout + fnext, format=evtformat) - except KeyError, e: + except KeyError as e: raise KeyError('''{0} export format not implemented: {1}'''.format(evtformat, e)) def getComp(self): + """ + + + :return: + """ return self.comp def getID(self): + """ + + + :return: + """ try: return self.evtdata.get('resource_id').id except: return None def filterWFData(self, kwargs): + """ + + :param kwargs: + """ self.getWFData().filter(**kwargs) self.dirty = True def setWFData(self, fnames): + """ + + :param fnames: + """ self.wfdata = Stream() self.wforiginal = None if fnames is not None: @@ -115,6 +162,10 @@ class Data(object): self.dirty = False def appendWFData(self, fnames): + """ + + :param fnames: + """ assert isinstance(fnames, list), "input parameter 'fnames' is " \ "supposed to be of type 'list' " \ "but is actually {0}".format(type( @@ -129,27 +180,51 @@ class Data(object): except TypeError: try: self.wfdata += read(fname, format='GSE2') - except Exception, e: + except Exception as e: warnmsg += '{0}\n{1}\n'.format(fname, e) if warnmsg: warnmsg = 'WARNING: unable to read\n' + warnmsg - print warnmsg + print(warnmsg) def getWFData(self): + """ + + + :return: + """ return self.wfdata def getOriginalWFData(self): + """ + + + :return: + """ return self.wforiginal def resetWFData(self): + """ + + + """ self.wfdata = self.getOriginalWFData().copy() self.dirty = False def resetPicks(self): + """ + + + """ self.getEvtData().picks = [] def restituteWFData(self, invdlpath, streams=None): - if streams == None: + """ + + :param invdlpath: + :param streams: + :return: + """ + if streams is None: st = self.getWFData() else: st = streams @@ -157,7 +232,7 @@ class Data(object): for tr in st: # remove underscores if tr.stats.station[3] == '_': - tr.stats.station = tr.stats.station[0:3] + tr.stats.station = tr.stats.station[0:3] dlp = '%s/*.dless' % invdlpath invp = '%s/*.xml' % invdlpath respp = '%s/*.resp' % invdlpath @@ -167,47 +242,52 @@ class Data(object): # check for dataless-SEED file if len(dlfile) >= 1: - print "Found dataless-SEED file(s)!" - print "Reading meta data information ..." + print("Found dataless-SEED file(s)!") + print("Reading meta data information ...") for j in range(len(dlfile)): - print "Found dataless-SEED file %s" % dlfile[j] - parser = Parser('%s' % dlfile[j]) - for i in range(len(st)): + print("Found dataless-SEED file %s" % dlfile[j]) + parser = Parser('%s' % dlfile[j]) + for i in range(len(st)): # check, whether this trace has already been corrected try: st[i].stats.processing except: - try: - print "Correcting %s, %s for instrument response ..." \ - % (st[i].stats.station, st[i].stats.channel) + try: + print( + "Correcting %s, %s for instrument response ..." \ + % (st[i].stats.station, st[i].stats.channel)) # get corner frequencies for pre-filtering fny = st[i].stats.sampling_rate / 2 fc21 = fny - (fny * 0.05) fc22 = fny - (fny * 0.02) prefilt = [0.5, 0.9, fc21, fc22] # instrument correction - st[i].simulate(pre_filt=prefilt, seedresp={'filename': parser, \ - 'date': st[i].stats.starttime, 'units': "VEL"}) - except ValueError, e: - vmsg = '{0}'.format(e) - print vmsg + st[i].simulate(pre_filt=prefilt, + seedresp={'filename': parser, \ + 'date': st[ + i].stats.starttime, + 'units': "VEL"}) + except ValueError as e: + vmsg = '{0}'.format(e) + print(vmsg) else: - print "Trace has already been corrected!" + print("Trace has already been corrected!") # check for inventory-xml file if len(invfile) >= 1: - print "Found inventory-xml file(s)!" - print "Reading meta data information ..." + print("Found inventory-xml file(s)!") + print("Reading meta data information ...") for j in range(len(invfile)): - print "Found inventory-xml file %s" % invfile[j] + print("Found inventory-xml file %s" % invfile[j]) inv = read_inventory(invfile[j], format="STATIONXML") - for i in range(len(st)): + for i in range(len(st)): # check, whether this trace has already been corrected try: st[i].stats.processing except: - try: - print "Correcting %s, %s for instrument response ..." \ - % (st[i].stats.station, st[i].stats.channel) + try: + print( + "Correcting %s, %s for instrument response ..." \ + % (st[i].stats.station, st[i].stats.channel)) # get corner frequencies for pre-filtering fny = st[i].stats.sampling_rate / 2 fc21 = fny - (fny * 0.05) @@ -216,57 +296,78 @@ class Data(object): # instrument correction st[i].attach_response(inv) st[i].remove_response(output='VEL', pre_filt=prefilt) - except ValueError, e: - vmsg = '{0}'.format(e) - print vmsg - else: - print "Trace has already been corrected!" + except ValueError as e: + vmsg = '{0}'.format(e) + print(vmsg) + else: + print("Trace has already been corrected!") # check for RESP-file if len(respfile) >= 1: - print "Found response file(s)!" - print "Reading meta data information ..." + print("Found response file(s)!") + print("Reading meta data information ...") for j in range(len(respfile)): - print "Found RESP-file %s" % respfile[j] - for i in range(len(st)): + print("Found RESP-file %s" % respfile[j]) + for i in range(len(st)): # check, whether this trace has already been corrected try: st[i].stats.processing except: - try: - print "Correcting %s, %s for instrument response ..." \ - % (st[i].stats.station, st[i].stats.channel) + try: + print( + "Correcting %s, %s for instrument response ..." \ + % (st[i].stats.station, st[i].stats.channel)) # get corner frequencies for pre-filtering fny = st[i].stats.sampling_rate / 2 fc21 = fny - (fny * 0.05) fc22 = fny - (fny * 0.02) prefilt = [0.5, 0.9, fc21, fc22] # instrument correction - seedresp={'filename': respfile[0], 'date': st[0].stats.starttime, \ - 'units': "VEL"} + seedresp={'filename': respfile[0], 'date': st[0].stats.starttime, \ + 'units': "VEL"} st[i].simulate(paz_remove=None, pre_filt=prefilt, seedresp=seedresp) - except ValueError, e: - vmsg = '{0}'.format(e) - print vmsg - else: - print "Trace has already been corrected!" + except ValueError as e: + vmsg = '{0}'.format(e) + print(vmsg) + else: + print("Trace has already been corrected!") if len(respfile) < 1 and len(invfile) < 1 and len(dlfile) < 1: - print "No dataless-SEED file,inventory-xml file nor RESP-file found!" - print "Go on processing data without source parameter determination!" + print( + "No dataless-SEED file,inventory-xml file nor RESP-file found!") + print( + "Go on processing data without source parameter determination!") return st def getEvtData(self): + """ + + + :return: + """ return self.evtdata def applyEVTData(self, data, type='pick', authority_id='rub'): + """ + + :param data: + :param type: + :param authority_id: + :raise OverwriteError: + """ + def applyPicks(picks): + """ + + :param picks: + :raise OverwriteError: + """ firstonset = None if self.getEvtData().picks: raise OverwriteError('Actual picks would be overwritten!') for station, onsets in picks.items(): - print 'Reading picks on station %s' % station + print('Reading picks on station %s' % station) for label, phase in onsets.items(): onset = phase['mpp'] epp = phase['epp'] @@ -282,8 +383,8 @@ class Data(object): self.getEvtData().picks.append(pick) try: polarity = phase['fm'] - except KeyError, e: - print 'No polarity information found for %s' % phase + except KeyError as e: + print('No polarity information found for %s' % phase) if firstonset is None or firstonset > onset: firstonset = onset @@ -294,9 +395,17 @@ class Data(object): self.getEvtData().resource_id = ID def applyArrivals(arrivals): + """ + + :param arrivals: + """ pass def applyEvent(event): + """ + + :param event: + """ pass applydata = {'pick': applyPicks, @@ -322,6 +431,10 @@ class GenericDataStructure(object): def modifyFields(self, **kwargs): + """ + + :param kwargs: + """ assert isinstance(kwargs, dict), 'dictionary type object expected' if not self.extraAllowed(): @@ -337,37 +450,67 @@ class GenericDataStructure(object): value = str(value) try: self.setFieldValue(key, value) - except KeyError, e: + except KeyError as e: errmsg = '' errmsg += 'WARNING:\n' errmsg += 'unable to set values for datastructure fields\n' errmsg += '%s; desired value was: %s\n' % (e, value) - print errmsg + print(errmsg) def isField(self, key): + """ + + :param key: + :return: + """ return key in self.getFields().keys() def getFieldValue(self, key): + """ + + :param key: + :return: + """ if self.isField(key): return self.getFields()[key] else: return def setFieldValue(self, key, value): + """ + + :param key: + :param value: + :raise KeyError: + """ if not self.extraAllowed() and key not in self.getAllowed(): raise KeyError else: if not self.isField(key): - print 'creating new field "%s"' % key + print('creating new field "%s"' % key) self.getFields()[key] = value def getFields(self): + """ + + + :return: + """ return self.dsFields def getExpandFields(self): + """ + + + :return: + """ return self.expandFields def setExpandFields(self, keys): + """ + + :param keys: + """ expandFields = [] for key in keys: if self.isField(key): @@ -375,18 +518,38 @@ class GenericDataStructure(object): self.expandFields = expandFields def getAllowed(self): + """ + + + :return: + """ return self.allowedFields def extraAllowed(self): + """ + + + :return: + """ return not self.allowedFields def updateNotAllowed(self, kwargs): + """ + + :param kwargs: + :return: + """ for key in kwargs: if key not in self.getAllowed(): kwargs.__delitem__(key) return kwargs def hasSuffix(self): + """ + + + :return: + """ try: self.getFieldValue('suffix') except KeyError: @@ -397,6 +560,11 @@ class GenericDataStructure(object): return False def expandDataPath(self): + """ + + + :return: + """ expandList = [] for item in self.getExpandFields(): expandList.append(self.getFieldValue(item)) @@ -405,6 +573,11 @@ class GenericDataStructure(object): return os.path.join(*expandList) def getCatalogName(self): + """ + + + :return: + """ return os.path.join(self.getFieldValue('root'), 'catalog.qml') @@ -463,6 +636,10 @@ class SeiscompDataStructure(GenericDataStructure): self.modifiyFields(**kwargs) def modifiyFields(self, **kwargs): + """ + + :param kwargs: + """ if kwargs and isinstance(kwargs, dict): for key, value in kwargs.iteritems(): key = str(key) @@ -473,14 +650,19 @@ class SeiscompDataStructure(GenericDataStructure): value = str(value) try: self.setField(key, value) - except KeyError, e: + except KeyError as e: errmsg = '' errmsg += 'WARNING:\n' errmsg += 'unable to set values for SDS fields\n' errmsg += '%s; desired value was: %s\n' % (e, value) - print errmsg + print(errmsg) def setFieldValue(self, key, value): + """ + + :param key: + :param value: + """ if self.isField(key): self.getFields()[key] = value else: @@ -488,12 +670,27 @@ class SeiscompDataStructure(GenericDataStructure): '{field}'.format(field=key)) def getFields(self): + """ + + + :return: + """ return self.__sdsFields def getName(self): + """ + + + :return: + """ return self.__name def expandDataPath(self): + """ + + + :return: + """ fullChan = '{0}.{1}'.format(self.getFields()['CHAN'], self.getType()) dataPath = os.path.join(self.getFields()['SDSdir'], self.getFields()['YEAR'], From 497ca39c91b1a7622e6ccca7c727c4ae3cf213d3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 28 Aug 2015 10:15:28 +0200 Subject: [PATCH 0535/1144] reformatted data.py to meet coding conventions --- pylot/core/read/data.py | 79 +++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index d5962b92..f3e07d30 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -14,20 +14,17 @@ from pylot.core.util.errors import FormatError, OverwriteError class Data(object): - ''' + """ Data container with attributes wfdata holding ~obspy.core.stream. :type parent: PySide.QtGui.QWidget object, optional :param parent: A PySide.QtGui.QWidget object utilized when called by a GUI to display a PySide.QtGui.QMessageBox instead of printing to standard out. - :type wfdata: ~obspy.core.stream.Stream object, optional - :param wfdata: ~obspy.core.stream.Stream object containing all available - waveform data for the actual event :type evtdata: ~obspy.core.event.Event object, optional :param evtdata ~obspy.core.event.Event object containing all derived or loaded event. Container object holding, e.g. phase arrivals, etc. - ''' + """ def __init__(self, parent=None, evtdata=None): self._parent = parent @@ -168,8 +165,8 @@ class Data(object): """ assert isinstance(fnames, list), "input parameter 'fnames' is " \ "supposed to be of type 'list' " \ - "but is actually {0}".format(type( - fnames)) + "but is actually" \ + " {0}".format(type(fnames)) if self.dirty: self.resetWFData() @@ -254,8 +251,9 @@ class Data(object): except: try: print( - "Correcting %s, %s for instrument response ..." \ - % (st[i].stats.station, st[i].stats.channel)) + "Correcting %s, %s for instrument response " + "..." % (st[i].stats.station, + st[i].stats.channel)) # get corner frequencies for pre-filtering fny = st[i].stats.sampling_rate / 2 fc21 = fny - (fny * 0.05) @@ -263,7 +261,7 @@ class Data(object): prefilt = [0.5, 0.9, fc21, fc22] # instrument correction st[i].simulate(pre_filt=prefilt, - seedresp={'filename': parser, \ + seedresp={'filename': parser, 'date': st[ i].stats.starttime, 'units': "VEL"}) @@ -285,9 +283,9 @@ class Data(object): st[i].stats.processing except: try: - print( - "Correcting %s, %s for instrument response ..." \ - % (st[i].stats.station, st[i].stats.channel)) + print("Correcting %s, %s for instrument response " + "..." % (st[i].stats.station, + st[i].stats.channel)) # get corner frequencies for pre-filtering fny = st[i].stats.sampling_rate / 2 fc21 = fny - (fny * 0.05) @@ -295,7 +293,8 @@ class Data(object): prefilt = [0.5, 0.9, fc21, fc22] # instrument correction st[i].attach_response(inv) - st[i].remove_response(output='VEL', pre_filt=prefilt) + st[i].remove_response(output='VEL', + pre_filt=prefilt) except ValueError as e: vmsg = '{0}'.format(e) print(vmsg) @@ -313,18 +312,20 @@ class Data(object): st[i].stats.processing except: try: - print( - "Correcting %s, %s for instrument response ..." \ - % (st[i].stats.station, st[i].stats.channel)) + print("Correcting %s, %s for instrument response " + "..." % (st[i].stats.station, + st[i].stats.channel)) # get corner frequencies for pre-filtering fny = st[i].stats.sampling_rate / 2 fc21 = fny - (fny * 0.05) fc22 = fny - (fny * 0.02) prefilt = [0.5, 0.9, fc21, fc22] # instrument correction - seedresp={'filename': respfile[0], 'date': st[0].stats.starttime, \ - 'units': "VEL"} - st[i].simulate(paz_remove=None, pre_filt=prefilt, seedresp=seedresp) + seedresp = {'filename': respfile[0], + 'date': st[0].stats.starttime, + 'units': "VEL"} + st[i].simulate(paz_remove=None, pre_filt=prefilt, + seedresp=seedresp) except ValueError as e: vmsg = '{0}'.format(e) print(vmsg) @@ -332,10 +333,10 @@ class Data(object): print("Trace has already been corrected!") if len(respfile) < 1 and len(invfile) < 1 and len(dlfile) < 1: - print( - "No dataless-SEED file,inventory-xml file nor RESP-file found!") - print( - "Go on processing data without source parameter determination!") + print("No dataless-SEED file,inventory-xml file nor RESP-file " + "found!") + print("Go on processing data without source parameter " + "determination!") return st @@ -416,10 +417,10 @@ class Data(object): class GenericDataStructure(object): - ''' + """ GenericDataBase type holds all information about the current data- base working on. - ''' + """ def __init__(self, **kwargs): @@ -582,10 +583,10 @@ class GenericDataStructure(object): class PilotDataStructure(GenericDataStructure): - ''' + """ Object containing the data access information for the old PILOT data structure. - ''' + """ def __init__(self, **fields): if not fields: @@ -598,14 +599,14 @@ class PilotDataStructure(GenericDataStructure): class SeiscompDataStructure(GenericDataStructure): - ''' + """ Dictionary containing the data access information for an SDS data archive: :param str dataType: Desired data type. Default: ``'waveform'`` :param sdate, edate: Either date string or an instance of :class:`obspy.core.utcdatetime.UTCDateTime. Default: ``None`` :type sdate, edate: str or UTCDateTime or None - ''' + """ def __init__(self, rootpath='/data/SDS', dataformat='MSEED', filesuffix=None, **kwargs): @@ -649,7 +650,7 @@ class SeiscompDataStructure(GenericDataStructure): else: value = str(value) try: - self.setField(key, value) + self.setFieldValue(key, value) except KeyError as e: errmsg = '' errmsg += 'WARNING:\n' @@ -669,22 +670,6 @@ class SeiscompDataStructure(GenericDataStructure): print('Warning: trying to set value of non-existent field ' '{field}'.format(field=key)) - def getFields(self): - """ - - - :return: - """ - return self.__sdsFields - - def getName(self): - """ - - - :return: - """ - return self.__name - def expandDataPath(self): """ From ef8ebc300e1fd6261405d5b4d1617070c70336cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 28 Aug 2015 11:27:09 +0200 Subject: [PATCH 0536/1144] New object to calculate magnitude. Finished class wapp to calculate amplitude as seen on Wood-Anderson seismograph. --- pylot/core/analysis/magnitude.py | 124 +++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 pylot/core/analysis/magnitude.py diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py new file mode 100644 index 00000000..c9db0ef2 --- /dev/null +++ b/pylot/core/analysis/magnitude.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +""" +Created August/September 2015. + +:author: Ludger Küperkoch / MAGS2 EP3 working group +""" + +import matplotlib.pyplot as plt +import numpy as np +from obspy.core import Stream +from pylot.core.pick.utils import getsignalwin + +class Magnitude(object): + ''' + Superclass for calculating Wood-Anderson peak-to-peak + amplitudes, local magnitudes and moment magnitudes. + ''' + + def __init__(self, wfstream, To, pwin, iplot): + ''' + :param: wfstream + :type: `~obspy.core.stream.Stream + + :param: To, onset time, P- or S phase + :type: float + + :param: pwin, pick window [To To+pwin] to get maximum + peak-to-peak amplitude + :type: float + + :param: iplot, no. of figure window for plotting interims results + :type: integer + + ''' + + assert isinstance(wfstream, Stream), "%s is not a stream object" % str(wfstream) + + self.setwfstream(wfstream) + self.setTo(To) + self.setpwin(pwin) + self.setiplot(iplot) + self.calcwapp() + + + def getwfstream(self): + return self.wfstream + + def setwfstream(self, wfstream): + self.wfstream = wfstream + + def getTo(self): + return self.To + + def setTo(self, To): + self.To = To + + def getpwin(self): + return self.pwin + + def setpwin(self, pwin): + self.pwin = pwin + + def getiplot(self): + return self.iplot + + def setiplot(self, iplot): + self.iplot = iplot + + def getwapp(self): + return self.wapp + + def calcwapp(self): + self.wapp = None + +class WApp(Magnitude): + ''' + Method to derive peak-to-peak amplitude as seen on a Wood-Anderson- + seismograph. Has to be derived from corrected traces! + ''' + + def calcwapp(self): + print "Getting Wood-Anderson peak-to-peak amplitude ..." + print "Simulating Wood-Anderson seismograph ..." + + self.wapp = None + stream = self.getwfstream() + + # poles, zeros and sensitivity of WA seismograph + # (see Uhrhammer & Collins, 1990, BSSA, pp. 702-716) + paz_wa = { + 'poles': [5.6089 - 5.4978j, -5.6089 - 5.4978j], + 'zeros': [0j, 0j], + 'gain': 2080, + 'sensitivity': 1} + + stream.simulate(paz_remove=None, paz_simulate=paz_wa) + + trH1 = stream[0].data + trH2 = stream[1].data + ilen = min([len(trH1), len(trH2)]) + # get RMS of both horizontal components + sqH = np.sqrt(np.power(trH1[0:ilen], 2) + np.power(trH2[0:ilen], 2)) + # get time array + th = np.arange(0, len(sqH) * stream[0].stats.delta, stream[0].stats.delta) + # get maximum peak within pick window + iwin = getsignalwin(th, self.getTo(), self.getpwin()) + self.wapp = np.max(sqH[iwin]) + print "Determined Wood-Anderson peak-to-peak amplitude: %f mm" % self.wapp + if self.getiplot() > 1: + stream.plot() + f = plt.figure(2) + plt.plot(th, sqH) + plt.plot(th[iwin], sqH[iwin], 'g') + plt.plot([self.getTo(), self.getTo()], [0, max(sqH)], 'r', linewidth=2) + plt.title('Station %s, RMS Horizontal Traces, WA-peak-to-peak=%4.1f mm' \ + % (stream[0].stats.station, self.wapp)) + plt.xlabel('Time [s]') + plt.ylabel('Displacement [mm]') + plt.show() + raw_input() + plt.close(f) + + + From 533ccc7b5c383d4fd6139f2bc9b12e38d532444d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 28 Aug 2015 11:29:00 +0200 Subject: [PATCH 0537/1144] Implemented new class wapp to calculate Wood-Anderson amplitudes for local magnitude calculation. Before calculating Wood-Anderson amplitude the certain traces are instrument corrected. --- pylot/core/pick/autopick.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 3c074a7d..7113d90a 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -15,6 +15,8 @@ from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ getSNR, fmpicker, checkPonsets, wadaticheck +from pylot.core.read.data import Data +from pylot.core.analysis.magnitude import WApp def autopickevent(data, param): stations = [] @@ -109,6 +111,8 @@ def autopickstation(wfstream, pickparam): nfacsl = pickparam.getParam('noisefactor') # parameter to check for spuriously picked S onset zfac = pickparam.getParam('zfac') + # path to inventory-, dataless- or resp-files + invdir = pickparam.getParam('invdir') # initialize output Pweight = 4 # weight for P onset @@ -132,6 +136,7 @@ def autopickstation(wfstream, pickparam): Pflag = 0 Sflag = 0 Pmarker = [] + Ao = None # split components zdat = wfstream.select(component="Z") @@ -501,6 +506,20 @@ def autopickstation(wfstream, pickparam): print 'autopickstation: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( Sweight, SNRS, SNRSdB) + ################################################################## + # get Wood-Anderson peak-to-peak amplitude + print "################################################" + # initialize Data object + data = Data() + # re-create stream object including both horizontal components + hdat = edat.copy() + hdat += ndat + h_copy = hdat.copy() + cordat = data.restituteWFData(invdir, h_copy) + # calculate WA-peak-to-peak amplitude + # using subclass WApp of superclass Magnitude + wapp = WApp(cordat, mpickS, 10, iplot) + Ao = wapp.getwapp() else: print 'Bad initial (AIC) S-pick, skipping this onset!' @@ -509,6 +528,21 @@ def autopickstation(wfstream, pickparam): print '(min. AIC-SNR=', minAICSSNR, ', min. AIC-Slope=', \ minAICSslope, 'counts/s)' + ############################################################ + # get Wood-Anderson peak-to-peak amplitude + print "################################################" + # initialize Data object + data = Data() + # re-create stream object including both horizontal components + hdat = edat.copy() + hdat += ndat + h_copy = hdat.copy() + cordat = data.restituteWFData(invdir, h_copy) + # calculate WA-peak-to-peak amplitude + # using subclass WApp of superclass Magnitude + wapp = WApp(cordat, mpickP, 20, iplot) + Ao = wapp.getwapp() + else: print 'autopickstation: No horizontal component data available or ' \ 'bad P onset, skipping S picking!' @@ -693,5 +727,7 @@ def autopickstation(wfstream, pickparam): phasepick = {'lpp': lpickS, 'epp': epickS, 'mpp': mpickS, 'spe': Serror, \ 'snr': SNRS, 'snrdb': SNRSdB, 'weight': Sweight, 'fm': None} picks[phase] = phasepick + # add Wood-Anderson amplitude + picks[phase]['Ao'] = Ao return picks From 827a07a7ef652c9ba8417a3d89460070625c8b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 28 Aug 2015 11:31:19 +0200 Subject: [PATCH 0538/1144] Skipped restitution of entire traces at the beginning of processing as this is too time consuming. Instead, only traces are corrected within autopickstation, where at least automatically a P pick has been set. --- autoPyLoT.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index c4e941ea..3084f1ad 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -70,8 +70,6 @@ def autoPyLoT(inputfile): # get path to inventory or dataless-seed file with station meta data invdir = parameter.getParam('invdir') - # get corner frequencies for pre-filtering traces - prefilt = parameter.getParam('prefilt') # multiple event processing # read each event in database @@ -83,8 +81,6 @@ def autoPyLoT(inputfile): print data wfdat = data.getWFData() # all available streams - # restitute waveform data getting responses from inventory-file - wfdat = data.restituteWFData(invdir, prefilt) ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) @@ -100,8 +96,6 @@ def autoPyLoT(inputfile): print data wfdat = data.getWFData() # all available streams - # restitute waveform data getting responses from inventory-file - wfdat = data.restituteWFData(invdir, prefilt) ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) From eb592a3426d970da0c5d6524a43a58ecc77d732f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 28 Aug 2015 11:39:39 +0200 Subject: [PATCH 0539/1144] Claculation of Wood-Anderson amplitude only, if S-weight < 4. --- pylot/core/pick/autopick.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 7113d90a..9c84d38f 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -507,19 +507,20 @@ def autopickstation(wfstream, pickparam): print 'autopickstation: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( Sweight, SNRS, SNRSdB) ################################################################## - # get Wood-Anderson peak-to-peak amplitude - print "################################################" - # initialize Data object - data = Data() - # re-create stream object including both horizontal components - hdat = edat.copy() - hdat += ndat - h_copy = hdat.copy() - cordat = data.restituteWFData(invdir, h_copy) - # calculate WA-peak-to-peak amplitude - # using subclass WApp of superclass Magnitude - wapp = WApp(cordat, mpickS, 10, iplot) - Ao = wapp.getwapp() + if Sweight < 4: + # get Wood-Anderson peak-to-peak amplitude + print "################################################" + # initialize Data object + data = Data() + # re-create stream object including both horizontal components + hdat = edat.copy() + hdat += ndat + h_copy = hdat.copy() + cordat = data.restituteWFData(invdir, h_copy) + # calculate WA-peak-to-peak amplitude + # using subclass WApp of superclass Magnitude + wapp = WApp(cordat, mpickS, 10, iplot) + Ao = wapp.getwapp() else: print 'Bad initial (AIC) S-pick, skipping this onset!' From 1f7049691c5ed78145836165668bf2d5dc6c6f5f Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 28 Aug 2015 16:01:42 +0200 Subject: [PATCH 0540/1144] [addresses #167] started fixing the multiple phase saving issue --- QtPyLoT.py | 13 +++++++++---- pylot/RELEASE-VERSION | 2 +- pylot/core/read/data.py | 13 +++++++++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 254e88f7..d14c51a0 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -405,13 +405,16 @@ class MainWindow(QMainWindow): except OverwriteError: msgBox = QMessageBox() msgBox.setText("Picks have been modified!") - msgBox.setInformativeText("Do you want to overwrite the picks and save?") - msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | - QMessageBox.Cancel) + msgBox.setInformativeText("Do you want to save the changes and overwrite the picks?") + msgBox.setDetailedText(self.getData().getPicksStr()) + msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Save) ret = msgBox.exec_() if ret == QMessageBox.Save: - print('Overwrite and Save') + self.getData().resetPicks() + self.saveData() + elif ret == QMessageBox.Cancel: + return False try: self.getData().exportEvent(self.fname, exform) except FormatError: @@ -425,6 +428,8 @@ class MainWindow(QMainWindow): fbasename, exform = os.path.splitext(fname[0]) if not fbasename: return False + elif not exform: + exform = fname[1].split('*')[1][:-1] self.getData().exportEvent(fbasename, exform) return True diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 52c00f77..541bd94d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -1abc-dirty +497c-dirty diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index f3e07d30..3c83e9aa 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -52,6 +52,13 @@ class Data(object): def __str__(self): return str(self.wfdata) + def getPicksStr(self): + picks_str = '' + for pick in self.getEvtData().picks: + picks_str += str(pick) + '\n' + return picks_str + + def getParent(self): """ @@ -360,9 +367,11 @@ class Data(object): def applyPicks(picks): """ - + Creates ObsPy pick objects and append it to the picks list from the + PyLoT dictionary contain all picks. :param picks: - :raise OverwriteError: + :raise OverwriteError: raises an OverwriteError if the picks list is + not empty. The GUI will then ask for a decision. """ firstonset = None if self.getEvtData().picks: From d0d38b81cb06b77a26e92147874aebf2b6644a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 31 Aug 2015 09:38:18 +0200 Subject: [PATCH 0541/1144] Removed indentation error. --- pylot/core/read/data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 3c83e9aa..e543e04f 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -305,8 +305,8 @@ class Data(object): except ValueError as e: vmsg = '{0}'.format(e) print(vmsg) - else: - print("Trace has already been corrected!") + else: + print("Trace has already been corrected!") # check for RESP-file if len(respfile) >= 1: print("Found response file(s)!") From d756f5d2e1c45b717f899ae2434bc533a4b0bb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 31 Aug 2015 10:10:42 +0200 Subject: [PATCH 0542/1144] Replaced hard coded window length for getting Wood-Anderson peak-to-peak amplitude with formerly set window length for calculating CF for S-picking. --- pylot/core/pick/autopick.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 9c84d38f..b8532638 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -519,7 +519,7 @@ def autopickstation(wfstream, pickparam): cordat = data.restituteWFData(invdir, h_copy) # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude - wapp = WApp(cordat, mpickS, 10, iplot) + wapp = WApp(cordat, mpickS, mpickP + sstop, iplot) Ao = wapp.getwapp() else: @@ -541,7 +541,7 @@ def autopickstation(wfstream, pickparam): cordat = data.restituteWFData(invdir, h_copy) # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude - wapp = WApp(cordat, mpickP, 20, iplot) + wapp = WApp(cordat, mpickP, mpickP + sstop, iplot) Ao = wapp.getwapp() else: From fb3b599f50dcd7660fc03c1e56f7e0e8f97d63f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 31 Aug 2015 10:24:17 +0200 Subject: [PATCH 0543/1144] restituteWFData: If input streams is None, a copy of streams derived by self.getWFData() is used for further processing. --- pylot/core/read/data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 3c83e9aa..7e12f793 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -229,7 +229,8 @@ class Data(object): :return: """ if streams is None: - st = self.getWFData() + st_raw = self.getWFData() + st = st_raw.copy() else: st = streams From 623f5d7b7eaca3992f4023898664be22fbba0a12 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 31 Aug 2015 13:37:18 +0200 Subject: [PATCH 0544/1144] [addresses #167] started fixing the multiple phase saving issue --- QtPyLoT.py | 54 ++++++++++++++++++++++++++++++++----------- pylot/RELEASE-VERSION | 2 +- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 254e88f7..23f8cc88 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -76,6 +76,7 @@ class MainWindow(QMainWindow): "Authority") settings.setValue("agency_id", agency) self.recentEvents = settings.value("data/recentEvents", []) + self.fname = None self.fnames = None structure_setting = settings.value("data/Structure", "PILOT") self.dataStructure = DATASTRUCTURE[structure_setting]() @@ -103,8 +104,8 @@ class MainWindow(QMainWindow): # load and display waveform data self.dirty = False - self.loadWaveformData() self.loadData() + self.loadWaveformData() self.updateFilterOptions() def setupUi(self): @@ -362,6 +363,9 @@ class MainWindow(QMainWindow): def getLastEvent(self): return self.recentEvents[0] + def addRecentEvent(self, event): + self.recentEvents.insert(0, event) + def getWFFnames(self): try: evt = self.getData().getEvtData() @@ -394,11 +398,36 @@ class MainWindow(QMainWindow): else: return + def getFileName(self): + return self.fname + + def setFileName(self, fname): + if self.getFileName() is not None: + self.addRecentEvent(self.getFileName()) + self.fname = fname + def getEventFileName(self): - return self.getData().getEventFileName() + if self.getFileName() is None: + self.setFileName(self.getData().getEventFileName()) + return self.getFileName() def saveData(self): + + def getSavePath(e): + print 'warning: {0}'.format(e) + directory = os.path.join(self.getRoot(), self.getEventFileName()) + file_filter = "QuakeML file (*.xml);;VELEST observation file format (*.cnv);;NonLinLoc observation file (*.obs)" + fname = QFileDialog.getSaveFileName(self, 'Save event data ...', + directory, file_filter) + + fbasename, exform = os.path.splitext(fname[0]) + + if not exform: + exform = file_filter[0].split('*')[1][:-1] + return fbasename, exform + settings = QSettings() + fbasename = self.getEventFileName() exform = settings.value('data/exportFormat', 'QUAKEML') try: self.getData().applyEVTData(self.getPicks()) @@ -413,19 +442,16 @@ class MainWindow(QMainWindow): if ret == QMessageBox.Save: print('Overwrite and Save') try: - self.getData().exportEvent(self.fname, exform) - except FormatError: - return False - except AttributeError, e: - print 'warning: {0}'.format(e) - directory = os.path.join(self.getRoot(), self.getEventFileName()) - file_filter = "QuakeML file (*.xml);;VELEST observation file format (*.cnv);;NonLinLoc observation file (*.obs)" - fname = QFileDialog.getSaveFileName(self, 'Save event data ...', - directory, file_filter) - fbasename, exform = os.path.splitext(fname[0]) - if not fbasename: - return False self.getData().exportEvent(fbasename, exform) + except FormatError as e: + fbasename, exform = getSavePath(e) + except AttributeError as e: + fbasename, exform = getSavePath(e) + if not fbasename: + return False + self.getData().exportEvent(fbasename, exform) + self.setDirty(False) + self.updateStatus('Event saved as %s' % (fbasename + exform)) return True def getComponent(self): diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 52c00f77..541bd94d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -1abc-dirty +497c-dirty From de5c65bc1d62c7492fbe7a69260f4e45286dfc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 1 Sep 2015 14:25:48 +0200 Subject: [PATCH 0545/1144] Wood-Anderson peak-to-peak amplitude is now calculated even if S weight is 4. At least P pick must be confident, if S weight > 3 a larger window for getting maximum peak-to-peak amplitude is used. --- pylot/core/pick/autopick.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index b8532638..89726764 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -507,20 +507,26 @@ def autopickstation(wfstream, pickparam): print 'autopickstation: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( Sweight, SNRS, SNRSdB) ################################################################## + # get Wood-Anderson peak-to-peak amplitude + print "################################################" + # initialize Data object + data = Data() + # re-create stream object including both horizontal components + hdat = edat.copy() + hdat += ndat + h_copy = hdat.copy() + cordat = data.restituteWFData(invdir, h_copy) + # calculate WA-peak-to-peak amplitude + # using subclass WApp of superclass Magnitude if Sweight < 4: - # get Wood-Anderson peak-to-peak amplitude - print "################################################" - # initialize Data object - data = Data() - # re-create stream object including both horizontal components - hdat = edat.copy() - hdat += ndat - h_copy = hdat.copy() - cordat = data.restituteWFData(invdir, h_copy) - # calculate WA-peak-to-peak amplitude - # using subclass WApp of superclass Magnitude wapp = WApp(cordat, mpickS, mpickP + sstop, iplot) Ao = wapp.getwapp() + else: + # use larger window for getting peak-to-peak amplitude + # as the S pick is quite unsure + wapp = WApp(cordat, mpickP, mpickP + sstop + \ + (0.5 * (mpickP + sstop)), iplot) + Ao = wapp.getwapp() else: print 'Bad initial (AIC) S-pick, skipping this onset!' @@ -541,7 +547,8 @@ def autopickstation(wfstream, pickparam): cordat = data.restituteWFData(invdir, h_copy) # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude - wapp = WApp(cordat, mpickP, mpickP + sstop, iplot) + wapp = WApp(cordat, mpickP, mpickP + sstop + (0.5 * (mpickP \ + + sstop)), iplot) Ao = wapp.getwapp() else: From 258b8b1ff4b7cde0b25912a8049482fe6bff4d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 1 Sep 2015 14:30:54 +0200 Subject: [PATCH 0546/1144] Marginal changes only. --- pylot/core/pick/autopick.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 89726764..6011f645 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -520,13 +520,13 @@ def autopickstation(wfstream, pickparam): # using subclass WApp of superclass Magnitude if Sweight < 4: wapp = WApp(cordat, mpickS, mpickP + sstop, iplot) - Ao = wapp.getwapp() else: # use larger window for getting peak-to-peak amplitude # as the S pick is quite unsure wapp = WApp(cordat, mpickP, mpickP + sstop + \ (0.5 * (mpickP + sstop)), iplot) - Ao = wapp.getwapp() + + Ao = wapp.getwapp() else: print 'Bad initial (AIC) S-pick, skipping this onset!' From 3ba57cfc56059df28beac6839174607212fa54c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 1 Sep 2015 14:32:09 +0200 Subject: [PATCH 0547/1144] Introduced new class DCfc for calculating the source spectrum and to derive the DC-value and the corner frquency of the source spectrum. Just at the beginning! --- pylot/core/analysis/magnitude.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index c9db0ef2..072984eb 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -72,6 +72,10 @@ class Magnitude(object): def calcwapp(self): self.wapp = None + + def calcsourcespec(self): + self.sourcespek = None + class WApp(Magnitude): ''' Method to derive peak-to-peak amplitude as seen on a Wood-Anderson- @@ -79,8 +83,8 @@ class WApp(Magnitude): ''' def calcwapp(self): - print "Getting Wood-Anderson peak-to-peak amplitude ..." - print "Simulating Wood-Anderson seismograph ..." + print ("Getting Wood-Anderson peak-to-peak amplitude ...") + print ("Simulating Wood-Anderson seismograph ...") self.wapp = None stream = self.getwfstream() @@ -105,7 +109,7 @@ class WApp(Magnitude): # get maximum peak within pick window iwin = getsignalwin(th, self.getTo(), self.getpwin()) self.wapp = np.max(sqH[iwin]) - print "Determined Wood-Anderson peak-to-peak amplitude: %f mm" % self.wapp + print ("Determined Wood-Anderson peak-to-peak amplitude: %f mm") % self.wapp if self.getiplot() > 1: stream.plot() f = plt.figure(2) @@ -121,4 +125,13 @@ class WApp(Magnitude): plt.close(f) +class DCfc(Magnitude): + ''' + Method to calculate the source spectrum and to derive from that the plateau + (the so-called DC-value) and the corner frequency assuming Aki's omega-square + source model. Has to be derived from corrected traces! + ''' + + def calcsourcespec(self): + print ("Calculating source spectrum ....") From bf1194ec3b6a3fe669ab0d9c7d92aa92ba676b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 1 Sep 2015 14:32:36 +0200 Subject: [PATCH 0548/1144] Changed?? --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 541bd94d..41931e59 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -497c-dirty +c940-dirty From 81b95e6a22bae7b82810e6e9d1df34333c83f579 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 3 Sep 2015 09:15:31 +0200 Subject: [PATCH 0549/1144] [addresses #167] started fixing the multiple phase saving issue --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 41931e59..586b7cb0 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -c940-dirty +0.0.0-gbf11 From cc0b1e0a5bca14d2b703601ea64268039fa7a899 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 3 Sep 2015 13:21:46 +0200 Subject: [PATCH 0550/1144] 176 open event failure --- QtPyLoT.py | 44 +++++++++++++++++++++----------------- pylot/RELEASE-VERSION | 2 +- pylot/core/read/data.py | 5 ++++- pylot/core/util/widgets.py | 6 +++--- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 38cab8b1..6726306a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -36,7 +36,7 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen, \ QActionGroup, QListWidget, QDockWidget import numpy as np -from obspy.core import UTCDateTime +from obspy import UTCDateTime, readEvents from pylot.core.read.data import Data from pylot.core.read.inputs import FilterOptions, AutoPickParameter @@ -339,26 +339,25 @@ class MainWindow(QMainWindow): return settings.value("data/dataRoot") def loadData(self, fname=None): + if not self.okToContinue(): + return if fname is None: - try: - self.data = Data(self, evtdata=self.fname) - except AttributeError: - action = self.sender() - if isinstance(action, QAction): - if action.data() is None: - filt = "Supported event formats (*.mat *.qml *.xml *.kor *.evt)" - caption = "Open an event file" - fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) - self.fname = fname[0] - else: - self.fname = unicode(action.data().toString()) - if not self.okToContinue(): - return - else: - self.fname = fname - self.data = Data(self, evtdata=self.fname) + action = self.sender() + if isinstance(action, QAction): + if action.data() is None: + filt = "Supported event formats (*.mat *.qml *.xml *.kor *.evt)" + caption = "Open an event file" + fname = QFileDialog().getOpenFileName(self, + caption=caption, + filter=filt) + fname = fname[0] + else: + fname = unicode(action.data().toString()) + event = readEvents(fname)[0] + self.setFileName(fname) + self.getData().applyEVTData(event, type='event') + self.convertPicks4PyLoT() + self.drawPicks() def getLastEvent(self): return self.recentEvents[0] @@ -694,6 +693,11 @@ class MainWindow(QMainWindow): raise Exception('FATAL: Should never occur!') self.getPicks()[station] = stat_picks + def convertPicks4PyLoT(self): + evt = self.getData().getEvtData() + for pick in evt.picks: + station = pick.waveform_id.getSEEDstring() + def drawPicks(self, station=None): # if picks to draw not specified, draw all picks available if not station: diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 586b7cb0..835782c8 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0.0.0-gbf11 +81b9-dirty diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 30f82c36..945d7dd6 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -417,7 +417,10 @@ class Data(object): :param event: """ - pass + if not self.evtdata: + self.evtdata = event + else: + raise OverwriteError('Acutal event would be overwritten!') applydata = {'pick': applyPicks, 'arrival': applyArrivals, diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 08ef0779..0afe5b7d 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -105,10 +105,10 @@ class MPLWidget(FigureCanvas): for level in noiselevel: self.getAxes().plot([time_ax[0], time_ax[-1]], [level, level], '--k') - xlabel = 'seconds since {0}'.format(wfstart) - ylabel = '' - self.updateWidget(xlabel, ylabel, title) self.setPlotDict(n, (station, channel)) + xlabel = 'seconds since {0}'.format(wfstart) + ylabel = '' + self.updateWidget(xlabel, ylabel, title) self.setXLims([0, wfend - wfstart]) self.setYLims([-0.5, n + 0.5]) if zoomx is not None: From 60b9f176f0608f5db1a2c43dff2aa77200a352a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 3 Sep 2015 14:55:25 +0200 Subject: [PATCH 0551/1144] Cosmetics, changed print commands to keep compatibility to Python 3. --- pylot/core/pick/utils.py | 87 ++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 161942cb..f891b6fe 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -7,7 +7,6 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ - import numpy as np import scipy as sc import matplotlib.pyplot as plt @@ -44,7 +43,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): LPick = None EPick = None PickError = None - print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' + print ("earllatepicker: Get earliest and latest possible pick relative to most likely pick ...") x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, @@ -60,8 +59,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ilup, = np.where(x[isignal] > nlevel) ildown, = np.where(x[isignal] < -nlevel) if not ilup.size and not ildown.size: - print 'earllatepicker: Signal lower than noise level!' - print 'Skip this trace!' + print ("earllatepicker: Signal lower than noise level!") + print ("Skip this trace!") return LPick, EPick, PickError il = min(np.min(ilup) if ilup.size else float('inf'), np.min(ildown) if ildown.size else float('inf')) @@ -143,7 +142,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): FM = None if Pick is not None: - print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' + print ("fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...") xraw = Xraw[0].data xfilt = Xfilt[0].data @@ -182,15 +181,15 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: li1 = index1[0] if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: - print 'fmpicker: Onset on unfiltered trace too emergent for first motion determination!' + print ("fmpicker: Onset on unfiltered trace too emergent for first motion determination!") P1 = None else: imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) if imax1 == 0: imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) if imax1 == 0: - print 'fmpicker: Zero crossings too close!' - print 'Skip first motion determination!' + print ("fmpicker: Zero crossings too close!") + print ("Skip first motion determination!") return FM islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) @@ -224,15 +223,15 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: li2 = index2[0] if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: - print 'fmpicker: Onset on filtered trace too emergent for first motion determination!' + print ("fmpicker: Onset on filtered trace too emergent for first motion determination!") P2 = None else: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) if imax2 == 0: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) if imax2 == 0: - print 'fmpicker: Zero crossings too close!' - print 'Skip first motion determination!' + print ("fmpicker: Zero crossings too close!") + print ("Skip first motion determination!") return FM islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) @@ -256,7 +255,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): elif P1[0] > 0 and P2[0] <= 0: FM = '+' - print 'fmpicker: Found polarity %s' % FM + print ("fmpicker: Found polarity %s" % FM) if iplot > 1: plt.figure(iplot) @@ -331,10 +330,10 @@ def getSNR(X, TSNR, t1): # get signal window isignal = getsignalwin(t, t1, TSNR[2]) if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' + print ("getSNR: Empty array inoise, check noise window!") return elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' + print ("getSNR: Empty array isignal, check signal window!") return # demean over entire waveform @@ -372,7 +371,7 @@ def getnoisewin(t, t1, tnoise, tgap): inoise, = np.where((t <= max([t1 - tgap, 0])) \ & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: - print 'getnoisewin: Empty array inoise, check noise window!' + print ("getnoisewin: Empty array inoise, check noise window!") return inoise @@ -396,7 +395,7 @@ def getsignalwin(t, t1, tsignal): isignal, = np.where((t <= min([t1 + tsignal, len(t)])) \ & (t >= t1)) if np.size(isignal) < 1: - print 'getsignalwin: Empty array isignal, check signal window!' + print ("getsignalwin: Empty array isignal, check signal window!") return isignal @@ -483,8 +482,8 @@ def wadaticheck(pickdic, dttolerance, iplot): # calculate vp/vs ratio before check vpvsr = p1[0] + 1 - print '###############################################' - print 'wadaticheck: Average Vp/Vs ratio before check:', vpvsr + print ("###############################################") + print ("wadaticheck: Average Vp/Vs ratio before check:", vpvsr) checkedPpicks = [] checkedSpicks = [] @@ -521,18 +520,18 @@ def wadaticheck(pickdic, dttolerance, iplot): # calculate vp/vs ratio after check cvpvsr = p2[0] + 1 - print 'wadaticheck: Average Vp/Vs ratio after check:', cvpvsr - print 'wadatacheck: Skipped %d S pick(s).' % ibad + print ("wadaticheck: Average Vp/Vs ratio after check:", cvpvsr) + print ("wadatacheck: Skipped %d S pick(s)." % ibad) else: - print '###############################################' - print 'wadatacheck: Not enough checked S-P times available!' - print 'Skip Wadati check!' + print ("###############################################") + print ("wadatacheck: Not enough checked S-P times available!") + print ("Skip Wadati check!") checkedonsets = pickdic else: - print 'wadaticheck: Not enough S-P times available for reliable regression!' - print 'Skip wadati check!' + print ("wadaticheck: Not enough S-P times available for reliable regression!") + print ("Skip wadati check!") wfitflag = 1 # plot results @@ -592,7 +591,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): assert isinstance(X, Stream), "%s is not a stream object" % str(X) - print 'Checking signal length ...' + print ("Checking signal length ...") x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, @@ -601,8 +600,8 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): # generate envelope function from Hilbert transform y = np.imag(sc.signal.hilbert(x)) e = np.sqrt(np.power(x, 2) + np.power(y, 2)) - # get noise window - inoise = getnoisewin(t, pick, TSNR[0], TSNR[1]) + # get noise window in front of pick plus saftey gap + inoise = getnoisewin(t, pick - 0.5, TSNR[0], TSNR[1]) # get signal window isignal = getsignalwin(t, pick, TSNR[2]) # calculate minimum adjusted signal level @@ -613,12 +612,12 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): numoverthr = len(np.where(e[isignal] >= minsiglevel)[0]) if numoverthr >= minnum: - print 'checksignallength: Signal reached required length.' + print ("checksignallength: Signal reached required length.") returnflag = 1 else: - print 'checksignallength: Signal shorter than required minimum signal length!' - print 'Presumably picked noise peak, pick is rejected!' - print '(min. signal length required:', minsiglength, 's)' + print ("checksignallength: Signal shorter than required minimum signal length!") + print ("Presumably picked noise peak, pick is rejected!") + print ("(min. signal length required:', minsiglength, 's)'") returnflag = 0 if iplot == 2: @@ -629,7 +628,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): p2, = plt.plot(t[inoise], e[inoise]) p3, = plt.plot(t[isignal],e[isignal], 'r') p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ - [minsiglevel, minsiglevel], 'g') + [minsiglevel, minsiglevel], 'g', linewidth=2) p5, = plt.plot([pick, pick], [min(x), max(x)], 'b', linewidth=2) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Envelope Noise Window', \ 'Envelope Signal Window', 'Minimum Signal Level', \ @@ -675,8 +674,8 @@ def checkPonsets(pickdic, dttolerance, iplot): stations.append(key) # apply jackknife bootstrapping on variance of P onsets - print '###############################################' - print 'checkPonsets: Apply jackknife bootstrapping on P-onset times ...' + print ("###############################################") + print ("checkPonsets: Apply jackknife bootstrapping on P-onset times ...") [xjack,PHI_pseudo,PHI_sub] = jackknife(Ppicks, 'VAR', 1) # get pseudo variances smaller than average variances # (times safety factor), these picks passed jackknife test @@ -684,7 +683,7 @@ def checkPonsets(pickdic, dttolerance, iplot): # these picks did not pass jackknife test badjk = np.where(PHI_pseudo > 2 * xjack) badjkstations = np.array(stations)[badjk] - print 'checkPonsets: %d pick(s) did not pass jackknife test!' % len(badjkstations) + print ("checkPonsets: %d pick(s) did not pass jackknife test!" % len(badjkstations)) # calculate median from these picks pmedian = np.median(np.array(Ppicks)[ij]) @@ -696,9 +695,9 @@ def checkPonsets(pickdic, dttolerance, iplot): goodstations = np.array(stations)[igood] badstations = np.array(stations)[ibad] - print 'checkPonsets: %d pick(s) deviate too much from median!' % len(ibad) - print 'checkPonsets: Skipped %d P pick(s) out of %d' % (len(badstations) \ - + len(badjkstations), len(stations)) + print ("checkPonsets: %d pick(s) deviate too much from median!" % len(ibad)) + print ("checkPonsets: Skipped %d P pick(s) out of %d" % (len(badstations) \ + + len(badjkstations), len(stations))) goodmarker = 'goodPonsetcheck' badmarker = 'badPonsetcheck' @@ -765,8 +764,8 @@ def jackknife(X, phi, h): g = len(X) / h if type(g) is not int: - print 'jackknife: Cannot divide quantity X in equal sized subgroups!' - print 'Choose another size for subgroups!' + print ("jackknife: Cannot divide quantity X in equal sized subgroups!") + print ("Choose another size for subgroups!") return PHI_jack, PHI_pseudo, PHI_sub else: # estimator of undisturbed spot check @@ -834,7 +833,7 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): assert isinstance(X, Stream), "%s is not a stream object" % str(X) - print 'Check for spuriously picked S onset instead of P onset ...' + print ("Check for spuriously picked S onset instead of P onset ...") returnflag = 0 @@ -875,9 +874,9 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): # vertical P-coda level must exceed horizontal P-coda level # zfac times encodalevel if zcodalevel < minsiglevel: - print 'checkZ4S: Maybe S onset? Skip this P pick!' + print ("checkZ4S: Maybe S onset? Skip this P pick!") else: - print 'checkZ4S: P onset passes checkZ4S test!' + print ("checkZ4S: P onset passes checkZ4S test!") returnflag = 1 if iplot > 1: From ab1d27747a7e5a7ce094dc2733958b1bef9ddd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 3 Sep 2015 15:42:20 +0200 Subject: [PATCH 0552/1144] Some reformating. --- pylot/core/pick/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index f891b6fe..4476e6fc 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -7,6 +7,7 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ + import numpy as np import scipy as sc import matplotlib.pyplot as plt @@ -483,7 +484,7 @@ def wadaticheck(pickdic, dttolerance, iplot): # calculate vp/vs ratio before check vpvsr = p1[0] + 1 print ("###############################################") - print ("wadaticheck: Average Vp/Vs ratio before check:", vpvsr) + print ("wadaticheck: Average Vp/Vs ratio before check: %f" % vpvsr) checkedPpicks = [] checkedSpicks = [] @@ -520,8 +521,8 @@ def wadaticheck(pickdic, dttolerance, iplot): # calculate vp/vs ratio after check cvpvsr = p2[0] + 1 - print ("wadaticheck: Average Vp/Vs ratio after check:", cvpvsr) - print ("wadatacheck: Skipped %d S pick(s)." % ibad) + print ("wadaticheck: Average Vp/Vs ratio after check: %f" % cvpvsr) + print ("wadatacheck: Skipped %d S pick(s)" % ibad) else: print ("###############################################") print ("wadatacheck: Not enough checked S-P times available!") @@ -617,7 +618,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): else: print ("checksignallength: Signal shorter than required minimum signal length!") print ("Presumably picked noise peak, pick is rejected!") - print ("(min. signal length required:', minsiglength, 's)'") + print ("(min. signal length required: %s s)" % minsiglength) returnflag = 0 if iplot == 2: From 0dc10910784bd48378cac7aa5b612d107fa65846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 11:12:25 +0200 Subject: [PATCH 0553/1144] restituteWFData: introduced return flag restflag to indicate whether restitution could be performed or not. --- pylot/core/read/data.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 30f82c36..ca50132d 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -228,6 +228,9 @@ class Data(object): :param streams: :return: """ + + restflag = 0 + if streams is None: st_raw = self.getWFData() st = st_raw.copy() @@ -236,7 +239,7 @@ class Data(object): for tr in st: # remove underscores - if tr.stats.station[3] == '_': + if len(tr.stats.station) > 3 and tr.stats.station[3] == '_': tr.stats.station = tr.stats.station[0:3] dlp = '%s/*.dless' % invdlpath invp = '%s/*.xml' % invdlpath @@ -273,6 +276,7 @@ class Data(object): 'date': st[ i].stats.starttime, 'units': "VEL"}) + restflag = 1 except ValueError as e: vmsg = '{0}'.format(e) print(vmsg) @@ -303,6 +307,7 @@ class Data(object): st[i].attach_response(inv) st[i].remove_response(output='VEL', pre_filt=prefilt) + restflag = 1 except ValueError as e: vmsg = '{0}'.format(e) print(vmsg) @@ -334,6 +339,7 @@ class Data(object): 'units': "VEL"} st[i].simulate(paz_remove=None, pre_filt=prefilt, seedresp=seedresp) + restflag = 1 except ValueError as e: vmsg = '{0}'.format(e) print(vmsg) @@ -346,7 +352,7 @@ class Data(object): print("Go on processing data without source parameter " "determination!") - return st + return st, restflag def getEvtData(self): """ From de608798b9b0aea80a8d8bc5f2bdf319390bdf98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 11:13:52 +0200 Subject: [PATCH 0554/1144] Modified checking of signal length, uses RMS trace of all components now (if available). --- pylot/core/pick/autopick.py | 82 ++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 6011f645..456023f5 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -41,9 +41,9 @@ def autopickevent(data, param): # quality control # median check and jackknife on P-onset times - jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, 2) + jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, iplot) # check S-P times (Wadati) - return wadaticheck(jk_checked_onsets, wdttolerance, 2) + return wadaticheck(jk_checked_onsets, wdttolerance, iplot) def autopickstation(wfstream, pickparam): """ @@ -196,10 +196,36 @@ def autopickstation(wfstream, pickparam): ############################################################## if aicpick.getpick() is not None: # check signal length to detect spuriously picked noise peaks + # use all available components to avoid skipping correct picks + # on vertical traces with weak P coda z_copy[0].data = tr_filt.data - Pflag = checksignallength(z_copy, aicpick.getpick(), tsnrz, - minsiglength, \ - nfacsl, minpercent, iplot) + zne = z_copy + if len(ndat) == 0 or len(edat) == 0: + print ("One or more horizontal components missing!") + print ("Signal length only checked on vertical component!") + print ("Decreasing minsiglengh from %f to %f" \ + % (minsiglength, minsiglength / 2)) + Pflag = checksignallength(zne, aicpick.getpick(), tsnrz, + minsiglength / 2, \ + nfacsl, minpercent, iplot) + else: + # filter and taper horizontal traces + trH1_filt = edat.copy() + trH2_filt = ndat.copy() + trH1_filt.filter('bandpass', freqmin=bph1[0], + freqmax=bph1[1], \ + zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph1[0], + freqmax=bph1[1], \ + zerophase=False) + trH1_filt.taper(max_percentage=0.05, type='hann') + trH2_filt.taper(max_percentage=0.05, type='hann') + zne += trH1_filt + zne += trH2_filt + Pflag = checksignallength(zne, aicpick.getpick(), tsnrz, + minsiglength, \ + nfacsl, minpercent, iplot) + if Pflag == 1: # check for spuriously picked S onset # both horizontal traces needed @@ -207,20 +233,6 @@ def autopickstation(wfstream, pickparam): print 'One or more horizontal components missing!' print 'Skipping control function checkZ4S.' else: - # filter and taper horizontal traces - trH1_filt = edat.copy() - trH2_filt = ndat.copy() - trH1_filt.filter('bandpass', freqmin=bph1[0], - freqmax=bph1[1], \ - zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph1[0], - freqmax=bph1[1], \ - zerophase=False) - trH1_filt.taper(max_percentage=0.05, type='hann') - trH2_filt.taper(max_percentage=0.05, type='hann') - zne = z_copy - zne += trH1_filt - zne += trH2_filt Pflag = checkZ4S(zne, aicpick.getpick(), zfac, \ tsnrz[3], iplot) if Pflag == 0: @@ -515,18 +527,19 @@ def autopickstation(wfstream, pickparam): hdat = edat.copy() hdat += ndat h_copy = hdat.copy() - cordat = data.restituteWFData(invdir, h_copy) + [cordat, restflag] = data.restituteWFData(invdir, h_copy) # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude - if Sweight < 4: - wapp = WApp(cordat, mpickS, mpickP + sstop, iplot) - else: - # use larger window for getting peak-to-peak amplitude - # as the S pick is quite unsure - wapp = WApp(cordat, mpickP, mpickP + sstop + \ - (0.5 * (mpickP + sstop)), iplot) + if restflag == 1: + if Sweight < 4: + wapp = WApp(cordat, mpickS, mpickP + sstop, iplot) + else: + # use larger window for getting peak-to-peak amplitude + # as the S pick is quite unsure + wapp = WApp(cordat, mpickP, mpickP + sstop + \ + (0.5 * (mpickP + sstop)), iplot) - Ao = wapp.getwapp() + Ao = wapp.getwapp() else: print 'Bad initial (AIC) S-pick, skipping this onset!' @@ -544,12 +557,13 @@ def autopickstation(wfstream, pickparam): hdat = edat.copy() hdat += ndat h_copy = hdat.copy() - cordat = data.restituteWFData(invdir, h_copy) - # calculate WA-peak-to-peak amplitude - # using subclass WApp of superclass Magnitude - wapp = WApp(cordat, mpickP, mpickP + sstop + (0.5 * (mpickP \ - + sstop)), iplot) - Ao = wapp.getwapp() + [cordat, restflag] = data.restituteWFData(invdir, h_copy) + if restflag == 1: + # calculate WA-peak-to-peak amplitude + # using subclass WApp of superclass Magnitude + wapp = WApp(cordat, mpickP, mpickP + sstop + (0.5 * (mpickP \ + + sstop)), iplot) + Ao = wapp.getwapp() else: print 'autopickstation: No horizontal component data available or ' \ From 23430c9d90ff85932f4eebfa747f1a4180a71772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 11:16:34 +0200 Subject: [PATCH 0555/1144] Modofied checksignallength: uses RMS trace of all components (if available) to check signal length. This avoids skipping of P pick, if P coda is very weak. If only vertical trace is available, rms of vertical trace is used instead with smaller required minimum signal length. --- pylot/core/pick/utils.py | 47 +++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 4476e6fc..f94cac76 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -7,7 +7,7 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ - +import pdb import numpy as np import scipy as sc import matplotlib.pyplot as plt @@ -562,9 +562,9 @@ def wadaticheck(pickdic, dttolerance, iplot): def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): ''' Function to detect spuriously picked noise peaks. - Uses envelope to determine, how many samples [per cent] after - P onset are below certain threshold, calculated from noise - level times noise factor. + Uses RMS trace of all 3 components (if available) to determine, + how many samples [per cent] after P onset are below certain + threshold, calculated from noise level times noise factor. : param: X, time series (seismogram) : type: `~obspy.core.stream.Stream` @@ -594,23 +594,32 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): print ("Checking signal length ...") - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, + if len(X) > 1: + # all three components available + # make sure, all components have equal lengths + ilen = min([len(X[0].data), len(X[1].data), len(X[2].data)]) + x1 = X[0][0:ilen] + x2 = X[1][0:ilen] + x3 = X[2][0:ilen] + # get RMS trace + rms = np.sqrt((np.power(x1, 2) + np.power(x2, 2) + np.power(x3, 2)) / 3) + else: + x1 = X[0].data + rms = np.sqrt(np.power(2, x1)) + + t = np.arange(0, ilen / X[0].stats.sampling_rate, X[0].stats.delta) - # generate envelope function from Hilbert transform - y = np.imag(sc.signal.hilbert(x)) - e = np.sqrt(np.power(x, 2) + np.power(y, 2)) # get noise window in front of pick plus saftey gap inoise = getnoisewin(t, pick - 0.5, TSNR[0], TSNR[1]) # get signal window - isignal = getsignalwin(t, pick, TSNR[2]) + isignal = getsignalwin(t, pick, minsiglength) # calculate minimum adjusted signal level - minsiglevel = max(e[inoise]) * nfac + minsiglevel = max(rms[inoise]) * nfac # minimum adjusted number of samples over minimum signal level minnum = len(isignal) * minpercent/100 # get number of samples above minimum adjusted signal level - numoverthr = len(np.where(e[isignal] >= minsiglevel)[0]) + numoverthr = len(np.where(rms[isignal] >= minsiglevel)[0]) if numoverthr >= minnum: print ("checksignallength: Signal reached required length.") @@ -623,16 +632,14 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): if iplot == 2: plt.figure(iplot) - p1, = plt.plot(t,x, 'k') - p2, = plt.plot(t[inoise], e[inoise], 'c') - p3, = plt.plot(t[isignal],e[isignal], 'r') - p2, = plt.plot(t[inoise], e[inoise]) - p3, = plt.plot(t[isignal],e[isignal], 'r') + p1, = plt.plot(t,rms, 'k') + p2, = plt.plot(t[inoise], rms[inoise], 'c') + p3, = plt.plot(t[isignal],rms[isignal], 'r') p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ [minsiglevel, minsiglevel], 'g', linewidth=2) - p5, = plt.plot([pick, pick], [min(x), max(x)], 'b', linewidth=2) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Envelope Noise Window', \ - 'Envelope Signal Window', 'Minimum Signal Level', \ + p5, = plt.plot([pick, pick], [min(rms), max(rms)], 'b', linewidth=2) + plt.legend([p1, p2, p3, p4, p5], ['RMS Data', 'RMS Noise Window', \ + 'RMS Signal Window', 'Minimum Signal Level', \ 'Onset'], loc='best') plt.xlabel('Time [s] since %s' % X[0].stats.starttime) plt.ylabel('Counts') From 1001da728e85713d85b4cf6db6dae492516a3cd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 11:17:46 +0200 Subject: [PATCH 0556/1144] Modified parameter minsiglength for modified routine checksignallength. --- autoPyLoT_local.in | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 18849c2e..c2ee8508 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -7,8 +7,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2013.02_Insheim #database# %name of data base -e0019.048.13 #eventID# %event ID for single event processing +2015.08_Insheim #database# %name of data base +e0013.241.15 #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything @@ -30,8 +30,8 @@ HYPOSAT #locrt# %location routine used ("HYPO 300 #Qp# %quality factor for P waves 100 #Qs# %quality factor for S waves #common settings picker# -20 #pstart# %start time [s] for calculating CF for P-picking -80 #pstop# %end time [s] for calculating CF for P-picking +15 #pstart# %start time [s] for calculating CF for P-picking +60 #pstop# %end time [s] for calculating CF for P-picking -1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking 7 #sstop# %end time [s] after P-onset for calculating CF for S-picking 2 20 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] @@ -81,14 +81,14 @@ ARH #algoS# %choose algorithm for S-onset #inital AIC onset# 0.01 0.02 0.04 0.08 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P 0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S -50 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +10 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected 1.2 #minAICPSNR# %below this SNR the initial P pick is rejected 6 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# -2.5 #minsiglength# %minimum required length of signal [s] -3 #noisefactor# %noiselevel*noisefactor=threshold -70 #minpercent# %required percentage of samples higher than threshold +5 #minsiglength# %minimum required length of signal [s] +1.8 #noisefactor# %noiselevel*noisefactor=threshold +50 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 2.0 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude #check statistics of P onsets# From 956e16fdcab32bd2a394ca9ec442d68d7cd23479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 11:17:56 +0200 Subject: [PATCH 0557/1144] Modified parameter minsiglength for modified routine checksignallength. --- autoPyLoT_regional.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in index abcc0812..2231d007 100644 --- a/autoPyLoT_regional.in +++ b/autoPyLoT_regional.in @@ -9,6 +9,7 @@ EVENT_DATA/LOCAL #datapath# %data path 2006.01_Nisyros #database# %name of data base e1412.008.06 #eventID# %event ID for single event processing +/DATA/Egelados/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file @@ -83,8 +84,8 @@ ARH #algoS# %choose algorithm for S-onset 8 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# -6 #minsiglength# %minimum required length of signal [s] -1.5 #noisefactor# %noiselevel*noisefactor=threshold +30 #minsiglength# %minimum required length of signal [s] +2.5 #noisefactor# %noiselevel*noisefactor=threshold 60 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 1.5 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude From 254c745f2524898aff8fc75c1aa829e962c4475b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 11:19:57 +0200 Subject: [PATCH 0558/1144] Marginal changes. --- pylot/core/pick/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index f94cac76..c24ce1f8 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -7,7 +7,7 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ -import pdb + import numpy as np import scipy as sc import matplotlib.pyplot as plt From 0753071cfb8298c551842ea3de42664b373f1052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 11:23:59 +0200 Subject: [PATCH 0559/1144] Removed import of scipy as this is no more necessary. --- pylot/core/pick/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index c24ce1f8..052c2870 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -9,7 +9,6 @@ """ import numpy as np -import scipy as sc import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime import warnings From ac7d239b4062daadd3efb871e9d0326e4bb419c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 12:09:17 +0200 Subject: [PATCH 0560/1144] Optimized parameter pstop, as this is a cruicial parameter for stable AIC-CF calculation. --- autoPyLoT_regional.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in index 2231d007..23cdce87 100644 --- a/autoPyLoT_regional.in +++ b/autoPyLoT_regional.in @@ -31,7 +31,7 @@ HYPOSAT #locrt# %location routine used ("HYPO 100 #Qs# %quality factor for S waves #common settings picker# 20 #pstart# %start time [s] for calculating CF for P-picking -160 #pstop# %end time [s] for calculating CF for P-picking +100 #pstop# %end time [s] for calculating CF for P-picking 3.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking 100 #sstop# %end time [s] after P-onset for calculating CF for S-picking 3 10 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] @@ -79,9 +79,9 @@ ARH #algoS# %choose algorithm for S-onset #inital AIC onset# 0.04 0.08 0.16 0.32 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P 0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S -5 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +3 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected 1.2 #minAICPSNR# %below this SNR the initial P pick is rejected -8 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +3 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# 30 #minsiglength# %minimum required length of signal [s] From 4eef4d238b168e8697165ba159359969290644ef Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 4 Sep 2015 15:01:59 +0200 Subject: [PATCH 0561/1144] 176 open event failure --- QtPyLoT.py | 16 +++++++++++++++- pylot/core/read/data.py | 14 +++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6726306a..70bf2496 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -695,8 +695,22 @@ class MainWindow(QMainWindow): def convertPicks4PyLoT(self): evt = self.getData().getEvtData() + picks = {} + onsets = {} for pick in evt.picks: - station = pick.waveform_id.getSEEDstring() + phase = {} + station = pick.waveform_id.station_code + mpp = pick.time + lpp = mpp + pick.time.upper_uncertainty + epp = mpp - pick.time.lower_uncertainty + spe = pick.time.uncertainty + phase['mpp'] = mpp + phase['epp'] = epp + phase['lpp'] = lpp + phase['spe'] = spe + + onsets[pick.phase_hint] = phase + picks[station] = onsets def drawPicks(self, station=None): # if picks to draw not specified, draw all picks available diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 945d7dd6..e942619c 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -33,7 +33,7 @@ class Data(object): else: self.comp = 'Z' self.wfdata = Stream() - self.newevent = False + self._new = False if evtdata is not None and isinstance(evtdata, Event): self.evtdata = evtdata elif evtdata is not None and not isinstance(evtdata, dict): @@ -41,8 +41,9 @@ class Data(object): self.evtdata = cat[0] elif evtdata is not None: cat = readPILOTEvent(**evtdata) + self.evtdata = cat[0] else: # create an empty Event object - self.newevent = True + self._new = True self.evtdata = Event() self.getEvtData().picks = [] self.wforiginal = None @@ -73,7 +74,7 @@ class Data(object): :return: """ - return self.newevent + return self._new def getCutTimes(self): """ @@ -356,6 +357,9 @@ class Data(object): """ return self.evtdata + def setEvtData(self, event): + self.evtdata = event + def applyEVTData(self, data, type='pick', authority_id='rub'): """ @@ -417,8 +421,8 @@ class Data(object): :param event: """ - if not self.evtdata: - self.evtdata = event + if not self.isNew(): + self.setEvtData(event) else: raise OverwriteError('Acutal event would be overwritten!') From cfca52e576025c5cd5dfeb9f4468304eaccd555d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 15:28:37 +0200 Subject: [PATCH 0562/1144] Debuged slope determination [counts/s] within AICPicker. --- pylot/core/pick/Picker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index d8a224e6..61eade5e 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -18,6 +18,7 @@ calculated after Diehl & Kissling (2009). :author: MAGS2 EP3 working group / Ludger Kueperkoch """ + import numpy as np import matplotlib.pyplot as plt from pylot.core.pick.utils import getnoisewin, getsignalwin @@ -245,8 +246,7 @@ class AICPicker(AutoPicking): if datafit[0] >= datafit[len(datafit) - 1]: print 'AICPicker: Negative slope, bad onset skipped!' return - - self.slope = 1 / tslope * datafit[len(dataslope) - 1] - datafit[0] + self.slope = 1 / tslope * (datafit[len(dataslope) - 1] - datafit[0]) else: self.SNR = None From 5d8346b1caebb4613ab3359e066305fb79ae89ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 15:28:57 +0200 Subject: [PATCH 0563/1144] Optimized some parameters. --- autoPyLoT_local.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index c2ee8508..84ae2137 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -7,8 +7,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2015.08_Insheim #database# %name of data base -e0013.241.15 #eventID# %event ID for single event processing +2013.02_Insheim #database# %name of data base +e0019.048.13 #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything From 70b3f031f8026fed3544c109ec22c71202088948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Sep 2015 15:29:04 +0200 Subject: [PATCH 0564/1144] Optimized some parameters. --- autoPyLoT_regional.in | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/autoPyLoT_regional.in b/autoPyLoT_regional.in index 23cdce87..52f5c465 100644 --- a/autoPyLoT_regional.in +++ b/autoPyLoT_regional.in @@ -32,7 +32,7 @@ HYPOSAT #locrt# %location routine used ("HYPO #common settings picker# 20 #pstart# %start time [s] for calculating CF for P-picking 100 #pstop# %end time [s] for calculating CF for P-picking -3.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking +1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking 100 #sstop# %end time [s] after P-onset for calculating CF for S-picking 3 10 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] 3 12 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] @@ -65,11 +65,11 @@ ARH #algoS# %choose algorithm for S-onset 0.3 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick 4 #Sarorder# %for AR-picker, order of AR process of H-components 10 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) -6 #pickwinS# %for initial AIC and refined pick, length of S-pick window [s] +25 #pickwinS# %for initial AIC and refined pick, length of S-pick window [s] 5 0.2 3.0 3.0 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] -3.0 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] +3.5 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] 1.0 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) -0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) +0.2 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) 1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) %first-motion picker% 1 #minfmweight# %minimum required p weight for first-motion determination @@ -81,16 +81,16 @@ ARH #algoS# %choose algorithm for S-onset 0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S 3 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected 1.2 #minAICPSNR# %below this SNR the initial P pick is rejected -3 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected -1.5 #minAICSSNR# %below this SNR the initial S pick is rejected +5 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +2.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# 30 #minsiglength# %minimum required length of signal [s] 2.5 #noisefactor# %noiselevel*noisefactor=threshold 60 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# -1.5 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude +1.0 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude #check statistics of P onsets# -35 #mdttolerance# %maximum allowed deviation of P picks from median [s] +45 #mdttolerance# %maximum allowed deviation of P picks from median [s] #wadati check# -2.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram +3.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram From f5fa4f4fafa45b89b13f6cb0e21dffbe908635b7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 7 Sep 2015 10:03:12 +0200 Subject: [PATCH 0565/1144] Python 3 compatibility --- QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 70bf2496..123d9991 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -389,8 +389,8 @@ class MainWindow(QMainWindow): else: raise DatastructureError('not specified') return self.fnames - except DatastructureError, e: - print e + except DatastructureError as e: + print(e) props = PropertiesDlg(self) if props.exec_() == QDialog.Accepted: return self.getWFFnames() From 57a9444478c10afd9416b7b5a5a18dc65dce4593 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 14 Sep 2015 11:01:23 +0200 Subject: [PATCH 0566/1144] commiting changes to suppress output; necessary for fast calculation in active mode --- pylot/core/pick/CharFuns.py | 4 ++-- pylot/core/pick/utils.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index b3ad1f07..2f6e8cd8 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -218,7 +218,7 @@ class AICcf(CharacteristicFunction): def calcCF(self, data): - print 'Calculating AIC ...' + #print 'Calculating AIC ...' ## MP MP output suppressed x = self.getDataArray() xnp = x[0].data nn = np.isnan(xnp) @@ -260,7 +260,7 @@ class HOScf(CharacteristicFunction): y = np.power(xnp, 3) y1 = np.power(xnp, 2) elif self.getOrder() == 4: # this is kurtosis - print 'Calculating kurtosis ...' + #print 'Calculating kurtosis ...' ## MP MP output suppressed y = np.power(xnp, 4) y1 = np.power(xnp, 2) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 052c2870..80277623 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -43,7 +43,13 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): LPick = None EPick = None PickError = None +<<<<<<< Updated upstream print ("earllatepicker: Get earliest and latest possible pick relative to most likely pick ...") +======= + # MP MP ++ output suppressed + #print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' + # MP MP -- +>>>>>>> Stashed changes x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, From 61c7e9f4172996155eea50172192c6e585047236 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 17 Sep 2015 11:07:22 +0200 Subject: [PATCH 0567/1144] removed stashed changes --- pylot/core/pick/utils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 80277623..b78d7946 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -43,13 +43,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): LPick = None EPick = None PickError = None -<<<<<<< Updated upstream - print ("earllatepicker: Get earliest and latest possible pick relative to most likely pick ...") -======= - # MP MP ++ output suppressed #print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' - # MP MP -- ->>>>>>> Stashed changes x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, From 26c958b421cb4aa295c2539133dbb0760a3e5cb9 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 17 Sep 2015 11:09:30 +0200 Subject: [PATCH 0568/1144] initial import of new modules working on active seismic data --- pylot/core/active/seismicshot.py | 721 +++++++++++++++++++++++++++++++ pylot/core/active/surveyUtils.py | 204 +++++++++ 2 files changed, 925 insertions(+) create mode 100644 pylot/core/active/seismicshot.py create mode 100644 pylot/core/active/surveyUtils.py diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py new file mode 100644 index 00000000..5e6ee8bf --- /dev/null +++ b/pylot/core/active/seismicshot.py @@ -0,0 +1,721 @@ +import os +import numpy as np +from obspy.core import read +from obspy import Stream +from obspy import Trace +from pylot.core.pick.CharFuns import HOScf +from pylot.core.pick.CharFuns import AICcf +from pylot.core.pick.utils import getSNR +from pylot.core.pick.utils import earllatepicker + +class SeismicShot(object): + ''' + SuperClass for a seismic shot object. + ''' + def __init__(self, obsfile): + ''' + Initialize seismic shot object giving an inputfile. + + :param: obsfile, ((!SEG2/SEGY!)) file readable by obspy + :type: string + ''' + self.traces = read(obsfile) + self.recCoordlist = None + self.srcCoordlist = None + self.traceIDs = None + self.pick = {} + self.pick_backup = {} + self.earliest = {} + self.latest = {} + self.pickwindow= {} + self.manualpicks= {} + self.snr = {} + self.snrthreshold = {} + self.timeArray = {} + self.paras = {} + self.paras['shotname'] = obsfile + + def removeEmptyTraces(self): + traceIDs = [] + coordlist = self.getRecCoordlist() + removed = [] + for i in range(0, len(coordlist)): + traceIDs.append(int(coordlist[i].split()[0])) + + for trace in self.traces: + try: + traceIDs.index(int(trace.stats.channel)) + except: + self.traces.remove(trace) + removed.append(int(trace.stats.channel)) + + if len(removed) > 0: + return removed + + def removeTrace(self, traceID): + for trace in self.traces: + if traceID == trace.stats.channel: + self.traces.remove(trace) + + # for traceID in TraceIDs: + # traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] + # if len(traces) is not 1: + # self.traces.remove(trace) + + def updateTraceList(self): + ''' + Looks for empty traces, returns a list of deleted traceIDs. + ''' + traceIDs = [] + for traceID in self.getTraceIDlist(): + if traceID not in self.getStreamTraceIDs(): + self.traceIDs.remove(traceID) + traceIDs.append(traceID) + return traceIDs + + def setParameters(self, name, value): + self.paras[name] = value + + def setCut(self, cut): + self.setParameters('cut', cut) + + def setTmovwind(self, tmovwind): + self.setParameters('tmovwind', tmovwind) + + def setOrder(self, order): + self.setParameters('order', order) + + def setTsignal(self, tsignal): + self.setParameters('tsignal', tsignal) + + def setTgap(self, tgap): + self.setParameters('tgap', tgap) + + def setShotnumber(self, shotname): + self.setParameters('shotname', shotname) + + def setRecfile(self, recfile): + self.setParameters('recfile', recfile) + + def setSourcefile(self, sourcefile): + self.setParameters('sourcefile', sourcefile) + + def getParas(self): + return self.paras + + def getShotname(self): + return self.paras['shotname'] + + def getCut(self): + return self.paras['cut'] + + def getTmovwind(self): + return self.paras['tmovwind'] + + def getOrder(self): + return self.paras['order'] + + def getTsignal(self): + return self.paras['tsignal'] + + def getTgap(self): + return self.paras['tgap'] + + def getShotnumber(self): + return self.paras['shotnumber'] + + def getRecfile(self): + return self.paras['recfile'] + + def getSourcefile(self): + return self.paras['sourcefile'] + + def getPick(self, traceID): + return self.pick[traceID] + + def getPick_backup(self, traceID): + return self.pick_backup[traceID] + + def getEarliest(self, traceID): + return self.earliest[traceID] + + def getLatest(self, traceID): + return self.latest[traceID] + + def getMistake(self, traceID): + mistake = abs(self.getEarliest(traceID) - self.getLatest(traceID)) + if np.isnan(mistake) == True: + print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) + return mistake + + def getStreamTraceIDs(self): + traceIDs = [] + for trace in self.traces: + traceIDs.append(int(trace.stats.channel)) + return traceIDs + + def getTraceIDlist(self): + ''' + Returns a list containing the traceIDs read from the receiver inputfile. + ''' + traceIDs = [] + if self.traceIDs == None: + recCoordlist = self.getRecCoordlist() + for i in range(0, len(recCoordlist)): + traceIDs.append(int(recCoordlist[i].split()[0])) + self.traceIDs = traceIDs + + return self.traceIDs + + def getPickwindow(self, traceID): + try: + self.pickwindow[traceID] + except KeyError, e: + print('no pickwindow for trace %s, set to %s' % (traceID, self.getCut())) + self.setPickwindow(traceID, self.getCut()) + return self.pickwindow[traceID] + + def getSNR(self, traceID): + return self.snr[traceID] + + def getSNRthreshold(self, traceID): + return self.snrthreshold[traceID] + + def getRecCoordlist(self): + if self.recCoordlist is None: + coordlist = open(self.getRecfile(),'r').readlines() + #print 'Reading receiver coordinates from %s' %(self.getRecfile()) + self.recCoordlist = coordlist + return self.recCoordlist + + def getSrcCoordlist(self): + if self.srcCoordlist is None: + coordlist = open(self.getSourcefile(),'r').readlines() + #print 'Reading shot coordinates from %s' %(self.getSourcefile()) + self.srcCoordlist = coordlist + return self.srcCoordlist + + def getTimeArray(self, traceID): + return self.timeArray[traceID] + + def getHOScf(self, traceID): + ''' + Returns the higher order statistics characteristic function for a trace using pylot. + + :param: traceID + :type: int + + :param: cut, cut out a part of the trace (t_start, t_end) [s] + :type: tuple + + :param: t2, size of the moving window [s] + :type: float + + :param: order, order of the characteristic function + :type: int + ''' + return HOScf(self.getSingleStream(traceID), self.getCut(), + self.getTmovwind(), self.getOrder()) + + def getAICcf(self, traceID): + ''' + Returns the Akaike criterion for a trace using pylot and the higher order statistics characteristic function. + + :param: traceID + :type: int + + :param: cut, cut out a part of the trace (t_start, t_end) [s] + :type: tuple + + :param: t2, size of the moving window [s] + :type: float + + :param: order, order of the characteristic function + :type: int + ''' + + st_cf = Stream() + tr_cf = Trace() + tr_cf.data = self.getHOScf(traceID).getCF() + st_cf += tr_cf + return AICcf(st_cf, self.getCut(), self.getTmovwind()) + + def getSingleStream(self, traceID): ########## SEG2 / SEGY ? ########## + ''' + Returns a Stream with only one trace (instead of just one trace) because it is needed for pylot. + + :param: traceID + :type: int + ''' + #traces = [trace for trace in self.traces if int(trace.stats.seg2['CHANNEL_NUMBER']) == traceID] + traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] + if len(traces) == 1: + return Stream(traces) + else: + self.setPick(traceID, None) + print 'Warning: ambigious or empty traceID: %s' % traceID + + #raise ValueError('ambigious or empty traceID: %s' % traceID) + + def pickTraces(self, traceID, pickmethod, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## + # LOCALMAX NOT IMPLEMENTED! + ''' + Intitiate picking for a trace. + + :param: traceID + :type: int + + :param: pickmethod, use either 'threshold' or 'localmax' method. (localmax not yet implemented 04_08_15) + :type: string + + :param: cutwindow (equals HOScf 'cut' variable) + :type: tuple + + :param: t2 (equals HOScf t2 variable) + :type: float + + :param: order (equals HOScf 'order' variable) + :type: int + + :param: windowsize, window around the returned HOS picktime, to search for the AIC minumum + :type: 'tuple' + + :param: folm, fraction of local maximumm (default = 0.6) + :type: 'real' + + :param: HosAic, get hos or aic pick (can be 'hos'(default) or 'aic') + :type: 'string' + ''' + hoscf = self.getHOScf(traceID) ### determination of both, HOS and AIC (need to change threshold-picker) ### + aiccf = self.getAICcf(traceID) + + self.timeArray[traceID] = hoscf.getTimeArray() + + if pickmethod == 'threshold': + aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) + + #setpick = {'threshold':self.threshold, + # 'localmax':self.localmax} + + #aiccftime, hoscftime = setpick[pickmethod](hoscf, aiccf, windowsize, pickwindow) + + setHosAic = {'hos': hoscftime, + 'aic': aiccftime} + + self.setPick(traceID, setHosAic[HosAic]) + self.pick_backup[traceID] = setHosAic[HosAic] ### verbessern (vor allem weil ueberschrieben bei 2tem mal picken) + + def setEarllatepick(self, traceID, nfac = 1.5): + tgap = self.getTgap() + tsignal = self.getTsignal() + tnoise = self.getPick(traceID) - tgap + + (self.earliest[traceID], self.latest[traceID], tmp) = earllatepicker(self.getSingleStream(traceID), + nfac, (tnoise, tgap, tsignal), + self.getPick(traceID)) + + def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm = 0.6): + ''' + Threshold picker, using the local maximum in a pickwindow to find the time at + which a fraction of the local maximum is reached for the first time. + + :param: hoscf, Higher Order Statistics Characteristic Function + :type: 'Characteristic Function' + + :param: aiccf, Characteristic Function after Akaike + :type: 'Characteristic Function' + + :param: windowsize, window around the returned HOS picktime, to search for the AIC minumum + :type: 'tuple' + + :param: pickwindow [seconds] + :type: 'tuple' + + :param: cutwindow [seconds], cut a part of the trace as in Characteristic Function + :type: 'tuple' + + :param: folm, fraction of local maximum (default = 0.6) + :type: 'real' + ''' + hoscflist = list(hoscf.getCF()) + leftb = int(pickwindow[0] / self.getCut()[1] * len(hoscflist)) + rightb = int(pickwindow[1] / self.getCut()[1] * len(hoscflist)) + + threshold = folm * max(hoscflist[leftb : rightb]) # combination of local maximum and threshold + + m = leftb + + while hoscflist[m] < threshold: + m += 1 + + hoscftime = list(hoscf.getTimeArray())[m] + + lb = max(0, m - windowsize[0]) # if window exceeds t = 0 + aiccfcut = list(aiccf.getCF())[lb : m + windowsize[1]] + n = aiccfcut.index(min(aiccfcut)) + + m = lb + n + + aiccftime = list(hoscf.getTimeArray())[m] + + return aiccftime, hoscftime + + def getDistance(self, traceID): + ''' + Returns the distance of the receiver with the ID == traceID to the source location (shot location). + Uses getSrcLoc and getRecLoc. + + :param: traceID + :type: int + ''' + shotX, shotY, shotZ = self.getSrcLoc() + recX, recY, recZ = self.getRecLoc(traceID) + dist = np.sqrt((shotX-recX)**2 + (shotY-recY)**2 + (shotZ-recZ)**2) + + if np.isnan(dist) == True: + raise ValueError("Distance is NaN for traceID %s" %traceID) + + return dist + #return abs(float(self.getSrcLoc(traceID))-float(self.getRecLoc(traceID))) + + def getRecLoc(self, traceID): ########## input FILENAME ########## + ''' + Returns the location (x, y, z) of the receiver with the ID == traceID. + RECEIVEIVER FILE MUST BE SET FIRST, TO BE IMPROVED. + + :param: traceID + :type: int + ''' + if traceID == 0: # artificial traceID 0 with pick at t = 0 + return self.getSrcLoc() + + coordlist = self.getRecCoordlist() + for i in range(0, len(coordlist)): + if int(coordlist[i].split()[0]) == traceID: + x = coordlist[i].split()[1] + y = coordlist[i].split()[2] + z = coordlist[i].split()[3] + return float(x), float(y), float(z) + + #print "WARNING: traceID %s not found" % traceID + raise ValueError("traceID %s not found" % traceID) + #return float(self.getSingleStream(traceID)[0].stats.seg2['RECEIVER_LOCATION']) + + def getSrcLoc(self): ########## input FILENAME ########## + ''' + Returns the location (x, y, z) of the shot. + SOURCE FILE MUST BE SET FIRST, TO BE IMPROVED. + ''' + coordlist = self.getSrcCoordlist() + for i in range(0, len(coordlist)): + if int(coordlist[i].split()[0]) == self.paras['shotnumber']: + x = coordlist[i].split()[1] + y = coordlist[i].split()[2] + z = coordlist[i].split()[3] + return float(x), float(y), float(z) + #return float(self.getSingleStream(traceID)[0].stats.seg2['SOURCE_LOCATION']) + + def getTraceIDs4Dist(self, distance = 0, distancebin = (0, 0)): ########## nur fuer 2D benutzt, 'distance bins' ########## + ''' + Returns the traceID(s) for a certain distance between source and receiver. + Used for 2D Tomography. TO BE IMPROVED. + + :param: distance + :type: real + + :param: distancebin + :type: tuple + ''' + + traceID_list = [] + for trace in self.traces: + #traceID = int(trace.stats.seg2['CHANNEL_NUMBER']) + traceID = int(trace.stats.channel) + if distance != 0: + if self.getDistance(traceID) == distance: + traceID_list.append(traceID) + if distancebin[0] >= 0 and distancebin[1] > 0: + if self.getDistance(traceID) > distancebin[0] and self.getDistance(traceID) < distancebin[1]: + traceID_list.append(traceID) + + if len(traceID_list) > 0: + return traceID_list + + def setManualPicks(self, traceID, picklist): ########## picklist momentan nicht allgemein, nur testweise benutzt ########## + ''' + Sets the manual picks for a receiver with the ID == traceID for comparison. + + :param: traceID + :type: int + + :param: picklist, list containing the manual picks (mostlikely, earliest, latest). + :type: list + ''' + picks = picklist[traceID - 1].split() + mostlikely = float(picks[1]) + earliest = float(picks[2]) + latest = float(picks[3]) + + if not self.manualpicks.has_key(traceID): + self.manualpicks[traceID] = (mostlikely, earliest, latest) + #else: + # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) + + def setPick(self, traceID, pick): ########## siehe Kommentar ########## + self.pick[traceID] = pick + # ++++++++++++++ Block raus genommen, da Error beim 2ten Mal picken! (Ueberschreiben von erstem Pick!) + # if not self.pick.has_key(traceID): + # self.getPick(traceID) = picks + # else: + # raise KeyError('pick to be set more than once for traceID %s' % traceID) + + # def readParameter(self, parfile): + # parlist = open(parfile,'r').readlines() + + def removePick(self, traceID): + self.setPick(traceID, None) + + def setPickwindow(self, traceID, pickwindow): + self.pickwindow[traceID] = pickwindow + + def setSNR(self, traceID): ########## FORCED HOS PICK ########## + ''' + Gets the SNR using pylot and then sets the SNR for the traceID. + + :param: traceID + :type: int + + :param: (tnoise, tgap, tsignal), as used in pylot SNR + ''' + + from pylot.core.pick.utils import getSNR + + tgap = self.getTgap() + tsignal = self.getTsignal() + tnoise = self.getPick(traceID) - tgap + + self.snr[traceID] = getSNR(self.getSingleStream(traceID), (tnoise,tgap,tsignal), self.getPick(traceID)) + + def setSNRthreshold(self, traceID, snrthreshold): + self.snrthreshold[traceID] = snrthreshold + + def getDistArray4ttcPlot(self): ########## nur fuer 2D benoetigt ########## + ''' + Function to create a distance array for the plots. 2D only! + ''' + distancearray = [] + + for traceID in self.pick.keys(): + if self.getRecLoc(traceID) > self.getSrcLoc(traceID): + distancearray.append(self.getDistance(traceID)) + elif self.getRecLoc(traceID) < self.getSrcLoc(traceID): + distancearray.append((-1)*self.getDistance(traceID)) + + return distancearray + + + # def plot2dttc(self, dist_med = 0): ########## 2D ########## + # ''' + # Function to plot the traveltime curve for automated picks (AIC & HOS) of a shot. 2d only! + + # :param: dist_med (optional) + # :type: 'dictionary' + # ''' + # import matplotlib.pyplot as plt + # plt.interactive('True') + # aictimearray = [] + # hostimearray = [] + # if dist_med is not 0: + # dist_medarray = [] + + # i = 1 + # for traceID in self.pick.keys(): + # aictimearray.append(self.getPick(traceID)) ###### HIER NICHT MEHR aic = [0] oder hos = [1] + # hostimearray.append(self.getPick(traceID)) + # if dist_med is not 0: dist_medarray.append(dist_med[self.getDistance(traceID)]) + # i += 1 + + # plt.plot(self.getDistArray4ttcPlot(), aictimearray, 'r', label = "AIC") + # plt.plot(self.getDistArray4ttcPlot(), hostimearray, 'y', label = "HOS") + # if dist_med is not 0: plt.plot(self.getDistArray4ttcPlot(), dist_medarray, ':b') + # plt.title(self.shotname) + + # def plotmanualttc(self): ########## 2D ########## + # ''' + # Function to plot the traveltime curve for manual picks of a shot. 2D only! + # ''' + # import matplotlib.pyplot as plt + # plt.interactive('True') + # manualpicktimesarray = [] + # dist_medarray = [] + + # i = 1 + # for traceID in self.manualpicks.keys(): + # if self.manualpicks[traceID][0] <= 0: + # manualpicktimesarray.append(None) + # else: + # manualpicktimesarray.append(self.manualpicks[traceID][0]) + # i += 1 + + # plt.plot(self.getDistArray4ttcPlot(), manualpicktimesarray, 'b', label = "manual") + + # def plotpickwindow(self): ########## 2D ########## + # ''' + # Plots the pickwindow of a shot for the 2nd iteration step of picking. 2D only! + # ''' + # import matplotlib.pyplot as plt + # plt.interactive('True') + # pickwindowarray_lowerb = [] + # pickwindowarray_upperb = [] + + # i = 1 + # for traceID in self.pickwindow.keys(): + # pickwindowarray_lowerb.append(self.pickwindow[traceID][0]) + # pickwindowarray_upperb.append(self.pickwindow[traceID][1]) + # i += 1 + + # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_lowerb, ':k') + # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_upperb, ':k') + + def plot_traces(self, traceID, folm = 0.6): ########## 2D, muss noch mehr verbessert werden ########## + import matplotlib.pyplot as plt + from pylot.core.util import getGlobalTimes + from pylot.core.util import prepTimeAxis + + stream = self.getSingleStream(traceID) + stime = getGlobalTimes(stream)[0] + timeaxis = prepTimeAxis(stime, stream[0]) + timeaxis -= stime + + plt.interactive('True') + + hoscf = self.getHOScf(traceID) + aiccf = self.getAICcf(traceID) + + fig = plt.figure() + ax1 = plt.subplot(2,1,1) + plt.title('Shot: %s, traceID: %s, pick: %s' %(self.getShotnumber(), traceID, self.getPick(traceID))) + ax1.plot(timeaxis, stream[0].data, 'k', label = 'trace') + ax1.plot([self.getPick(traceID), self.getPick(traceID)], + [min(stream[0].data), + max(stream[0].data)], + 'r', label = 'mostlikely') + plt.legend() + ax2 = plt.subplot(2,1,2, sharex = ax1) + ax2.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label = 'HOS') + ax2.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label = 'AIC') + ax2.plot([self.getPick(traceID), self.getPick(traceID)], + [min(np.minimum(hoscf.getCF(), aiccf.getCF())), + max(np.maximum(hoscf.getCF(), aiccf.getCF()))], + 'r', label = 'mostlikely') + ax2.plot([0, self.getPick(traceID)], + [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], + 'm:', label = 'folm = %s' %folm) + plt.xlabel('Time [s]') + plt.legend() + + def plot3dttc(self, step = 0.5, contour = False, plotpicks = False, method = 'linear', ax = None): + ''' + Plots a 3D 'traveltime cone' as surface plot by interpolating on a regular grid over the traveltimes, not yet regarding the vertical offset of the receivers. + + :param: step (optional), gives the stepsize for the interpolated grid. Default is 0.5 + :type: 'float' + + :param: contour (optional), plot contour plot instead of surface plot + :type: 'logical' + + :param: plotpicks (optional), plot the data points onto the interpolated grid + :type: 'logical' + + :param: method (optional), interpolation method; can be 'linear' (default) or 'cubic' + :type: 'string' + ''' + import matplotlib.pyplot as plt + from scipy.interpolate import griddata + from matplotlib import cm + from mpl_toolkits.mplot3d import Axes3D + + x = [] + y = [] + z = [] + for traceID in self.pick.keys(): + if self.getPick(traceID) != None: + x.append(self.getRecLoc(traceID)[0]) + y.append(self.getRecLoc(traceID)[1]) + z.append(self.getPick(traceID)) + + xaxis = np.arange(min(x)+1, max(x), step) + yaxis = np.arange(min(y)+1, max(y), step) + xgrid, ygrid = np.meshgrid(xaxis, yaxis) + zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) + + if ax == None: + fig = plt.figure() + ax = plt.axes(projection = '3d') + + xsrc, ysrc, zsrc = self.getSrcLoc() + + if contour == True: + ax.contour3D(xgrid,ygrid,zgrid,20) + else: + ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) + ax.plot([xsrc], [ysrc], [self.getPick(0)], 'k*', markersize = 20) # plot source location + ax.plot([xsrc], [ysrc], [self.getPick(0)], 'r*', markersize = 15) # plot source location + + if plotpicks == True: + ax.plot(x, y, z, 'k.') + + def plotttc(self, method, *args): + plotmethod = {'2d': self.plot2dttc, '3d': self.plot3dttc} + + plotmethod[method](*args) + + def matshow(self, step = 0.5, method = 'linear', ax = None, plotRec = False, annotations = False): + ''' + Plots a 2D matrix of the interpolated traveltimes. This needs less performance than plot3dttc + + :param: step (optional), gives the stepsize for the interpolated grid. Default is 0.5 + :type: 'float' + + :param: method (optional), interpolation method; can be 'linear' (default) or 'cubic' + :type: 'string' + + :param: plotRec (optional), plot the receiver positions + :type: 'logical' + + :param: annotations (optional), displays traceIDs as annotations + :type: 'logical' + ''' + import matplotlib.pyplot as plt + from scipy.interpolate import griddata +# plt.interactive('True') + + x = [] + y = [] + z = [] + for traceID in self.pick.keys(): + if self.getPick(traceID) != None: + x.append(self.getRecLoc(traceID)[0]) + y.append(self.getRecLoc(traceID)[1]) + z.append(self.getPick(traceID)) + + xaxis = np.arange(min(x)+1, max(x), step) + yaxis = np.arange(min(y)+1, max(y), step) + xgrid, ygrid = np.meshgrid(xaxis, yaxis) + zgrid = griddata((x, y), z, (xgrid, ygrid), method='linear') + + if ax == None: + fig = plt.figure() + ax = plt.axes() + + ax.imshow(zgrid, interpolation = 'none', extent = [min(x), max(x), min(y), max(y)]) + + if annotations == True: + for i, traceID in enumerate(self.pick.keys()): + if shot.picks[traceID] != None: + ax.annotate('%s' % traceID, xy=(x[i], y[i]), fontsize = 'x-small') + + if plotRec == True: + ax.plot(x, y, 'k.') + + plt.show() diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py new file mode 100644 index 00000000..ef29e3a9 --- /dev/null +++ b/pylot/core/active/surveyUtils.py @@ -0,0 +1,204 @@ +from pylab import * +startpos = [] +endpos = [] + +def generateSurvey(obsdir, shotlist): + from obspy.core import read + from pylot.core.active import seismicshot + + shot_dict = {} + for shotnumber in shotlist: # loop over data files + # generate filenames and read manual picks to a list + obsfile = obsdir + str(shotnumber) + '_pickle.dat' + #obsfile = obsdir + str(shotnumber) + '.dat' + + if not obsfile in shot_dict.keys(): + shot_dict[shotnumber] = [] + shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) + shot_dict[shotnumber].setParameters('shotnumber', shotnumber) + + return shot_dict + +def setParametersForShots(cutwindow, tmovwind, tsignal, tgap, receiverfile, sourcefile, shot_dict): + for shot in shot_dict.values(): + shot.setCut(cutwindow) + shot.setTmovwind(tmovwind) + shot.setTsignal(tsignal) + shot.setTgap(tgap) + shot.setRecfile(receiverfile) + shot.setSourcefile(sourcefile) + shot.setOrder(order = 4) + +def removeEmptyTraces(shot_dict): + filename = 'removeEmptyTraces.out' + filename2 = 'updateTraces.out' + outfile = open(filename, 'w') + outfile2 = open(filename2, 'w') + for shot in shot_dict.values(): + del_traceIDs = shot.updateTraceList() + removed = shot.removeEmptyTraces() + if removed is not None: + outfile.writelines('shot: %s, removed empty traces: %s\n' %(shot.getShotnumber(), removed)) + outfile2.writelines('shot: %s, removed traceID(s) %s because they were not found in the corresponding stream\n' %(shot.getShotnumber(), del_traceIDs)) + print '\nremoveEmptyTraces, updateTraces: Finished! See %s and %s for more information of removed traces.\n' %(filename, filename2) + outfile.close() + outfile2.close() + + +def readParameters(parfile, parameter): + from ConfigParser import ConfigParser + parameterConfig = ConfigParser() + parameterConfig.read('parfile') + + value = parameterConfig.get('vars', parameter).split('\t')[0] + + return value + +def setArtificialPick(shot_dict, traceID, pick): + for shot in shot_dict.values(): + shot.setPick(traceID, pick) + shot.setPickwindow(traceID, shot.getCut()) + +def fitSNR4dist(shot_dict, shiftdist = 5): + import numpy as np + dists = [] + picks = [] + snrs = [] + snr_sqrt_inv = [] + snrthresholds = [] + for shot in shot_dict.values(): + for traceID in shot.getTraceIDlist(): + if shot.getSNR(traceID)[0] >= 1: + dists.append(shot.getDistance(traceID)) + picks.append(shot.getPick_backup(traceID)) + snrs.append(shot.getSNR(traceID)[0]) + snr_sqrt_inv.append(1/np.sqrt(shot.getSNR(traceID)[0])) + fit = np.polyfit(dists, snr_sqrt_inv, 1) + fit_fn = np.poly1d(fit) + for dist in dists: + dist += shiftdist + snrthresholds.append(1/(fit_fn(dist)**2)) + plotFittedSNR(dists, snrthresholds, snrs) + return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben + + +def plotFittedSNR(dists, snrthresholds, snrs): + import matplotlib.pyplot as plt + plt.interactive(True) + fig = plt.figure() + plt.plot(dists, snrs, '.', markersize = 0.5, label = 'SNR values') + plt.plot(dists, snrthresholds, 'r.', markersize = 1, label = 'Fitted threshold') + plt.xlabel('Distance[m]') + plt.ylabel('SNR') + plt.legend() + +def setFittedSNR(shot_dict, shiftdist = 5, p1 = 0.004, p2 = -0.004): + import numpy as np + #fit_fn = fitSNR4dist(shot_dict) + fit_fn = np.poly1d([p1, p2]) + for shot in shot_dict.values(): + for traceID in shot.getTraceIDlist(): ### IMPROVE + shot.setSNRthreshold(traceID, 1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) ### s.o. + print "setFittedSNR: Finished setting of fitted SNR-threshold" + +#def linearInterp(dist_med, dist_start + +def exportFMTOMO(shot_dict, directory = 'FMTOMO_export', sourcefile = 'input_sf.in', ttFileExtension = '.tt'): + count = 0 + fmtomo_factor = 1000 # transforming [m/s] -> [km/s] + LatAll = []; LonAll = []; DepthAll = [] + srcfile = open(directory + '/' + sourcefile, 'w') + srcfile.writelines('%10s\n' %len(shot_dict)) # number of sources + for shotnumber in getShotlist(shot_dict): + shot = getShotForShotnumber(shot_dict, shotnumber) + ttfilename = str(shotnumber) + ttFileExtension + (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) + srcfile.writelines('%10s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z)) # lat, lon, depth + LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) + srcfile.writelines('%10s\n' %1) # + srcfile.writelines('%10s %10s %10s\n' %(1, 1, ttfilename)) + ttfile = open(directory + '/' + ttfilename, 'w') + traceIDlist = shot.getTraceIDlist() + traceIDlist.sort() + ttfile.writelines(str(countPickedTraces(shot)) + '\n') + for traceID in traceIDlist: + if shot.getPick(traceID) is not None: + pick = shot.getPick(traceID) * fmtomo_factor + ### MP MP + + + #delta = shot.getMistake(traceID) * fmtomo_factor + delta = 0.2 + ### MP MP - - + (x, y, z) = shot.getRecLoc(traceID) + ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) + LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) + count += 1 + ttfile.close() + srcfile.close() + print 'Wrote output for %s traces' %count + print 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems to take Lat, Lon, Depth and creates output for FMTOMO as Depth, Lat, Lon' + print 'Dimensions of the seismic Array, transformed for FMTOMO, are Depth(%s, %s), Lat(%s, %s), Lon(%s, %s)'%( + min(DepthAll), max(DepthAll), min(LatAll), max(LatAll), min(LonAll), max(LonAll)) + +def getShotlist(shot_dict): + shotlist = [] + for shot in shot_dict.values(): + shotlist.append(shot.getShotnumber()) + shotlist.sort() + return shotlist + +def getShotForShotnumber(shot_dict, shotnumber): + for shot in shot_dict.values(): + if shot.getShotnumber() == shotnumber: + return shot + +def getAngle(distance): + ''' + Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. + ''' + import numpy as np + PI = np.pi + R = 6371. + angle = distance * 180 / (PI * R) + return angle + +def countPickedTraces(shot): + numtraces = 0 + for traceID in shot.getTraceIDlist(): + if shot.getPick(traceID) is not None: + numtraces += 1 + print "countPickedTraces: Found %s picked traces in shot number %s" %(numtraces, shot.getShotnumber()) + return numtraces + +def countAllPickedTraces(shot_dict): + traces = 0 + for shot in shot_dict.values(): + traces += countPickedTraces(shot) + return traces + +def findTracesInRanges(shot_dict, distancebin, pickbin): + ''' + Returns traces corresponding to a certain area in a plot with all picks over the distances. + + :param: shot_dict, dictionary containing all shots that are used + :type: dictionary + + :param: distancebin + :type: tuple, (dist1[m], dist2[m]) + + :param: pickbin + :type: tuple, (t1[s], t2[s]) + + ''' + shots_found = {} + for shot in shot_dict.values(): + if shot.getTraceIDs4Dist(distancebin = distancebin) is not None: + for traceID in shot.getTraceIDs4Dist(distancebin = distancebin): + if pickbin[0] < shot.getPick(traceID) < pickbin[1]: + if shot.getShotnumber() not in shots_found.keys(): + shots_found[shot.getShotnumber()] = [] + shots_found[shot.getShotnumber()].append(traceID) + + return shots_found + + + From 7a475946144e5c49bdc44253d90f201a13084f10 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 17 Sep 2015 17:45:10 +0200 Subject: [PATCH 0569/1144] debugged some code fragments while trying to solve the load event data problem --- QtPyLoT.py | 38 ++++++++++++++++++++------------------ pylot/RELEASE-VERSION | 2 +- pylot/core/read/data.py | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 38cab8b1..e7eb612c 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -340,25 +340,24 @@ class MainWindow(QMainWindow): def loadData(self, fname=None): if fname is None: - try: - self.data = Data(self, evtdata=self.fname) - except AttributeError: - action = self.sender() - if isinstance(action, QAction): - if action.data() is None: - filt = "Supported event formats (*.mat *.qml *.xml *.kor *.evt)" - caption = "Open an event file" - fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) - self.fname = fname[0] - else: - self.fname = unicode(action.data().toString()) - if not self.okToContinue(): - return + action = self.sender() + if isinstance(action, QAction): + if action.data() is None: + filt = "Supported event formats (*.mat *.qml *.xml *.kor *.evt)" + caption = "Open an event file" + fname = QFileDialog().getOpenFileName(self, + caption=caption, + filter=filt) + self.setFileName(fname[0]) + else: + self.setFileName(unicode(action.data().toString())) + if not self.okToContinue(): + return else: - self.fname = fname - self.data = Data(self, evtdata=self.fname) + self.setFileName(fname) + self.data += Data(self, evtdata=self.getFileName()) + self.updatePicks() + self.updateStatus('Event data loaded ...') def getLastEvent(self): return self.recentEvents[0] @@ -469,6 +468,9 @@ class MainWindow(QMainWindow): def getPicks(self): return self.picks + def updatePicks(self): + pass + def getPicksOnStation(self, station): try: return self.getPicks()[station] diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 41931e59..01eb3d3d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -c940-dirty +ac7d-dirty diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index ca50132d..99b7441d 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -33,16 +33,16 @@ class Data(object): else: self.comp = 'Z' self.wfdata = Stream() - self.newevent = False - if evtdata is not None and isinstance(evtdata, Event): + self._new = False + if isinstance(evtdata, Event): self.evtdata = evtdata - elif evtdata is not None and not isinstance(evtdata, dict): + elif isinstance(evtdata, dict): + cat = readPILOTEvent(**evtdata) + elif evtdata: cat = readEvents(evtdata) self.evtdata = cat[0] - elif evtdata is not None: - cat = readPILOTEvent(**evtdata) else: # create an empty Event object - self.newevent = True + self.setNew() self.evtdata = Event() self.getEvtData().picks = [] self.wforiginal = None @@ -52,6 +52,27 @@ class Data(object): def __str__(self): return str(self.wfdata) + def __add__(self, other): + assert isinstance(other, Data), "operands must be of same type 'Data'" + if other.isNew() and not self.isNew(): + picks_to_add = other.getEvtData().picks + old_picks = self.getEvtData().picks + for pick in picks_to_add: + if pick not in old_picks: + old_picks.append(pick) + elif not other.isNew() and self.isNew(): + new = other + self + self.evtdata = new.getEvtData() + elif self.isNew() and other.isNew(): + pass + elif self.getEvtData().get('id') == other.getEvtData().get('id'): + other.setNew() + return self + other + else: + raise ValueError("both Data objects have differing " + "unique Event identifiers") + return self + def getPicksStr(self): picks_str = '' for pick in self.getEvtData().picks: @@ -73,7 +94,10 @@ class Data(object): :return: """ - return self.newevent + return self._new + + def setNew(self): + self._new = True def getCutTimes(self): """ From ff2b50f61539240dab80fa330e97d1863aceae5b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 18 Sep 2015 09:54:29 +0200 Subject: [PATCH 0570/1144] made some changes to make the code more idiomatic and renamed method convertPicks4PyLoT to updatePicks --- QtPyLoT.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 11fdc660..68ced75d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -54,11 +54,9 @@ from pylot.core.util.thread import AutoPickThread from pylot.core.util.version import get_git_version as _getVersionString import icons_rc -# Version information -__version__ = _getVersionString() - class MainWindow(QMainWindow): + __version__ = _getVersionString() closing = Signal() def __init__(self, parent=None): @@ -340,7 +338,7 @@ class MainWindow(QMainWindow): def loadData(self, fname=None): if not self.okToContinue(): - return + return if fname is None: action = self.sender() if isinstance(action, QAction): @@ -355,7 +353,7 @@ class MainWindow(QMainWindow): fname = unicode(action.data().toString()) self.setFileName(fname) self.data += Data(self, evtdata=self.getFileName()) - self.convertPicks4PyLoT() + self.updatePicks() self.drawPicks() def getLastEvent(self): @@ -692,7 +690,7 @@ class MainWindow(QMainWindow): raise Exception('FATAL: Should never occur!') self.getPicks()[station] = stat_picks - def convertPicks4PyLoT(self): + def updatePicks(self): evt = self.getData().getEvtData() picks = {} onsets = {} @@ -809,20 +807,24 @@ def main(): app_icon = QIcon() app_icon.addPixmap(QPixmap(':/icons/pylot.png')) - # set Application Information - pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") - pylot_app.setOrganizationDomain("rub.de") - pylot_app.setApplicationName("PyLoT") - pylot_app.setApplicationVersion(__version__) - pylot_app.setWindowIcon(app_icon) - # create the main window pylot_form = MainWindow() splash.showMessage('Loading. Please wait ...') pylot_app.processEvents() + # set Application Information + pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") + pylot_app.setOrganizationDomain("rub.de") + pylot_app.processEvents() + pylot_app.setApplicationName("PyLoT") + pylot_app.setApplicationVersion(pylot_form.__version__) + pylot_app.setWindowIcon(app_icon) + pylot_app.processEvents() + # Show main window and run the app pylot_form.showMaximized() + pylot_app.processEvents() + splash.finish(pylot_form) pylot_app.exec_() From d1f1fb42f34f02bfc7218968b3b41143c6b69ca4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 21 Sep 2015 08:48:29 +0200 Subject: [PATCH 0571/1144] [fixes #176] event files are now opened and loaded correctly and picks are plotted in place --- QtPyLoT.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 68ced75d..e4904c98 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -693,21 +693,26 @@ class MainWindow(QMainWindow): def updatePicks(self): evt = self.getData().getEvtData() picks = {} - onsets = {} for pick in evt.picks: phase = {} station = pick.waveform_id.station_code + try: + onsets = picks[station] + except KeyError as e: + print(e) + onsets = {} mpp = pick.time - lpp = mpp + pick.time.upper_uncertainty - epp = mpp - pick.time.lower_uncertainty - spe = pick.time.uncertainty + lpp = mpp + pick.time_errors.upper_uncertainty + epp = mpp - pick.time_errors.lower_uncertainty + spe = pick.time_errors.uncertainty phase['mpp'] = mpp phase['epp'] = epp phase['lpp'] = lpp phase['spe'] = spe - onsets[pick.phase_hint] = phase - picks[station] = onsets + onsets[pick.phase_hint] = phase.copy() + picks[station] = onsets.copy() + self.picks.update(picks) def drawPicks(self, station=None): # if picks to draw not specified, draw all picks available From 130dc8db4e6672e487298aa283e2f8d5055f8680 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 22 Sep 2015 10:31:37 +0200 Subject: [PATCH 0572/1144] [bugfix] wrong functions import fixed --- pylot/core/active/seismicshot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 5e6ee8bf..1d28a3f5 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -579,8 +579,8 @@ class SeismicShot(object): def plot_traces(self, traceID, folm = 0.6): ########## 2D, muss noch mehr verbessert werden ########## import matplotlib.pyplot as plt - from pylot.core.util import getGlobalTimes - from pylot.core.util import prepTimeAxis + from pylot.core.util.utils import getGlobalTimes + from pylot.core.util.utils import prepTimeAxis stream = self.getSingleStream(traceID) stime = getGlobalTimes(stream)[0] From dedf6eff00111d076219fe0d56beab6c704a7800 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 22 Sep 2015 11:58:43 +0200 Subject: [PATCH 0573/1144] Changed earllatepicker. If EPick = Nan, signal window is doubled. --- pylot/core/pick/utils.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index b78d7946..79fc4348 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -68,12 +68,26 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): # get earliest possible pick - # determine all zero crossings in signal window (demeaned) - zc = crossings_nonzero_all(x[isignal] - x[isignal].mean()) - # calculate mean half period T0 of signal as the average of the - T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length! - # T0/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - T0 / 2 + EPick = np.nan + while np.isnan(EPick): + # determine all zero crossings in signal window (demeaned) + zc = crossings_nonzero_all(x[isignal] - x[isignal].mean()) + # calculate mean half period T0 of signal as the average of the + T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length! + # T0/4 is assumed as time difference between most likely and earliest possible pick! + EPick = Pick1 - T0 / 2 + if np.isnan(EPick): + print "earllatepicker: Doubled signal window size because of NaN for earliest pick." + isigDoubleWinStart = isignal[-1] + 1 + isignalDoubleWin = np.arange(isigDoubleWinStart, isigDoubleWinStart + len(isignal)) + if (isigDoubleWinStart + len(isignal)) < X[0].data.size: + isignal = np.concatenate((isignal, isignalDoubleWin)) + else: + isignalDoubleWin = np.arange(isigDoubleWinStart, X[0].data.size) + isignal = np.concatenate((isignal, isignalDoubleWin)) + print "Could not double signal window. Index out of bounds." + break + # get symmetric pick error as mean from earliest and latest possible pick # by weighting latest possible pick two times earliest possible pick From 844708bbac7a6c5657436a7bba2ff1a824fb7169 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 22 Sep 2015 12:29:42 +0200 Subject: [PATCH 0574/1144] [hotfix] earllatepicker recursively modifies isignal to obtain zero-crossing also for low frequency onsets --- pylot/core/pick/utils.py | 45 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 79fc4348..5bb80064 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -59,7 +59,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ilup, = np.where(x[isignal] > nlevel) ildown, = np.where(x[isignal] < -nlevel) if not ilup.size and not ildown.size: - print ("earllatepicker: Signal lower than noise level!") + print ("earllatepicker: Signal lower than noise level!") print ("Skip this trace!") return LPick, EPick, PickError il = min(np.min(ilup) if ilup.size else float('inf'), @@ -69,24 +69,25 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): # get earliest possible pick EPick = np.nan + pis = isignal[:len(isignal) / 2] if not len(isignal) % 2 else \ + isignal[:len(isignal) / 2 + 1] while np.isnan(EPick): + print("earllatepicker: Doubled signal window size because of NaN for " + "earliest pick.") + isigDoubleWinStart = pis[-1] + 1 + isignalDoubleWin = np.arange(isigDoubleWinStart, + isigDoubleWinStart + len(pis)) + if (isigDoubleWinStart + len(pis)) < X[0].data.size: + pis = np.concatenate((pis, isignalDoubleWin)) + else: + print("Could not double signal window. Index out of bounds.") + break # determine all zero crossings in signal window (demeaned) - zc = crossings_nonzero_all(x[isignal] - x[isignal].mean()) + zc = crossings_nonzero_all(x[pis] - x[pis].mean()) # calculate mean half period T0 of signal as the average of the - T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length! + T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length # T0/4 is assumed as time difference between most likely and earliest possible pick! EPick = Pick1 - T0 / 2 - if np.isnan(EPick): - print "earllatepicker: Doubled signal window size because of NaN for earliest pick." - isigDoubleWinStart = isignal[-1] + 1 - isignalDoubleWin = np.arange(isigDoubleWinStart, isigDoubleWinStart + len(isignal)) - if (isigDoubleWinStart + len(isignal)) < X[0].data.size: - isignal = np.concatenate((isignal, isignalDoubleWin)) - else: - isignalDoubleWin = np.arange(isigDoubleWinStart, X[0].data.size) - isignal = np.concatenate((isignal, isignalDoubleWin)) - print "Could not double signal window. Index out of bounds." - break # get symmetric pick error as mean from earliest and latest possible pick @@ -200,11 +201,11 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) if imax1 == 0: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][index1[1]]])) if imax1 == 0: - print ("fmpicker: Zero crossings too close!") - print ("Skip first motion determination!") - return FM + print ("fmpicker: Zero crossings too close!") + print ("Skip first motion determination!") + return FM islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) # calculate slope as polynomal fit of order 1 @@ -242,11 +243,11 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): else: imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) if imax2 == 0: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][index2[1]]])) if imax2 == 0: - print ("fmpicker: Zero crossings too close!") - print ("Skip first motion determination!") - return FM + print ("fmpicker: Zero crossings too close!") + print ("Skip first motion determination!") + return FM islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) # calculate slope as polynomal fit of order 1 From 217db9bbddb9528aba9e28fc2cdba9a8501b26db Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 22 Sep 2015 13:41:19 +0200 Subject: [PATCH 0575/1144] [bugfix] updated EPick nan fix --- pylot/core/pick/utils.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 5bb80064..9e9f1492 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -68,20 +68,23 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): # get earliest possible pick - EPick = np.nan - pis = isignal[:len(isignal) / 2] if not len(isignal) % 2 else \ - isignal[:len(isignal) / 2 + 1] + EPick = np.nan; count = 0 + pis = isignal + + # if EPick stays NaN the signal window size will be doubled while np.isnan(EPick): - print("earllatepicker: Doubled signal window size because of NaN for " - "earliest pick.") - isigDoubleWinStart = pis[-1] + 1 - isignalDoubleWin = np.arange(isigDoubleWinStart, + if count > 0: + print("earllatepicker: Doubled signal window size %s time(s) " + "because of NaN for earliest pick." %count) + isigDoubleWinStart = pis[-1] + 1 + isignalDoubleWin = np.arange(isigDoubleWinStart, isigDoubleWinStart + len(pis)) - if (isigDoubleWinStart + len(pis)) < X[0].data.size: - pis = np.concatenate((pis, isignalDoubleWin)) - else: - print("Could not double signal window. Index out of bounds.") - break + if (isigDoubleWinStart + len(pis)) < X[0].data.size: + pis = np.concatenate((pis, isignalDoubleWin)) + else: + print("Could not double signal window. Index out of bounds.") + break + count += 1 # determine all zero crossings in signal window (demeaned) zc = crossings_nonzero_all(x[pis] - x[pis].mean()) # calculate mean half period T0 of signal as the average of the From bb97df61797d3e93ecb1ca50f84a169449c5ad34 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 22 Sep 2015 14:35:04 +0200 Subject: [PATCH 0576/1144] name change --- pylot/core/active/seismicshot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 1d28a3f5..132dbc73 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -142,7 +142,7 @@ class SeismicShot(object): def getLatest(self, traceID): return self.latest[traceID] - def getMistake(self, traceID): + def getPickError(self, traceID): mistake = abs(self.getEarliest(traceID) - self.getLatest(traceID)) if np.isnan(mistake) == True: print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) From 19e4435497ab75564e2549a94c28dbc0919a2f4f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 22 Sep 2015 14:36:19 +0200 Subject: [PATCH 0577/1144] name change --- pylot/core/active/seismicshot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 132dbc73..5a13651f 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -143,10 +143,10 @@ class SeismicShot(object): return self.latest[traceID] def getPickError(self, traceID): - mistake = abs(self.getEarliest(traceID) - self.getLatest(traceID)) - if np.isnan(mistake) == True: + pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) + if np.isnan(pickerror) == True: print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) - return mistake + return pickerror def getStreamTraceIDs(self): traceIDs = [] From 9d5b7ad5ae69451ba6272f248fec44c65670c075 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 22 Sep 2015 14:38:27 +0200 Subject: [PATCH 0578/1144] name change --- pylot/core/active/surveyUtils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index ef29e3a9..6ba0aa9b 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -124,10 +124,7 @@ def exportFMTOMO(shot_dict, directory = 'FMTOMO_export', sourcefile = 'input_sf. for traceID in traceIDlist: if shot.getPick(traceID) is not None: pick = shot.getPick(traceID) * fmtomo_factor - ### MP MP + + - #delta = shot.getMistake(traceID) * fmtomo_factor - delta = 0.2 - ### MP MP - - + delta = shot.getPickError(traceID) * fmtomo_factor (x, y, z) = shot.getRecLoc(traceID) ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) From d38adb75b26835a66c4489cdb6f48598a8ebf80e Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 23 Sep 2015 14:11:24 +0200 Subject: [PATCH 0579/1144] deleted import * from pylab --- pylot/core/active/surveyUtils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 6ba0aa9b..73e1fbc7 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -1,7 +1,3 @@ -from pylab import * -startpos = [] -endpos = [] - def generateSurvey(obsdir, shotlist): from obspy.core import read from pylot.core.active import seismicshot @@ -50,7 +46,8 @@ def readParameters(parfile, parameter): parameterConfig = ConfigParser() parameterConfig.read('parfile') - value = parameterConfig.get('vars', parameter).split('\t')[0] + value = parameterConfig.get('vars', parameter).split('#')[0] + value = value.replace(" ", "") return value From 30ee81a39d1d5238fb43dfa982ccf7ac3bb2578b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 23 Sep 2015 16:31:48 +0200 Subject: [PATCH 0580/1144] New class DCfc of object Magnitude for calculating source spectrum and to derive DC value and corner frequency. --- pylot/core/analysis/magnitude.py | 56 +++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 072984eb..2af92b37 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -25,7 +25,8 @@ class Magnitude(object): :type: float :param: pwin, pick window [To To+pwin] to get maximum - peak-to-peak amplitude + peak-to-peak amplitude (WApp) or to calculate + source spectrum (DCfc) :type: float :param: iplot, no. of figure window for plotting interims results @@ -40,6 +41,7 @@ class Magnitude(object): self.setpwin(pwin) self.setiplot(iplot) self.calcwapp() + self.calcsourcespec() def getwfstream(self): @@ -68,18 +70,23 @@ class Magnitude(object): def getwapp(self): return self.wapp + + def getw0(self): + return self.w0 + + def getfc(self): + return self.fc def calcwapp(self): self.wapp = None - def calcsourcespec(self): self.sourcespek = None class WApp(Magnitude): ''' Method to derive peak-to-peak amplitude as seen on a Wood-Anderson- - seismograph. Has to be derived from corrected traces! + seismograph. Has to be derived from instrument corrected traces! ''' def calcwapp(self): @@ -110,6 +117,7 @@ class WApp(Magnitude): iwin = getsignalwin(th, self.getTo(), self.getpwin()) self.wapp = np.max(sqH[iwin]) print ("Determined Wood-Anderson peak-to-peak amplitude: %f mm") % self.wapp + if self.getiplot() > 1: stream.plot() f = plt.figure(2) @@ -128,10 +136,48 @@ class WApp(Magnitude): class DCfc(Magnitude): ''' Method to calculate the source spectrum and to derive from that the plateau - (the so-called DC-value) and the corner frequency assuming Aki's omega-square - source model. Has to be derived from corrected traces! + (so-called DC-value) and the corner frequency assuming Aki's omega-square + source model. Has to be derived from instrument corrected displacement traces! ''' def calcsourcespec(self): print ("Calculating source spectrum ....") + self.w0 = None # DC-value + self.fc = None # corner frequency + + stream = self.getwfstream() + tr = stream[0] + + # get time array + t = np.arange(0, len(tr) * tr.stats.delta, tr.stats.delta) + iwin = getsignalwin(t, self.getTo(), self.getpwin()) + xdat = tr.data[iwin] + + # fft + fny = tr.stats.sampling_rate / 2 + N = 1024 + y = tr.stats.delta * np.fft.fft(xdat, N) + Y = abs(y[: N/2]) + L = (N - 1) / tr.stats.sampling_rate + f = np.arange(0, fny, 1 / L) + + #if self.getiplot() > 1: + iplot=2 + if iplot > 1: + f1 = plt.figure(1) + plt.subplot(2,1,1) + plt.plot(t, np.multiply(tr, 1000), 'k') # show displacement in mm + plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') # show displacement in mm + plt.title('Seismogram and P pulse, station %s' % tr.stats.station) + plt.xlabel('Time since %s' % tr.stats.starttime) + plt.ylabel('Displacement [mm]') + + plt.subplot(2,1,2) + plt.semilogy(f, Y.real) + plt.title('Source Spectrum from P Pulse') + plt.xlabel('Frequency [Hz]') + plt.ylabel('Amplitude [m/Hz]') + plt.show() + raw_input() + plt.close(f1) From c790b4f35322e5f16330d6fbc9766aa264be9f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 23 Sep 2015 16:32:18 +0200 Subject: [PATCH 0581/1144] Implemented new class DCfc. --- pylot/core/pick/autopick.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 456023f5..10b4f7d5 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,12 +11,13 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np +from scipy import integrate from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ - getSNR, fmpicker, checkPonsets, wadaticheck + getSNR, fmpicker, checkPonsets, wadaticheck, crossings_nonzero_all from pylot.core.read.data import Data -from pylot.core.analysis.magnitude import WApp +from pylot.core.analysis.magnitude import WApp, DCfc def autopickevent(data, param): stations = [] @@ -309,6 +310,26 @@ def autopickstation(wfstream, pickparam): else: FM = 'N' + ############################################################## + # get DC value (w0) and corner frequency (fc) of source spectrum + # from P pulse + # restitute streams + # initialize Data object + data = Data() + [corzdat, restflag] = data.restituteWFData(invdir, zdat) + if restflag == 1: + # integrate to displacement + corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta) + # class needs stream object => build it + z_copy = zdat.copy() + z_copy[0].data = corintzdat + # calculate source spectrum and get w0 and fc + calcwin = 1 / bpz2[0] # largest detectable period == window length + # around P pulse for calculating source spectrum + specpara = DCfc(z_copy, mpickP, calcwin, iplot) + w0 = specpara.getw0() + fc = specpara.getfc() + print 'autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) Sflag = 1 From b391f5e08216513047e14e41d70f983967a159fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 23 Sep 2015 17:02:27 +0200 Subject: [PATCH 0582/1144] Modified class DCfc: calculation of fft bins after Bath. --- pylot/core/analysis/magnitude.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 2af92b37..4391d16b 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -156,15 +156,17 @@ class DCfc(Magnitude): # fft fny = tr.stats.sampling_rate / 2 - N = 1024 + l = len(xdat) / tr.stats.sampling_rate + n = tr.stats.sampling_rate * l # number of fft bins after Bath + # find next power of 2 of data length + m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2))) + N = int(np.power(m, 2)) y = tr.stats.delta * np.fft.fft(xdat, N) Y = abs(y[: N/2]) L = (N - 1) / tr.stats.sampling_rate - f = np.arange(0, fny, 1 / L) + f = np.arange(0, fny, 1/L) - #if self.getiplot() > 1: - iplot=2 - if iplot > 1: + if self.getiplot() > 1: f1 = plt.figure(1) plt.subplot(2,1,1) plt.plot(t, np.multiply(tr, 1000), 'k') # show displacement in mm From 98df4db95d624692b4921d3c6a4a425644b538a3 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 24 Sep 2015 10:14:39 +0200 Subject: [PATCH 0583/1144] cosmetics --- pylot/core/active/surveyUtils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 73e1fbc7..2f1c990b 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -96,7 +96,7 @@ def setFittedSNR(shot_dict, shiftdist = 5, p1 = 0.004, p2 = -0.004): for shot in shot_dict.values(): for traceID in shot.getTraceIDlist(): ### IMPROVE shot.setSNRthreshold(traceID, 1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) ### s.o. - print "setFittedSNR: Finished setting of fitted SNR-threshold" + print "\nsetFittedSNR: Finished setting of fitted SNR-threshold" #def linearInterp(dist_med, dist_start From 33164d4d1f116ef04d1038a246d7c4362403b473 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 25 Sep 2015 09:03:59 +0200 Subject: [PATCH 0584/1144] corrected for some python 3 compatibility issues; added a new delete picks button to the picking window --- icons.qrc | 1 + icons/delete.png | Bin 0 -> 5087 bytes icons_rc.py | 8 ++++---- pylot/core/read/inputs.py | 18 +++++++++--------- pylot/core/util/widgets.py | 11 ++++++++--- 5 files changed, 22 insertions(+), 16 deletions(-) create mode 100755 icons/delete.png diff --git a/icons.qrc b/icons.qrc index 0b815788..efd43234 100644 --- a/icons.qrc +++ b/icons.qrc @@ -3,6 +3,7 @@ icons/pylot.ico icons/pylot.png icons/printer.png + icons/delete.png icons/key_E.png icons/key_N.png icons/key_P.png diff --git a/icons/delete.png b/icons/delete.png new file mode 100755 index 0000000000000000000000000000000000000000..74c37cf9e2a9ac6f0c8f2ddecc302a491dee3f50 GIT binary patch literal 5087 zcmb7H2{@GN-yVjsN65as36acd&u;8XwnC+5%#0;tW@0P}V{1@I>J(Z?Orp67R6li}`WFD9%Y3<80~ z939Y}+$a3oPY?nE@c{g7n?WGH)(~%Bs;{ey362~F#p1~U1ZYecg&PF|nOek9u((hH z6$l^%g^xZH6MLfxGTkm5FFwV8&2?wb@Rr>hT@FzY8K|grZFa5fG`3T z3&eyGNf9P7W@_JYO}OvhKEu?2?;+GsGc~Jkgn+NBJ77Z&Cjfd-gf0$_G6eJtphzR6 zfw93(z?Dn_tSI3C90?%da61Npou5i?oUSU$V@GmN~M^< zVA0Xh&}bBt93BKi7#kbI;7Aw}smqPfjW|l8Vqg$6lw^U^cyBN zfE-CRQ&Z#413#C8rFez}1ydt_QII13G>&T_ECx$~A)s(r`1cstKQjOCoWBO+alc|I zk>SMe3&7)G1R^1fK%z!)IS@aH!L(@bIi zK>fyo|8FJ!!em9H{+;PtQA~(fQjnQij4qxKh>avt)y!wF)&Z_qJOSXA5zBqUZPnZs zW3}u1jSzz)Mn-Vu0fFRjz$*+J5gbAa;sym_V?#)Ue-rwX^}Eo23i~EDA~N7G0r#i0 z@0-yY8}xUf<{>yTDdO+wzw8PmVx!2B;s48Q#7~W3|Elp9%FikTR5I|R&VL8|G5V?O z`(pko{M$l)?Argo;f*)G9Zq8);3)7vN)P;-ioaRsI*DL=k(PdB%>zh;H8#%8cB?`r!gk5aTbWoUPbKq;b|@){(rD`#_R0nr zEak9N-d`@iQA>L&kuas62bph>1g2@V9E>khON3z+XO}nOBepag6|LIo2oiSgxY*s&&Z^(4CfcHsJQxS?XKufzzac0hg4k|GYjZDW zbO4+ck(8O=1O()bQ%lZ5uRFdad;sg<*(;M$DXa5+nJz;^=1&Y)RtBC9y{pB6uqpey z-=|1}_$25zEu$b7HS&j>8tb1d$&RI+-{eM`7JGRO`%}|;dem@DJd3* z#*y3LtaQ#Te<0Ie9!huuv#9V9WMc1MUGTxFL_uYsZV(wLl-{^MTF#fo!LmD_H9jwA zG|qh(Fa$Qba#$KwLP{#zRZL^bbvvFmKWl546Td$4`%NFwt(uymp}FQ;oZ|Hn zLwM`*P+hgkOvyft--1pjo91>9nW9BSX~sg*{50CvKzPDjtT`OWm9td9$O&3D+)8k+3TB9*#+Uu;NAJ&s@y=nbSQOs0| zhn5F$Zm?QAI_jJF+yX^8mJO@RFcuMY>}eOH08CL-%dHGzjOe3>GeMVb|m zqET910B zL?F;lj)EOCe9DpQ8>?9(vRaI!%>ulbp(GZ2h(%}dNS54rogvGDyP&9r2yf=5J$ab>Q<3zT+0neRy%UW#- zk!?451cBT>$s39M=g}_v-tkrL_Zc($U7k-)KIvcpo^Sgm3H~E}H29b*ycJvrAIkW= z48IQ5S*4RU(Zwx8qBI7gl_>!{^etL7a()^3EOv0w}7w433 z%B~js<^)f#*A$$00O^Q`phVQglMod#0gzs1qqV5x z_O8Tu5V*xM1!erZ+y<{0L}QLtA0TqJ!b+)n>o(Yvvf{fG+iC94 zE38E~#P22YILyYf(?=Hi%X8A{EuS||o|o&r_tIl0TjaqojJ2?xtXjXIS#2G&v?8a~ z3vB)(>sbZz?~PnmVb%DM>)(;0tAc!QmqG$l+DqH}x88>EZ`OiZt|qQpYgsYFHa@R)tEL61o=x;yLA=5X-$R#rl;o?56B|B$2lD+1E`&Z18hmcY|C-!fD!<* znSYCi%v-@jPA7ad-lpatB~+PFp&^|EuSg{WWydZ!a~`^O9cGC4)_1R?K3wEjM0C+= zXD=A<28xYq$0rqZZib3nDmR(gE7wC%B-%M;r=Kk}@jMnOeJo$1@k&ix(AcF<_JL2( z_m|Fls^}X(E-WcK%KK2=)2!r#L^%-GpM#>?y&Er&pPPICQVQinVU#iOFGGF$+!UQY zr0=MZ>KN_YF3st``7*SBVX05^I`g)l|MPdR9{Qi|rA(h0o>G#~+hTNUE4@V2 zBma;}p`msL;f(CV1ImGvxY#=}?r^p?piUeo%{wZlM9B4BZ+eCGLlKhig12;k>9UVP z8PNqZnW9y$srp$}J?6Rj`19Qn!O!^CXre}N{epID$TKvrkf{Gr%9KdcxJ5}*%d~qc zm06-oi2Y-5#q4P-qgPA=B{|6~i;E8ym5|EdE%WnAyj^XkmH0qR@I!zjLQ+8BG^kd~ z%cT(^6KnCCU+pj}XvpJkQnicG;k%_}I&V(oqz*ZMBzD_NOA|&v1QOH~gY|0l;*_%Q z?6;ac2ctZG>wMok@2qPvT1ueOoxLX5TuGm*Q!gwAa3K;4&3wx98To)Gv8f?0-THsh|ydxJ(GM#gYIG1?aZ6 zOOOcLQC6GK%yE4i4YO+VS6a$d+1Xn+YPE*#w=3Iv?`G|pUGnfR=|PN}Ek608vzj33 z4LTOdt15fKCD@VKvVxkx*9x@onKOz-Zi=ik%gXK$Fh=5>1Wndbt%pA+m+=XCP|{^s zDVk3yGv)G1T0J}OwAoX2!HshQ4=&=FyiRG>%PYMfU37yFH+t#@z8sFOb}fyc&wF|$ zUDeu{aXi$wtSY*Fx>QDQ3n!T&HDzn0z*JBffJiNEoS^`YHW5)RBh7d57lc#?<>Y7U zl(R|n;h{j6)tshH58l6;%FLRS)T+>yo1P3_JT~3RCI}ud4Bf-iaBg^LYlSGg6KbDZ zBlJ9qA1y9VpXnLv3DlRIp+_B9uu7Nu3cEY;0Q7cWDAC(i+I;!l+Zocp+umE!`>%f% zl<2c7mQfcTkyo^|E+}eH9p0v(r&&;hvGF*m4a-s(J87Z0BfUva+bI}q{v6aI04cjw zk#w#qmzUq^ar9$i^m12B;LPB$pa=e!c{46t8hm(a&}^u~IHGp8{quP*ZpFDN2zHs2oMAr$i_JzV{;%ts~jc)bwr6v7CLwDt4 zZbglKeD6N+=p62rICIfz?qJq?_MY*;6N>>kjlIIIz?Trd^XV3Xi*;L$U+$eBOMqT5 zs#?v`-!p9}TK`8uBwBzLQ}I>D zLV4PzJ%16ijid-&jS5LsEN65%73>lVS5)Jxa7Cp1pQud2Sey`46|*@;Xr9st&obJU zSk@nUt$+62+DaGb$(eRd$9E;ybF7YfpRU&`E}HB}WSd;ot;0a+w>2BR)2o zf4t7jyH6Z@qrZmawR~{orGDJdV#cRYv<2iwm}#^!V+%9CBKV20nEy3QwEcF6>~7;{ zqe5fYsCkEk*phNtbE6k{lU!v1zQA*t$!TUd4Xb1LoWCfA74JIj;3jt;A`TXj!;-}=nwxxuMrR9`OBdib62Lx?o zJsAOovQgARuvorD`XiyWofnYuGbim%?`uxlRDig5%2CX2MY=KQMfu@3p6OeZ4}W@{ z=;O0VXxQQ7>+$W5gZA<_e1xKfb{s&L7EFS7oX~m|)PG#(>>2gUJcP2r)K|wQ&;(}F z$Ib4G2_AcpmRAC^mM8pBn5o}U7r{F&9#nMm>tQ@cpRH2rf0U+K`QUi=?#{f7-S<@b z*ZEa^Jp0r+2DbT!=j}VSkKM@!YRVg09r#{#UxdK)5>AQ;y83zE0$N^z-W_u1Nvhgw z)%K7!U?(MyKCwTaG;S!`pHcaCpB<=Y!(rC8&M6LB+y;{V?aAQI8-Dz8&(_L5R`ytj ztaU==igFIuc*%@CeY1M0V&wLG+!rFa)Z+mtXDbMO27)y>SO$r#)*zwBPned5H`{ex zwx*A?M=p*973?eO1HQz+G9^ZpG&S!NQ24w!68L=Vh)Rj&$LF*6Kl4RKZQf9EK<8zh z=dFl8#;KP-`MA}B2n`(-C@r1kd)7FiyEVP6K)GaUH4kG>v^xK0A3{L*ZhV(##`0A` z;>scWE6TXk5=U97oF%4u(fnim#;~=~@Pf%wpR|>|x*hYB>#A#moMvQ!8J4#? z#i~{fA@nTy!gQqV{(=U_}p*i4f*0IbkR_Yd1Of4?SsK2)m>VVhNJ>7Hf_xm#$B3huM_>9pI7-l}5mf1%Ld++qM_ zx-H*J8F?+eV7;{UVx^Y*K!0D`0USQDEv6%Tj!$7I-aj^R+I@J0wdZKZgC&ix*4{dj=(3{Ty(aJcxUES*>$ip?_yEe6lAj zcIw5kc-j5)y)p)4!7-zZ^jJRc#y?oE-|wEUwtG7pDPZ^5d)C2q>3wLyuIt)yvX9c9 zqsh7IZVGlxGsl3s7RiwzNbdIez2GX7(1<@6!2wEpzVy?Th`)=buy3Xa>^~O_?eW&` zmgvbXZnni8ciVdktN6*KVlk+!d!5|AgTrh8xRB Date: Fri, 25 Sep 2015 15:06:59 +0200 Subject: [PATCH 0585/1144] some pick plotting issues solved; replotting main window if picks have been changed, otherwise just plot additional/deleted picks --- QtPyLoT.py | 27 +++++++++++++++++---------- pylot/core/util/widgets.py | 30 ++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e4904c98..cae007c4 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -386,8 +386,8 @@ class MainWindow(QMainWindow): else: raise DatastructureError('not specified') return self.fnames - except DatastructureError, e: - print e + except DatastructureError as e: + print(e) props = PropertiesDlg(self) if props.exec_() == QDialog.Accepted: return self.getWFFnames() @@ -410,7 +410,7 @@ class MainWindow(QMainWindow): def saveData(self): def getSavePath(e): - print 'warning: {0}'.format(e) + print('warning: {0}'.format(e)) directory = os.path.join(self.getRoot(), self.getEventFileName()) file_filter = "QuakeML file (*.xml);;VELEST observation file format (*.cnv);;NonLinLoc observation file (*.obs)" fname = QFileDialog.getSaveFileName(self, 'Save event data ...', @@ -513,7 +513,7 @@ class MainWindow(QMainWindow): def plotWaveformData(self): zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'} comp = self.getComponent() - title = 'overview: {0} components'.format(zne_text[comp]) + title = 'section: {0} components'.format(zne_text[comp]) wfst = self.getData().getWFData().select(component=comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title) self.draw() @@ -574,8 +574,8 @@ class MainWindow(QMainWindow): def getFilterOptions(self): try: return self.filteroptions[self.getSeismicPhase()] - except AttributeError, e: - print e + except AttributeError as e: + print(e) return FilterOptions(None, None, None) def getFilters(self): @@ -592,12 +592,12 @@ class MainWindow(QMainWindow): settings = QSettings() if settings.value("filterdefaults", None) is None and not self.getFilters(): - for key, value in FILTERDEFAULTS.iteritems(): + for key, value in FILTERDEFAULTS.items(): self.setFilterOptions(FilterOptions(**value), key) elif settings.value("filterdefaults", None) is not None: for key, value in settings.value("filterdefaults"): self.setFilterOptions(FilterOptions(**value), key) - except Exception, e: + except Exception as e: self.updateStatus('Error ...') emsg = QErrorMessage(self) emsg.showMessage('Error: {0}'.format(e)) @@ -636,8 +636,12 @@ class MainWindow(QMainWindow): if pickDlg.exec_(): self.setDirty(True) self.updateStatus('picks accepted ({0})'.format(station)) - self.addPicks(station, pickDlg.getPicks()) - self.drawPicks(station) + replot = self.addPicks(station, pickDlg.getPicks()) + if replot: + self.plotWaveformData() + self.drawPicks() + else: + self.drawPicks(station) else: self.updateStatus('picks discarded ({0})'.format(station)) @@ -667,6 +671,7 @@ class MainWindow(QMainWindow): def addPicks(self, station, picks): stat_picks = self.getPicksOnStation(station) + rval = False if not stat_picks: stat_picks = picks else: @@ -684,11 +689,13 @@ class MainWindow(QMainWindow): ret = msgBox.exec_() if ret == QMessageBox.Save: stat_picks = picks + rval = True elif ret == QMessageBox.Cancel: pass else: raise Exception('FATAL: Should never occur!') self.getPicks()[station] = stat_picks + return rval def updatePicks(self): evt = self.getData().getEvtData() diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1e7dd07a..6e680d81 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -240,7 +240,7 @@ class PickDlg(QDialog): home_icon = QIcon() home_icon.addPixmap(QPixmap(':/icons/zoom_0.png')) del_icon = QIcon() - del_icon.addPixmap(QPixmap(':/icon/delete.png')) + del_icon.addPixmap(QPixmap(':/icons/delete.png')) # create actions self.filterAction = createAction(parent=self, text='Filter', @@ -275,6 +275,8 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.zoomAction) _dialtoolbar.addSeparator() _dialtoolbar.addAction(self.resetZoomAction) + _dialtoolbar.addSeparator() + _dialtoolbar.addAction(self.resetPicksAction) # layout the innermost widget _innerlayout = QVBoxLayout() @@ -376,7 +378,7 @@ class PickDlg(QDialog): traceIDs = [] for channel in channels: channel = channel.upper() - for traceID, channelID in plotDict.iteritems(): + for traceID, channelID in plotDict.items(): if channelID[1].upper().endswith(channel): traceIDs.append(traceID) return traceIDs @@ -427,6 +429,13 @@ class PickDlg(QDialog): def getPicks(self): return self.picks + def resetPicks(self): + self.picks = {} + + def delPicks(self): + self.resetPicks() + self.resetPlot() + def setIniPick(self, gui_event): trace_number = round(gui_event.ydata) @@ -677,8 +686,21 @@ class PickDlg(QDialog): zoomx=self.getXLims(), zoomy=self.getYLims()) self.setPlotLabels() + self.drawPicks() self.draw() + def resetPlot(self): + self.updateCurrentLimits() + data = self.getWFData().copy() + title = self.getPlotWidget().getAxes().get_title() + self.getPlotWidget().plotWFData(wfdata=data, title=title, + zoomx=self.getXLims(), + zoomy=self.getYLims()) + self.setPlotLabels() + self.drawPicks() + self.draw() + + def setPlotLabels(self): # get channel labels @@ -716,7 +738,7 @@ class PickDlg(QDialog): else: # deal with something that should never happen scale_factor = 1 - print gui_event.button + print(gui_event.button) new_xlim = gui_event.xdata - \ scale_factor * (gui_event.xdata - self.getXLims()) @@ -747,7 +769,7 @@ class PickDlg(QDialog): def apply(self): picks = self.getPicks() for pick in picks: - print pick, picks[pick] + print(pick, picks[pick]) def accept(self): self.apply() From f7878cdb4aca4982e93bc5ca46ace32bbe52b2cc Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 28 Sep 2015 10:20:26 +0200 Subject: [PATCH 0586/1144] implemented vgrids2VTK to surveyUtils --- pylot/core/active/surveyUtils.py | 112 ++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 2f1c990b..a7086102 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -193,6 +193,116 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): shots_found[shot.getShotnumber()].append(traceID) return shots_found - +def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): + def getDistance(angle): + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + + def readNumberOfPoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + nR = int(vglines[1].split()[0]) + nTheta = int(vglines[1].split()[1]) + nPhi = int(vglines[1].split()[2]) + + fin.close() + return nR, nTheta, nPhi + + def readDelta(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + dR = float(vglines[2].split()[0]) + dTheta = float(vglines[2].split()[1]) + dPhi = float(vglines[2].split()[2]) + + fin.close() + return dR, dTheta, dPhi + + def readStartpoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + sR = float(vglines[3].split()[0]) + sTheta = float(vglines[3].split()[1]) + sPhi = float(vglines[3].split()[2]) + + fin.close() + return sR, sTheta, sPhi + + def readVelocity(filename): + vel = []; count = 0 + fin = open(filename, 'r') + vglines = fin.readlines() + + for line in vglines: + count += 1 + if count > 4: + vel.append(float(line.split()[0])) + + print("Read %d points out of file: %s" %(count - 4, filename)) + return vel + + R = 6371 # earth radius + outfile = open(outputfile, 'w') + + # Theta, Phi in radians, R in km + nR, nTheta, nPhi = readNumberOfPoints(inputfile) + dR, dTheta, dPhi = readDelta(inputfile) + sR, sTheta, sPhi = readStartpoints(inputfile) + vel = readVelocity(inputfile) + + nX = nPhi; nY = nTheta; nZ = nR + + sZ = sR - R + sX = getDistance(np.rad2deg(sPhi)) + sY = getDistance(np.rad2deg(sTheta)) + + dX = getDistance(np.rad2deg(dPhi)) + dY = getDistance(np.rad2deg(dTheta)) + + xGrid = np.linspace(sX, sX + (dX * nX), nX) + yGrid = np.linspace(sZ, sZ + (nY * dY), nY) + zGrid = np.linspace(sZ, sZ + (nR * dR), nR) + nPoints = len(xGrid) * len(yGrid) * len(zGrid) + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Velocity on FMTOMO vgrids.in points\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + print("Writing coordinates to VTK file...") + for z in zGrid: + for y in yGrid: + for x in xGrid: + outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + + + outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + + # write indices + print("Writing indices to VTK file...") + for index in range(nPoints): + outfile.writelines('%10d %10d\n' %(1, index)) + + outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('SCALARS velocity float %d\n' %(1)) + outfile.writelines('LOOKUP_TABLE default\n') + + # write velocity + print("Writing velocity values to VTK file...") + for velocity in vel: + outfile.writelines('%10f\n' %velocity) + + outfile.close() + print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + return From fd3d4cc4768fbbf3b44e814178613ed2d282a394 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 28 Sep 2015 10:23:41 +0200 Subject: [PATCH 0587/1144] -> import numpy --- pylot/core/active/surveyUtils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index a7086102..64d81a05 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -1,3 +1,5 @@ +import numpy as np + def generateSurvey(obsdir, shotlist): from obspy.core import read from pylot.core.active import seismicshot @@ -57,7 +59,6 @@ def setArtificialPick(shot_dict, traceID, pick): shot.setPickwindow(traceID, shot.getCut()) def fitSNR4dist(shot_dict, shiftdist = 5): - import numpy as np dists = [] picks = [] snrs = [] @@ -90,7 +91,6 @@ def plotFittedSNR(dists, snrthresholds, snrs): plt.legend() def setFittedSNR(shot_dict, shiftdist = 5, p1 = 0.004, p2 = -0.004): - import numpy as np #fit_fn = fitSNR4dist(shot_dict) fit_fn = np.poly1d([p1, p2]) for shot in shot_dict.values(): @@ -149,7 +149,6 @@ def getAngle(distance): ''' Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. ''' - import numpy as np PI = np.pi R = 6371. angle = distance * 180 / (PI * R) From 4bd92565b9585b3f754ffe595deea1cadd3ea466 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 28 Sep 2015 10:57:48 +0200 Subject: [PATCH 0588/1144] reformat code (fix indentation and python 3.x issues) --- pylot/core/pick/Picker.py | 366 +++++++++++++++++++------------------- 1 file changed, 183 insertions(+), 183 deletions(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 61eade5e..f9ef82d4 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -147,7 +147,7 @@ class AICPicker(AutoPicking): def calcPick(self): - print 'AICPicker: Get initial onset time (pick) from AIC-CF ...' + print('AICPicker: Get initial onset time (pick) from AIC-CF ...') self.Pick = None self.slope = None @@ -155,7 +155,7 @@ class AICPicker(AutoPicking): #find NaN's nn = np.isnan(self.cf) if len(nn) > 1: - self.cf[nn] = 0 + self.cf[nn] = 0 #taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) @@ -163,15 +163,15 @@ class AICPicker(AutoPicking): ismooth = int(round(self.Tsmooth / self.dt)) aicsmooth = np.zeros(len(aic)) if len(aic) < ismooth: - print 'AICPicker: Tsmooth larger than CF!' - return + print('AICPicker: Tsmooth larger than CF!') + return else: - for i in range(1, len(aic)): - if i > ismooth: - ii1 = i - ismooth - aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth - else: - aicsmooth[i] = np.mean(aic[1 : i]) + for i in range(1, len(aic)): + if i > ismooth: + ii1 = i - ismooth + aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth + else: + aicsmooth[i] = np.mean(aic[1 : i]) #remove offset offset = abs(min(aic) - min(aicsmooth)) aicsmooth = aicsmooth - offset @@ -180,7 +180,7 @@ class AICPicker(AutoPicking): #find NaN's nn = np.isnan(diffcf) if len(nn) > 1: - diffcf[nn] = 0 + diffcf[nn] = 0 #taper CF to get rid off side maxima tap = np.hanning(len(diffcf)) diffcf = tap * diffcf * max(abs(aicsmooth)) @@ -189,104 +189,104 @@ class AICPicker(AutoPicking): #find minimum in AIC-CF front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): - if aicsmooth[i - 1] >= aicsmooth[i]: - self.Pick = self.Tcf[i] - break + if aicsmooth[i - 1] >= aicsmooth[i]: + self.Pick = self.Tcf[i] + break #if no minimum could be found: #search in 1st derivative of AIC-CF if self.Pick is None: - for i in range(icfmax -1, max([icfmax -lpickwindow, 2]), -1): - if diffcf[i -1] >= diffcf[i]: - self.Pick = self.Tcf[i] - break + for i in range(icfmax -1, max([icfmax -lpickwindow, 2]), -1): + if diffcf[i -1] >= diffcf[i]: + self.Pick = self.Tcf[i] + break # quality assessment using SNR and slope from CF if self.Pick is not None: - # get noise window - inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1]) - # check, if these are counts or m/s, important for slope estimation! - # this is quick and dirty, better solution? - if max(self.Data[0].data < 1e-3): - self.Data[0].data = self.Data[0].data * 1000000 - # get signal window - isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) - # calculate SNR from CF - self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / max(abs(aic[inoise] \ - - np.mean(aic[inoise]))) - # calculate slope from CF after initial pick - # get slope window - tslope = self.TSNR[3] #slope determination window - islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ - & (self.Tcf >= self.Pick)) - # find maximum within slope determination window - # 'cause slope should be calculated up to first local minimum only! - imax = np.argmax(self.Data[0].data[islope]) - if imax == 0: - print 'AICPicker: Maximum for slope determination right at the beginning of the window!' - print 'Choose longer slope determination window!' - if self.iplot > 1: - p = plt.figure(self.iplot) - x = self.Data[0].data - p1, = plt.plot(self.Tcf, x / max(x), 'k') - p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') - plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) - plt.show() - raw_input() - plt.close(p) - return - islope = islope[0][0 :imax] - dataslope = self.Data[0].data[islope] - # calculate slope as polynomal fit of order 1 - xslope = np.arange(0, len(dataslope), 1) - P = np.polyfit(xslope, dataslope, 1) - datafit = np.polyval(P, xslope) - if datafit[0] >= datafit[len(datafit) - 1]: - print 'AICPicker: Negative slope, bad onset skipped!' - return - self.slope = 1 / tslope * (datafit[len(dataslope) - 1] - datafit[0]) + # get noise window + inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1]) + # check, if these are counts or m/s, important for slope estimation! + # this is quick and dirty, better solution? + if max(self.Data[0].data < 1e-3): + self.Data[0].data = self.Data[0].data * 1000000 + # get signal window + isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) + # calculate SNR from CF + self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / \ + max(abs(aic[inoise] - np.mean(aic[inoise]))) + # calculate slope from CF after initial pick + # get slope window + tslope = self.TSNR[3] #slope determination window + islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ + & (self.Tcf >= self.Pick)) + # find maximum within slope determination window + # 'cause slope should be calculated up to first local minimum only! + imax = np.argmax(self.Data[0].data[islope]) + if imax == 0: + print('AICPicker: Maximum for slope determination right at the beginning of the window!') + print('Choose longer slope determination window!') + if self.iplot > 1: + p = plt.figure(self.iplot) + x = self.Data[0].data + p1, = plt.plot(self.Tcf, x / max(x), 'k') + p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') + plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(p) + return + islope = islope[0][0 :imax] + dataslope = self.Data[0].data[islope] + # calculate slope as polynomal fit of order 1 + xslope = np.arange(0, len(dataslope), 1) + P = np.polyfit(xslope, dataslope, 1) + datafit = np.polyval(P, xslope) + if datafit[0] >= datafit[len(datafit) - 1]: + print('AICPicker: Negative slope, bad onset skipped!') + return + self.slope = 1 / tslope * (datafit[len(dataslope) - 1] - datafit[0]) else: - self.SNR = None - self.slope = None + self.SNR = None + self.slope = None if self.iplot > 1: - p = plt.figure(self.iplot) - x = self.Data[0].data - p1, = plt.plot(self.Tcf, x / max(x), 'k') - p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') - if self.Pick is not None: - p3, = plt.plot([self.Pick, self.Pick], [-0.1 , 0.5], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) - else: - plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) + p = plt.figure(self.iplot) + x = self.Data[0].data + p1, = plt.plot(self.Tcf, x / max(x), 'k') + p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') + if self.Pick is not None: + p3, = plt.plot([self.Pick, self.Pick], [-0.1 , 0.5], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) + else: + plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) - if self.Pick is not None: - plt.figure(self.iplot + 1) - p11, = plt.plot(self.Tcf, x, 'k') - p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) - p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') - p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') - p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) - plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], \ - loc='best') - plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, \ - self.SNR, self.slope)) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.ylabel('Counts') - plt.yticks([]) + if self.Pick is not None: + plt.figure(self.iplot + 1) + p11, = plt.plot(self.Tcf, x, 'k') + p12, = plt.plot(self.Tcf[inoise], self.Data[0].data[inoise]) + p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') + p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') + p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) + plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], + loc='best') + plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, + self.SNR, self.slope)) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.ylabel('Counts') + plt.yticks([]) - plt.show() - raw_input() - plt.close(p) + plt.show() + raw_input() + plt.close(p) if self.Pick == None: - print 'AICPicker: Could not find minimum, picking window too short?' + print('AICPicker: Could not find minimum, picking window too short?') class PragPicker(AutoPicking): @@ -297,102 +297,102 @@ class PragPicker(AutoPicking): def calcPick(self): if self.getpick1() is not None: - print 'PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...' + print('PragPicker: Get most likely pick from HOS- or AR-CF using pragmatic picking algorithm ...') - self.Pick = None - self.SNR = None - self.slope = None - pickflag = 0 - #smooth CF - ismooth = int(round(self.Tsmooth / self.dt)) - cfsmooth = np.zeros(len(self.cf)) - if len(self.cf) < ismooth: - print 'PragPicker: Tsmooth larger than CF!' - return - else: - for i in range(1, len(self.cf)): - if i > ismooth: - ii1 = i - ismooth; - cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth - else: - cfsmooth[i] = np.mean(self.cf[1 : i]) + self.Pick = None + self.SNR = None + self.slope = None + pickflag = 0 + #smooth CF + ismooth = int(round(self.Tsmooth / self.dt)) + cfsmooth = np.zeros(len(self.cf)) + if len(self.cf) < ismooth: + print('PragPicker: Tsmooth larger than CF!') + return + else: + for i in range(1, len(self.cf)): + if i > ismooth: + ii1 = i - ismooth; + cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth + else: + cfsmooth[i] = np.mean(self.cf[1 : i]) - #select picking window - #which is centered around tpick1 - ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ - & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) - cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) - Tcfpick = self.Tcf[ipick] - cfsmoothipick = cfsmooth[ipick]- np.mean(self.cf[ipick]) - ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) - cfpick1 = 2 * self.cf[ipick1] + #select picking window + #which is centered around tpick1 + ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ + & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) + cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) + Tcfpick = self.Tcf[ipick] + cfsmoothipick = cfsmooth[ipick]- np.mean(self.cf[ipick]) + ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) + cfpick1 = 2 * self.cf[ipick1] - #check trend of CF, i.e. differences of CF and adjust aus regarding this trend - #prominent trend: decrease aus - #flat: use given aus - cfdiff = np.diff(cfipick); - i0diff = np.where(cfdiff > 0) - cfdiff = cfdiff[i0diff] - minaus = min(cfdiff * (1 + self.aus)); - aus1 = max([minaus, self.aus]); + #check trend of CF, i.e. differences of CF and adjust aus regarding this trend + #prominent trend: decrease aus + #flat: use given aus + cfdiff = np.diff(cfipick) + i0diff = np.where(cfdiff > 0) + cfdiff = cfdiff[i0diff] + minaus = min(cfdiff * (1 + self.aus)) + aus1 = max([minaus, self.aus]) - #at first we look to the right until the end of the pick window is reached - flagpick_r = 0 - flagpick_l = 0 - cfpick_r = 0 - cfpick_l = 0 - lpickwindow = int(round(self.PickWindow / self.dt)) - for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_r = self.Tcf[i] - self.Pick = pick_r - flagpick_l = 1 - cfpick_r = self.cf[i] - break + #at first we look to the right until the end of the pick window is reached + flagpick_r = 0 + flagpick_l = 0 + cfpick_r = 0 + cfpick_l = 0 + lpickwindow = int(round(self.PickWindow / self.dt)) + for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_r = self.Tcf[i] + self.Pick = pick_r + flagpick_l = 1 + cfpick_r = self.cf[i] + break - # now we look to the left - for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): - if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: - if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: - if cfpick1 >= self.cf[i]: - pick_l = self.Tcf[i] - self.Pick = pick_l - flagpick_r = 1 - cfpick_l = self.cf[i] - break + # now we look to the left + for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): + if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: + if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: + if cfpick1 >= self.cf[i]: + pick_l = self.Tcf[i] + self.Pick = pick_l + flagpick_r = 1 + cfpick_l = self.cf[i] + break - # now decide which pick: left or right? - if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: - self.Pick = pick_l - pickflag = 1 - elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: - self.Pick = pick_r - pickflag = 1 - elif flagpick_l == 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + # now decide which pick: left or right? + if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: self.Pick = pick_l pickflag = 1 - else: - print 'PragPicker: Could not find reliable onset!' - self.Pick = None - pickflag = 0 + elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + self.Pick = pick_r + pickflag = 1 + elif flagpick_l == 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: + self.Pick = pick_l + pickflag = 1 + else: + print('PragPicker: Could not find reliable onset!') + self.Pick = None + pickflag = 0 - if self.getiplot() > 1: - p = plt.figure(self.getiplot()) - p1, = plt.plot(Tcfpick,cfipick, 'k') - p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') - if pickflag > 0: - p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) - plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) - plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - plt.yticks([]) - plt.title(self.Data[0].stats.station) - plt.show() - raw_input() - plt.close(p) + if self.getiplot() > 1: + p = plt.figure(self.getiplot()) + p1, = plt.plot(Tcfpick,cfipick, 'k') + p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') + if pickflag > 0: + p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) + plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) + plt.xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + plt.yticks([]) + plt.title(self.Data[0].stats.station) + plt.show() + raw_input() + plt.close(p) else: - print 'PragPicker: No initial onset time given! Check input!' - self.Pick = None - return + print('PragPicker: No initial onset time given! Check input!') + self.Pick = None + return From 5b8e2da59ec34474bcb1ac55f1428e7c09dc4215 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 28 Sep 2015 12:24:28 +0200 Subject: [PATCH 0589/1144] *** empty log message *** --- pylot/core/active/seismicshot.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 5a13651f..5b21024a 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -257,17 +257,13 @@ class SeismicShot(object): #raise ValueError('ambigious or empty traceID: %s' % traceID) - def pickTraces(self, traceID, pickmethod, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## - # LOCALMAX NOT IMPLEMENTED! + def pickTraces(self, traceID, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## ''' Intitiate picking for a trace. :param: traceID :type: int - :param: pickmethod, use either 'threshold' or 'localmax' method. (localmax not yet implemented 04_08_15) - :type: string - :param: cutwindow (equals HOScf 'cut' variable) :type: tuple @@ -291,13 +287,7 @@ class SeismicShot(object): self.timeArray[traceID] = hoscf.getTimeArray() - if pickmethod == 'threshold': - aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) - - #setpick = {'threshold':self.threshold, - # 'localmax':self.localmax} - - #aiccftime, hoscftime = setpick[pickmethod](hoscf, aiccf, windowsize, pickwindow) + aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) setHosAic = {'hos': hoscftime, 'aic': aiccftime} @@ -487,7 +477,6 @@ class SeismicShot(object): :param: (tnoise, tgap, tsignal), as used in pylot SNR ''' - from pylot.core.pick.utils import getSNR tgap = self.getTgap() From 8035903fa5025955c822595f2c67423eaa93a77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 29 Sep 2015 11:15:51 +0200 Subject: [PATCH 0590/1144] Zero crossings are calculated to derive only P pulse for calculating source spectrum. --- pylot/core/pick/autopick.py | 43 +++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 10b4f7d5..e3771d7a 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -313,7 +313,6 @@ def autopickstation(wfstream, pickparam): ############################################################## # get DC value (w0) and corner frequency (fc) of source spectrum # from P pulse - # restitute streams # initialize Data object data = Data() [corzdat, restflag] = data.restituteWFData(invdir, zdat) @@ -323,33 +322,45 @@ def autopickstation(wfstream, pickparam): # class needs stream object => build it z_copy = zdat.copy() z_copy[0].data = corintzdat - # calculate source spectrum and get w0 and fc - calcwin = 1 / bpz2[0] # largest detectable period == window length - # around P pulse for calculating source spectrum - specpara = DCfc(z_copy, mpickP, calcwin, iplot) - w0 = specpara.getw0() - fc = specpara.getfc() + # largest detectable period == window length + # after P pulse for calculating source spectrum + winzc = (1 / bpz2[0]) * z_copy[0].stats.sampling_rate + impickP = mpickP * z_copy[0].stats.sampling_rate + wfzc = z_copy[0].data[impickP : impickP + winzc] + # calculate spectrum using only first cycles of + # waveform after P onset! + zc = crossings_nonzero_all(wfzc) + if np.size(zc) == 0: + print ("Something is wrong with the waveform, " \ + "no zero crossings derived!") + print ("Cannot calculate source spectrum!") + else: + calcwin = (zc[3] - zc[0]) * z_copy[0].stats.delta + # calculate source spectrum and get w0 and fc + specpara = DCfc(z_copy, mpickP, calcwin, iplot) + w0 = specpara.getw0() + fc = specpara.getfc() - print 'autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, ' \ - 'Polarity: %s' % (Pweight, SNRP, SNRPdB, FM) + print ("autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, " \ + "Polarity: %s" % (Pweight, SNRP, SNRPdB, FM)) Sflag = 1 else: - print 'Bad initial (AIC) P-pick, skipping this onset!' + print ("Bad initial (AIC) P-pick, skipping this onset!") print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope(), 'counts/s' print '(min. AIC-SNR=', minAICPSNR, ', min. AIC-Slope=', minAICPslope, 'counts/s)' Sflag = 0 else: - print 'autopickstation: No vertical component data available!, ' \ - 'Skipping station!' + print ("autopickstation: No vertical component data available!, " \ + "Skipping station!") if edat is not None and ndat is not None and len(edat) > 0 and len( ndat) > 0 and Pweight < 4: - print 'Go on picking S onset ...' - print '##################################################' - print 'Working on S onset of station %s' % edat[0].stats.station - print 'Filtering horizontal traces ...' + print ("Go on picking S onset ...") + print ("##################################################") + print ("Working on S onset of station %s" % edat[0].stats.station) + print ("Filtering horizontal traces ...") # determine time window for calculating CF after P onset cuttimesh = [round(max([mpickP + sstart, 0])), From ce57f184e755201de5f480a879b9d051755a91bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 29 Sep 2015 11:17:47 +0200 Subject: [PATCH 0591/1144] In order to calculate DC value and corner frequency of source spectrum a synthetic spectrum is calculated and optimized using scipys curve_fit. --- pylot/core/analysis/magnitude.py | 66 +++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 4391d16b..17d3c16f 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -9,6 +9,7 @@ import matplotlib.pyplot as plt import numpy as np from obspy.core import Stream from pylot.core.pick.utils import getsignalwin +from scipy.optimize import curve_fit class Magnitude(object): ''' @@ -165,21 +166,74 @@ class DCfc(Magnitude): Y = abs(y[: N/2]) L = (N - 1) / tr.stats.sampling_rate f = np.arange(0, fny, 1/L) + + # remove zero-frequency and frequencies above + # corner frequency of seismometer (assumed + # to be 100 Hz) + fi = np.where((f >= 1) & (f < 100)) + F = f[fi] + YY = Y[fi] + # get plateau (DC value) and corner frequency + # initial guess of plateau + DCin = np.mean(YY[0:100]) + # initial guess of corner frequency + # where spectral level reached 50% of flat level + iin = np.where(YY >= 0.5 * DCin) + Fcin = F[iin[0][np.size(iin) - 1]] + fit = synthsourcespec(F, DCin, Fcin) + [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [DCin, Fcin]) + self.w0 = optspecfit[0] + self.fc = optspecfit[1] + print ("DCfc: Determined DC-value: %f, \n" \ + "Determined corner frequency: %f" % (self.w0, self.fc)) - if self.getiplot() > 1: - f1 = plt.figure(1) + + #if self.getiplot() > 1: + iplot=2 + if iplot > 1: + f1 = plt.figure() plt.subplot(2,1,1) - plt.plot(t, np.multiply(tr, 1000), 'k') # show displacement in mm - plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') # show displacement in mm + # show displacement in mm + plt.plot(t, np.multiply(tr, 1000), 'k') + plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') plt.title('Seismogram and P pulse, station %s' % tr.stats.station) plt.xlabel('Time since %s' % tr.stats.starttime) plt.ylabel('Displacement [mm]') plt.subplot(2,1,2) - plt.semilogy(f, Y.real) - plt.title('Source Spectrum from P Pulse') + plt.semilogy(f, Y.real, 'k') + plt.semilogy(F, YY.real) + plt.semilogy(F, fit, 'g') + plt.title('Source Spectrum from P Pulse, DC=%f m/Hz, fc=%4.1f Hz' \ + % (self.w0, self.fc)) plt.xlabel('Frequency [Hz]') plt.ylabel('Amplitude [m/Hz]') plt.show() raw_input() plt.close(f1) + + +def synthsourcespec(f, omega0, fcorner): + ''' + Calculates synthetic source spectrum from given plateau and corner + frequency assuming Akis omega-square model. + + :param: f, frequencies + :type: array + + :param: omega0, DC-value (plateau) of source spectrum + :type: float + + :param: fcorner, corner frequency of source spectrum + :type: float + ''' + + #ssp = omega0 / (pow(2, (1 + f / fcorner))) + ssp = omega0 / (1 + pow(2, (f / fcorner))) + + #plt.plot(f, ssp) + #plt.show() + #raw_input() + + return ssp + From f4b905c2e63c3f8bec92a97f5050dbd899d25766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 29 Sep 2015 11:19:25 +0200 Subject: [PATCH 0592/1144] Removed inserted plot command for debugging purposes. --- pylot/core/analysis/magnitude.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 17d3c16f..4fa02704 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -188,8 +188,7 @@ class DCfc(Magnitude): "Determined corner frequency: %f" % (self.w0, self.fc)) - #if self.getiplot() > 1: - iplot=2 + if self.getiplot() > 1: if iplot > 1: f1 = plt.figure() plt.subplot(2,1,1) From 708f0a1f1a096551a61316f0c51baacec859d33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 29 Sep 2015 14:55:15 +0200 Subject: [PATCH 0593/1144] Some cosmetics on DCfc of class magnitude. --- pylot/core/analysis/magnitude.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 4fa02704..9d24392e 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -184,12 +184,11 @@ class DCfc(Magnitude): [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [DCin, Fcin]) self.w0 = optspecfit[0] self.fc = optspecfit[1] - print ("DCfc: Determined DC-value: %f, \n" \ - "Determined corner frequency: %f" % (self.w0, self.fc)) + print ("DCfc: Determined DC-value: %e m/Hz, \n" \ + "Determined corner frequency: %f Hz" % (self.w0, self.fc)) if self.getiplot() > 1: - if iplot > 1: f1 = plt.figure() plt.subplot(2,1,1) # show displacement in mm @@ -200,13 +199,14 @@ class DCfc(Magnitude): plt.ylabel('Displacement [mm]') plt.subplot(2,1,2) - plt.semilogy(f, Y.real, 'k') - plt.semilogy(F, YY.real) - plt.semilogy(F, fit, 'g') - plt.title('Source Spectrum from P Pulse, DC=%f m/Hz, fc=%4.1f Hz' \ + plt.loglog(f, Y.real, 'k') + plt.loglog(F, YY.real) + plt.loglog(F, fit, 'g') + plt.title('Source Spectrum from P Pulse, DC=%e m/Hz, fc=%4.1f Hz' \ % (self.w0, self.fc)) plt.xlabel('Frequency [Hz]') plt.ylabel('Amplitude [m/Hz]') + plt.grid() plt.show() raw_input() plt.close(f1) @@ -230,9 +230,5 @@ def synthsourcespec(f, omega0, fcorner): #ssp = omega0 / (pow(2, (1 + f / fcorner))) ssp = omega0 / (1 + pow(2, (f / fcorner))) - #plt.plot(f, ssp) - #plt.show() - #raw_input() - return ssp From 3473ce732c04341fbe2b34e3095b87eed40ab765 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 30 Sep 2015 13:06:51 +0200 Subject: [PATCH 0594/1144] changed structure -> all FMTOMO to vtk functions to one module --- pylot/core/active/surveyUtils.py | 113 ------------------------------- 1 file changed, 113 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 64d81a05..0c766166 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -192,116 +192,3 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): shots_found[shot.getShotnumber()].append(traceID) return shots_found - -def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): - def getDistance(angle): - PI = np.pi - R = 6371. - distance = angle / 180 * (PI * R) - return distance - - def readNumberOfPoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - nR = int(vglines[1].split()[0]) - nTheta = int(vglines[1].split()[1]) - nPhi = int(vglines[1].split()[2]) - - fin.close() - return nR, nTheta, nPhi - - def readDelta(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - dR = float(vglines[2].split()[0]) - dTheta = float(vglines[2].split()[1]) - dPhi = float(vglines[2].split()[2]) - - fin.close() - return dR, dTheta, dPhi - - def readStartpoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - sR = float(vglines[3].split()[0]) - sTheta = float(vglines[3].split()[1]) - sPhi = float(vglines[3].split()[2]) - - fin.close() - return sR, sTheta, sPhi - - def readVelocity(filename): - vel = []; count = 0 - fin = open(filename, 'r') - vglines = fin.readlines() - - for line in vglines: - count += 1 - if count > 4: - vel.append(float(line.split()[0])) - - print("Read %d points out of file: %s" %(count - 4, filename)) - return vel - - R = 6371 # earth radius - outfile = open(outputfile, 'w') - - # Theta, Phi in radians, R in km - nR, nTheta, nPhi = readNumberOfPoints(inputfile) - dR, dTheta, dPhi = readDelta(inputfile) - sR, sTheta, sPhi = readStartpoints(inputfile) - vel = readVelocity(inputfile) - - nX = nPhi; nY = nTheta; nZ = nR - - sZ = sR - R - sX = getDistance(np.rad2deg(sPhi)) - sY = getDistance(np.rad2deg(sTheta)) - - dX = getDistance(np.rad2deg(dPhi)) - dY = getDistance(np.rad2deg(dTheta)) - - xGrid = np.linspace(sX, sX + (dX * nX), nX) - yGrid = np.linspace(sZ, sZ + (nY * dY), nY) - zGrid = np.linspace(sZ, sZ + (nR * dR), nR) - nPoints = len(xGrid) * len(yGrid) * len(zGrid) - - # write header - print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Velocity on FMTOMO vgrids.in points\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) - - # write coordinates - print("Writing coordinates to VTK file...") - for z in zGrid: - for y in yGrid: - for x in xGrid: - outfile.writelines('%10f %10f %10f \n' %(x, y, z)) - - - outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) - - # write indices - print("Writing indices to VTK file...") - for index in range(nPoints): - outfile.writelines('%10d %10d\n' %(1, index)) - - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - outfile.writelines('SCALARS velocity float %d\n' %(1)) - outfile.writelines('LOOKUP_TABLE default\n') - - # write velocity - print("Writing velocity values to VTK file...") - for velocity in vel: - outfile.writelines('%10f\n' %velocity) - - outfile.close() - print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) - return - From 03eac54ced0702e617407056f11192cd18514973 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 30 Sep 2015 13:58:13 +0200 Subject: [PATCH 0595/1144] added new files to pylot active --- pylot/core/active/activeSeismoPick.py | 218 ++++++ pylot/core/active/fmtomo2vtk.py | 204 ++++++ pylot/core/active/picking_script.py | 103 +++ pylot/core/active/seismicArrayPreperation.py | 665 +++++++++++++++++++ pylot/core/active/surveyPlotTools.py | 250 +++++++ 5 files changed, 1440 insertions(+) create mode 100644 pylot/core/active/activeSeismoPick.py create mode 100644 pylot/core/active/fmtomo2vtk.py create mode 100644 pylot/core/active/picking_script.py create mode 100644 pylot/core/active/seismicArrayPreperation.py create mode 100644 pylot/core/active/surveyPlotTools.py diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py new file mode 100644 index 00000000..ec73c85f --- /dev/null +++ b/pylot/core/active/activeSeismoPick.py @@ -0,0 +1,218 @@ +from pylot.core.active import seismicshot +import numpy as np + +class Survey(object): + def __init__(self, path, sourcefile, receiverfile, useDefaultParas = False): + ''' + The Survey Class contains all shots [type: seismicshot] of a survey + as well as the aquisition geometry and the topography. + ''' + self.data = {} + self._topography = None + self._recfile = receiverfile + self._sourcefile = sourcefile + self._obsdir = path + self._generateSurvey() + if useDefaultParas == True: + self.setParametersForShots() + self._removeAllEmptyTraces() + self._updateShots() + + def _generateSurvey(self): + from obspy.core import read + + shot_dict = {} + shotlist = self.getShotlist() + for shotnumber in shotlist: # loop over data files + # generate filenames and read manual picks to a list + obsfile = self._obsdir + str(shotnumber) + '_pickle.dat' + + if not obsfile in shot_dict.keys(): + shot_dict[shotnumber] = [] + shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) + shot_dict[shotnumber].setParameters('shotnumber', shotnumber) + + self.setArtificialPick(0, 0) # artificial pick at source origin + + self.data = shot_dict + print ("Generated Survey object for %d shots" % len(shotlist)) + print ("Total number of traces: %d \n" %self.countAllTraces()) + + def setParametersForShots(self, cutwindow = (0, 0.2), tmovwind = 0.3, tsignal = 0.03, tgap = 0.0007): + if (cutwindow == (0, 0.2) and tmovwind == 0.3 and + tsignal == 0.03 and tgap == 0.0007): + print ("Warning: Standard values used for " + "setParamters. This may not be clever.") + # CHANGE this later. Parameters only needed for survey, not for each shot. + for shot in self.data.values(): + shot.setCut(cutwindow) + shot.setTmovwind(tmovwind) + shot.setTsignal(tsignal) + shot.setTgap(tgap) + shot.setRecfile(self.getPath() + self.getReceiverfile()) + shot.setSourcefile(self.getPath() + self.getSourcefile()) + shot.setOrder(order = 4) + print ("setParametersForShots: Parameters set to:\n" + "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" + %(cutwindow, tmovwind, tsignal, tgap)) + + def _removeAllEmptyTraces(self): + filename = 'removeEmptyTraces.out' + count = 0 + for shot in self.data.values(): + removed = shot.removeEmptyTraces() + if removed is not None: + if count == 0: outfile = open(filename, 'w') + count += 1 + outfile.writelines('shot: %s, removed empty traces: %s\n' + %(shot.getShotnumber(), removed)) + print ("\nremoveEmptyTraces: Finished! Removed %d traces" %count) + if count > 0: + print ("See %s for more information " + "on removed traces."%(filename)) + outfile.close() + + def _updateShots(self): + filename = 'updateShots.out' + count = 0; countTraces = 0 + for shot in self.data.values(): + del_traceIDs = shot.updateTraceList() + if len(del_traceIDs) > 0: + if count == 0: outfile = open(filename, 'w') + count += 1 + countTraces += len(del_traceIDs) + outfile.writelines("shot: %s, removed traceID(s) %s because " + "they were not found in the corresponding stream\n" + %(shot.getShotnumber(), del_traceIDs)) + + print ("\nupdateShots: Finished! Updated %d shots and removed " + "%d traces" %(count, countTraces)) + if count > 0: + print ("See %s for more information " + "on removed traces."%(filename)) + outfile.close() + + def pickAllShots(self, HosAic = 'hos', vmin = 333, vmax = 5500): + ''' + Automatically pick all traces of all shots of the survey. + ''' + from datetime import datetime + count = 0 + + for shot in self.data.values(): + tstartpick = datetime.now(); count += 1 + for traceID in shot.getTraceIDlist(): + distance = shot.getDistance(traceID) # receive distance + + pickwin_used = shot.getCut() + cutwindow = shot.getCut() + + # for higher distances use a linear vmin/vmax to cut out late/early regions with high noise + if distance > 5.: + pwleft = distance/vmax ################## TEST + pwright = distance/vmin + if pwright > cutwindow[1]: + pwright = cutwindow[1] + pickwin_used = (pwleft, pwright) + + shot.setPickwindow(traceID, pickwin_used) + shot.pickTraces(traceID, pickmethod, windowsize, folm, HosAic) # picker + + # ++ TEST: set and check SNR before adding to distance bin ############################ + shot.setSNR(traceID) + #if shot.getSNR(traceID)[0] < snrthreshold: + if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): + shot.removePick(traceID) + # -- TEST: set and check SNR before adding to distance bin ############################ + + if shot.getPick(traceID) is not None: + shot.setEarllatepick(traceID) + + tpicksum += (datetime.now() - tstartpick); tpick = tpicksum/count + tremain = (tpick * (len(survey.getShotDict()) - count)) + tend = datetime.now() + tremain + print 'shot: %s, est. time to be finished is %s:%s:%s' % (shot.getShotname(), tend.hour, tend.minute, tend.second) + + + + + def setArtificialPick(self, traceID, pick): + for shot in self.data.values(): + shot.setPick(traceID, pick) + shot.setPickwindow(traceID, shot.getCut()) + + def countAllTraces(self): + numtraces = 0 + for line in self.getShotlist(): + for line in self.getReceiverlist(): + numtraces += 1 + return numtraces + + def getShotlist(self): + filename = self.getPath() + self.getSourcefile() + srcfile = open(filename,'r') + shotlist = [] + for line in srcfile.readlines(): + line = line.split() + shotlist.append(int(line[0])) + + return shotlist + + def getReceiverlist(self): + filename = self.getPath() + self.getReceiverfile() + recfile = open(filename,'r') + reclist = [] + for line in recfile.readlines(): + line = line.split() + reclist.append(int(line[0])) + + return reclist + + def getShotDict(self): + return self.data + + def getShot(self, shotnumber): + return self.data[shotnumber] + + def getSourcefile(self): + return self._sourcefile + + def getReceiverfile(self): + return self._recfile + + def getPath(self): + return self._obsdir + + def getStats(self): + info_dict = {} + for shot in self.data.values(): + pickedTraces = 0 + snrlist = [] + dist = [] + numtraces = len(shot.getTraceIDlist()) + for traceID in shot.getTraceIDlist(): + snrlist.append(shot.getSNR(traceID)[0]) + dist.append(shot.getDistance(traceID)) + if shot.getPick(traceID) is not None: + pickedTraces += 1 + info_dict[shot.getShotnumber()] = {'numtraces': numtraces, + 'picked traces': [pickedTraces, + '%2.2f %%'%(pickedTraces / + numtraces * 100)], + 'mean SNR': np.mean(snrlist), + 'mean distance': np.mean(dist)} + + return info_dict + + def saveSurvey(self, filename = 'survey.pickle'): + import cPickle + outfile = open(filename, 'wb') + + cPickle.dump(self, outfile, -1) + print('saved Survey to file %s'%(filename)) + + @staticmethod + def from_pickle(filename): + import cPickle + infile = open(filename, 'rb') + return cPickle.load(infile) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py new file mode 100644 index 00000000..2c35958a --- /dev/null +++ b/pylot/core/active/fmtomo2vtk.py @@ -0,0 +1,204 @@ +def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): + ''' + Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in + ''' + def getDistance(angle): + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + + def readNumberOfPoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + nR = int(vglines[1].split()[0]) + nTheta = int(vglines[1].split()[1]) + nPhi = int(vglines[1].split()[2]) + + fin.close() + return nR, nTheta, nPhi + + def readDelta(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + dR = float(vglines[2].split()[0]) + dTheta = float(vglines[2].split()[1]) + dPhi = float(vglines[2].split()[2]) + + fin.close() + return dR, dTheta, dPhi + + def readStartpoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + sR = float(vglines[3].split()[0]) + sTheta = float(vglines[3].split()[1]) + sPhi = float(vglines[3].split()[2]) + + fin.close() + return sR, sTheta, sPhi + + def readVelocity(filename): + vel = []; count = 0 + fin = open(filename, 'r') + vglines = fin.readlines() + + for line in vglines: + count += 1 + if count > 4: + vel.append(float(line.split()[0])) + + print("Read %d points out of file: %s" %(count - 4, filename)) + return vel + + R = 6371 # earth radius + outfile = open(outputfile, 'w') + + # Theta, Phi in radians, R in km + nR, nTheta, nPhi = readNumberOfPoints(inputfile) + dR, dTheta, dPhi = readDelta(inputfile) + sR, sTheta, sPhi = readStartpoints(inputfile) + vel = readVelocity(inputfile) + + nX = nPhi; nY = nTheta; nZ = nR + + sZ = sR - R + sX = getDistance(np.rad2deg(sPhi)) + sY = getDistance(np.rad2deg(sTheta)) + + dX = getDistance(np.rad2deg(dPhi)) + dY = getDistance(np.rad2deg(dTheta)) + + xGrid = np.linspace(sX, sX + (dX * nX), nX) + yGrid = np.linspace(sZ, sZ + (nY * dY), nY) + zGrid = np.linspace(sZ, sZ + (nR * dR), nR) + nPoints = len(xGrid) * len(yGrid) * len(zGrid) + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Velocity on FMTOMO vgrids.in points\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + print("Writing coordinates to VTK file...") + for z in zGrid: + for y in yGrid: + for x in xGrid: + outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + + + outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + + # write indices + print("Writing indices to VTK file...") + for index in range(nPoints): + outfile.writelines('%10d %10d\n' %(1, index)) + + outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('SCALARS velocity float %d\n' %(1)) + outfile.writelines('LOOKUP_TABLE default\n') + + # write velocity + print("Writing velocity values to VTK file...") + for velocity in vel: + outfile.writelines('%10f\n' %velocity) + + outfile.close() + print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + return + +def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): + ''' + Writes VTK file(s) for FMTOMO rays from rays.dat + + :param: nthPoint, plot every nth point of the ray + :type: integer + ''' + def getDistance(angle): + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + + infile = open(fnin, 'r') + R = 6371 + rays = {} + raynumber = 0 + nPoints = 0 + + ### NOTE: rays.dat seems to be in km and radians + + while True: + raynumber += 1 + firstline = infile.readline() + if firstline == '': break # break at EOF + shotnumber = int(firstline.split()[1]) + nRayPoints = int(infile.readline().split()[0]) + if not shotnumber in rays.keys(): + rays[shotnumber] = {} + rays[shotnumber][raynumber] = [] + for index in range(nRayPoints): + if index%nthPoint is 0 or index == nRayPoints: + rad, lat, lon = infile.readline().split() + rays[shotnumber][raynumber].append([getDistance(np.rad2deg(float(lon))), getDistance(np.rad2deg(float(lat))), float(rad) - R]) + else: + dummy = infile.readline() + + infile.close() + + for shotnumber in rays.keys(): + fnameout = fdirout + 'rays%03d.vtk'%(shotnumber) + outfile = open(fnameout, 'w') + + nPoints = 0 + for raynumber in rays[shotnumber]: + for ray in rays[shotnumber][raynumber]: + nPoints += 1 + + # write header + #print("Writing header for VTK file...") + print("Writing shot %d to file %s" %(shotnumber, fnameout)) + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('FMTOMO rays\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + #print("Writing coordinates to VTK file...") + + for raynumber in rays[shotnumber].keys(): + for raypoint in rays[shotnumber][raynumber]: + outfile.writelines('%10f %10f %10f \n' %(raypoint[0], raypoint[1], raypoint[2])) + + outfile.writelines('LINES %15d %15d\n' %(len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) + + # write indices + #print("Writing indices to VTK file...") + + count = 0 + for raynumber in rays[shotnumber].keys(): + outfile.writelines('%d ' %(len(rays[shotnumber][raynumber]))) + for index in range(len(rays[shotnumber][raynumber])): + outfile.writelines('%d ' %(count)) + count += 1 + outfile.writelines('\n') + + # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + # outfile.writelines('SCALARS rays float %d\n' %(1)) + # outfile.writelines('LOOKUP_TABLE default\n') + + # # write velocity + # print("Writing velocity values to VTK file...") + # for velocity in vel: + # outfile.writelines('%10f\n' %velocity) + + # outfile.close() + # print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + diff --git a/pylot/core/active/picking_script.py b/pylot/core/active/picking_script.py new file mode 100644 index 00000000..dc37e29b --- /dev/null +++ b/pylot/core/active/picking_script.py @@ -0,0 +1,103 @@ +import sys +from obspy import read +from obspy import Stream +from obspy import Trace +from datetime import datetime +import numpy as np + +from pylot.core.active import surveyUtils +from pylot.core.active import seismicshot +import activeSeismoPick +reload(seismicshot) +reload(surveyUtils) + +##################################################################################### +# parameter definitions:############################################################# +#traceslist = list(np.arange(1, 49, 1)) # traces (1-48) +#shotlist = list(np.arange(302, 352, 1)) # shot-files(.dat) (Paffrath: 302-352) (Hauburg: 353-401) (arange+1!) +cutwindow = (0, 0.2) # cut out a part of the trace [seconds] +tmovwind = 0.3 # size of the moving window +windowsize = (5, 0) # windowsize for AIC picker (indices around HOS picks!!) +pickwindow = cutwindow # for local max and threshold picker: fraction of the seismogram used (0...1) TO BE DONE: depends on cutwindow!!!! +folm = 0.6 + +rockeskyll = False +if rockeskyll == True: + receiverfile = "Geophone_interpoliert_rockes" + sourcefile = "Schusspunkte_rockes" + obsdir = "../rockeskyll_200615_270615/" +else: + receiverfile = "Geophone_interpoliert_GZB" + sourcefile = "Schusspunkte_GZB" + obsdir = "../GZB_26_06_15_01/" + +# SNR +tsignal = 0.03 +tgap = 0.0007 +snrthreshold = 1 +###################################################################################### + +vmin = 333 +vmax = 5500 +distBinSize = 2 + +########################################### +############# Settings: ################### +thresholdpick=True +localmaxpick=False + +if thresholdpick == True: pickmethod = "threshold" +if localmaxpick == True: pickmethod = "localmax" + +HosAic = 'hos' # can be 'hos' or 'aic' +########################################### + +starttime = datetime.now() + +print '\n--------------------Starting Script at %s -------------------\n' %starttime.time() +if thresholdpick == True: print 'Using treshold pickmethod!\n' +elif localmaxpick == True: print 'Using local maximum pick method!\n' +if HosAic == 'aic': print 'picking with AIC\n' +if HosAic == 'hos': print 'picking with HOS\n' + +survey = activeSeismoPick.Survey(obsdir, sourcefile, receiverfile, True) +surveyUtils.setFittedSNR(survey.getShotDict()) +print '\nDone after %s seconds!\n------------------------------------------------------------------------------\n' % (datetime.now() - starttime).seconds + +count = 0; tpicksum = starttime - starttime + +for shot in survey.data.values(): + tstartpick = datetime.now(); count += 1 + for traceID in shot.getTraceIDlist(): + distance = shot.getDistance(traceID) # receive distance + + pickwin_used = pickwindow # use pickwindow set in the parameter section + # for higher distances use a linear vmin/vmax to cut out late/early regions with high noise + if distance > 5.: + pwleft = distance/vmax ################## TEST + pwright = distance/vmin + if pwright > cutwindow[1]: + pwright = cutwindow[1] + pickwin_used = (pwleft, pwright) + + shot.setPickwindow(traceID, pickwin_used) + shot.pickTraces(traceID, windowsize, folm, HosAic) # picker + #shot.setManualPicks(traceID, picklist) # set manual picks if given (yet used on 2D only) + + # ++ TEST: set and check SNR before adding to distance bin ############################ + shot.setSNR(traceID) + #if shot.getSNR(traceID)[0] < snrthreshold: + if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): + shot.removePick(traceID) + # -- TEST: set and check SNR before adding to distance bin ############################ + + if shot.getPick(traceID) is not None: + shot.setEarllatepick(traceID) + + tpicksum += (datetime.now() - tstartpick); tpick = tpicksum/count + tremain = (tpick * (len(survey.getShotDict()) - count)) + tend = datetime.now() + tremain + print 'shot: %s, est. time to be finished is %s:%s:%s' % (shot.getShotname(), tend.hour, tend.minute, tend.second) + +print '\n--- Finished script ---' +print 'Elapsed time:', datetime.now()-starttime diff --git a/pylot/core/active/seismicArrayPreperation.py b/pylot/core/active/seismicArrayPreperation.py new file mode 100644 index 00000000..ae1b42ec --- /dev/null +++ b/pylot/core/active/seismicArrayPreperation.py @@ -0,0 +1,665 @@ +import sys +import numpy as np +from scipy.interpolate import griddata + +class SeisArray(object): + ''' + Can be used to interpolate missing values of a receiver grid, if only support points were measured. + Input file should contain in each line: ('traceID' 'receiverLineID' 'number of the geophone on recLine' 'X' 'Y' 'Z') + + Can be used to generate a velocity grid file (vgrids.in) for FMTOMO with a topography adapting gradient. + + Can be used to generate an interface file for FMTOMO (right now only interface.z used by grid3dg) for the topography. + + Supports vtk output for sources and receivers. + Note: Source and Receiver files for FMTOMO will be generated by the Survey object (because traveltimes will be added directly). + ''' + def __init__(self, recfile): + self._receiverlines = {} + self._receiverCoords = {} + self._measuredReceivers = {} + self._measuredTopo = {} + self._sourceLocs = {} + self._geophoneNumbers = {} + self._receiverlist = open(recfile, 'r').readlines() + self._generateReceiverlines() + self._setReceiverCoords() + self._setGeophoneNumbers() + + def _generateReceiverlines(self): + ''' + Connects the traceIDs to the lineIDs + for each receiverline in a dictionary. + ''' + for receiver in self._receiverlist: + traceID = int(receiver.split()[0]) + lineID = int(receiver.split()[1]) + if not lineID in self._receiverlines.keys(): + self._receiverlines[lineID] = [] + self._receiverlines[lineID].append(traceID) + + def _setReceiverCoords(self): + ''' + Fills the three x, y, z dictionaries with measured coordinates + ''' + for line in self._getReceiverlist(): + traceID = int(line.split()[0]) + x = float(line.split()[3]) + y = float(line.split()[4]) + z = float(line.split()[5]) + self._receiverCoords[traceID] = (x, y, z) + self._measuredReceivers[traceID] = (x, y, z) + + def _setGeophoneNumbers(self): + for line in self._getReceiverlist(): + traceID = int(line.split()[0]) + gphoneNum = float(line.split()[2]) + self._geophoneNumbers[traceID] = gphoneNum + + def _getReceiverlines(self): + return self._receiverlines + + def _getReceiverlist(self): + return self._receiverlist + + def getReceiverCoordinates(self): + return self._receiverCoords + + def _getXreceiver(self, traceID): + return self._receiverCoords[traceID][0] + + def _getYreceiver(self, traceID): + return self._receiverCoords[traceID][1] + + def _getZreceiver(self, traceID): + return self._receiverCoords[traceID][2] + + def _getXshot(self, shotnumber): + return self._sourceLocs[shotnumber][0] + + def _getYshot(self, shotnumber): + return self._sourceLocs[shotnumber][1] + + def _getZshot(self, shotnumber): + return self._sourceLocs[shotnumber][2] + + def _getReceiverValue(self, traceID, coordinate): + setCoordinate = {'X': self._getXreceiver, + 'Y': self._getYreceiver, + 'Z': self._getZreceiver} + return setCoordinate[coordinate](traceID) + + def _getGeophoneNumber(self, traceID): + return self._geophoneNumbers[traceID] + + def getMeasuredReceivers(self): + return self._measuredReceivers + + def getMeasuredTopo(self): + return self._measuredTopo + + def getSourceLocations(self): + return self._sourceLocs + + def _setXvalue(self, traceID, value): + self._checkKey(traceID) + self._receiverCoords[traceID][0] = value + + def _setYvalue(self, traceID, value): + self._checkKey(traceID) + self._receiverCoords[traceID][1] = value + + def _setZvalue(self, traceID, value): + self._checkKey(traceID) + self._receiverCoords[traceID][2] = value + + def _setValue(self, traceID, coordinate, value): + setCoordinate = {'X': self._setXvalue, + 'Y': self._setYvalue, + 'Z': self._setZvalue} + setCoordinate[coordinate](traceID, value) + + def _checkKey(self, traceID): + if not traceID in self._receiverCoords.keys(): + self._receiverCoords[traceID] = [None, None, None] + + def _checkTraceIDdirection(self, traceID1, traceID2): + if traceID2 > traceID1: + direction = +1 + return direction + if traceID2 < traceID1: + direction = -1 + return direction + print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) + + def _checkCoordDirection(self, traceID1, traceID2, coordinate): + ''' + Checks whether the interpolation direction is positive or negative + ''' + if self._getReceiverValue(traceID1, coordinate) < self._getReceiverValue(traceID2, coordinate): + direction = +1 + return direction + if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): + direction = -1 + return direction + print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) + + def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): + ''' + Returns the mean distance between two traceID's depending on the number of geophones in between + ''' + num_spaces = abs(self._getGeophoneNumber(traceID1) - self._getGeophoneNumber(traceID2)) + mean_distance = abs(self._getReceiverValue(traceID1, coordinate) - self._getReceiverValue(traceID2, coordinate))/num_spaces + return mean_distance + + def interpolateValues(self, coordinate): + ''' + Interpolates and sets all values (linear) for coordinate = 'X', 'Y' or 'Z' + ''' + for lineID in self._getReceiverlines().keys(): + number_measured = len(self._getReceiverlines()[lineID]) + for index, traceID1 in enumerate(self._getReceiverlines()[lineID]): + if index + 1 < number_measured: + traceID2 = self._getReceiverlines()[lineID][index + 1] + + traceID_dir = self._checkTraceIDdirection(traceID1, traceID2) + traceID_interp = traceID1 + traceID_dir + + coord_dir = self._checkCoordDirection(traceID1, traceID2, coordinate) + mean_distance = self._interpolateMeanDistances(traceID1, traceID2, coordinate) + + while (traceID_dir * traceID_interp) < (traceID_dir * traceID2): + self._setValue(traceID_interp, coordinate, + (self._getReceiverValue(traceID_interp - traceID_dir, coordinate) + + coord_dir * (mean_distance))) + traceID_interp += traceID_dir + + def addMeasuredTopographyPoints(self, filename): + ''' + Use more measured points for a higher precision of height interpolation. + Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') + ''' + topolist = open(filename, 'r').readlines() + for line in topolist: + line = line.split() + pointID = int(line[0]) + x = float(line[1]) + y = float(line[2]) + z = float(line[3]) + self._measuredTopo[pointID] = (x, y, z) + + def addSourceLocations(self, filename): + ''' + Use source locations for a higher precision of height interpolation. + Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') + + Source locations must be added before they can be written to vtk files. + ''' + topolist = open(filename, 'r').readlines() + for line in topolist: + line = line.split() + pointID = int(line[0]) + x = float(line[1]) + y = float(line[2]) + z = float(line[3]) + self._sourceLocs[pointID] = (x, y, z) + + def interpZcoords4rec(self, method = 'linear'): + ''' + Interpolates z values for all receivers. + ''' + measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() + + for traceID in self.getReceiverCoordinates().keys(): + if type(self.getReceiverCoordinates()[traceID]) is not tuple: + z = griddata((measured_x, measured_y), measured_z, (self._getXreceiver(traceID), self._getYreceiver(traceID)), method = method) + self._setZvalue(traceID, float(z)) + + def _getAngle(self, distance): + ''' + Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. + ''' + PI = np.pi + R = 6371. + angle = distance * 180 / (PI * R) + return angle + + def _getDistance(self, angle): + ''' + Function returns the distance [km] on a Sphere of the radius R = 6371 [km] for an angle. + ''' + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + + def getMeasuredReceiverLists(self): + ''' + Returns a list of all measured receivers known to SeisArray. + ''' + x = []; y = []; z = [] + for traceID in self.getMeasuredReceivers().keys(): + x.append(self.getMeasuredReceivers()[traceID][0]) + y.append(self.getMeasuredReceivers()[traceID][1]) + z.append(self.getMeasuredReceivers()[traceID][2]) + return x, y, z + + def getMeasuredTopoLists(self): + ''' + Returns a list of all measured topography points known to the SeisArray. + ''' + x = []; y = []; z = [] + for pointID in self.getMeasuredTopo().keys(): + x.append(self.getMeasuredTopo()[pointID][0]) + y.append(self.getMeasuredTopo()[pointID][1]) + z.append(self.getMeasuredTopo()[pointID][2]) + return x, y, z + + def getSourceLocsLists(self): + ''' + Returns a list of all measured source locations known to SeisArray. + ''' + x = []; y = []; z = [] + for pointID in self.getSourceLocations().keys(): + x.append(self.getSourceLocations()[pointID][0]) + y.append(self.getSourceLocations()[pointID][1]) + z.append(self.getSourceLocations()[pointID][2]) + return x, y, z + + def getAllMeasuredPointsLists(self): + ''' + Returns a list of all measured points known to SeisArray. + ''' + mtopo_x, mtopo_y, mtopo_z = self.getMeasuredTopoLists() + msource_x, msource_y, msource_z = self.getSourceLocsLists() + mrec_x, mrec_y, mrec_z = self.getMeasuredReceiverLists() + + x = mtopo_x + mrec_x + msource_x + y = mtopo_y + mrec_y + msource_y + z = mtopo_z + mrec_z + msource_z + return x, y, z + + def getReceiverLists(self): + ''' + Returns a list of all receivers (measured and interpolated). + ''' + x = []; y =[]; z = [] + for traceID in self.getReceiverCoordinates().keys(): + x.append(self.getReceiverCoordinates()[traceID][0]) + y.append(self.getReceiverCoordinates()[traceID][1]) + z.append(self.getReceiverCoordinates()[traceID][2]) + return x, y, z + + def _interpolateXY4rec(self): + ''' + Interpolates the X and Y coordinates for all receivers. + ''' + for coordinate in ('X', 'Y'): + self.interpolateValues(coordinate) + + def interpolateAll(self): + self._interpolateXY4rec() + self.interpZcoords4rec() + + def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, method = 'linear', filename = 'interface1.in'): + ''' + Interpolate Z values on a regular grid with cushion nodes to use it as FMTOMO topography interface. + Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). + + :param: nTheta, number of points in theta (NS) + type: integer + + :param: nPhi, number of points in phi (WE) + type: integer + + :param: thetaSN (S, N) extensions of the model in degree + type: tuple + + :param: phiWE (W, E) extensions of the model in degree + type: tuple + ''' + + surface = [] + elevation = 0.25 # elevate topography so that no source lies above the surface + + if filename is not None: + outfile = open(filename, 'w') + + print "Interpolating topography on regular grid with the dimensions:" + print "nTheta = %s, nPhi = %s, thetaSN = %s, phiWE = %s"%(nTheta, nPhi, thetaSN, phiWE) + print "method = %s, filename = %s" %(method, filename) + + thetaS, thetaN = thetaSN + phiW, phiE = phiWE + + measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() + + # need to determine the delta to add two cushion nodes around the min/max values + thetaDelta = (thetaN - thetaS) / (nTheta - 1) + phiDelta = (phiE - phiW) / (nPhi - 1) + + thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes + + nTotal = len(thetaGrid) * len(phiGrid); count = 0 + for theta in thetaGrid: + for phi in phiGrid: + xval = self._getDistance(phi) + yval = self._getDistance(theta) + z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = method) + # in case the point lies outside, nan will be returned. Find nearest: + if np.isnan(z) == True: + z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = 'nearest') + z = float(z) + surface.append((xval, yval, z)) + count += 1 + progress = float(count) / float(nTotal) * 100 + self._update_progress(progress) + + if filename is not None: + outfile.writelines('%10s\n'%(z + elevation)) + + return surface + + def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, + thetaSN = (-0.2, 1.2), phiWE = (-0.2, 1.2), + Rbt = (-62.0, 6.0), vbot = 5.5, filename = 'vgrids.in', + method = 'linear' ): + ''' + Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. + + :param: nTheta, number of points in theta (NS) + type: integer + + :param: nPhi, number of points in phi (WE) + type: integer + + :param: nR, number of points in depth + type: integer + + :param: thetaSN (S, N) extensions of the model in degree + type: tuple + + :param: phiWE (W, E) extensions of the model in degree + type: tuple + + :param: Rbt (bot, top) extensions of the model in km + type: tuple + + :param: vbot, velocity at the bottom of the model + type: real + ''' + + def getRad(angle): + PI = np.pi + rad = angle / 180 * PI + return rad + + def getZmax(surface): + z = [] + for point in surface: + z.append(point[2]) + return max(z) + + R = 6371 + vmin = 0.34 + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + outfile = open(filename, 'w') + + thetaS, thetaN = thetaSN + phiW, phiE = phiWE + rbot = Rbt[0] + R + rtop = Rbt[1] + R + + # need to determine the delta to add two cushion nodes around the min/max values + thetaDelta = abs(thetaN - thetaS) / (nTheta - 1) + phiDelta = abs(phiE - phiW) / (nPhi - 1) + rDelta = abs(rbot - rtop) / (nR - 1) + + # create a regular grid including +2 cushion nodes in every direction + thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes + rGrid = np.linspace(rbot - rDelta, rtop + rDelta, num = nR + 2) # +2 cushion nodes + + nTotal = len(rGrid) * len(thetaGrid) * len(phiGrid) + print "Total number of grid nodes: %s"%nTotal + + # write header for velocity grid file (in RADIANS) + outfile.writelines('%10s %10s \n' %(1, 1)) + outfile.writelines('%10s %10s %10s\n' %(nR + 2, nTheta + 2, nPhi + 2)) + outfile.writelines('%10s %10s %10s\n' %(rDelta, getRad(thetaDelta), getRad(phiDelta))) + outfile.writelines('%10s %10s %10s\n' %(rbot - rDelta, getRad(thetaS - thetaDelta), getRad(phiW - phiDelta))) + + surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method, filename = None) + zmax = getZmax(surface) + + print "\nGenerating velocity grid for FMTOMO. Output filename = %s, interpolation method = %s"%(filename, method) + print "nTheta = %s, nPhi = %s, nR = %s, thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt) + count = 0 + for radius in rGrid: + for theta in thetaGrid: + for phi in phiGrid: + xval = self._getDistance(phi) + yval = self._getDistance(theta) + for point in surface: + if point[0] == xval and point[1] == yval: + z = point[2] + if radius > (R + z + 1): + vel = 0.0 + # elif radius > (R + z - 15): ########### TESTING + # vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * 1.0 + vmin ########################## + else: + vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * vbot + vmin ########################## + count += 1 + outfile.writelines('%10s %10s\n'%(vel, decm)) + + progress = float(count) / float(nTotal) * 100 + self._update_progress(progress) + + outfile.close() + + def exportAll(self, filename = 'interpolated_receivers.out'): + recfile_out = open(filename, 'w') + count = 0 + for traceID in self.getReceiverCoordinates().keys(): + count += 1 + x, y, z = self.getReceiverCoordinates()[traceID] + recfile_out.writelines('%5s %15s %15s %15s\n' %(traceID, x, y, z)) + print "Exported coordinates for %s traces to file > %s" %(count, filename) + recfile_out.close() + + def plotArray2D(self, plot_topo = False, highlight_measured = False, annotations = True): + import matplotlib.pyplot as plt + plt.interactive(True) + plt.figure() + xmt, ymt, zmt = self.getMeasuredTopoLists() + xsc, ysc, zsc = self.getSourceLocsLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xrc, yrc, zrc = self.getReceiverLists() + + plt.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') + plt.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') + + if plot_topo == True: + plt.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') + if highlight_measured == True: + plt.plot(xmr, ymr, 'ro', label = 'measured receivers') + + plt.xlabel('X [m]') + plt.ylabel('Y [m]') + plt.legend() + if annotations == True: + for traceID in self.getReceiverCoordinates().keys(): + plt.annotate(str(traceID), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small') + + def plotArray3D(self, ax = None): + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d import Axes3D + plt.interactive(True) + + if ax == None: + fig = plt.figure() + ax = plt.axes(projection = '3d') + + xmt, ymt, zmt = self.getMeasuredTopoLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xin, yin, zin = self.getReceiverLists() + + ax.plot(xmt, ymt, zmt, 'b*', markersize = 10, label = 'measured topo points') + ax.plot(xin, yin, zin, 'k.', markersize = 10, label = 'interpolated receivers') + ax.plot(xmr, ymr, zmr, 'ro', label = 'measured receivers') + ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') + ax.legend() + + return ax + + + def plotSurface3D(self, ax = None, step = 0.5, method = 'linear'): + from matplotlib import cm + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d import Axes3D + plt.interactive(True) + + if ax == None: + fig = plt.figure() + ax = plt.axes(projection = '3d') + + xmt, ymt, zmt = self.getMeasuredTopoLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + + x = xmt + xmr + y = ymt + ymr + z = zmt + zmr + + xaxis = np.arange(min(x)+1, max(x), step) + yaxis = np.arange(min(y)+1, max(y), step) + + xgrid, ygrid = np.meshgrid(xaxis, yaxis) + + zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) + + ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) + + ax.set_zlim(-(max(x) - min(x)/2),(max(x) - min(x)/2)) + ax.set_aspect('equal') + + ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') + ax.legend() + + return ax + + def _update_progress(self, progress): + sys.stdout.write("%d%% done \r" % (progress) ) + sys.stdout.flush() + + def receivers2VTK(self, filename = 'receivers.vtk'): + ''' + Generates vtk files from all receivers of the SeisArray object. + ''' + outfile = open(filename, 'w') + traceIDs = [] + + for traceID in self.getReceiverCoordinates(): + traceIDs.append(traceID) + + nPoints = len(traceIDs) + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Receivers with traceIDs\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + print("Writing coordinates to VTK file...") + for traceID in traceIDs: + x = self._getXreceiver(traceID) + y = self._getYreceiver(traceID) + z = self._getZreceiver(traceID) + + outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + + outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + + # write indices + print("Writing indices to VTK file...") + for index in range(nPoints): + outfile.writelines('%10d %10d\n' %(1, index)) + + outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('SCALARS traceIDs int %d\n' %(1)) + outfile.writelines('LOOKUP_TABLE default\n') + + # write traceIDs + print("Writing traceIDs to VTK file...") + for traceID in traceIDs: + outfile.writelines('%10d\n' %traceID) + + outfile.close() + print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) + return + + def sources2VTK(self, filename = 'sources.vtk'): + ''' + Generates vtk-files for all source locations in the SeisArray object. + ''' + outfile = open(filename, 'w') + shotnumbers = [] + + for shotnumber in self.getSourceLocations(): + shotnumbers.append(shotnumber) + + nPoints = len(shotnumbers) + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Shots with shotnumbers\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + print("Writing coordinates to VTK file...") + for shotnumber in shotnumbers: + x = self._getXshot(shotnumber) + y = self._getYshot(shotnumber) + z = self._getZshot(shotnumber) + + outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + + outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + + # write indices + print("Writing indices to VTK file...") + for index in range(nPoints): + outfile.writelines('%10d %10d\n' %(1, index)) + + outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('SCALARS shotnumbers int %d\n' %(1)) + outfile.writelines('LOOKUP_TABLE default\n') + + # write shotnumber + print("Writing shotnumbers to VTK file...") + for shotnumber in shotnumbers: + outfile.writelines('%10d\n' %shotnumber) + + outfile.close() + print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) + return + + + def saveSeisArray(self, filename = 'seisArray.pickle'): + import cPickle + outfile = open(filename, 'wb') + + cPickle.dump(self, outfile, -1) + print('saved SeisArray to file %s'%(filename)) + + @staticmethod + def from_pickle(filename): + import cPickle + infile = open(filename, 'rb') + return cPickle.load(infile) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py new file mode 100644 index 00000000..d91998c9 --- /dev/null +++ b/pylot/core/active/surveyPlotTools.py @@ -0,0 +1,250 @@ +import matplotlib.pyplot as plt +import math +#from selectRegions import regions + +plt.interactive(True) + +def plotAllPicks(shot_dict, dist_med = None): + ''' + Plots all picks over the distance between source and receiver. Returns (ax, region) + ''' + dist = [] + pick = [] + snrloglist = [] + for shot in shot_dict.values(): + for traceID in shot.getTraceIDlist(): + if shot.getPick(traceID) is not None: + dist.append(shot.getDistance(traceID)) + pick.append(shot.getPick(traceID)) + snrloglist.append(math.log10(shot.getSNR(traceID)[0])) + + ax = createPlot(dist, pick, snrloglist, label = 'log10(SNR)') + region = regions(ax, shot_dict) + if dist_med is not None: + ax = addDistMed(ax, dist_med) + ax.legend() + + return ax, region + +def plotAllPicks_withCutOutTraces(shot_dict, dist_med = None): + ''' + Plots all picks over the distance between source and receiver. Returns (ax, region) + ''' + dist = [] + pick = [] + snrloglist = [] + for shot in shot_dict.values(): + for traceID in shot.getTraceIDlist(): + if shot.getSNR(traceID)[0] > 3: + dist.append(shot.getDistance(traceID)) + pick.append(shot.getPick_backup(traceID)) + snrloglist.append(math.log10(shot.getSNR(traceID)[0])) + + ax = createPlot(dist, pick, snrloglist, label = 'log10(SNR)') + region = regions(ax, shot_dict) + if dist_med is not None: + ax = addDistMed(ax, dist_med) + ax.legend() + + return ax, region + +def createPlot(dist, pick, inkByVal, label): + cm = plt.cm.jet + + fig = plt.figure() + ax = fig.add_subplot(111) + fig = ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) + cbar = plt.colorbar(fig, fraction = 0.05) + cbar.set_label(label) + plt.title('Plot of all Picks') + plt.xlabel('Distance [m]') + plt.ylabel('Time [s]') + + return ax + +def addDistMed(ax, dist_med): + ''' + Add shot dictionary containing the Median for several distancebins to the ax. + ''' + x = [] + y = [] + for dist in dist_med.keys(): + x.append(dist) + y.append(dist_med[dist]) + + xy = sorted(zip(x,y)) + x1 = [x for (x,y) in xy] + y1 = [y for (x,y) in xy] + + ax.plot(x1, y1, 'k', label = 'Median') + + return ax + +class regions(object): + def __init__(self, ax, shot_dict): + self.ax = ax + self.shot_dict = shot_dict + self._x0 = [] + self._y0 = [] + self._x1 = [] + self._y1 = [] + self.shots_found = {} + self.shots_for_deletion = {} + + def _onselect(self, eclick, erelease): + 'eclick and erelease are matplotlib events at press and release' #print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) + #print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) + print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)'%(eclick.xdata, eclick.ydata, erelease.xdata, erelease.ydata) + x0 = min(eclick.xdata, erelease.xdata) + x1 = max(eclick.xdata, erelease.xdata) + y0 = min(eclick.ydata, erelease.ydata) + y1 = max(eclick.ydata, erelease.ydata) + self._x0.append(x0) + self._x1.append(x1) + self._y0.append(y0) + self._y1.append(y1) + self.markCurrentRegion(x0, x1, y0, y1) + + def chooseRectangles(self): + from matplotlib.widgets import RectangleSelector + + print 'Select rectangle is active' + return RectangleSelector(self.ax, self._onselect) + + def _getx0(self): + return self._x0 + + def _getx1(self): + return self._x1 + + def _gety0(self): + return self._y0 + + def _gety1(self): + return self._y1 + + def getShotDict(self): + return self.shot_dict + + def getShotsForDeletion(self): + return self.shots_for_deletion + + def findTracesInShotDict(self, picks = 'normal'): + ''' + Returns traces corresponding to a certain area in a plot with all picks over the distances. + ''' + print "findTracesInShotDict: Searching for marked traces in the shot dictionary... " + + for shot in self.shot_dict.values(): + whichpicks = {'normal': shot.getPick, + 'includeCutOut': shot.getPick_backup} + for index in range(len(self._getx1())): + distancebin = (self._getx0()[index], self._getx1()[index]) + pickbin = (self._gety0()[index], self._gety1()[index]) + if shot.getTraceIDs4Dist(distancebin = distancebin) is not None: + for traceID in shot.getTraceIDs4Dist(distancebin = distancebin): + if pickbin[0] < whichpicks[picks](traceID) < pickbin[1]: + self.highlightPick(shot, traceID) + if shot.getShotnumber() not in self.shots_found.keys(): + self.shots_found[shot.getShotnumber()] = [] + if traceID not in self.shots_found[shot.getShotnumber()]: + self.shots_found[shot.getShotnumber()].append(traceID) + self.refreshFigure() + print self.shots_found + + def highlightPick(self, shot, traceID, annotations = True): + self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s = 50, marker = 'o', facecolors = 'none', edgecolors = 'm', alpha = 1) + if annotations == True: + self.ax.annotate(s = 's%s|t%s'%(shot.getShotnumber(), traceID), xy = (shot.getDistance(traceID), shot.getPick(traceID)), fontsize = 'xx-small') + self.ax.set_ylim(shot.getCut()) + + def plotTracesInRegion(self): + import matplotlib.pyplot as plt + count = 0 + maxfigures = 20 + # if len(self.shots_found) == 0: + self.findTracesInShotDict() + + if len(self.shots_found) > 0: + for shot in self.shot_dict.values(): + for shotnumber in self.shots_found: + if shot.getShotnumber() == shotnumber: + for traceID in self.shots_found[shotnumber]: + count += 1 + if count > maxfigures: + print 'Maximum number of figures (%s) reached. %sth figure was not opened.' %(maxfigures, count) + break + shot.plot_traces(traceID) + else: + print 'No picks yet defined in the regions x = (%s, %s), y = (%s, %s)' %(self._x0, self._x1, self._y0, self._y1) + + def plotTracesInRegion_withCutOutTraces(self): + import matplotlib.pyplot as plt + count = 0 + maxfigures = 20 + # if len(self.shots_found) == 0: + self.findTracesInShotDict(picks = 'includeCutOut') + + if len(self.shots_found) > 0: + for shot in self.shot_dict.values(): + for shotnumber in self.shots_found: + if shot.getShotnumber() == shotnumber: + for traceID in self.shots_found[shotnumber]: + count += 1 + if count > maxfigures: + print 'Maximum number of figures (%s) reached. %sth figure was not opened.' %(maxfigures, count) + break + shot.plot_traces(traceID) + else: + print 'No picks yet defined in the regions x = (%s, %s), y = (%s, %s)' %(self._x0, self._x1, self._y0, self._y1) + + + def setCurrentRegionsForDeletion(self): + # if len(self.shots_found) == 0: + self.findTracesInShotDict() + + for shotnumber in self.shots_found: + if not shotnumber in self.shots_for_deletion: + self.shots_for_deletion[shotnumber] = [] + for traceID in self.shots_found[shotnumber]: + if not traceID in self.shots_for_deletion[shotnumber]: + self.shots_for_deletion[shotnumber].append(traceID) + self.markAllRegions(color = 'red') + print 'Marked regions for deletion' + + def markAllRegions(self, color = 'grey'): + from matplotlib.patches import Rectangle + + for index in range(len(self._getx0())): + x0 = self._getx0()[index] + y0 = self._gety0()[index] + x1 = self._getx1()[index] + y1 = self._gety1()[index] + + self.ax.add_patch(Rectangle((x0, y0), (x1 - x0), (y1 - y0), alpha=0.5, facecolor = color)) + self.refreshFigure() + + def markCurrentRegion(self, x0, x1, y0, y1, color = 'grey'): + from matplotlib.patches import Rectangle + + self.ax.add_patch(Rectangle((x0, y0), (x1 - x0), (y1 - y0), alpha=0.1, facecolor = color)) + self.refreshFigure() + + def deleteMarkedPicks(self): + for shot in self.getShotDict().values(): + for shotnumber in self.getShotsForDeletion(): + if shot.getShotnumber() == shotnumber: + for traceID in self.getShotsForDeletion()[shotnumber]: + shot.removePick(traceID) + print "Deleted the pick for traceID %s on shot number %s" %(traceID, shotnumber) + self.shots_for_deletion = {} # clear dictionary + + def highlightPicksForShot(self, shot, annotations = False): + for traceID in shot.getTraceIDlist(): + if shot.getPick(traceID) is not None: + self.highlightPick(shot, traceID, annotations) + self.refreshFigure() + + def refreshFigure(self): + import matplotlib.pyplot as plt + plt.draw() From e78314461b9c9b5b6ee97487aeaa18e81dc3d4d4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 30 Sep 2015 14:31:55 +0200 Subject: [PATCH 0596/1144] *** empty log message *** --- pylot/core/active/activeSeismoPick.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index ec73c85f..dd155224 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -197,8 +197,8 @@ class Survey(object): pickedTraces += 1 info_dict[shot.getShotnumber()] = {'numtraces': numtraces, 'picked traces': [pickedTraces, - '%2.2f %%'%(pickedTraces / - numtraces * 100)], + '%2.2f %%'%(float(pickedTraces) / + float(numtraces) * 100)], 'mean SNR': np.mean(snrlist), 'mean distance': np.mean(dist)} From e41b1436f96cf10dfdbc9d8fcb90eefb6b5ce7fe Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 30 Sep 2015 14:32:08 +0200 Subject: [PATCH 0597/1144] *** empty log message *** --- pylot/core/active/picking_script.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/picking_script.py b/pylot/core/active/picking_script.py index dc37e29b..fbb3ef62 100644 --- a/pylot/core/active/picking_script.py +++ b/pylot/core/active/picking_script.py @@ -25,11 +25,13 @@ rockeskyll = False if rockeskyll == True: receiverfile = "Geophone_interpoliert_rockes" sourcefile = "Schusspunkte_rockes" - obsdir = "../rockeskyll_200615_270615/" + obsdir = "/rscratch/minos22/marcel/flachseismik/rockeskyll_200615_270615/" + filename = 'survey_rockes.pickle' else: receiverfile = "Geophone_interpoliert_GZB" sourcefile = "Schusspunkte_GZB" - obsdir = "../GZB_26_06_15_01/" + obsdir = "/rscratch/minos22/marcel/flachseismik/GZB_26_06_15_01/" + filename = 'survey_GZB.pickle' # SNR tsignal = 0.03 @@ -99,5 +101,6 @@ for shot in survey.data.values(): tend = datetime.now() + tremain print 'shot: %s, est. time to be finished is %s:%s:%s' % (shot.getShotname(), tend.hour, tend.minute, tend.second) +survey.saveSurvey(filename) print '\n--- Finished script ---' print 'Elapsed time:', datetime.now()-starttime From dd5523b2acd6b38b6c1b2318cafc78cd4f41d37a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 10:35:47 +0200 Subject: [PATCH 0598/1144] *** empty log message *** --- pylot/core/pick/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9e9f1492..4df52c42 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -75,7 +75,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): while np.isnan(EPick): if count > 0: print("earllatepicker: Doubled signal window size %s time(s) " - "because of NaN for earliest pick." %count) + "because of NaN for earliest pick.\n" %count) isigDoubleWinStart = pis[-1] + 1 isignalDoubleWin = np.arange(isigDoubleWinStart, isigDoubleWinStart + len(pis)) From 2c1e9c63d844a4b2df7e11cf23469e7c3372e997 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 11:30:16 +0200 Subject: [PATCH 0599/1144] changed structure: added plot option to survey methods --- pylot/core/active/activeSeismoPick.py | 121 ++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 9 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index dd155224..50c1ddad 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -1,5 +1,6 @@ -from pylot.core.active import seismicshot +import sys import numpy as np +from pylot.core.active import seismicshot class Survey(object): def __init__(self, path, sourcefile, receiverfile, useDefaultParas = False): @@ -92,12 +93,13 @@ class Survey(object): "on removed traces."%(filename)) outfile.close() - def pickAllShots(self, HosAic = 'hos', vmin = 333, vmax = 5500): + def pickAllShots(self, windowsize, HosAic = 'hos', vmin = 333, vmax = 5500, folm = 0.6): ''' Automatically pick all traces of all shots of the survey. ''' from datetime import datetime - count = 0 + starttime = datetime.now() + count = 0; tpicksum = starttime - starttime for shot in self.data.values(): tstartpick = datetime.now(); count += 1 @@ -116,7 +118,7 @@ class Survey(object): pickwin_used = (pwleft, pwright) shot.setPickwindow(traceID, pickwin_used) - shot.pickTraces(traceID, pickmethod, windowsize, folm, HosAic) # picker + shot.pickTraces(traceID, windowsize, folm, HosAic) # picker # ++ TEST: set and check SNR before adding to distance bin ############################ shot.setSNR(traceID) @@ -129,12 +131,11 @@ class Survey(object): shot.setEarllatepick(traceID) tpicksum += (datetime.now() - tstartpick); tpick = tpicksum/count - tremain = (tpick * (len(survey.getShotDict()) - count)) + tremain = (tpick * (len(self.getShotDict()) - count)) tend = datetime.now() + tremain - print 'shot: %s, est. time to be finished is %s:%s:%s' % (shot.getShotname(), tend.hour, tend.minute, tend.second) - - - + progress = float(count) / float(len(self.getShotDict())) * 100 + self._update_progress(shot.getShotname(), tend, progress) + print('\npickAllShots: Finished\n') def setArtificialPick(self, traceID, pick): for shot in self.data.values(): @@ -204,6 +205,108 @@ class Survey(object): return info_dict + def getShotForShotnumber(self, shotnumber): + for shot in self.data.values(): + if shot.getShotnumber() == shotnumber: + return shot + + def exportFMTOMO(self, directory = 'FMTOMO_export', sourcefile = 'input_sf.in', ttFileExtension = '.tt'): + def getAngle(distance): + PI = np.pi + R = 6371. + angle = distance * 180 / (PI * R) + return angle + + count = 0 + fmtomo_factor = 1000 # transforming [m/s] -> [km/s] + LatAll = []; LonAll = []; DepthAll = [] + srcfile = open(directory + '/' + sourcefile, 'w') + srcfile.writelines('%10s\n' %len(self.data)) # number of sources + for shotnumber in self.getShotlist(): + shot = self.getShotForShotnumber(shotnumber) + ttfilename = str(shotnumber) + ttFileExtension + (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) + srcfile.writelines('%10s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z)) # lat, lon, depth + LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) + srcfile.writelines('%10s\n' %1) # + srcfile.writelines('%10s %10s %10s\n' %(1, 1, ttfilename)) + ttfile = open(directory + '/' + ttfilename, 'w') + traceIDlist = shot.getTraceIDlist() + traceIDlist.sort() + ttfile.writelines(str(self.countPickedTraces(shot)) + '\n') + for traceID in traceIDlist: + if shot.getPick(traceID) is not None: + pick = shot.getPick(traceID) * fmtomo_factor + delta = shot.getPickError(traceID) * fmtomo_factor + (x, y, z) = shot.getRecLoc(traceID) + ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) + LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) + count += 1 + ttfile.close() + srcfile.close() + print 'Wrote output for %s traces' %count + print 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems to take Lat, Lon, Depth and creates output for FMTOMO as Depth, Lat, Lon' + print 'Dimensions of the seismic Array, transformed for FMTOMO, are Depth(%s, %s), Lat(%s, %s), Lon(%s, %s)'%( + min(DepthAll), max(DepthAll), min(LatAll), max(LatAll), min(LonAll), max(LonAll)) + + def countPickedTraces(self, shot): + count = 0 + for traceID in shot.getTraceIDlist(): + if shot.getPick(traceID) is not None: + count += 1 + return count + + def plotAllPicks(self, plotDeleted = False): + ''' + Plots all picks over the distance between source and receiver. Returns (ax, region) + ''' + import matplotlib.pyplot as plt + import math + plt.interactive(True) + from pylot.core.active.surveyPlotTools import regions + + dist = [] + pick = [] + snrloglist = [] + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if plotDeleted == False: + if shot.getPick(traceID) is not None: + dist.append(shot.getDistance(traceID)) + pick.append(shot.getPick(traceID)) + snrloglist.append(math.log10(shot.getSNR(traceID)[0])) + elif plotDeleted == True: + dist.append(shot.getDistance(traceID)) + pick.append(shot.getPick(traceID)) + snrloglist.append(math.log10(shot.getSNR(traceID)[0])) + + ax = self.createPlot(dist, pick, snrloglist, label = 'log10(SNR)') + region = regions(ax, self.data) + ax.legend() + + return ax, region + + def createPlot(self, dist, pick, inkByVal, label): + import matplotlib.pyplot as plt + plt.interactive(True) + cm = plt.cm.jet + + fig = plt.figure() + ax = fig.add_subplot(111) + fig = ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) + cbar = plt.colorbar(fig, fraction = 0.05) + cbar.set_label(label) + plt.title('Plot of all Picks') + plt.xlabel('Distance [m]') + plt.ylabel('Time [s]') + + return ax + + def _update_progress(self, shotname, tend, progress): + sys.stdout.write("Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r" + %(shotname, tend.hour, tend.minute, tend.second, progress)) + sys.stdout.flush() + def saveSurvey(self, filename = 'survey.pickle'): import cPickle outfile = open(filename, 'wb') From 3e7e693a66b6148beebf4c0c3da15756b5623b34 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 11:30:46 +0200 Subject: [PATCH 0600/1144] changed structure: plot option -> survey method --- pylot/core/active/surveyPlotTools.py | 82 ---------------------------- 1 file changed, 82 deletions(-) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index d91998c9..2f7b2a0a 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -1,85 +1,6 @@ import matplotlib.pyplot as plt -import math -#from selectRegions import regions - plt.interactive(True) -def plotAllPicks(shot_dict, dist_med = None): - ''' - Plots all picks over the distance between source and receiver. Returns (ax, region) - ''' - dist = [] - pick = [] - snrloglist = [] - for shot in shot_dict.values(): - for traceID in shot.getTraceIDlist(): - if shot.getPick(traceID) is not None: - dist.append(shot.getDistance(traceID)) - pick.append(shot.getPick(traceID)) - snrloglist.append(math.log10(shot.getSNR(traceID)[0])) - - ax = createPlot(dist, pick, snrloglist, label = 'log10(SNR)') - region = regions(ax, shot_dict) - if dist_med is not None: - ax = addDistMed(ax, dist_med) - ax.legend() - - return ax, region - -def plotAllPicks_withCutOutTraces(shot_dict, dist_med = None): - ''' - Plots all picks over the distance between source and receiver. Returns (ax, region) - ''' - dist = [] - pick = [] - snrloglist = [] - for shot in shot_dict.values(): - for traceID in shot.getTraceIDlist(): - if shot.getSNR(traceID)[0] > 3: - dist.append(shot.getDistance(traceID)) - pick.append(shot.getPick_backup(traceID)) - snrloglist.append(math.log10(shot.getSNR(traceID)[0])) - - ax = createPlot(dist, pick, snrloglist, label = 'log10(SNR)') - region = regions(ax, shot_dict) - if dist_med is not None: - ax = addDistMed(ax, dist_med) - ax.legend() - - return ax, region - -def createPlot(dist, pick, inkByVal, label): - cm = plt.cm.jet - - fig = plt.figure() - ax = fig.add_subplot(111) - fig = ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) - cbar = plt.colorbar(fig, fraction = 0.05) - cbar.set_label(label) - plt.title('Plot of all Picks') - plt.xlabel('Distance [m]') - plt.ylabel('Time [s]') - - return ax - -def addDistMed(ax, dist_med): - ''' - Add shot dictionary containing the Median for several distancebins to the ax. - ''' - x = [] - y = [] - for dist in dist_med.keys(): - x.append(dist) - y.append(dist_med[dist]) - - xy = sorted(zip(x,y)) - x1 = [x for (x,y) in xy] - y1 = [y for (x,y) in xy] - - ax.plot(x1, y1, 'k', label = 'Median') - - return ax - class regions(object): def __init__(self, ax, shot_dict): self.ax = ax @@ -159,7 +80,6 @@ class regions(object): self.ax.set_ylim(shot.getCut()) def plotTracesInRegion(self): - import matplotlib.pyplot as plt count = 0 maxfigures = 20 # if len(self.shots_found) == 0: @@ -179,7 +99,6 @@ class regions(object): print 'No picks yet defined in the regions x = (%s, %s), y = (%s, %s)' %(self._x0, self._x1, self._y0, self._y1) def plotTracesInRegion_withCutOutTraces(self): - import matplotlib.pyplot as plt count = 0 maxfigures = 20 # if len(self.shots_found) == 0: @@ -246,5 +165,4 @@ class regions(object): self.refreshFigure() def refreshFigure(self): - import matplotlib.pyplot as plt plt.draw() From 43990042772e5616e9fc6e29364970f66841996f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 11:32:28 +0200 Subject: [PATCH 0601/1144] cleaned up surveyUtils --- pylot/core/active/surveyUtils.py | 122 +------------------------------ 1 file changed, 1 insertion(+), 121 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 0c766166..4fbbe603 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -1,48 +1,5 @@ import numpy as np -def generateSurvey(obsdir, shotlist): - from obspy.core import read - from pylot.core.active import seismicshot - - shot_dict = {} - for shotnumber in shotlist: # loop over data files - # generate filenames and read manual picks to a list - obsfile = obsdir + str(shotnumber) + '_pickle.dat' - #obsfile = obsdir + str(shotnumber) + '.dat' - - if not obsfile in shot_dict.keys(): - shot_dict[shotnumber] = [] - shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) - shot_dict[shotnumber].setParameters('shotnumber', shotnumber) - - return shot_dict - -def setParametersForShots(cutwindow, tmovwind, tsignal, tgap, receiverfile, sourcefile, shot_dict): - for shot in shot_dict.values(): - shot.setCut(cutwindow) - shot.setTmovwind(tmovwind) - shot.setTsignal(tsignal) - shot.setTgap(tgap) - shot.setRecfile(receiverfile) - shot.setSourcefile(sourcefile) - shot.setOrder(order = 4) - -def removeEmptyTraces(shot_dict): - filename = 'removeEmptyTraces.out' - filename2 = 'updateTraces.out' - outfile = open(filename, 'w') - outfile2 = open(filename2, 'w') - for shot in shot_dict.values(): - del_traceIDs = shot.updateTraceList() - removed = shot.removeEmptyTraces() - if removed is not None: - outfile.writelines('shot: %s, removed empty traces: %s\n' %(shot.getShotnumber(), removed)) - outfile2.writelines('shot: %s, removed traceID(s) %s because they were not found in the corresponding stream\n' %(shot.getShotnumber(), del_traceIDs)) - print '\nremoveEmptyTraces, updateTraces: Finished! See %s and %s for more information of removed traces.\n' %(filename, filename2) - outfile.close() - outfile2.close() - - def readParameters(parfile, parameter): from ConfigParser import ConfigParser parameterConfig = ConfigParser() @@ -53,11 +10,6 @@ def readParameters(parfile, parameter): return value -def setArtificialPick(shot_dict, traceID, pick): - for shot in shot_dict.values(): - shot.setPick(traceID, pick) - shot.setPickwindow(traceID, shot.getCut()) - def fitSNR4dist(shot_dict, shiftdist = 5): dists = [] picks = [] @@ -79,7 +31,6 @@ def fitSNR4dist(shot_dict, shiftdist = 5): plotFittedSNR(dists, snrthresholds, snrs) return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben - def plotFittedSNR(dists, snrthresholds, snrs): import matplotlib.pyplot as plt plt.interactive(True) @@ -98,78 +49,8 @@ def setFittedSNR(shot_dict, shiftdist = 5, p1 = 0.004, p2 = -0.004): shot.setSNRthreshold(traceID, 1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) ### s.o. print "\nsetFittedSNR: Finished setting of fitted SNR-threshold" -#def linearInterp(dist_med, dist_start - -def exportFMTOMO(shot_dict, directory = 'FMTOMO_export', sourcefile = 'input_sf.in', ttFileExtension = '.tt'): - count = 0 - fmtomo_factor = 1000 # transforming [m/s] -> [km/s] - LatAll = []; LonAll = []; DepthAll = [] - srcfile = open(directory + '/' + sourcefile, 'w') - srcfile.writelines('%10s\n' %len(shot_dict)) # number of sources - for shotnumber in getShotlist(shot_dict): - shot = getShotForShotnumber(shot_dict, shotnumber) - ttfilename = str(shotnumber) + ttFileExtension - (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) - srcfile.writelines('%10s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z)) # lat, lon, depth - LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) - srcfile.writelines('%10s\n' %1) # - srcfile.writelines('%10s %10s %10s\n' %(1, 1, ttfilename)) - ttfile = open(directory + '/' + ttfilename, 'w') - traceIDlist = shot.getTraceIDlist() - traceIDlist.sort() - ttfile.writelines(str(countPickedTraces(shot)) + '\n') - for traceID in traceIDlist: - if shot.getPick(traceID) is not None: - pick = shot.getPick(traceID) * fmtomo_factor - delta = shot.getPickError(traceID) * fmtomo_factor - (x, y, z) = shot.getRecLoc(traceID) - ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) - LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) - count += 1 - ttfile.close() - srcfile.close() - print 'Wrote output for %s traces' %count - print 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems to take Lat, Lon, Depth and creates output for FMTOMO as Depth, Lat, Lon' - print 'Dimensions of the seismic Array, transformed for FMTOMO, are Depth(%s, %s), Lat(%s, %s), Lon(%s, %s)'%( - min(DepthAll), max(DepthAll), min(LatAll), max(LatAll), min(LonAll), max(LonAll)) - -def getShotlist(shot_dict): - shotlist = [] - for shot in shot_dict.values(): - shotlist.append(shot.getShotnumber()) - shotlist.sort() - return shotlist - -def getShotForShotnumber(shot_dict, shotnumber): - for shot in shot_dict.values(): - if shot.getShotnumber() == shotnumber: - return shot - -def getAngle(distance): - ''' - Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. - ''' - PI = np.pi - R = 6371. - angle = distance * 180 / (PI * R) - return angle - -def countPickedTraces(shot): - numtraces = 0 - for traceID in shot.getTraceIDlist(): - if shot.getPick(traceID) is not None: - numtraces += 1 - print "countPickedTraces: Found %s picked traces in shot number %s" %(numtraces, shot.getShotnumber()) - return numtraces - -def countAllPickedTraces(shot_dict): - traces = 0 - for shot in shot_dict.values(): - traces += countPickedTraces(shot) - return traces - def findTracesInRanges(shot_dict, distancebin, pickbin): - ''' + ''' Returns traces corresponding to a certain area in a plot with all picks over the distances. :param: shot_dict, dictionary containing all shots that are used @@ -180,7 +61,6 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): :param: pickbin :type: tuple, (t1[s], t2[s]) - ''' shots_found = {} for shot in shot_dict.values(): From c58bb530023d47bb83e14c433a90df721ed0129d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 11:33:39 +0200 Subject: [PATCH 0602/1144] output cosmetics --- pylot/core/pick/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 4df52c42..16fd2cec 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -74,8 +74,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): # if EPick stays NaN the signal window size will be doubled while np.isnan(EPick): if count > 0: - print("earllatepicker: Doubled signal window size %s time(s) " - "because of NaN for earliest pick.\n" %count) + print("\nearllatepicker: Doubled signal window size %s time(s) " + "because of NaN for earliest pick." %count) isigDoubleWinStart = pis[-1] + 1 isignalDoubleWin = np.arange(isigDoubleWinStart, isigDoubleWinStart + len(pis)) From 376d1cc6f8f2e6162167851c577b9ab0ecd031b5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 13:14:44 +0200 Subject: [PATCH 0603/1144] name change --- pylot/core/active/seismicArrayPreparation.py | 665 +++++++++++++++++++ 1 file changed, 665 insertions(+) create mode 100644 pylot/core/active/seismicArrayPreparation.py diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py new file mode 100644 index 00000000..ae1b42ec --- /dev/null +++ b/pylot/core/active/seismicArrayPreparation.py @@ -0,0 +1,665 @@ +import sys +import numpy as np +from scipy.interpolate import griddata + +class SeisArray(object): + ''' + Can be used to interpolate missing values of a receiver grid, if only support points were measured. + Input file should contain in each line: ('traceID' 'receiverLineID' 'number of the geophone on recLine' 'X' 'Y' 'Z') + + Can be used to generate a velocity grid file (vgrids.in) for FMTOMO with a topography adapting gradient. + + Can be used to generate an interface file for FMTOMO (right now only interface.z used by grid3dg) for the topography. + + Supports vtk output for sources and receivers. + Note: Source and Receiver files for FMTOMO will be generated by the Survey object (because traveltimes will be added directly). + ''' + def __init__(self, recfile): + self._receiverlines = {} + self._receiverCoords = {} + self._measuredReceivers = {} + self._measuredTopo = {} + self._sourceLocs = {} + self._geophoneNumbers = {} + self._receiverlist = open(recfile, 'r').readlines() + self._generateReceiverlines() + self._setReceiverCoords() + self._setGeophoneNumbers() + + def _generateReceiverlines(self): + ''' + Connects the traceIDs to the lineIDs + for each receiverline in a dictionary. + ''' + for receiver in self._receiverlist: + traceID = int(receiver.split()[0]) + lineID = int(receiver.split()[1]) + if not lineID in self._receiverlines.keys(): + self._receiverlines[lineID] = [] + self._receiverlines[lineID].append(traceID) + + def _setReceiverCoords(self): + ''' + Fills the three x, y, z dictionaries with measured coordinates + ''' + for line in self._getReceiverlist(): + traceID = int(line.split()[0]) + x = float(line.split()[3]) + y = float(line.split()[4]) + z = float(line.split()[5]) + self._receiverCoords[traceID] = (x, y, z) + self._measuredReceivers[traceID] = (x, y, z) + + def _setGeophoneNumbers(self): + for line in self._getReceiverlist(): + traceID = int(line.split()[0]) + gphoneNum = float(line.split()[2]) + self._geophoneNumbers[traceID] = gphoneNum + + def _getReceiverlines(self): + return self._receiverlines + + def _getReceiverlist(self): + return self._receiverlist + + def getReceiverCoordinates(self): + return self._receiverCoords + + def _getXreceiver(self, traceID): + return self._receiverCoords[traceID][0] + + def _getYreceiver(self, traceID): + return self._receiverCoords[traceID][1] + + def _getZreceiver(self, traceID): + return self._receiverCoords[traceID][2] + + def _getXshot(self, shotnumber): + return self._sourceLocs[shotnumber][0] + + def _getYshot(self, shotnumber): + return self._sourceLocs[shotnumber][1] + + def _getZshot(self, shotnumber): + return self._sourceLocs[shotnumber][2] + + def _getReceiverValue(self, traceID, coordinate): + setCoordinate = {'X': self._getXreceiver, + 'Y': self._getYreceiver, + 'Z': self._getZreceiver} + return setCoordinate[coordinate](traceID) + + def _getGeophoneNumber(self, traceID): + return self._geophoneNumbers[traceID] + + def getMeasuredReceivers(self): + return self._measuredReceivers + + def getMeasuredTopo(self): + return self._measuredTopo + + def getSourceLocations(self): + return self._sourceLocs + + def _setXvalue(self, traceID, value): + self._checkKey(traceID) + self._receiverCoords[traceID][0] = value + + def _setYvalue(self, traceID, value): + self._checkKey(traceID) + self._receiverCoords[traceID][1] = value + + def _setZvalue(self, traceID, value): + self._checkKey(traceID) + self._receiverCoords[traceID][2] = value + + def _setValue(self, traceID, coordinate, value): + setCoordinate = {'X': self._setXvalue, + 'Y': self._setYvalue, + 'Z': self._setZvalue} + setCoordinate[coordinate](traceID, value) + + def _checkKey(self, traceID): + if not traceID in self._receiverCoords.keys(): + self._receiverCoords[traceID] = [None, None, None] + + def _checkTraceIDdirection(self, traceID1, traceID2): + if traceID2 > traceID1: + direction = +1 + return direction + if traceID2 < traceID1: + direction = -1 + return direction + print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) + + def _checkCoordDirection(self, traceID1, traceID2, coordinate): + ''' + Checks whether the interpolation direction is positive or negative + ''' + if self._getReceiverValue(traceID1, coordinate) < self._getReceiverValue(traceID2, coordinate): + direction = +1 + return direction + if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): + direction = -1 + return direction + print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) + + def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): + ''' + Returns the mean distance between two traceID's depending on the number of geophones in between + ''' + num_spaces = abs(self._getGeophoneNumber(traceID1) - self._getGeophoneNumber(traceID2)) + mean_distance = abs(self._getReceiverValue(traceID1, coordinate) - self._getReceiverValue(traceID2, coordinate))/num_spaces + return mean_distance + + def interpolateValues(self, coordinate): + ''' + Interpolates and sets all values (linear) for coordinate = 'X', 'Y' or 'Z' + ''' + for lineID in self._getReceiverlines().keys(): + number_measured = len(self._getReceiverlines()[lineID]) + for index, traceID1 in enumerate(self._getReceiverlines()[lineID]): + if index + 1 < number_measured: + traceID2 = self._getReceiverlines()[lineID][index + 1] + + traceID_dir = self._checkTraceIDdirection(traceID1, traceID2) + traceID_interp = traceID1 + traceID_dir + + coord_dir = self._checkCoordDirection(traceID1, traceID2, coordinate) + mean_distance = self._interpolateMeanDistances(traceID1, traceID2, coordinate) + + while (traceID_dir * traceID_interp) < (traceID_dir * traceID2): + self._setValue(traceID_interp, coordinate, + (self._getReceiverValue(traceID_interp - traceID_dir, coordinate) + + coord_dir * (mean_distance))) + traceID_interp += traceID_dir + + def addMeasuredTopographyPoints(self, filename): + ''' + Use more measured points for a higher precision of height interpolation. + Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') + ''' + topolist = open(filename, 'r').readlines() + for line in topolist: + line = line.split() + pointID = int(line[0]) + x = float(line[1]) + y = float(line[2]) + z = float(line[3]) + self._measuredTopo[pointID] = (x, y, z) + + def addSourceLocations(self, filename): + ''' + Use source locations for a higher precision of height interpolation. + Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') + + Source locations must be added before they can be written to vtk files. + ''' + topolist = open(filename, 'r').readlines() + for line in topolist: + line = line.split() + pointID = int(line[0]) + x = float(line[1]) + y = float(line[2]) + z = float(line[3]) + self._sourceLocs[pointID] = (x, y, z) + + def interpZcoords4rec(self, method = 'linear'): + ''' + Interpolates z values for all receivers. + ''' + measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() + + for traceID in self.getReceiverCoordinates().keys(): + if type(self.getReceiverCoordinates()[traceID]) is not tuple: + z = griddata((measured_x, measured_y), measured_z, (self._getXreceiver(traceID), self._getYreceiver(traceID)), method = method) + self._setZvalue(traceID, float(z)) + + def _getAngle(self, distance): + ''' + Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. + ''' + PI = np.pi + R = 6371. + angle = distance * 180 / (PI * R) + return angle + + def _getDistance(self, angle): + ''' + Function returns the distance [km] on a Sphere of the radius R = 6371 [km] for an angle. + ''' + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + + def getMeasuredReceiverLists(self): + ''' + Returns a list of all measured receivers known to SeisArray. + ''' + x = []; y = []; z = [] + for traceID in self.getMeasuredReceivers().keys(): + x.append(self.getMeasuredReceivers()[traceID][0]) + y.append(self.getMeasuredReceivers()[traceID][1]) + z.append(self.getMeasuredReceivers()[traceID][2]) + return x, y, z + + def getMeasuredTopoLists(self): + ''' + Returns a list of all measured topography points known to the SeisArray. + ''' + x = []; y = []; z = [] + for pointID in self.getMeasuredTopo().keys(): + x.append(self.getMeasuredTopo()[pointID][0]) + y.append(self.getMeasuredTopo()[pointID][1]) + z.append(self.getMeasuredTopo()[pointID][2]) + return x, y, z + + def getSourceLocsLists(self): + ''' + Returns a list of all measured source locations known to SeisArray. + ''' + x = []; y = []; z = [] + for pointID in self.getSourceLocations().keys(): + x.append(self.getSourceLocations()[pointID][0]) + y.append(self.getSourceLocations()[pointID][1]) + z.append(self.getSourceLocations()[pointID][2]) + return x, y, z + + def getAllMeasuredPointsLists(self): + ''' + Returns a list of all measured points known to SeisArray. + ''' + mtopo_x, mtopo_y, mtopo_z = self.getMeasuredTopoLists() + msource_x, msource_y, msource_z = self.getSourceLocsLists() + mrec_x, mrec_y, mrec_z = self.getMeasuredReceiverLists() + + x = mtopo_x + mrec_x + msource_x + y = mtopo_y + mrec_y + msource_y + z = mtopo_z + mrec_z + msource_z + return x, y, z + + def getReceiverLists(self): + ''' + Returns a list of all receivers (measured and interpolated). + ''' + x = []; y =[]; z = [] + for traceID in self.getReceiverCoordinates().keys(): + x.append(self.getReceiverCoordinates()[traceID][0]) + y.append(self.getReceiverCoordinates()[traceID][1]) + z.append(self.getReceiverCoordinates()[traceID][2]) + return x, y, z + + def _interpolateXY4rec(self): + ''' + Interpolates the X and Y coordinates for all receivers. + ''' + for coordinate in ('X', 'Y'): + self.interpolateValues(coordinate) + + def interpolateAll(self): + self._interpolateXY4rec() + self.interpZcoords4rec() + + def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, method = 'linear', filename = 'interface1.in'): + ''' + Interpolate Z values on a regular grid with cushion nodes to use it as FMTOMO topography interface. + Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). + + :param: nTheta, number of points in theta (NS) + type: integer + + :param: nPhi, number of points in phi (WE) + type: integer + + :param: thetaSN (S, N) extensions of the model in degree + type: tuple + + :param: phiWE (W, E) extensions of the model in degree + type: tuple + ''' + + surface = [] + elevation = 0.25 # elevate topography so that no source lies above the surface + + if filename is not None: + outfile = open(filename, 'w') + + print "Interpolating topography on regular grid with the dimensions:" + print "nTheta = %s, nPhi = %s, thetaSN = %s, phiWE = %s"%(nTheta, nPhi, thetaSN, phiWE) + print "method = %s, filename = %s" %(method, filename) + + thetaS, thetaN = thetaSN + phiW, phiE = phiWE + + measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() + + # need to determine the delta to add two cushion nodes around the min/max values + thetaDelta = (thetaN - thetaS) / (nTheta - 1) + phiDelta = (phiE - phiW) / (nPhi - 1) + + thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes + + nTotal = len(thetaGrid) * len(phiGrid); count = 0 + for theta in thetaGrid: + for phi in phiGrid: + xval = self._getDistance(phi) + yval = self._getDistance(theta) + z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = method) + # in case the point lies outside, nan will be returned. Find nearest: + if np.isnan(z) == True: + z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = 'nearest') + z = float(z) + surface.append((xval, yval, z)) + count += 1 + progress = float(count) / float(nTotal) * 100 + self._update_progress(progress) + + if filename is not None: + outfile.writelines('%10s\n'%(z + elevation)) + + return surface + + def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, + thetaSN = (-0.2, 1.2), phiWE = (-0.2, 1.2), + Rbt = (-62.0, 6.0), vbot = 5.5, filename = 'vgrids.in', + method = 'linear' ): + ''' + Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. + + :param: nTheta, number of points in theta (NS) + type: integer + + :param: nPhi, number of points in phi (WE) + type: integer + + :param: nR, number of points in depth + type: integer + + :param: thetaSN (S, N) extensions of the model in degree + type: tuple + + :param: phiWE (W, E) extensions of the model in degree + type: tuple + + :param: Rbt (bot, top) extensions of the model in km + type: tuple + + :param: vbot, velocity at the bottom of the model + type: real + ''' + + def getRad(angle): + PI = np.pi + rad = angle / 180 * PI + return rad + + def getZmax(surface): + z = [] + for point in surface: + z.append(point[2]) + return max(z) + + R = 6371 + vmin = 0.34 + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + outfile = open(filename, 'w') + + thetaS, thetaN = thetaSN + phiW, phiE = phiWE + rbot = Rbt[0] + R + rtop = Rbt[1] + R + + # need to determine the delta to add two cushion nodes around the min/max values + thetaDelta = abs(thetaN - thetaS) / (nTheta - 1) + phiDelta = abs(phiE - phiW) / (nPhi - 1) + rDelta = abs(rbot - rtop) / (nR - 1) + + # create a regular grid including +2 cushion nodes in every direction + thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes + rGrid = np.linspace(rbot - rDelta, rtop + rDelta, num = nR + 2) # +2 cushion nodes + + nTotal = len(rGrid) * len(thetaGrid) * len(phiGrid) + print "Total number of grid nodes: %s"%nTotal + + # write header for velocity grid file (in RADIANS) + outfile.writelines('%10s %10s \n' %(1, 1)) + outfile.writelines('%10s %10s %10s\n' %(nR + 2, nTheta + 2, nPhi + 2)) + outfile.writelines('%10s %10s %10s\n' %(rDelta, getRad(thetaDelta), getRad(phiDelta))) + outfile.writelines('%10s %10s %10s\n' %(rbot - rDelta, getRad(thetaS - thetaDelta), getRad(phiW - phiDelta))) + + surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method, filename = None) + zmax = getZmax(surface) + + print "\nGenerating velocity grid for FMTOMO. Output filename = %s, interpolation method = %s"%(filename, method) + print "nTheta = %s, nPhi = %s, nR = %s, thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt) + count = 0 + for radius in rGrid: + for theta in thetaGrid: + for phi in phiGrid: + xval = self._getDistance(phi) + yval = self._getDistance(theta) + for point in surface: + if point[0] == xval and point[1] == yval: + z = point[2] + if radius > (R + z + 1): + vel = 0.0 + # elif radius > (R + z - 15): ########### TESTING + # vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * 1.0 + vmin ########################## + else: + vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * vbot + vmin ########################## + count += 1 + outfile.writelines('%10s %10s\n'%(vel, decm)) + + progress = float(count) / float(nTotal) * 100 + self._update_progress(progress) + + outfile.close() + + def exportAll(self, filename = 'interpolated_receivers.out'): + recfile_out = open(filename, 'w') + count = 0 + for traceID in self.getReceiverCoordinates().keys(): + count += 1 + x, y, z = self.getReceiverCoordinates()[traceID] + recfile_out.writelines('%5s %15s %15s %15s\n' %(traceID, x, y, z)) + print "Exported coordinates for %s traces to file > %s" %(count, filename) + recfile_out.close() + + def plotArray2D(self, plot_topo = False, highlight_measured = False, annotations = True): + import matplotlib.pyplot as plt + plt.interactive(True) + plt.figure() + xmt, ymt, zmt = self.getMeasuredTopoLists() + xsc, ysc, zsc = self.getSourceLocsLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xrc, yrc, zrc = self.getReceiverLists() + + plt.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') + plt.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') + + if plot_topo == True: + plt.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') + if highlight_measured == True: + plt.plot(xmr, ymr, 'ro', label = 'measured receivers') + + plt.xlabel('X [m]') + plt.ylabel('Y [m]') + plt.legend() + if annotations == True: + for traceID in self.getReceiverCoordinates().keys(): + plt.annotate(str(traceID), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small') + + def plotArray3D(self, ax = None): + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d import Axes3D + plt.interactive(True) + + if ax == None: + fig = plt.figure() + ax = plt.axes(projection = '3d') + + xmt, ymt, zmt = self.getMeasuredTopoLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xin, yin, zin = self.getReceiverLists() + + ax.plot(xmt, ymt, zmt, 'b*', markersize = 10, label = 'measured topo points') + ax.plot(xin, yin, zin, 'k.', markersize = 10, label = 'interpolated receivers') + ax.plot(xmr, ymr, zmr, 'ro', label = 'measured receivers') + ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') + ax.legend() + + return ax + + + def plotSurface3D(self, ax = None, step = 0.5, method = 'linear'): + from matplotlib import cm + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d import Axes3D + plt.interactive(True) + + if ax == None: + fig = plt.figure() + ax = plt.axes(projection = '3d') + + xmt, ymt, zmt = self.getMeasuredTopoLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + + x = xmt + xmr + y = ymt + ymr + z = zmt + zmr + + xaxis = np.arange(min(x)+1, max(x), step) + yaxis = np.arange(min(y)+1, max(y), step) + + xgrid, ygrid = np.meshgrid(xaxis, yaxis) + + zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) + + ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) + + ax.set_zlim(-(max(x) - min(x)/2),(max(x) - min(x)/2)) + ax.set_aspect('equal') + + ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') + ax.legend() + + return ax + + def _update_progress(self, progress): + sys.stdout.write("%d%% done \r" % (progress) ) + sys.stdout.flush() + + def receivers2VTK(self, filename = 'receivers.vtk'): + ''' + Generates vtk files from all receivers of the SeisArray object. + ''' + outfile = open(filename, 'w') + traceIDs = [] + + for traceID in self.getReceiverCoordinates(): + traceIDs.append(traceID) + + nPoints = len(traceIDs) + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Receivers with traceIDs\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + print("Writing coordinates to VTK file...") + for traceID in traceIDs: + x = self._getXreceiver(traceID) + y = self._getYreceiver(traceID) + z = self._getZreceiver(traceID) + + outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + + outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + + # write indices + print("Writing indices to VTK file...") + for index in range(nPoints): + outfile.writelines('%10d %10d\n' %(1, index)) + + outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('SCALARS traceIDs int %d\n' %(1)) + outfile.writelines('LOOKUP_TABLE default\n') + + # write traceIDs + print("Writing traceIDs to VTK file...") + for traceID in traceIDs: + outfile.writelines('%10d\n' %traceID) + + outfile.close() + print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) + return + + def sources2VTK(self, filename = 'sources.vtk'): + ''' + Generates vtk-files for all source locations in the SeisArray object. + ''' + outfile = open(filename, 'w') + shotnumbers = [] + + for shotnumber in self.getSourceLocations(): + shotnumbers.append(shotnumber) + + nPoints = len(shotnumbers) + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Shots with shotnumbers\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + print("Writing coordinates to VTK file...") + for shotnumber in shotnumbers: + x = self._getXshot(shotnumber) + y = self._getYshot(shotnumber) + z = self._getZshot(shotnumber) + + outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + + outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + + # write indices + print("Writing indices to VTK file...") + for index in range(nPoints): + outfile.writelines('%10d %10d\n' %(1, index)) + + outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('SCALARS shotnumbers int %d\n' %(1)) + outfile.writelines('LOOKUP_TABLE default\n') + + # write shotnumber + print("Writing shotnumbers to VTK file...") + for shotnumber in shotnumbers: + outfile.writelines('%10d\n' %shotnumber) + + outfile.close() + print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) + return + + + def saveSeisArray(self, filename = 'seisArray.pickle'): + import cPickle + outfile = open(filename, 'wb') + + cPickle.dump(self, outfile, -1) + print('saved SeisArray to file %s'%(filename)) + + @staticmethod + def from_pickle(filename): + import cPickle + infile = open(filename, 'rb') + return cPickle.load(infile) From 9faf42fd797684bb98eb78ef0228ae99a32113b0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 17:20:01 +0200 Subject: [PATCH 0604/1144] bugfix: import numpy missing --- pylot/core/active/fmtomo2vtk.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index 2c35958a..8de95354 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -1,3 +1,5 @@ +import numpy as np + def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): ''' Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in From 3d3cac70850d5408088c29b6e6ff752397e29580 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 17:21:18 +0200 Subject: [PATCH 0605/1144] cosmetics --- pylot/core/active/seismicArrayPreparation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index ae1b42ec..65226423 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -598,7 +598,7 @@ class SeisArray(object): outfile.writelines('%10d\n' %traceID) outfile.close() - print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) + print("Wrote %d receiver for to file: %s" %(nPoints, filename)) return def sources2VTK(self, filename = 'sources.vtk'): @@ -647,7 +647,7 @@ class SeisArray(object): outfile.writelines('%10d\n' %shotnumber) outfile.close() - print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) + print("Wrote %d sources to file: %s" %(nPoints, filename)) return From 7120a20499b5563797f8528b325ce4f257aac155 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 1 Oct 2015 17:30:57 +0200 Subject: [PATCH 0606/1144] changed vtk file structure for vgrids, making it way faster and smaller and enabling paraview to display surfaces instead of points --- pylot/core/active/fmtomo2vtk.py | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index 8de95354..a4edf32d 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -74,33 +74,20 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): dX = getDistance(np.rad2deg(dPhi)) dY = getDistance(np.rad2deg(dTheta)) - xGrid = np.linspace(sX, sX + (dX * nX), nX) - yGrid = np.linspace(sZ, sZ + (nY * dY), nY) - zGrid = np.linspace(sZ, sZ + (nR * dR), nR) - nPoints = len(xGrid) * len(yGrid) * len(zGrid) + nPoints = nX * nY * nZ + + dZ = dR # write header print("Writing header for VTK file...") outfile.writelines('# vtk DataFile Version 3.1\n') outfile.writelines('Velocity on FMTOMO vgrids.in points\n') outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) + outfile.writelines('DATASET STRUCTURED_POINTS\n') - # write coordinates - print("Writing coordinates to VTK file...") - for z in zGrid: - for y in yGrid: - for x in xGrid: - outfile.writelines('%10f %10f %10f \n' %(x, y, z)) - - - outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) - - # write indices - print("Writing indices to VTK file...") - for index in range(nPoints): - outfile.writelines('%10d %10d\n' %(1, index)) + outfile.writelines('DIMENSIONS %d %d %d\n' %(nX, nY, nZ)) + outfile.writelines('ORIGIN %f %f %f\n' %(sX, sY, sZ)) + outfile.writelines('SPACING %f %f %f\n' %(dX, dY, dZ)) outfile.writelines('POINT_DATA %15d\n' %(nPoints)) outfile.writelines('SCALARS velocity float %d\n' %(1)) From 41096968001a1aac38a51a3d395d8a4f8958254f Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 2 Oct 2015 06:06:26 +0200 Subject: [PATCH 0607/1144] meet style conventions --- pylot/core/active/seismicshot.py | 9 ++++++--- pylot/core/active/surveyUtils.py | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 5b21024a..ecb602c2 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + import os import numpy as np from obspy.core import read @@ -145,7 +148,7 @@ class SeismicShot(object): def getPickError(self, traceID): pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) if np.isnan(pickerror) == True: - print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) + print("SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID)) return pickerror def getStreamTraceIDs(self): @@ -170,7 +173,7 @@ class SeismicShot(object): def getPickwindow(self, traceID): try: self.pickwindow[traceID] - except KeyError, e: + except KeyError as e: print('no pickwindow for trace %s, set to %s' % (traceID, self.getCut())) self.setPickwindow(traceID, self.getCut()) return self.pickwindow[traceID] @@ -253,7 +256,7 @@ class SeismicShot(object): return Stream(traces) else: self.setPick(traceID, None) - print 'Warning: ambigious or empty traceID: %s' % traceID + print('Warning: ambigious or empty traceID: %s' % traceID) #raise ValueError('ambigious or empty traceID: %s' % traceID) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 4fbbe603..909fed13 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + import numpy as np def readParameters(parfile, parameter): From cfbcc9d36251cacbc527a6608c8e5cc99b5d76ca Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 6 Oct 2015 11:42:33 +0200 Subject: [PATCH 0608/1144] bugfix: if invalid ray is generated by FMTOMO it will be skipped --- pylot/core/active/fmtomo2vtk.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index a4edf32d..5514c243 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -127,7 +127,12 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): raynumber += 1 firstline = infile.readline() if firstline == '': break # break at EOF + raynumber = int(firstline.split()[0]) shotnumber = int(firstline.split()[1]) + rayValid = int(firstline.split()[4]) # is zero if the ray is invalid + if rayValid == 0: + print('Invalid ray number %d for shot number %d'%(raynumber, shotnumber)) + continue nRayPoints = int(infile.readline().split()[0]) if not shotnumber in rays.keys(): rays[shotnumber] = {} From 09f0cd3e7130e1a005c40b2f713ec273bdcf269b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 7 Oct 2015 14:51:00 +0200 Subject: [PATCH 0609/1144] shots no longer None if they are deleted, but flag = 0 --- pylot/core/active/surveyPlotTools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index 2f7b2a0a..d7d8318a 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -160,7 +160,7 @@ class regions(object): def highlightPicksForShot(self, shot, annotations = False): for traceID in shot.getTraceIDlist(): - if shot.getPick(traceID) is not None: + if shot.getFlag(traceID) is not 0: self.highlightPick(shot, traceID, annotations) self.refreshFigure() From c71e28ecb752b21451082935c5fc8384ce350c30 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 7 Oct 2015 14:51:20 +0200 Subject: [PATCH 0610/1144] shots no longer None if they are removed, but flag = 0 --- pylot/core/active/activeSeismoPick.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 50c1ddad..4adc21af 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -127,7 +127,7 @@ class Survey(object): shot.removePick(traceID) # -- TEST: set and check SNR before adding to distance bin ############################ - if shot.getPick(traceID) is not None: + if shot.getFlag(traceID) is not 0: shot.setEarllatepick(traceID) tpicksum += (datetime.now() - tstartpick); tpick = tpicksum/count @@ -194,7 +194,7 @@ class Survey(object): for traceID in shot.getTraceIDlist(): snrlist.append(shot.getSNR(traceID)[0]) dist.append(shot.getDistance(traceID)) - if shot.getPick(traceID) is not None: + if shot.getFlag(traceID) is not 0: pickedTraces += 1 info_dict[shot.getShotnumber()] = {'numtraces': numtraces, 'picked traces': [pickedTraces, @@ -235,7 +235,7 @@ class Survey(object): traceIDlist.sort() ttfile.writelines(str(self.countPickedTraces(shot)) + '\n') for traceID in traceIDlist: - if shot.getPick(traceID) is not None: + if shot.getFlag(traceID) is not 0: pick = shot.getPick(traceID) * fmtomo_factor delta = shot.getPickError(traceID) * fmtomo_factor (x, y, z) = shot.getRecLoc(traceID) @@ -252,7 +252,7 @@ class Survey(object): def countPickedTraces(self, shot): count = 0 for traceID in shot.getTraceIDlist(): - if shot.getPick(traceID) is not None: + if shot.getFlag(traceID) is not 0: count += 1 return count @@ -271,7 +271,7 @@ class Survey(object): for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if plotDeleted == False: - if shot.getPick(traceID) is not None: + if shot.getFlag(traceID) is not 0: dist.append(shot.getDistance(traceID)) pick.append(shot.getPick(traceID)) snrloglist.append(math.log10(shot.getSNR(traceID)[0])) From 8e7b2e5b8a24a08fe5df708d74907b6c01b7c4ba Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 7 Oct 2015 14:53:10 +0200 Subject: [PATCH 0611/1144] removed pick_backup. implied flag for each pick instead --- pylot/core/active/seismicshot.py | 33 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 5b21024a..85d48e93 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -24,9 +24,6 @@ class SeismicShot(object): self.srcCoordlist = None self.traceIDs = None self.pick = {} - self.pick_backup = {} - self.earliest = {} - self.latest = {} self.pickwindow= {} self.manualpicks= {} self.snr = {} @@ -131,16 +128,13 @@ class SeismicShot(object): return self.paras['sourcefile'] def getPick(self, traceID): - return self.pick[traceID] - - def getPick_backup(self, traceID): - return self.pick_backup[traceID] + return self.pick[traceID]['mpp'] def getEarliest(self, traceID): - return self.earliest[traceID] + return self.pick[traceID]['epp'] def getLatest(self, traceID): - return self.latest[traceID] + return self.pick[traceID]['lpp'] def getPickError(self, traceID): pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) @@ -293,14 +287,13 @@ class SeismicShot(object): 'aic': aiccftime} self.setPick(traceID, setHosAic[HosAic]) - self.pick_backup[traceID] = setHosAic[HosAic] ### verbessern (vor allem weil ueberschrieben bei 2tem mal picken) def setEarllatepick(self, traceID, nfac = 1.5): tgap = self.getTgap() tsignal = self.getTsignal() tnoise = self.getPick(traceID) - tgap - (self.earliest[traceID], self.latest[traceID], tmp) = earllatepicker(self.getSingleStream(traceID), + (self.pick[traceID]['epp'], self.pick[traceID]['lpp'], tmp) = earllatepicker(self.getSingleStream(traceID), nfac, (tnoise, tgap, tsignal), self.getPick(traceID)) @@ -452,7 +445,10 @@ class SeismicShot(object): # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) def setPick(self, traceID, pick): ########## siehe Kommentar ########## - self.pick[traceID] = pick + if not traceID in self.pick.keys(): + self.pick[traceID] = {} + self.pick[traceID]['mpp'] = pick + self.pick[traceID]['flag'] = 1 # ++++++++++++++ Block raus genommen, da Error beim 2ten Mal picken! (Ueberschreiben von erstem Pick!) # if not self.pick.has_key(traceID): # self.getPick(traceID) = picks @@ -463,7 +459,14 @@ class SeismicShot(object): # parlist = open(parfile,'r').readlines() def removePick(self, traceID): - self.setPick(traceID, None) + self.setFlag(traceID, 0) + + def setFlag(self, traceID, flag): + 'Set flag = 0 if pick is invalid, else flag = 1' + self.pick[traceID]['flag'] = 0 + + def getFlag(self, traceID): + return self.pick[traceID]['flag'] def setPickwindow(self, traceID, pickwindow): self.pickwindow[traceID] = pickwindow @@ -628,7 +631,7 @@ class SeismicShot(object): y = [] z = [] for traceID in self.pick.keys(): - if self.getPick(traceID) != None: + if self.getFlag(traceID) != 0: x.append(self.getRecLoc(traceID)[0]) y.append(self.getRecLoc(traceID)[1]) z.append(self.getPick(traceID)) @@ -683,7 +686,7 @@ class SeismicShot(object): y = [] z = [] for traceID in self.pick.keys(): - if self.getPick(traceID) != None: + if self.getFlag(traceID) != 0: x.append(self.getRecLoc(traceID)[0]) y.append(self.getRecLoc(traceID)[1]) z.append(self.getPick(traceID)) From 5bb50d5be4025bb1fbf32f3475eff68fbbaf76bf Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 8 Oct 2015 12:16:03 +0200 Subject: [PATCH 0612/1144] added captions for shotnumbers in plotArray2D --- pylot/core/active/seismicArrayPreparation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 65226423..2eeab244 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -490,7 +490,11 @@ class SeisArray(object): plt.legend() if annotations == True: for traceID in self.getReceiverCoordinates().keys(): - plt.annotate(str(traceID), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small') + plt.annotate((' ' + str(traceID)), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small', color = 'k') + for shotnumber in self.getSourceLocations().keys(): + plt.annotate((' ' + str(shotnumber)), xy = (self._getXshot(shotnumber), self._getYshot(shotnumber)), fontsize = 'x-small', color = 'b') + + def plotArray3D(self, ax = None): import matplotlib.pyplot as plt From 0adc890aeffd543fbc1a90460de3e41781f5a3f5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Oct 2015 12:59:53 +0200 Subject: [PATCH 0613/1144] implied stealth mode to suppress huge amounts of text output --- pylot/core/pick/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 16fd2cec..43c3a782 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -14,7 +14,7 @@ from obspy.core import Stream, UTCDateTime import warnings -def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): +def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) as reasonable uncertainties. Latest possible pick is based on noise level, @@ -43,7 +43,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): LPick = None EPick = None PickError = None - #print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' + if stealthMode is False: + print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, @@ -74,8 +75,9 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): # if EPick stays NaN the signal window size will be doubled while np.isnan(EPick): if count > 0: - print("\nearllatepicker: Doubled signal window size %s time(s) " - "because of NaN for earliest pick." %count) + if stealthMode is False: + print("\nearllatepicker: Doubled signal window size %s time(s) " + "because of NaN for earliest pick." %count) isigDoubleWinStart = pis[-1] + 1 isignalDoubleWin = np.arange(isigDoubleWinStart, isigDoubleWinStart + len(pis)) From a19cdc4feeb11a3f970dad252027500007392c8e Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Oct 2015 13:00:09 +0200 Subject: [PATCH 0614/1144] implied stealth mode --- pylot/core/pick/CharFuns.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 2f6e8cd8..d929b238 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -24,7 +24,7 @@ class CharacteristicFunction(object): ''' SuperClass for different types of characteristic functions. ''' - def __init__(self, data, cut, t2=None, order=None, t1=None, fnoise=None): + def __init__(self, data, cut, t2=None, order=None, t1=None, fnoise=None, stealthMode=False): ''' Initialize data type object with information from the original Seismogram. @@ -61,6 +61,7 @@ class CharacteristicFunction(object): self.calcCF(self.getDataArray()) self.arpara = np.array([]) self.xpred = np.array([]) + self.stealthMode = stealthMode def __str__(self): return '''\n\t{name} object:\n @@ -218,7 +219,8 @@ class AICcf(CharacteristicFunction): def calcCF(self, data): - #print 'Calculating AIC ...' ## MP MP output suppressed + if self.stealthMode is False: + print 'Calculating AIC ...' x = self.getDataArray() xnp = x[0].data nn = np.isnan(xnp) @@ -256,11 +258,13 @@ class HOScf(CharacteristicFunction): if len(nn) > 1: xnp[nn] = 0 if self.getOrder() == 3: # this is skewness - print 'Calculating skewness ...' + if self.stealthMode is False: + print 'Calculating skewness ...' y = np.power(xnp, 3) y1 = np.power(xnp, 2) elif self.getOrder() == 4: # this is kurtosis - #print 'Calculating kurtosis ...' ## MP MP output suppressed + if self.stealthMode is False: + print 'Calculating kurtosis ...' y = np.power(xnp, 4) y1 = np.power(xnp, 2) From 34abad46e28566b46880508ca9ed74edb09d3fb8 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Oct 2015 14:39:40 +0200 Subject: [PATCH 0615/1144] refresh plot for plotAllPicks by replotting --- pylot/core/active/activeSeismoPick.py | 150 +++++++++++++++++++++----- 1 file changed, 125 insertions(+), 25 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 4adc21af..44a863c0 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -18,6 +18,7 @@ class Survey(object): self.setParametersForShots() self._removeAllEmptyTraces() self._updateShots() + self.setArtificialPick(0, 0) # artificial pick at source origin def _generateSurvey(self): from obspy.core import read @@ -33,17 +34,23 @@ class Survey(object): shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) shot_dict[shotnumber].setParameters('shotnumber', shotnumber) - self.setArtificialPick(0, 0) # artificial pick at source origin - self.data = shot_dict print ("Generated Survey object for %d shots" % len(shotlist)) print ("Total number of traces: %d \n" %self.countAllTraces()) + def setArtificialPick(self, traceID, pick): + ''' + Sets an artificial pick for a traceID of all shots in the survey object. + (This can be used to create a pick with t = 0 at the source origin) + ''' + for shot in self.data.values(): + shot.setPick(traceID, pick) + def setParametersForShots(self, cutwindow = (0, 0.2), tmovwind = 0.3, tsignal = 0.03, tgap = 0.0007): if (cutwindow == (0, 0.2) and tmovwind == 0.3 and tsignal == 0.03 and tgap == 0.0007): print ("Warning: Standard values used for " - "setParamters. This may not be clever.") + "setParamters. This might not be clever.") # CHANGE this later. Parameters only needed for survey, not for each shot. for shot in self.data.values(): shot.setCut(cutwindow) @@ -120,14 +127,13 @@ class Survey(object): shot.setPickwindow(traceID, pickwin_used) shot.pickTraces(traceID, windowsize, folm, HosAic) # picker - # ++ TEST: set and check SNR before adding to distance bin ############################ shot.setSNR(traceID) #if shot.getSNR(traceID)[0] < snrthreshold: if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): shot.removePick(traceID) - # -- TEST: set and check SNR before adding to distance bin ############################ - - if shot.getFlag(traceID) is not 0: + + # set epp and lpp if SNR > 1 (else earllatepicker cant set values) + if shot.getSNR(traceID)[0] > 1: shot.setEarllatepick(traceID) tpicksum += (datetime.now() - tstartpick); tpick = tpicksum/count @@ -137,6 +143,22 @@ class Survey(object): self._update_progress(shot.getShotname(), tend, progress) print('\npickAllShots: Finished\n') + def recover(self): + ''' + Recovers all (accidently) removed picks. Still regards SNR threshold. + ''' + print('Recovering survey...') + numpicks = 0 + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if shot.getFlag(traceID) == 0: + shot.setFlag(traceID, 1) + if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): + shot.removePick(traceID) + else: + numpicks += 1 + print('Recovered %d picks'%numpicks) + def setArtificialPick(self, traceID, pick): for shot in self.data.values(): shot.setPick(traceID, pick) @@ -256,49 +278,127 @@ class Survey(object): count += 1 return count - def plotAllPicks(self, plotDeleted = False): + def countAllPickedTraces(self): + count = 0 + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if shot.getFlag(traceID) is not 0: + count += 1 + return count + + def plotAllPicks(self, plotRemoved = False, ax = None): ''' - Plots all picks over the distance between source and receiver. Returns (ax, region) + Plots all picks over the distance between source and receiver. Returns (ax, region). + Picks can be checked and removed by using region class. + + Examples: + + region.chooseRectangles(): + - lets the user choose several rectangular regions in the plot + + region.plotTracesInRegions(): + - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) + + region.setActiveRegionsForDeletion(): + - highlights all shots in a the active regions for deletion + + region.deleteMarkedPicks(): + - deletes the picks (pick flag set to 0) for all shots set for deletion + + region.deselectSelection(number): + - deselects the region of number = number + ''' import matplotlib.pyplot as plt import math plt.interactive(True) from pylot.core.active.surveyPlotTools import regions + refreshPlot = False + + if ax is not None: refreshPlot = True dist = [] pick = [] snrloglist = [] for shot in self.data.values(): for traceID in shot.getTraceIDlist(): - if plotDeleted == False: + if plotRemoved == False: if shot.getFlag(traceID) is not 0: dist.append(shot.getDistance(traceID)) pick.append(shot.getPick(traceID)) snrloglist.append(math.log10(shot.getSNR(traceID)[0])) - elif plotDeleted == True: + elif plotRemoved == True: dist.append(shot.getDistance(traceID)) - pick.append(shot.getPick(traceID)) + pick.append(shot.getPickIncludeRemoved(traceID)) snrloglist.append(math.log10(shot.getSNR(traceID)[0])) - ax = self.createPlot(dist, pick, snrloglist, label = 'log10(SNR)') - region = regions(ax, self.data) - ax.legend() + if refreshPlot is False: + ax = self.createPlot(dist, pick, snrloglist, label = 'log10(SNR)') + region = regions(ax, self) + ax.legend() + return ax, region + elif refreshPlot is True: + ax = self.createPlot(dist, pick, snrloglist, label = 'log10(SNR)', ax = ax) + ax.legend() + return ax - return ax, region + def plotAllShots(self, rows = 3, columns = 4): + ''' + Plots all shots as Matrices with the color corresponding to the traveltime for each receiver. + NOTE: Topography (z - coordinate) is not considered in the diagrams! + ''' + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d import Axes3D + plt.interactive(True) - def createPlot(self, dist, pick, inkByVal, label): + fig = plt.figure() + ax = fig.add_subplot(111) + + figPerSubplot = columns * rows + + index = 1 + #shotnames = [] + #shotnumbers = [] + + # for shot in self.data.values(): + # shotnames.append(shot.getShotname()) + # shotnumbers.append(shot.getShotnumber()) + + # shotnumbers = [shotnumbers for (shotnumbers, shotnames) in sorted(zip(shotnumbers, shotnames))] + + for shotnumber in self.getShotlist(): + if index <= figPerSubplot: + #ax = fig.add_subplot(3,3,i, projection = '3d', title = 'shot:' + #+str(shot_dict[shotnumber].getShotnumber()), xlabel = 'X', ylabel = 'Y', zlabel = 'traveltime') + #shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) + ax = fig.add_subplot(3, 4, index) + self.getShot(shotnumber).matshow(ax = ax, colorbar = False, annotations = True) + index += 1 + if index > figPerSubplot: + fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1, wspace = 0, hspace = 0) + fig = plt.figure() + index = 1 + + fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1, wspace = 0, hspace = 0) + + def createPlot(self, dist, pick, inkByVal, label, ax = None): import matplotlib.pyplot as plt plt.interactive(True) cm = plt.cm.jet - fig = plt.figure() - ax = fig.add_subplot(111) - fig = ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) - cbar = plt.colorbar(fig, fraction = 0.05) - cbar.set_label(label) - plt.title('Plot of all Picks') - plt.xlabel('Distance [m]') - plt.ylabel('Time [s]') + if ax is None: + print('Generating new plot...') + fig = plt.figure() + ax = fig.add_subplot(111) + fig = ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) + cbar = plt.colorbar(fig, fraction = 0.05) + cbar.set_label(label) + plt.title('Plot of all Picks') + plt.xlabel('Distance [m]') + plt.ylabel('Time [s]') + else: + print('Refreshing plot...') + ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) return ax From cdd33d7e2f461cc513557e63c42ed74c5ddddb78 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Oct 2015 14:40:29 +0200 Subject: [PATCH 0616/1144] improvements, refreshFigure() now possible and done automatically after deleting of picks --- pylot/core/active/surveyPlotTools.py | 288 ++++++++++++++++++--------- 1 file changed, 192 insertions(+), 96 deletions(-) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index d7d8318a..f75762ac 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -2,47 +2,98 @@ import matplotlib.pyplot as plt plt.interactive(True) class regions(object): - def __init__(self, ax, shot_dict): + ''' + A class used for manual inspection and processing of all picks for the user. + + Examples: + + region.chooseRectangles(): + - lets the user choose several rectangular regions in the plot + + region.plotTracesInRegions(): + - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) + + region.setActiveRegionsForDeletion(): + - highlights all shots in a the active regions for deletion + + region.deleteMarkedPicks(): + - deletes the picks (pick flag set to 0) for all shots set for deletion + + region.deselectSelection(number): + - deselects the region of number = number + + ''' + def __init__(self, ax, survey): self.ax = ax - self.shot_dict = shot_dict + self.survey = survey + self.shot_dict = self.survey.getShotDict() self._x0 = [] self._y0 = [] self._x1 = [] self._y1 = [] + self._allpicks = None self.shots_found = {} self.shots_for_deletion = {} + self._generateList() def _onselect(self, eclick, erelease): - 'eclick and erelease are matplotlib events at press and release' #print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) - #print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) + 'eclick and erelease are matplotlib events at press and release' + #print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) + #print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)'%(eclick.xdata, eclick.ydata, erelease.xdata, erelease.ydata) x0 = min(eclick.xdata, erelease.xdata) x1 = max(eclick.xdata, erelease.xdata) y0 = min(eclick.ydata, erelease.ydata) y1 = max(eclick.ydata, erelease.ydata) - self._x0.append(x0) - self._x1.append(x1) - self._y0.append(y0) - self._y1.append(y1) - self.markCurrentRegion(x0, x1, y0, y1) + shots = self.findTracesInShotDict((x0, x1), (y0, y1)) + if self.shots_found.keys() == []: + key = 1 + else: + key = max(self.shots_found.keys()) + 1 + + self.shots_found[key] = {'shots': shots, + 'distbin': (x0, x1), + 'pickbin': (y0, y1)} + self.markRegion((x0, x1), (y0, y1), key) + def chooseRectangles(self): + ''' + Activates matplotlib widget RectangleSelector. + ''' from matplotlib.widgets import RectangleSelector print 'Select rectangle is active' return RectangleSelector(self.ax, self._onselect) - def _getx0(self): - return self._x0 + def deselectLastSelection(self): + if self.shots_found.keys() == []: + print('No selection found.') + return + key = max(self.shots_found.keys()) + self.deselectSelection(key) - def _getx1(self): - return self._x1 + def deselectSelection(self, key, color = 'green', alpha = 0.1): + try: + if color is not None: + self.markRegion(self.shots_found[key]['distbin'], + self.shots_found[key]['pickbin'], + key = key, color = color, alpha = alpha, linewidth = 0) + value = self.shots_found.pop(key) + print('Deselected selection number %d'% key) + return + except: + print('No selection found.') + return - def _gety0(self): - return self._y0 - - def _gety1(self): - return self._y1 + def _generateList(self): + allpicks = [] + for shot in self.shot_dict.values(): + for traceID in shot.getTraceIDlist(): + allpicks.append((shot.getDistance(traceID), shot.getPickIncludeRemoved(traceID), + shot.getShotnumber(), traceID, shot.getFlag(traceID))) + allpicks.sort() + self._allpicks = allpicks def getShotDict(self): return self.shot_dict @@ -50,119 +101,164 @@ class regions(object): def getShotsForDeletion(self): return self.shots_for_deletion - def findTracesInShotDict(self, picks = 'normal'): - ''' - Returns traces corresponding to a certain area in a plot with all picks over the distances. + def findTracesInShotDict(self, (x0, x1), (y0, y1), picks = 'normal'): ''' - print "findTracesInShotDict: Searching for marked traces in the shot dictionary... " + Returns traces corresponding to a certain area in the plot with all picks over the distances. + ''' + shots_found = {}; numtraces = 0 + if picks == 'normal': pickflag = 0 + elif picks == 'includeCutOut': pickflag = None - for shot in self.shot_dict.values(): - whichpicks = {'normal': shot.getPick, - 'includeCutOut': shot.getPick_backup} - for index in range(len(self._getx1())): - distancebin = (self._getx0()[index], self._getx1()[index]) - pickbin = (self._gety0()[index], self._gety1()[index]) - if shot.getTraceIDs4Dist(distancebin = distancebin) is not None: - for traceID in shot.getTraceIDs4Dist(distancebin = distancebin): - if pickbin[0] < whichpicks[picks](traceID) < pickbin[1]: - self.highlightPick(shot, traceID) - if shot.getShotnumber() not in self.shots_found.keys(): - self.shots_found[shot.getShotnumber()] = [] - if traceID not in self.shots_found[shot.getShotnumber()]: - self.shots_found[shot.getShotnumber()].append(traceID) - self.refreshFigure() - print self.shots_found + for line in self._allpicks: + dist, pick, shotnumber, traceID, flag = line + if flag == pickflag: continue ### IMPROVE THAT + if (x0 <= dist <= x1 and y0 <= pick <= y1): + if not shotnumber in shots_found.keys(): + shots_found[shotnumber] = [] + shots_found[shotnumber].append(traceID) + numtraces += 1 + + print('Found %d traces: %s' %(numtraces, shots_found)) + return shots_found def highlightPick(self, shot, traceID, annotations = True): + ''' + Highlights a single pick for a shot(object)/shotnumber and traceID. + If annotations == True: Displays shotnumber and traceID in the plot. + ''' + if type(shot) == int: + shot = self.survey.getShotDict()[shot] + self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s = 50, marker = 'o', facecolors = 'none', edgecolors = 'm', alpha = 1) if annotations == True: self.ax.annotate(s = 's%s|t%s'%(shot.getShotnumber(), traceID), xy = (shot.getDistance(traceID), shot.getPick(traceID)), fontsize = 'xx-small') self.ax.set_ylim(shot.getCut()) - def plotTracesInRegion(self): + def highlightAllRegions(self): + ''' + Highlights all picks in all active regions. + ''' + for key in self.shots_found.keys(): + for shotnumber in self.shots_found[key]['shots'].keys(): + for traceID in self.shots_found[key]['shots'][shotnumber]: + self.highlightPick(self.shot_dict[shotnumber], traceID) + self.drawFigure() + + def plotTracesInRegions(self, keys = 'all', maxfigures = 20): + ''' + Plots all traces in the active region or for all specified keys. + + :param: keys + :type: int or list + + :param: maxfigures, maximum value of figures opened + :type: int + ''' count = 0 - maxfigures = 20 - # if len(self.shots_found) == 0: - self.findTracesInShotDict() + if keys == 'all': + keys = self.shots_found.keys() + elif type(keys) == int: + keys = [keys] if len(self.shots_found) > 0: for shot in self.shot_dict.values(): - for shotnumber in self.shots_found: - if shot.getShotnumber() == shotnumber: - for traceID in self.shots_found[shotnumber]: - count += 1 - if count > maxfigures: - print 'Maximum number of figures (%s) reached. %sth figure was not opened.' %(maxfigures, count) - break - shot.plot_traces(traceID) + for key in keys: + for shotnumber in self.shots_found[key]['shots']: + if shot.getShotnumber() == shotnumber: + for traceID in self.shots_found[key]['shots'][shotnumber]: + count += 1 + if count > maxfigures: + print 'Maximum number of figures (%s) reached. %sth figure was not opened.' %(maxfigures, count) + break + shot.plot_traces(traceID) else: - print 'No picks yet defined in the regions x = (%s, %s), y = (%s, %s)' %(self._x0, self._x1, self._y0, self._y1) + print('No picks defined in that region(s)') - def plotTracesInRegion_withCutOutTraces(self): - count = 0 - maxfigures = 20 - # if len(self.shots_found) == 0: - self.findTracesInShotDict(picks = 'includeCutOut') + def setActiveRegionsForDeletion(self): + keys = [] + for key in self.shots_found.keys(): + keys.append(key) + self.setRegionForDeletion(keys) - if len(self.shots_found) > 0: - for shot in self.shot_dict.values(): - for shotnumber in self.shots_found: - if shot.getShotnumber() == shotnumber: - for traceID in self.shots_found[shotnumber]: - count += 1 - if count > maxfigures: - print 'Maximum number of figures (%s) reached. %sth figure was not opened.' %(maxfigures, count) - break - shot.plot_traces(traceID) - else: - print 'No picks yet defined in the regions x = (%s, %s), y = (%s, %s)' %(self._x0, self._x1, self._y0, self._y1) - + def setRegionForDeletion(self, keys): + if type(keys) == int: + keys = [keys] - def setCurrentRegionsForDeletion(self): - # if len(self.shots_found) == 0: - self.findTracesInShotDict() + for key in keys: + for shotnumber in self.shots_found[key]['shots'].keys(): + if not shotnumber in self.shots_for_deletion: + self.shots_for_deletion[shotnumber] = [] + for traceID in self.shots_found[key]['shots'][shotnumber]: + if not traceID in self.shots_for_deletion[shotnumber]: + self.shots_for_deletion[shotnumber].append(traceID) + self.deselectSelection(key, color = 'red', alpha = 0.2) - for shotnumber in self.shots_found: - if not shotnumber in self.shots_for_deletion: - self.shots_for_deletion[shotnumber] = [] - for traceID in self.shots_found[shotnumber]: - if not traceID in self.shots_for_deletion[shotnumber]: - self.shots_for_deletion[shotnumber].append(traceID) - self.markAllRegions(color = 'red') - print 'Marked regions for deletion' + print 'Set region(s) %s for deletion'%keys - def markAllRegions(self, color = 'grey'): + def markAllActiveRegions(self): + for key in self.shots_found.keys(): + self.markRegion(self.shots_found[key]['distbin'], + self.shots_found[key]['pickbin'], key = key) + + + def markRegion(self, (x0, x1), (y0, y1), key = None, color = 'grey', alpha = 0.1, linewidth = 0.1): + ''' + Mark a rectangular region on the axes. + ''' from matplotlib.patches import Rectangle - for index in range(len(self._getx0())): - x0 = self._getx0()[index] - y0 = self._gety0()[index] - x1 = self._getx1()[index] - y1 = self._gety1()[index] + self.ax.add_patch(Rectangle((x0, y0), (x1 - x0), (y1 - y0), + alpha = alpha, facecolor = color, linewidth = linewidth)) + if key is not None: + self.ax.text((x0 + (x1 - x0) / 2), (y0 + (y1 - y0) / 2), str(key)) + self.drawFigure() - self.ax.add_patch(Rectangle((x0, y0), (x1 - x0), (y1 - y0), alpha=0.5, facecolor = color)) - self.refreshFigure() + def refreshFigure(self): + print('Refreshing figure...') + self.ax.clear() + self.ax = self.survey.plotAllPicks(ax = self.ax) + self.markAllActiveRegions() + self.drawFigure() + print('Done!') - def markCurrentRegion(self, x0, x1, y0, y1, color = 'grey'): - from matplotlib.patches import Rectangle - - self.ax.add_patch(Rectangle((x0, y0), (x1 - x0), (y1 - y0), alpha=0.1, facecolor = color)) - self.refreshFigure() + def clearShotsForDeletion(self): + ''' + Clears the list of shots marked for deletion. + ''' + self.shots_for_deletion = {} + print('Cleared all shots that were set for deletion.') + def getShotsForDeletion(self): + return self.shots_for_deletion + def deleteMarkedPicks(self): + ''' + Deletes all shots set for deletion. + ''' + if len(self.getShotsForDeletion()) is 0: + print('No shots set for deletion.') + return + for shot in self.getShotDict().values(): for shotnumber in self.getShotsForDeletion(): if shot.getShotnumber() == shotnumber: for traceID in self.getShotsForDeletion()[shotnumber]: shot.removePick(traceID) print "Deleted the pick for traceID %s on shot number %s" %(traceID, shotnumber) - self.shots_for_deletion = {} # clear dictionary + self.clearShotsForDeletion() + self.refreshFigure() def highlightPicksForShot(self, shot, annotations = False): + ''' + Highlight all picks for a given shot. + ''' + if type(shot) is int: + shot = self.survey.getShotDict()[shotnumber] + for traceID in shot.getTraceIDlist(): if shot.getFlag(traceID) is not 0: self.highlightPick(shot, traceID, annotations) - self.refreshFigure() + self.drawFigure() - def refreshFigure(self): + def drawFigure(self): plt.draw() From ddbfb03f2769a32d7f0b33a5287a712a48639d75 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Oct 2015 15:10:53 +0200 Subject: [PATCH 0617/1144] *** empty log message *** --- pylot/core/active/surveyPlotTools.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index f75762ac..70a64e5e 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -7,19 +7,19 @@ class regions(object): Examples: - region.chooseRectangles(): + regions.chooseRectangles(): - lets the user choose several rectangular regions in the plot - region.plotTracesInRegions(): + regions.plotTracesInRegions(): - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) - region.setActiveRegionsForDeletion(): + regions.setActiveRegionsForDeletion(): - highlights all shots in a the active regions for deletion - region.deleteMarkedPicks(): + regions.deleteMarkedPicks(): - deletes the picks (pick flag set to 0) for all shots set for deletion - region.deselectSelection(number): + regions.deselectSelection(number): - deselects the region of number = number ''' @@ -216,7 +216,7 @@ class regions(object): def refreshFigure(self): print('Refreshing figure...') self.ax.clear() - self.ax = self.survey.plotAllPicks(ax = self.ax) + self.ax = self.survey.plotAllPicks(ax = self.ax, refreshPlot = True) self.markAllActiveRegions() self.drawFigure() print('Done!') From 07395802b7789bbef01fab6585e2072c45724f0f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Oct 2015 15:13:08 +0200 Subject: [PATCH 0618/1144] minor changes (plotAllPicks: inkByVal) --- pylot/core/active/activeSeismoPick.py | 125 ++++++++++++++------------ 1 file changed, 66 insertions(+), 59 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 44a863c0..a48c6eb1 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -286,66 +286,10 @@ class Survey(object): count += 1 return count - def plotAllPicks(self, plotRemoved = False, ax = None): - ''' - Plots all picks over the distance between source and receiver. Returns (ax, region). - Picks can be checked and removed by using region class. - - Examples: - - region.chooseRectangles(): - - lets the user choose several rectangular regions in the plot - - region.plotTracesInRegions(): - - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) - - region.setActiveRegionsForDeletion(): - - highlights all shots in a the active regions for deletion - - region.deleteMarkedPicks(): - - deletes the picks (pick flag set to 0) for all shots set for deletion - - region.deselectSelection(number): - - deselects the region of number = number - - ''' - import matplotlib.pyplot as plt - import math - plt.interactive(True) - from pylot.core.active.surveyPlotTools import regions - refreshPlot = False - - if ax is not None: refreshPlot = True - - dist = [] - pick = [] - snrloglist = [] - for shot in self.data.values(): - for traceID in shot.getTraceIDlist(): - if plotRemoved == False: - if shot.getFlag(traceID) is not 0: - dist.append(shot.getDistance(traceID)) - pick.append(shot.getPick(traceID)) - snrloglist.append(math.log10(shot.getSNR(traceID)[0])) - elif plotRemoved == True: - dist.append(shot.getDistance(traceID)) - pick.append(shot.getPickIncludeRemoved(traceID)) - snrloglist.append(math.log10(shot.getSNR(traceID)[0])) - - if refreshPlot is False: - ax = self.createPlot(dist, pick, snrloglist, label = 'log10(SNR)') - region = regions(ax, self) - ax.legend() - return ax, region - elif refreshPlot is True: - ax = self.createPlot(dist, pick, snrloglist, label = 'log10(SNR)', ax = ax) - ax.legend() - return ax - def plotAllShots(self, rows = 3, columns = 4): ''' Plots all shots as Matrices with the color corresponding to the traveltime for each receiver. - NOTE: Topography (z - coordinate) is not considered in the diagrams! + IMPORTANT NOTE: Topography (z - coordinate) is not considered in the diagrams! ''' import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D @@ -381,11 +325,75 @@ class Survey(object): fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1, wspace = 0, hspace = 0) + def plotAllPicks(self, plotRemoved = False, colorByVal = 'log10SNR', ax = None, refreshPlot = False): + ''' + Plots all picks over the distance between source and receiver. Returns (ax, region). + Picks can be checked and removed by using region class (pylot.core.active.surveyPlotTools.regions) + + :param: plotRemoved, if True plots traces that were picked but removed from the survey (flag = 0) + :type: logical + + :param: colorByVal, can be "log10SNR", "pickerror", or "spe" + :type: str + + Examples: + + regions.chooseRectangles(): + - lets the user choose several rectangular regions in the plot + + regions.plotTracesInRegions(): + - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) + + regions.setActiveRegionsForDeletion(): + - highlights all shots in a the active regions for deletion + + regions.deleteMarkedPicks(): + - deletes the picks (pick flag set to 0) for all shots set for deletion + + regions.deselectSelection(number): + - deselects the region of number = number + + ''' + + import matplotlib.pyplot as plt + import math + plt.interactive(True) + from pylot.core.active.surveyPlotTools import regions + + dist = [] + pick = [] + snrlog = [] + pickerror = [] + spe = [] + + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if plotRemoved == False: + if shot.getFlag(traceID) is not 0 or plotRemoved == True: + dist.append(shot.getDistance(traceID)) + pick.append(shot.getPick(traceID)) + snrlog.append(math.log10(shot.getSNR(traceID)[0])) + pickerror.append(shot.getPickError(traceID)) + spe.append(shot.getSymmetricPickError(traceID)) + + color = {'log10SNR': snrlog, + 'pickerror': pickerror, + 'spe': spe} + + if refreshPlot is False: + ax = self.createPlot(dist, pick, color[colorByVal], label = '%s'%colorByVal) + region = regions(ax, self) + ax.legend() + return ax, region + elif refreshPlot is True: + ax = self.createPlot(dist, pick, color[colorByVal], label = '%s'%colorByVal, ax = ax) + ax.legend() + return ax + def createPlot(self, dist, pick, inkByVal, label, ax = None): import matplotlib.pyplot as plt plt.interactive(True) cm = plt.cm.jet - if ax is None: print('Generating new plot...') fig = plt.figure() @@ -397,7 +405,6 @@ class Survey(object): plt.xlabel('Distance [m]') plt.ylabel('Time [s]') else: - print('Refreshing plot...') ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) return ax From d78b0f1cff4228bc8eecd5286659567ed58a7d53 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 13 Oct 2015 18:41:55 +0200 Subject: [PATCH 0619/1144] added polygon selection!! --- pylot/core/active/surveyPlotTools.py | 189 +++++++++++++++++++++++---- 1 file changed, 166 insertions(+), 23 deletions(-) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index 70a64e5e..33b21812 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -1,4 +1,6 @@ import matplotlib.pyplot as plt +import math +import numpy as np plt.interactive(True) class regions(object): @@ -31,12 +33,14 @@ class regions(object): self._y0 = [] self._x1 = [] self._y1 = [] + self._polyx = [] + self._polyy = [] self._allpicks = None self.shots_found = {} self.shots_for_deletion = {} self._generateList() - def _onselect(self, eclick, erelease): + def _onselect_clicks(self, eclick, erelease): 'eclick and erelease are matplotlib events at press and release' #print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) #print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) @@ -46,26 +50,107 @@ class regions(object): y0 = min(eclick.ydata, erelease.ydata) y1 = max(eclick.ydata, erelease.ydata) - shots = self.findTracesInShotDict((x0, x1), (y0, y1)) + shots, numtraces = self.findTracesInShotDict((x0, x1), (y0, y1)) + print('Found %d traces in rectangle: %s' %(numtraces, shots)) + + key = self.getKey() + self.shots_found[key] = {'shots': shots, + 'selection': 'rect', + 'xvalues': (x0, x1), + 'yvalues': (y0, y1)} + self.markRectangle((x0, x1), (y0, y1), key) + + def _onselect_verts(self, verts): + x = verts[0][0] + y = verts[0][1] + self._polyx.append(x) + self._polyy.append(y) + + self.drawPolyLine() + + def _onpress(self, event): + if event.button == 3: + self.disconnectPoly() + + def getKey(self): if self.shots_found.keys() == []: key = 1 else: key = max(self.shots_found.keys()) + 1 + return key + def drawPolyLine(self): + x = self._polyx + y = self._polyy + if len(x) >= 2 and len(y) >= 2: + plt.plot(x[-2:], y[-2:], 'k') + + def drawLastPolyLine(self): + x = self._polyx + y = self._polyy + if len(x) >= 2 and len(y) >= 2: + plt.plot((x[-1], x[0]), (y[-1], y[0]), 'k') + + def finishPolygon(self): + self.drawLastPolyLine() + x = self._polyx + y = self._polyy + self._polyx = []; self._polyy = [] + + key = self.getKey() + self.markPolygon(x, y, key = key) + + shots, numtraces = self.findTracesInPoly(x, y) self.shots_found[key] = {'shots': shots, - 'distbin': (x0, x1), - 'pickbin': (y0, y1)} - self.markRegion((x0, x1), (y0, y1), key) - + 'selection': 'poly', + 'xvalues': x, + 'yvalues': y} + + print('Found %d traces in polygon: %s' %(numtraces, shots)) + + def markPolygon(self, x, y, key = None, color = 'grey', alpha = 0.1, linewidth = 1): + from matplotlib.patches import Polygon + poly = Polygon(np.array(zip(x, y)), color = color, alpha = alpha, lw = linewidth) + self.ax.add_patch(poly) + if key is not None: + self.ax.text((min(x) + (max(x) - min(x)) / 2), (min(y) + (max(y) - min(y)) / 2), str(key)) + self.drawFigure() + + def disconnectPoly(self): + self.ax.figure.canvas.mpl_disconnect(self._cid) + del self._cid + self.finishPolygon() + self._lasso.disconnect_events() + print('disconnected poly selection\n') + + def disconnectRect(self): + self.ax.figure.canvas.mpl_disconnect(self._cid) + del self._cid + self._rectangle.disconnect_events() + print('disconnected rectangle selection\n') + def chooseRectangles(self): ''' Activates matplotlib widget RectangleSelector. ''' from matplotlib.widgets import RectangleSelector - print 'Select rectangle is active' - return RectangleSelector(self.ax, self._onselect) + print('Select rectangle is active') + self._cid = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) + self._rectangle = RectangleSelector(self.ax, self._onselect_clicks) + return self._rectangle + def choosePolygon(self): + ''' + Activates matplotlib widget LassoSelector. + ''' + from matplotlib.widgets import LassoSelector + + print('Select polygon is active') + self._cid = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) + self._lasso = LassoSelector(self.ax, self._onselect_verts) + return self._lasso + def deselectLastSelection(self): if self.shots_found.keys() == []: print('No selection found.') @@ -74,17 +159,23 @@ class regions(object): self.deselectSelection(key) def deselectSelection(self, key, color = 'green', alpha = 0.1): - try: - if color is not None: - self.markRegion(self.shots_found[key]['distbin'], - self.shots_found[key]['pickbin'], - key = key, color = color, alpha = alpha, linewidth = 0) - value = self.shots_found.pop(key) - print('Deselected selection number %d'% key) - return - except: + if not key in self.shots_found.keys(): print('No selection found.') return + if color is not None: + if self.shots_found[key]['selection'] == 'rect': + self.markRectangle(self.shots_found[key]['xvalues'], + self.shots_found[key]['yvalues'], + key = key, color = color, alpha = alpha, + linewidth = 1) + elif self.shots_found[key]['selection'] == 'poly': + self.markPolygon(self.shots_found[key]['xvalues'], + self.shots_found[key]['yvalues'], + key = key, color = color, alpha = alpha, + linewidth = 1) + value = self.shots_found.pop(key) + print('Deselected selection number %d'% key) + return def _generateList(self): allpicks = [] @@ -101,7 +192,53 @@ class regions(object): def getShotsForDeletion(self): return self.shots_for_deletion - def findTracesInShotDict(self, (x0, x1), (y0, y1), picks = 'normal'): + def findTracesInPoly(self, x, y, picks = 'normal', highlight = True): + def dotproduct(v1, v2): + return sum((a*b) for a, b in zip(v1, v2)) + + def getlength(v): + return math.sqrt(dotproduct(v, v)) + + def getangle(v1, v2): + return np.rad2deg(math.acos(dotproduct(v1, v2) / (getlength(v1) * getlength(v2)))) + + def insidePoly(x, y, pickX, pickY): + angle = 0 + epsilon = 10e-8 + for index in range(len(x)): + xval1 = x[index - 1]; yval1 = y[index - 1] + xval2 = x[index]; yval2 = y[index] + angle += getangle([xval1 - pickX, yval1 - pickY], [xval2 - pickX, yval2 - pickY]) + if 360 - epsilon <= angle <= 360 + epsilon: ### IMPROVE THAT?? + return True + + if len(x) == 0 or len(y) == 0: + print('No polygon defined.') + return + + shots_found = {}; numtraces = 0 + x0 = min(x); x1 = max(x) + y0 = min(y); y1 = max(y) + + shots, numtracesrect = self.findTracesInShotDict((x0, x1), (y0, y1), highlight = False) + for shotnumber in shots.keys(): + shot = self.shot_dict[shotnumber] + for traceID in shots[shotnumber]: + if shot.getFlag(traceID) is not 0: + pickX = shot.getDistance(traceID) + pickY = shot.getPick(traceID) + if insidePoly(x, y, pickX, pickY): + if not shotnumber in shots_found.keys(): + shots_found[shotnumber] = [] + shots_found[shotnumber].append(traceID) + if highlight == True: + self.highlightPick(shot, traceID) + numtraces += 1 + + self.drawFigure() + return shots_found, numtraces + + def findTracesInShotDict(self, (x0, x1), (y0, y1), picks = 'normal', highlight = True): ''' Returns traces corresponding to a certain area in the plot with all picks over the distances. ''' @@ -116,10 +253,12 @@ class regions(object): if not shotnumber in shots_found.keys(): shots_found[shotnumber] = [] shots_found[shotnumber].append(traceID) + if highlight == True: + self.highlightPick(self.shot_dict[shotnumber], traceID) numtraces += 1 - print('Found %d traces: %s' %(numtraces, shots_found)) - return shots_found + self.drawFigure() + return shots_found, numtraces def highlightPick(self, shot, traceID, annotations = True): ''' @@ -197,11 +336,15 @@ class regions(object): def markAllActiveRegions(self): for key in self.shots_found.keys(): - self.markRegion(self.shots_found[key]['distbin'], - self.shots_found[key]['pickbin'], key = key) + if self.shots_found[key]['selection'] == 'rect': + self.markRectangle(self.shots_found[key]['xvalues'], + self.shots_found[key]['yvalues'], key = key) + if self.shots_found[key]['selection'] == 'poly': + self.markPolygon(self.shots_found[key]['xvalues'], + self.shots_found[key]['yvalues'], key = key) - def markRegion(self, (x0, x1), (y0, y1), key = None, color = 'grey', alpha = 0.1, linewidth = 0.1): + def markRectangle(self, (x0, x1), (y0, y1), key = None, color = 'grey', alpha = 0.1, linewidth = 1): ''' Mark a rectangular region on the axes. ''' From b49206407ac3e10b964189f8f4067225838e5255 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sun, 18 Oct 2015 21:23:09 +0200 Subject: [PATCH 0620/1144] Merge using remote to resolve conflicts --- pylot/core/active/seismicshot.py | 107 ++++++++++++----------- pylot/core/active/surveyUtils.py | 143 +++++++++++++++++++++++++++++-- 2 files changed, 194 insertions(+), 56 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index ecb602c2..df623479 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -44,14 +44,14 @@ class SeismicShot(object): removed = [] for i in range(0, len(coordlist)): traceIDs.append(int(coordlist[i].split()[0])) - + for trace in self.traces: try: traceIDs.index(int(trace.stats.channel)) except: - self.traces.remove(trace) + self.traces.remove(trace) removed.append(int(trace.stats.channel)) - + if len(removed) > 0: return removed @@ -59,7 +59,7 @@ class SeismicShot(object): for trace in self.traces: if traceID == trace.stats.channel: self.traces.remove(trace) - + # for traceID in TraceIDs: # traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] # if len(traces) is not 1: @@ -147,10 +147,10 @@ class SeismicShot(object): def getPickError(self, traceID): pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) - if np.isnan(pickerror) == True: + if np.isnan(pickerror) == True: print("SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID)) - return pickerror - + return pickerror + def getStreamTraceIDs(self): traceIDs = [] for trace in self.traces: @@ -172,15 +172,15 @@ class SeismicShot(object): def getPickwindow(self, traceID): try: - self.pickwindow[traceID] + self.pickwindow[traceID] except KeyError as e: print('no pickwindow for trace %s, set to %s' % (traceID, self.getCut())) self.setPickwindow(traceID, self.getCut()) return self.pickwindow[traceID] - + def getSNR(self, traceID): return self.snr[traceID] - + def getSNRthreshold(self, traceID): return self.snrthreshold[traceID] @@ -257,16 +257,20 @@ class SeismicShot(object): else: self.setPick(traceID, None) print('Warning: ambigious or empty traceID: %s' % traceID) - + #raise ValueError('ambigious or empty traceID: %s' % traceID) - - def pickTraces(self, traceID, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## + + def pickTraces(self, traceID, pickmethod, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## + # LOCALMAX NOT IMPLEMENTED! ''' Intitiate picking for a trace. :param: traceID :type: int + :param: pickmethod, use either 'threshold' or 'localmax' method. (localmax not yet implemented 04_08_15) + :type: string + :param: cutwindow (equals HOScf 'cut' variable) :type: tuple @@ -290,7 +294,13 @@ class SeismicShot(object): self.timeArray[traceID] = hoscf.getTimeArray() - aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) + if pickmethod == 'threshold': + aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) + + #setpick = {'threshold':self.threshold, + # 'localmax':self.localmax} + + #aiccftime, hoscftime = setpick[pickmethod](hoscf, aiccf, windowsize, pickwindow) setHosAic = {'hos': hoscftime, 'aic': aiccftime} @@ -303,15 +313,15 @@ class SeismicShot(object): tsignal = self.getTsignal() tnoise = self.getPick(traceID) - tgap - (self.earliest[traceID], self.latest[traceID], tmp) = earllatepicker(self.getSingleStream(traceID), - nfac, (tnoise, tgap, tsignal), + (self.earliest[traceID], self.latest[traceID], tmp) = earllatepicker(self.getSingleStream(traceID), + nfac, (tnoise, tgap, tsignal), self.getPick(traceID)) def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm = 0.6): ''' - Threshold picker, using the local maximum in a pickwindow to find the time at + Threshold picker, using the local maximum in a pickwindow to find the time at which a fraction of the local maximum is reached for the first time. - + :param: hoscf, Higher Order Statistics Characteristic Function :type: 'Characteristic Function' @@ -337,34 +347,34 @@ class SeismicShot(object): threshold = folm * max(hoscflist[leftb : rightb]) # combination of local maximum and threshold m = leftb - + while hoscflist[m] < threshold: m += 1 - + hoscftime = list(hoscf.getTimeArray())[m] lb = max(0, m - windowsize[0]) # if window exceeds t = 0 aiccfcut = list(aiccf.getCF())[lb : m + windowsize[1]] n = aiccfcut.index(min(aiccfcut)) - + m = lb + n - + aiccftime = list(hoscf.getTimeArray())[m] - + return aiccftime, hoscftime def getDistance(self, traceID): ''' Returns the distance of the receiver with the ID == traceID to the source location (shot location). Uses getSrcLoc and getRecLoc. - + :param: traceID :type: int ''' shotX, shotY, shotZ = self.getSrcLoc() recX, recY, recZ = self.getRecLoc(traceID) dist = np.sqrt((shotX-recX)**2 + (shotY-recY)**2 + (shotZ-recZ)**2) - + if np.isnan(dist) == True: raise ValueError("Distance is NaN for traceID %s" %traceID) @@ -375,7 +385,7 @@ class SeismicShot(object): ''' Returns the location (x, y, z) of the receiver with the ID == traceID. RECEIVEIVER FILE MUST BE SET FIRST, TO BE IMPROVED. - + :param: traceID :type: int ''' @@ -389,7 +399,7 @@ class SeismicShot(object): y = coordlist[i].split()[2] z = coordlist[i].split()[3] return float(x), float(y), float(z) - + #print "WARNING: traceID %s not found" % traceID raise ValueError("traceID %s not found" % traceID) #return float(self.getSingleStream(traceID)[0].stats.seg2['RECEIVER_LOCATION']) @@ -412,7 +422,7 @@ class SeismicShot(object): ''' Returns the traceID(s) for a certain distance between source and receiver. Used for 2D Tomography. TO BE IMPROVED. - + :param: distance :type: real @@ -437,7 +447,7 @@ class SeismicShot(object): def setManualPicks(self, traceID, picklist): ########## picklist momentan nicht allgemein, nur testweise benutzt ########## ''' Sets the manual picks for a receiver with the ID == traceID for comparison. - + :param: traceID :type: int @@ -452,15 +462,15 @@ class SeismicShot(object): if not self.manualpicks.has_key(traceID): self.manualpicks[traceID] = (mostlikely, earliest, latest) #else: - # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) - + # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) + def setPick(self, traceID, pick): ########## siehe Kommentar ########## self.pick[traceID] = pick # ++++++++++++++ Block raus genommen, da Error beim 2ten Mal picken! (Ueberschreiben von erstem Pick!) # if not self.pick.has_key(traceID): # self.getPick(traceID) = picks # else: - # raise KeyError('pick to be set more than once for traceID %s' % traceID) + # raise KeyError('pick to be set more than once for traceID %s' % traceID) # def readParameter(self, parfile): # parlist = open(parfile,'r').readlines() @@ -474,12 +484,13 @@ class SeismicShot(object): def setSNR(self, traceID): ########## FORCED HOS PICK ########## ''' Gets the SNR using pylot and then sets the SNR for the traceID. - + :param: traceID :type: int :param: (tnoise, tgap, tsignal), as used in pylot SNR ''' + from pylot.core.pick.utils import getSNR tgap = self.getTgap() @@ -509,7 +520,7 @@ class SeismicShot(object): # def plot2dttc(self, dist_med = 0): ########## 2D ########## # ''' # Function to plot the traveltime curve for automated picks (AIC & HOS) of a shot. 2d only! - + # :param: dist_med (optional) # :type: 'dictionary' # ''' @@ -517,7 +528,7 @@ class SeismicShot(object): # plt.interactive('True') # aictimearray = [] # hostimearray = [] - # if dist_med is not 0: + # if dist_med is not 0: # dist_medarray = [] # i = 1 @@ -573,14 +584,14 @@ class SeismicShot(object): import matplotlib.pyplot as plt from pylot.core.util.utils import getGlobalTimes from pylot.core.util.utils import prepTimeAxis - + stream = self.getSingleStream(traceID) stime = getGlobalTimes(stream)[0] - timeaxis = prepTimeAxis(stime, stream[0]) + timeaxis = prepTimeAxis(stime, stream[0]) timeaxis -= stime plt.interactive('True') - + hoscf = self.getHOScf(traceID) aiccf = self.getAICcf(traceID) @@ -588,16 +599,16 @@ class SeismicShot(object): ax1 = plt.subplot(2,1,1) plt.title('Shot: %s, traceID: %s, pick: %s' %(self.getShotnumber(), traceID, self.getPick(traceID))) ax1.plot(timeaxis, stream[0].data, 'k', label = 'trace') - ax1.plot([self.getPick(traceID), self.getPick(traceID)], - [min(stream[0].data), + ax1.plot([self.getPick(traceID), self.getPick(traceID)], + [min(stream[0].data), max(stream[0].data)], 'r', label = 'mostlikely') plt.legend() ax2 = plt.subplot(2,1,2, sharex = ax1) ax2.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label = 'HOS') ax2.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label = 'AIC') - ax2.plot([self.getPick(traceID), self.getPick(traceID)], - [min(np.minimum(hoscf.getCF(), aiccf.getCF())), + ax2.plot([self.getPick(traceID), self.getPick(traceID)], + [min(np.minimum(hoscf.getCF(), aiccf.getCF())), max(np.maximum(hoscf.getCF(), aiccf.getCF()))], 'r', label = 'mostlikely') ax2.plot([0, self.getPick(traceID)], @@ -605,7 +616,7 @@ class SeismicShot(object): 'm:', label = 'folm = %s' %folm) plt.xlabel('Time [s]') plt.legend() - + def plot3dttc(self, step = 0.5, contour = False, plotpicks = False, method = 'linear', ax = None): ''' Plots a 3D 'traveltime cone' as surface plot by interpolating on a regular grid over the traveltimes, not yet regarding the vertical offset of the receivers. @@ -644,7 +655,7 @@ class SeismicShot(object): if ax == None: fig = plt.figure() ax = plt.axes(projection = '3d') - + xsrc, ysrc, zsrc = self.getSrcLoc() if contour == True: @@ -656,12 +667,12 @@ class SeismicShot(object): if plotpicks == True: ax.plot(x, y, z, 'k.') - + def plotttc(self, method, *args): plotmethod = {'2d': self.plot2dttc, '3d': self.plot3dttc} - + plotmethod[method](*args) - + def matshow(self, step = 0.5, method = 'linear', ax = None, plotRec = False, annotations = False): ''' Plots a 2D matrix of the interpolated traveltimes. This needs less performance than plot3dttc @@ -701,7 +712,7 @@ class SeismicShot(object): ax = plt.axes() ax.imshow(zgrid, interpolation = 'none', extent = [min(x), max(x), min(y), max(y)]) - + if annotations == True: for i, traceID in enumerate(self.pick.keys()): if shot.picks[traceID] != None: diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 909fed13..9fedd432 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -1,19 +1,69 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import numpy as np +from pylab import * +startpos = [] +endpos = [] + +def generateSurvey(obsdir, shotlist): + from obspy.core import read + from pylot.core.active import seismicshot + + shot_dict = {} + for shotnumber in shotlist: # loop over data files + # generate filenames and read manual picks to a list + obsfile = obsdir + str(shotnumber) + '_pickle.dat' + #obsfile = obsdir + str(shotnumber) + '.dat' + + if not obsfile in shot_dict.keys(): + shot_dict[shotnumber] = [] + shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) + shot_dict[shotnumber].setParameters('shotnumber', shotnumber) + + return shot_dict + +def setParametersForShots(cutwindow, tmovwind, tsignal, tgap, receiverfile, sourcefile, shot_dict): + for shot in shot_dict.values(): + shot.setCut(cutwindow) + shot.setTmovwind(tmovwind) + shot.setTsignal(tsignal) + shot.setTgap(tgap) + shot.setRecfile(receiverfile) + shot.setSourcefile(sourcefile) + shot.setOrder(order = 4) + +def removeEmptyTraces(shot_dict): + filename = 'removeEmptyTraces.out' + filename2 = 'updateTraces.out' + outfile = open(filename, 'w') + outfile2 = open(filename2, 'w') + for shot in shot_dict.values(): + del_traceIDs = shot.updateTraceList() + removed = shot.removeEmptyTraces() + if removed is not None: + outfile.writelines('shot: %s, removed empty traces: %s\n' %(shot.getShotnumber(), removed)) + outfile2.writelines('shot: %s, removed traceID(s) %s because they were not found in the corresponding stream\n' %(shot.getShotnumber(), del_traceIDs)) + print '\nremoveEmptyTraces, updateTraces: Finished! See %s and %s for more information of removed traces.\n' %(filename, filename2) + outfile.close() + outfile2.close() + def readParameters(parfile, parameter): from ConfigParser import ConfigParser parameterConfig = ConfigParser() parameterConfig.read('parfile') - - value = parameterConfig.get('vars', parameter).split('#')[0] - value = value.replace(" ", "") + + value = parameterConfig.get('vars', parameter).split('\t')[0] return value +def setArtificialPick(shot_dict, traceID, pick): + for shot in shot_dict.values(): + shot.setPick(traceID, pick) + shot.setPickwindow(traceID, shot.getCut()) + def fitSNR4dist(shot_dict, shiftdist = 5): + import numpy as np dists = [] picks = [] snrs = [] @@ -34,6 +84,7 @@ def fitSNR4dist(shot_dict, shiftdist = 5): plotFittedSNR(dists, snrthresholds, snrs) return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben + def plotFittedSNR(dists, snrthresholds, snrs): import matplotlib.pyplot as plt plt.interactive(True) @@ -45,25 +96,98 @@ def plotFittedSNR(dists, snrthresholds, snrs): plt.legend() def setFittedSNR(shot_dict, shiftdist = 5, p1 = 0.004, p2 = -0.004): + import numpy as np #fit_fn = fitSNR4dist(shot_dict) fit_fn = np.poly1d([p1, p2]) for shot in shot_dict.values(): for traceID in shot.getTraceIDlist(): ### IMPROVE shot.setSNRthreshold(traceID, 1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) ### s.o. - print "\nsetFittedSNR: Finished setting of fitted SNR-threshold" + print "setFittedSNR: Finished setting of fitted SNR-threshold" + +#def linearInterp(dist_med, dist_start + +def exportFMTOMO(shot_dict, directory = 'FMTOMO_export', sourcefile = 'input_sf.in', ttFileExtension = '.tt'): + count = 0 + fmtomo_factor = 1000 # transforming [m/s] -> [km/s] + LatAll = []; LonAll = []; DepthAll = [] + srcfile = open(directory + '/' + sourcefile, 'w') + srcfile.writelines('%10s\n' %len(shot_dict)) # number of sources + for shotnumber in getShotlist(shot_dict): + shot = getShotForShotnumber(shot_dict, shotnumber) + ttfilename = str(shotnumber) + ttFileExtension + (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) + srcfile.writelines('%10s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z)) # lat, lon, depth + LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) + srcfile.writelines('%10s\n' %1) # + srcfile.writelines('%10s %10s %10s\n' %(1, 1, ttfilename)) + ttfile = open(directory + '/' + ttfilename, 'w') + traceIDlist = shot.getTraceIDlist() + traceIDlist.sort() + ttfile.writelines(str(countPickedTraces(shot)) + '\n') + for traceID in traceIDlist: + if shot.getPick(traceID) is not None: + pick = shot.getPick(traceID) * fmtomo_factor + delta = shot.getPickError(traceID) * fmtomo_factor + (x, y, z) = shot.getRecLoc(traceID) + ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) + LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) + count += 1 + ttfile.close() + srcfile.close() + print 'Wrote output for %s traces' %count + print 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems to take Lat, Lon, Depth and creates output for FMTOMO as Depth, Lat, Lon' + print 'Dimensions of the seismic Array, transformed for FMTOMO, are Depth(%s, %s), Lat(%s, %s), Lon(%s, %s)'%( + min(DepthAll), max(DepthAll), min(LatAll), max(LatAll), min(LonAll), max(LonAll)) + +def getShotlist(shot_dict): + shotlist = [] + for shot in shot_dict.values(): + shotlist.append(shot.getShotnumber()) + shotlist.sort() + return shotlist + +def getShotForShotnumber(shot_dict, shotnumber): + for shot in shot_dict.values(): + if shot.getShotnumber() == shotnumber: + return shot + +def getAngle(distance): + ''' + Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. + ''' + import numpy as np + PI = np.pi + R = 6371. + angle = distance * 180 / (PI * R) + return angle + +def countPickedTraces(shot): + numtraces = 0 + for traceID in shot.getTraceIDlist(): + if shot.getPick(traceID) is not None: + numtraces += 1 + print "countPickedTraces: Found %s picked traces in shot number %s" %(numtraces, shot.getShotnumber()) + return numtraces + +def countAllPickedTraces(shot_dict): + traces = 0 + for shot in shot_dict.values(): + traces += countPickedTraces(shot) + return traces def findTracesInRanges(shot_dict, distancebin, pickbin): ''' Returns traces corresponding to a certain area in a plot with all picks over the distances. - + :param: shot_dict, dictionary containing all shots that are used :type: dictionary - + :param: distancebin :type: tuple, (dist1[m], dist2[m]) - + :param: pickbin :type: tuple, (t1[s], t2[s]) + ''' shots_found = {} for shot in shot_dict.values(): @@ -75,3 +199,6 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): shots_found[shot.getShotnumber()].append(traceID) return shots_found + + + From 0fa701a878fff6bd95bc872338f939335e82f67b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 19 Oct 2015 05:32:10 +0200 Subject: [PATCH 0621/1144] general code clean-up --- autoPyLoT.py | 2 +- icons.qrc | 8 +- pylot/core/__init__.py | 1 + pylot/core/active/__init__.py | 1 + pylot/core/active/activeSeismoPick.py | 29 ++-- pylot/core/active/fmtomo2vtk.py | 7 +- pylot/core/active/picking_script.py | 7 +- pylot/core/active/seismicArrayPreparation.py | 75 +++++------ pylot/core/active/seismicArrayPreperation.py | 75 +++++------ pylot/core/active/seismicshot.py | 3 +- pylot/core/active/surveyPlotTools.py | 11 +- pylot/core/analysis/__init__.py | 1 + pylot/core/analysis/coinctimes.py | 4 +- pylot/core/analysis/magnitude.py | 39 +++--- pylot/core/pick/CharFuns.py | 39 +++--- pylot/core/pick/Picker.py | 2 +- pylot/core/pick/__init__.py | 3 +- pylot/core/pick/autopick.py | 50 +++---- pylot/core/pick/utils.py | 133 ++++++++++--------- pylot/core/read/__init__.py | 2 +- pylot/core/read/inputs.py | 3 +- pylot/core/util/__init__.py | 1 + pylot/core/util/testUtils.py | 3 +- pylot/core/util/testWidgets.py | 3 +- pylot/core/util/thread.py | 1 + pylot/core/util/utils.py | 1 + testHelpForm.py | 1 + testPickDlg.py | 1 + testPropDlg.py | 3 +- testUIcomponents.py | 10 +- 30 files changed, 270 insertions(+), 249 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 3084f1ad..af61bcb1 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -8,11 +8,11 @@ import glob import matplotlib.pyplot as plt from obspy.core import read -from pylot.core.util import _getVersionString from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter from pylot.core.util.structure import DATASTRUCTURE from pylot.core.pick.autopick import autopickevent +from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() diff --git a/icons.qrc b/icons.qrc index efd43234..010efb1b 100644 --- a/icons.qrc +++ b/icons.qrc @@ -1,10 +1,10 @@ - icons/pylot.ico - icons/pylot.png + icons/pylot.ico + icons/pylot.png icons/printer.png icons/delete.png - icons/key_E.png + icons/key_E.png icons/key_N.png icons/key_P.png icons/key_Q.png @@ -14,7 +14,7 @@ icons/key_U.png icons/key_V.png icons/key_W.png - icons/key_Z.png + icons/key_Z.png icons/filter.png icons/sync.png icons/zoom_0.png diff --git a/pylot/core/__init__.py b/pylot/core/__init__.py index e69de29b..40a96afc 100755 --- a/pylot/core/__init__.py +++ b/pylot/core/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pylot/core/active/__init__.py b/pylot/core/active/__init__.py index ccbcc844..9ef8ae9f 100644 --- a/pylot/core/active/__init__.py +++ b/pylot/core/active/__init__.py @@ -1 +1,2 @@ +# -*- coding: utf-8 -*- __author__ = 'sebastianw' diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 50c1ddad..8e201b21 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys import numpy as np from pylot.core.active import seismicshot @@ -14,11 +15,11 @@ class Survey(object): self._sourcefile = sourcefile self._obsdir = path self._generateSurvey() - if useDefaultParas == True: + if useDefaultParas == True: self.setParametersForShots() self._removeAllEmptyTraces() self._updateShots() - + def _generateSurvey(self): from obspy.core import read @@ -65,7 +66,7 @@ class Survey(object): if removed is not None: if count == 0: outfile = open(filename, 'w') count += 1 - outfile.writelines('shot: %s, removed empty traces: %s\n' + outfile.writelines('shot: %s, removed empty traces: %s\n' %(shot.getShotnumber(), removed)) print ("\nremoveEmptyTraces: Finished! Removed %d traces" %count) if count > 0: @@ -83,7 +84,7 @@ class Survey(object): count += 1 countTraces += len(del_traceIDs) outfile.writelines("shot: %s, removed traceID(s) %s because " - "they were not found in the corresponding stream\n" + "they were not found in the corresponding stream\n" %(shot.getShotnumber(), del_traceIDs)) print ("\nupdateShots: Finished! Updated %d shots and removed " @@ -121,7 +122,7 @@ class Survey(object): shot.pickTraces(traceID, windowsize, folm, HosAic) # picker # ++ TEST: set and check SNR before adding to distance bin ############################ - shot.setSNR(traceID) + shot.setSNR(traceID) #if shot.getSNR(traceID)[0] < snrthreshold: if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): shot.removePick(traceID) @@ -144,8 +145,8 @@ class Survey(object): def countAllTraces(self): numtraces = 0 - for line in self.getShotlist(): - for line in self.getReceiverlist(): + for shot in self.getShotlist(): + for rec in self.getReceiverlist(): numtraces += 1 return numtraces @@ -180,7 +181,7 @@ class Survey(object): def getReceiverfile(self): return self._recfile - + def getPath(self): return self._obsdir @@ -228,7 +229,7 @@ class Survey(object): (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) srcfile.writelines('%10s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z)) # lat, lon, depth LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) - srcfile.writelines('%10s\n' %1) # + srcfile.writelines('%10s\n' %1) # srcfile.writelines('%10s %10s %10s\n' %(1, 1, ttfilename)) ttfile = open(directory + '/' + ttfilename, 'w') traceIDlist = shot.getTraceIDlist() @@ -243,7 +244,7 @@ class Survey(object): LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) count += 1 ttfile.close() - srcfile.close() + srcfile.close() print 'Wrote output for %s traces' %count print 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems to take Lat, Lon, Depth and creates output for FMTOMO as Depth, Lat, Lon' print 'Dimensions of the seismic Array, transformed for FMTOMO, are Depth(%s, %s), Lat(%s, %s), Lon(%s, %s)'%( @@ -271,7 +272,7 @@ class Survey(object): for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if plotDeleted == False: - if shot.getPick(traceID) is not None: + if shot.getPick(traceID) is not None: dist.append(shot.getDistance(traceID)) pick.append(shot.getPick(traceID)) snrloglist.append(math.log10(shot.getSNR(traceID)[0])) @@ -303,7 +304,7 @@ class Survey(object): return ax def _update_progress(self, shotname, tend, progress): - sys.stdout.write("Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r" + sys.stdout.write("Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r" %(shotname, tend.hour, tend.minute, tend.second, progress)) sys.stdout.flush() @@ -313,8 +314,8 @@ class Survey(object): cPickle.dump(self, outfile, -1) print('saved Survey to file %s'%(filename)) - - @staticmethod + + @staticmethod def from_pickle(filename): import cPickle infile = open(filename, 'rb') diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index a4edf32d..c165b045 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import numpy as np def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): @@ -73,7 +74,7 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): dX = getDistance(np.rad2deg(dPhi)) dY = getDistance(np.rad2deg(dTheta)) - + nPoints = nX * nY * nZ dZ = dR @@ -114,7 +115,7 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): R = 6371. distance = angle / 180 * (PI * R) return distance - + infile = open(fnin, 'r') R = 6371 rays = {} @@ -124,7 +125,7 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): ### NOTE: rays.dat seems to be in km and radians while True: - raynumber += 1 + raynumber += 1 firstline = infile.readline() if firstline == '': break # break at EOF shotnumber = int(firstline.split()[1]) diff --git a/pylot/core/active/picking_script.py b/pylot/core/active/picking_script.py index fbb3ef62..6a92b61b 100644 --- a/pylot/core/active/picking_script.py +++ b/pylot/core/active/picking_script.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys from obspy import read from obspy import Stream @@ -28,7 +29,7 @@ if rockeskyll == True: obsdir = "/rscratch/minos22/marcel/flachseismik/rockeskyll_200615_270615/" filename = 'survey_rockes.pickle' else: - receiverfile = "Geophone_interpoliert_GZB" + receiverfile = "Geophone_interpoliert_GZB" sourcefile = "Schusspunkte_GZB" obsdir = "/rscratch/minos22/marcel/flachseismik/GZB_26_06_15_01/" filename = 'survey_GZB.pickle' @@ -85,9 +86,9 @@ for shot in survey.data.values(): shot.setPickwindow(traceID, pickwin_used) shot.pickTraces(traceID, windowsize, folm, HosAic) # picker #shot.setManualPicks(traceID, picklist) # set manual picks if given (yet used on 2D only) - + # ++ TEST: set and check SNR before adding to distance bin ############################ - shot.setSNR(traceID) + shot.setSNR(traceID) #if shot.getSNR(traceID)[0] < snrthreshold: if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): shot.removePick(traceID) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 65226423..01637b23 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys import numpy as np from scipy.interpolate import griddata @@ -28,12 +29,12 @@ class SeisArray(object): def _generateReceiverlines(self): ''' - Connects the traceIDs to the lineIDs + Connects the traceIDs to the lineIDs for each receiverline in a dictionary. ''' for receiver in self._receiverlist: - traceID = int(receiver.split()[0]) - lineID = int(receiver.split()[1]) + traceID = int(receiver.split()[0]) + lineID = int(receiver.split()[1]) if not lineID in self._receiverlines.keys(): self._receiverlines[lineID] = [] self._receiverlines[lineID].append(traceID) @@ -43,16 +44,16 @@ class SeisArray(object): Fills the three x, y, z dictionaries with measured coordinates ''' for line in self._getReceiverlist(): - traceID = int(line.split()[0]) - x = float(line.split()[3]) - y = float(line.split()[4]) - z = float(line.split()[5]) + traceID = int(line.split()[0]) + x = float(line.split()[3]) + y = float(line.split()[4]) + z = float(line.split()[5]) self._receiverCoords[traceID] = (x, y, z) self._measuredReceivers[traceID] = (x, y, z) def _setGeophoneNumbers(self): for line in self._getReceiverlist(): - traceID = int(line.split()[0]) + traceID = int(line.split()[0]) gphoneNum = float(line.split()[2]) self._geophoneNumbers[traceID] = gphoneNum @@ -93,7 +94,7 @@ class SeisArray(object): return self._geophoneNumbers[traceID] def getMeasuredReceivers(self): - return self._measuredReceivers + return self._measuredReceivers def getMeasuredTopo(self): return self._measuredTopo @@ -139,11 +140,11 @@ class SeisArray(object): if self._getReceiverValue(traceID1, coordinate) < self._getReceiverValue(traceID2, coordinate): direction = +1 return direction - if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): + if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): direction = -1 return direction print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) - + def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): ''' Returns the mean distance between two traceID's depending on the number of geophones in between @@ -186,7 +187,7 @@ class SeisArray(object): x = float(line[1]) y = float(line[2]) z = float(line[3]) - self._measuredTopo[pointID] = (x, y, z) + self._measuredTopo[pointID] = (x, y, z) def addSourceLocations(self, filename): ''' @@ -202,7 +203,7 @@ class SeisArray(object): x = float(line[1]) y = float(line[2]) z = float(line[3]) - self._sourceLocs[pointID] = (x, y, z) + self._sourceLocs[pointID] = (x, y, z) def interpZcoords4rec(self, method = 'linear'): ''' @@ -239,9 +240,9 @@ class SeisArray(object): ''' x = []; y = []; z = [] for traceID in self.getMeasuredReceivers().keys(): - x.append(self.getMeasuredReceivers()[traceID][0]) + x.append(self.getMeasuredReceivers()[traceID][0]) y.append(self.getMeasuredReceivers()[traceID][1]) - z.append(self.getMeasuredReceivers()[traceID][2]) + z.append(self.getMeasuredReceivers()[traceID][2]) return x, y, z def getMeasuredTopoLists(self): @@ -250,9 +251,9 @@ class SeisArray(object): ''' x = []; y = []; z = [] for pointID in self.getMeasuredTopo().keys(): - x.append(self.getMeasuredTopo()[pointID][0]) + x.append(self.getMeasuredTopo()[pointID][0]) y.append(self.getMeasuredTopo()[pointID][1]) - z.append(self.getMeasuredTopo()[pointID][2]) + z.append(self.getMeasuredTopo()[pointID][2]) return x, y, z def getSourceLocsLists(self): @@ -261,9 +262,9 @@ class SeisArray(object): ''' x = []; y = []; z = [] for pointID in self.getSourceLocations().keys(): - x.append(self.getSourceLocations()[pointID][0]) + x.append(self.getSourceLocations()[pointID][0]) y.append(self.getSourceLocations()[pointID][1]) - z.append(self.getSourceLocations()[pointID][2]) + z.append(self.getSourceLocations()[pointID][2]) return x, y, z def getAllMeasuredPointsLists(self): @@ -289,7 +290,7 @@ class SeisArray(object): y.append(self.getReceiverCoordinates()[traceID][1]) z.append(self.getReceiverCoordinates()[traceID][2]) return x, y, z - + def _interpolateXY4rec(self): ''' Interpolates the X and Y coordinates for all receivers. @@ -317,7 +318,7 @@ class SeisArray(object): :param: phiWE (W, E) extensions of the model in degree type: tuple - ''' + ''' surface = [] elevation = 0.25 # elevate topography so that no source lies above the surface @@ -356,9 +357,9 @@ class SeisArray(object): progress = float(count) / float(nTotal) * 100 self._update_progress(progress) - if filename is not None: + if filename is not None: outfile.writelines('%10s\n'%(z + elevation)) - + return surface def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, @@ -415,7 +416,7 @@ class SeisArray(object): thetaDelta = abs(thetaN - thetaS) / (nTheta - 1) phiDelta = abs(phiE - phiW) / (nPhi - 1) rDelta = abs(rbot - rtop) / (nR - 1) - + # create a regular grid including +2 cushion nodes in every direction thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes @@ -455,7 +456,7 @@ class SeisArray(object): progress = float(count) / float(nTotal) * 100 self._update_progress(progress) - + outfile.close() def exportAll(self, filename = 'interpolated_receivers.out'): @@ -463,7 +464,7 @@ class SeisArray(object): count = 0 for traceID in self.getReceiverCoordinates().keys(): count += 1 - x, y, z = self.getReceiverCoordinates()[traceID] + x, y, z = self.getReceiverCoordinates()[traceID] recfile_out.writelines('%5s %15s %15s %15s\n' %(traceID, x, y, z)) print "Exported coordinates for %s traces to file > %s" %(count, filename) recfile_out.close() @@ -472,15 +473,15 @@ class SeisArray(object): import matplotlib.pyplot as plt plt.interactive(True) plt.figure() - xmt, ymt, zmt = self.getMeasuredTopoLists() + xmt, ymt, zmt = self.getMeasuredTopoLists() xsc, ysc, zsc = self.getSourceLocsLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xrc, yrc, zrc = self.getReceiverLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xrc, yrc, zrc = self.getReceiverLists() plt.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') plt.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') - if plot_topo == True: + if plot_topo == True: plt.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') if highlight_measured == True: plt.plot(xmr, ymr, 'ro', label = 'measured receivers') @@ -501,9 +502,9 @@ class SeisArray(object): fig = plt.figure() ax = plt.axes(projection = '3d') - xmt, ymt, zmt = self.getMeasuredTopoLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xin, yin, zin = self.getReceiverLists() + xmt, ymt, zmt = self.getMeasuredTopoLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xin, yin, zin = self.getReceiverLists() ax.plot(xmt, ymt, zmt, 'b*', markersize = 10, label = 'measured topo points') ax.plot(xin, yin, zin, 'k.', markersize = 10, label = 'interpolated receivers') @@ -512,8 +513,8 @@ class SeisArray(object): ax.legend() return ax - - + + def plotSurface3D(self, ax = None, step = 0.5, method = 'linear'): from matplotlib import cm import matplotlib.pyplot as plt @@ -657,8 +658,8 @@ class SeisArray(object): cPickle.dump(self, outfile, -1) print('saved SeisArray to file %s'%(filename)) - - @staticmethod + + @staticmethod def from_pickle(filename): import cPickle infile = open(filename, 'rb') diff --git a/pylot/core/active/seismicArrayPreperation.py b/pylot/core/active/seismicArrayPreperation.py index ae1b42ec..0bd3b1ad 100644 --- a/pylot/core/active/seismicArrayPreperation.py +++ b/pylot/core/active/seismicArrayPreperation.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys import numpy as np from scipy.interpolate import griddata @@ -28,12 +29,12 @@ class SeisArray(object): def _generateReceiverlines(self): ''' - Connects the traceIDs to the lineIDs + Connects the traceIDs to the lineIDs for each receiverline in a dictionary. ''' for receiver in self._receiverlist: - traceID = int(receiver.split()[0]) - lineID = int(receiver.split()[1]) + traceID = int(receiver.split()[0]) + lineID = int(receiver.split()[1]) if not lineID in self._receiverlines.keys(): self._receiverlines[lineID] = [] self._receiverlines[lineID].append(traceID) @@ -43,16 +44,16 @@ class SeisArray(object): Fills the three x, y, z dictionaries with measured coordinates ''' for line in self._getReceiverlist(): - traceID = int(line.split()[0]) - x = float(line.split()[3]) - y = float(line.split()[4]) - z = float(line.split()[5]) + traceID = int(line.split()[0]) + x = float(line.split()[3]) + y = float(line.split()[4]) + z = float(line.split()[5]) self._receiverCoords[traceID] = (x, y, z) self._measuredReceivers[traceID] = (x, y, z) def _setGeophoneNumbers(self): for line in self._getReceiverlist(): - traceID = int(line.split()[0]) + traceID = int(line.split()[0]) gphoneNum = float(line.split()[2]) self._geophoneNumbers[traceID] = gphoneNum @@ -93,7 +94,7 @@ class SeisArray(object): return self._geophoneNumbers[traceID] def getMeasuredReceivers(self): - return self._measuredReceivers + return self._measuredReceivers def getMeasuredTopo(self): return self._measuredTopo @@ -139,11 +140,11 @@ class SeisArray(object): if self._getReceiverValue(traceID1, coordinate) < self._getReceiverValue(traceID2, coordinate): direction = +1 return direction - if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): + if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): direction = -1 return direction print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) - + def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): ''' Returns the mean distance between two traceID's depending on the number of geophones in between @@ -186,7 +187,7 @@ class SeisArray(object): x = float(line[1]) y = float(line[2]) z = float(line[3]) - self._measuredTopo[pointID] = (x, y, z) + self._measuredTopo[pointID] = (x, y, z) def addSourceLocations(self, filename): ''' @@ -202,7 +203,7 @@ class SeisArray(object): x = float(line[1]) y = float(line[2]) z = float(line[3]) - self._sourceLocs[pointID] = (x, y, z) + self._sourceLocs[pointID] = (x, y, z) def interpZcoords4rec(self, method = 'linear'): ''' @@ -239,9 +240,9 @@ class SeisArray(object): ''' x = []; y = []; z = [] for traceID in self.getMeasuredReceivers().keys(): - x.append(self.getMeasuredReceivers()[traceID][0]) + x.append(self.getMeasuredReceivers()[traceID][0]) y.append(self.getMeasuredReceivers()[traceID][1]) - z.append(self.getMeasuredReceivers()[traceID][2]) + z.append(self.getMeasuredReceivers()[traceID][2]) return x, y, z def getMeasuredTopoLists(self): @@ -250,9 +251,9 @@ class SeisArray(object): ''' x = []; y = []; z = [] for pointID in self.getMeasuredTopo().keys(): - x.append(self.getMeasuredTopo()[pointID][0]) + x.append(self.getMeasuredTopo()[pointID][0]) y.append(self.getMeasuredTopo()[pointID][1]) - z.append(self.getMeasuredTopo()[pointID][2]) + z.append(self.getMeasuredTopo()[pointID][2]) return x, y, z def getSourceLocsLists(self): @@ -261,9 +262,9 @@ class SeisArray(object): ''' x = []; y = []; z = [] for pointID in self.getSourceLocations().keys(): - x.append(self.getSourceLocations()[pointID][0]) + x.append(self.getSourceLocations()[pointID][0]) y.append(self.getSourceLocations()[pointID][1]) - z.append(self.getSourceLocations()[pointID][2]) + z.append(self.getSourceLocations()[pointID][2]) return x, y, z def getAllMeasuredPointsLists(self): @@ -289,7 +290,7 @@ class SeisArray(object): y.append(self.getReceiverCoordinates()[traceID][1]) z.append(self.getReceiverCoordinates()[traceID][2]) return x, y, z - + def _interpolateXY4rec(self): ''' Interpolates the X and Y coordinates for all receivers. @@ -317,7 +318,7 @@ class SeisArray(object): :param: phiWE (W, E) extensions of the model in degree type: tuple - ''' + ''' surface = [] elevation = 0.25 # elevate topography so that no source lies above the surface @@ -356,9 +357,9 @@ class SeisArray(object): progress = float(count) / float(nTotal) * 100 self._update_progress(progress) - if filename is not None: + if filename is not None: outfile.writelines('%10s\n'%(z + elevation)) - + return surface def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, @@ -415,7 +416,7 @@ class SeisArray(object): thetaDelta = abs(thetaN - thetaS) / (nTheta - 1) phiDelta = abs(phiE - phiW) / (nPhi - 1) rDelta = abs(rbot - rtop) / (nR - 1) - + # create a regular grid including +2 cushion nodes in every direction thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes @@ -455,7 +456,7 @@ class SeisArray(object): progress = float(count) / float(nTotal) * 100 self._update_progress(progress) - + outfile.close() def exportAll(self, filename = 'interpolated_receivers.out'): @@ -463,7 +464,7 @@ class SeisArray(object): count = 0 for traceID in self.getReceiverCoordinates().keys(): count += 1 - x, y, z = self.getReceiverCoordinates()[traceID] + x, y, z = self.getReceiverCoordinates()[traceID] recfile_out.writelines('%5s %15s %15s %15s\n' %(traceID, x, y, z)) print "Exported coordinates for %s traces to file > %s" %(count, filename) recfile_out.close() @@ -472,15 +473,15 @@ class SeisArray(object): import matplotlib.pyplot as plt plt.interactive(True) plt.figure() - xmt, ymt, zmt = self.getMeasuredTopoLists() + xmt, ymt, zmt = self.getMeasuredTopoLists() xsc, ysc, zsc = self.getSourceLocsLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xrc, yrc, zrc = self.getReceiverLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xrc, yrc, zrc = self.getReceiverLists() plt.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') plt.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') - if plot_topo == True: + if plot_topo == True: plt.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') if highlight_measured == True: plt.plot(xmr, ymr, 'ro', label = 'measured receivers') @@ -501,9 +502,9 @@ class SeisArray(object): fig = plt.figure() ax = plt.axes(projection = '3d') - xmt, ymt, zmt = self.getMeasuredTopoLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xin, yin, zin = self.getReceiverLists() + xmt, ymt, zmt = self.getMeasuredTopoLists() + xmr, ymr, zmr = self.getMeasuredReceiverLists() + xin, yin, zin = self.getReceiverLists() ax.plot(xmt, ymt, zmt, 'b*', markersize = 10, label = 'measured topo points') ax.plot(xin, yin, zin, 'k.', markersize = 10, label = 'interpolated receivers') @@ -512,8 +513,8 @@ class SeisArray(object): ax.legend() return ax - - + + def plotSurface3D(self, ax = None, step = 0.5, method = 'linear'): from matplotlib import cm import matplotlib.pyplot as plt @@ -657,8 +658,8 @@ class SeisArray(object): cPickle.dump(self, outfile, -1) print('saved SeisArray to file %s'%(filename)) - - @staticmethod + + @staticmethod def from_pickle(filename): import cPickle infile = open(filename, 'rb') diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index df623479..6bec9c70 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -35,8 +35,7 @@ class SeismicShot(object): self.snr = {} self.snrthreshold = {} self.timeArray = {} - self.paras = {} - self.paras['shotname'] = obsfile + self.paras = {'shotname': obsfile} def removeEmptyTraces(self): traceIDs = [] diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index 2f7b2a0a..de528e68 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import matplotlib.pyplot as plt plt.interactive(True) @@ -13,9 +14,9 @@ class regions(object): self.shots_for_deletion = {} def _onselect(self, eclick, erelease): - 'eclick and erelease are matplotlib events at press and release' #print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) - #print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) - print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)'%(eclick.xdata, eclick.ydata, erelease.xdata, erelease.ydata) + 'eclick and erelease are matplotlib events at press and release' #print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) + #print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) + print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)'%(eclick.xdata, eclick.ydata, erelease.xdata, erelease.ydata) x0 = min(eclick.xdata, erelease.xdata) x1 = max(eclick.xdata, erelease.xdata) y0 = min(eclick.ydata, erelease.ydata) @@ -51,7 +52,7 @@ class regions(object): return self.shots_for_deletion def findTracesInShotDict(self, picks = 'normal'): - ''' + ''' Returns traces corresponding to a certain area in a plot with all picks over the distances. ''' print "findTracesInShotDict: Searching for marked traces in the shot dictionary... " @@ -116,7 +117,7 @@ class regions(object): shot.plot_traces(traceID) else: print 'No picks yet defined in the regions x = (%s, %s), y = (%s, %s)' %(self._x0, self._x1, self._y0, self._y1) - + def setCurrentRegionsForDeletion(self): # if len(self.shots_found) == 0: diff --git a/pylot/core/analysis/__init__.py b/pylot/core/analysis/__init__.py index e69de29b..40a96afc 100644 --- a/pylot/core/analysis/__init__.py +++ b/pylot/core/analysis/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pylot/core/analysis/coinctimes.py b/pylot/core/analysis/coinctimes.py index b1486898..1ecc7a9e 100644 --- a/pylot/core/analysis/coinctimes.py +++ b/pylot/core/analysis/coinctimes.py @@ -6,7 +6,7 @@ from obspy.signal.trigger import coincidenceTrigger -class CoincidenceTimes(): +class CoincidenceTimes(object): def __init__(self, st, comp='Z', coinum=4, sta=1., lta=10., on=5., off=1.): _type = 'recstalta' @@ -54,4 +54,4 @@ def main(): if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 9d24392e..3beaac90 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- """ Created August/September 2015. @@ -34,7 +35,7 @@ class Magnitude(object): :type: integer ''' - + assert isinstance(wfstream, Stream), "%s is not a stream object" % str(wfstream) self.setwfstream(wfstream) @@ -62,7 +63,7 @@ class Magnitude(object): def setpwin(self, pwin): self.pwin = pwin - + def getiplot(self): return self.iplot @@ -71,7 +72,7 @@ class Magnitude(object): def getwapp(self): return self.wapp - + def getw0(self): return self.w0 @@ -103,7 +104,7 @@ class WApp(Magnitude): 'poles': [5.6089 - 5.4978j, -5.6089 - 5.4978j], 'zeros': [0j, 0j], 'gain': 2080, - 'sensitivity': 1} + 'sensitivity': 1} stream.simulate(paz_remove=None, paz_simulate=paz_wa) @@ -133,19 +134,19 @@ class WApp(Magnitude): raw_input() plt.close(f) - + class DCfc(Magnitude): ''' - Method to calculate the source spectrum and to derive from that the plateau - (so-called DC-value) and the corner frequency assuming Aki's omega-square + Method to calculate the source spectrum and to derive from that the plateau + (so-called DC-value) and the corner frequency assuming Aki's omega-square source model. Has to be derived from instrument corrected displacement traces! ''' def calcsourcespec(self): - print ("Calculating source spectrum ....") + print ("Calculating source spectrum ....") self.w0 = None # DC-value - self.fc = None # corner frequency + self.fc = None # corner frequency stream = self.getwfstream() tr = stream[0] @@ -155,7 +156,7 @@ class DCfc(Magnitude): iwin = getsignalwin(t, self.getTo(), self.getpwin()) xdat = tr.data[iwin] - # fft + # fft fny = tr.stats.sampling_rate / 2 l = len(xdat) / tr.stats.sampling_rate n = tr.stats.sampling_rate * l # number of fft bins after Bath @@ -167,7 +168,7 @@ class DCfc(Magnitude): L = (N - 1) / tr.stats.sampling_rate f = np.arange(0, fny, 1/L) - # remove zero-frequency and frequencies above + # remove zero-frequency and frequencies above # corner frequency of seismometer (assumed # to be 100 Hz) fi = np.where((f >= 1) & (f < 100)) @@ -185,15 +186,15 @@ class DCfc(Magnitude): self.w0 = optspecfit[0] self.fc = optspecfit[1] print ("DCfc: Determined DC-value: %e m/Hz, \n" \ - "Determined corner frequency: %f Hz" % (self.w0, self.fc)) - - + "Determined corner frequency: %f Hz" % (self.w0, self.fc)) + + if self.getiplot() > 1: f1 = plt.figure() plt.subplot(2,1,1) # show displacement in mm - plt.plot(t, np.multiply(tr, 1000), 'k') - plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') + plt.plot(t, np.multiply(tr, 1000), 'k') + plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') plt.title('Seismogram and P pulse, station %s' % tr.stats.station) plt.xlabel('Time since %s' % tr.stats.starttime) plt.ylabel('Displacement [mm]') @@ -214,8 +215,8 @@ class DCfc(Magnitude): def synthsourcespec(f, omega0, fcorner): ''' - Calculates synthetic source spectrum from given plateau and corner - frequency assuming Akis omega-square model. + Calculates synthetic source spectrum from given plateau and corner + frequency assuming Akis omega-square model. :param: f, frequencies :type: array @@ -226,7 +227,7 @@ def synthsourcespec(f, omega0, fcorner): :param: fcorner, corner frequency of source spectrum :type: float ''' - + #ssp = omega0 / (pow(2, (1 + f / fcorner))) ssp = omega0 / (1 + pow(2, (f / fcorner))) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 2f6e8cd8..0ffe9b52 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- """ Created Oct/Nov 2014 @@ -119,7 +120,7 @@ class CharacteristicFunction(object): def getTimeArray(self): incr = self.getIncrement() - self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] + self.TimeArray = np.arange(0, len(self.getCF()) * incr, incr) + self.getCut()[0] return self.TimeArray def getFnoise(self): @@ -175,7 +176,7 @@ class CharacteristicFunction(object): h2 = hh[1].copy() hh[0].data = h1.data[int(start):int(stop)] hh[1].data = h2.data[int(start):int(stop)] - data = hh + data = hh return data elif len(self.orig_data) == 3: if self.cut[0] == 0 and self.cut[1] == 0: @@ -196,12 +197,12 @@ class CharacteristicFunction(object): hh[0].data = h1.data[int(start):int(stop)] hh[1].data = h2.data[int(start):int(stop)] hh[2].data = h3.data[int(start):int(stop)] - data = hh + data = hh return data else: data = self.orig_data.copy() return data - + def calcCF(self, data=None): self.cf = data @@ -285,7 +286,7 @@ class HOScf(CharacteristicFunction): LTA[j] = lta / np.power(lta1, 1.5) elif self.getOrder() == 4: LTA[j] = lta / np.power(lta1, 2) - + nn = np.isnan(LTA) if len(nn) > 1: LTA[nn] = 0 @@ -315,7 +316,7 @@ class ARZcf(CharacteristicFunction): cf = np.zeros(len(xnp)) loopstep = self.getARdetStep() arcalci = ldet + self.getOrder() #AR-calculation index - for i in range(ldet + self.getOrder(), tend - lpred - 1): + for i in range(ldet + self.getOrder(), tend - lpred - 1): if i == arcalci: #determination of AR coefficients #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! @@ -362,7 +363,7 @@ class ARZcf(CharacteristicFunction): rhs = np.zeros(self.getOrder()) for k in range(0, self.getOrder()): for i in range(rind, ldet+1): - ki = k + 1 + ki = k + 1 rhs[k] = rhs[k] + data[i] * data[i - ki] #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) @@ -382,7 +383,7 @@ class ARZcf(CharacteristicFunction): def arPredZ(self, data, arpara, rind, lpred): ''' Function to predict waveform, assuming an autoregressive process of order - p (=size(arpara)), with AR parameters arpara calculated in arDet. After + p (=size(arpara)), with AR parameters arpara calculated in arDet. After Thomas Meier (CAU), published in Kueperkoch et al. (2012). :param: data, time series to be predicted :type: array @@ -400,9 +401,9 @@ class ARZcf(CharacteristicFunction): ''' #be sure of the summation indeces if rind < len(arpara): - rind = len(arpara) + rind = len(arpara) if rind > len(data) - lpred : - rind = len(data) - lpred + rind = len(data) - lpred if lpred < 1: lpred = 1 if lpred > len(data) - 2: @@ -422,7 +423,7 @@ class ARHcf(CharacteristicFunction): def calcCF(self, data): print 'Calculating AR-prediction error from both horizontal traces ...' - + xnp = self.getDataArray(self.getCut()) n0 = np.isnan(xnp[0].data) if len(n0) > 1: @@ -430,7 +431,7 @@ class ARHcf(CharacteristicFunction): n1 = np.isnan(xnp[1].data) if len(n1) > 1: xnp[1].data[n1] = 0 - + #some parameters needed #add noise to time series xenoise = xnp[0].data + np.random.normal(0.0, 1.0, len(xnp[0].data)) * self.getFnoise() * max(abs(xnp[0].data)) @@ -441,7 +442,7 @@ class ARHcf(CharacteristicFunction): #Time2: length of AR-prediction window [sec] ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] - + cf = np.zeros(len(xenoise)) loopstep = self.getARdetStep() arcalci = lpred + self.getOrder() - 1 #AR-calculation index @@ -515,7 +516,7 @@ class ARHcf(CharacteristicFunction): def arPredH(self, data, arpara, rind, lpred): ''' Function to predict waveform, assuming an autoregressive process of order - p (=size(arpara)), with AR parameters arpara calculated in arDet. After + p (=size(arpara)), with AR parameters arpara calculated in arDet. After Thomas Meier (CAU), published in Kueperkoch et al. (2012). :param: data, horizontal component seismograms to be predicted :type: structured array @@ -558,7 +559,7 @@ class AR3Ccf(CharacteristicFunction): def calcCF(self, data): print 'Calculating AR-prediction error from all 3 components ...' - + xnp = self.getDataArray(self.getCut()) n0 = np.isnan(xnp[0].data) if len(n0) > 1: @@ -569,7 +570,7 @@ class AR3Ccf(CharacteristicFunction): n2 = np.isnan(xnp[2].data) if len(n2) > 1: xnp[2].data[n2] = 0 - + #some parameters needed #add noise to time series xenoise = xnp[0].data + np.random.normal(0.0, 1.0, len(xnp[0].data)) * self.getFnoise() * max(abs(xnp[0].data)) @@ -581,7 +582,7 @@ class AR3Ccf(CharacteristicFunction): #Time2: length of AR-prediction window [sec] ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] - + cf = np.zeros(len(xenoise)) loopstep = self.getARdetStep() arcalci = ldet + self.getOrder() - 1 #AR-calculation index @@ -616,7 +617,7 @@ class AR3Ccf(CharacteristicFunction): Function to calculate AR parameters arpara after Thomas Meier (CAU), published in Kueperkoch et al. (2012). This function solves SLE using the Moore- Penrose inverse, i.e. the least-squares approach. "data" is a structured array. - AR parameters are calculated based on both horizontal components and vertical + AR parameters are calculated based on both horizontal components and vertical componant. :param: data, horizontal component seismograms to calculate AR parameters from :type: structured array @@ -658,7 +659,7 @@ class AR3Ccf(CharacteristicFunction): def arPred3C(self, data, arpara, rind, lpred): ''' Function to predict waveform, assuming an autoregressive process of order - p (=size(arpara)), with AR parameters arpara calculated in arDet3C. After + p (=size(arpara)), with AR parameters arpara calculated in arDet3C. After Thomas Meier (CAU), published in Kueperkoch et al. (2012). :param: data, horizontal and vertical component seismograms to be predicted :type: structured array diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index f9ef82d4..41ac2439 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -312,7 +312,7 @@ class PragPicker(AutoPicking): else: for i in range(1, len(self.cf)): if i > ismooth: - ii1 = i - ismooth; + ii1 = i - ismooth cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth else: cfsmooth[i] = np.mean(self.cf[1 : i]) diff --git a/pylot/core/pick/__init__.py b/pylot/core/pick/__init__.py index 4287ca86..ec51c5a2 100644 --- a/pylot/core/pick/__init__.py +++ b/pylot/core/pick/__init__.py @@ -1 +1,2 @@ -# \ No newline at end of file +# -*- coding: utf-8 -*- +# diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index e3771d7a..b4da61b3 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -317,29 +317,29 @@ def autopickstation(wfstream, pickparam): data = Data() [corzdat, restflag] = data.restituteWFData(invdir, zdat) if restflag == 1: - # integrate to displacement - corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta) - # class needs stream object => build it - z_copy = zdat.copy() - z_copy[0].data = corintzdat - # largest detectable period == window length - # after P pulse for calculating source spectrum - winzc = (1 / bpz2[0]) * z_copy[0].stats.sampling_rate - impickP = mpickP * z_copy[0].stats.sampling_rate - wfzc = z_copy[0].data[impickP : impickP + winzc] - # calculate spectrum using only first cycles of - # waveform after P onset! - zc = crossings_nonzero_all(wfzc) - if np.size(zc) == 0: - print ("Something is wrong with the waveform, " \ - "no zero crossings derived!") - print ("Cannot calculate source spectrum!") - else: - calcwin = (zc[3] - zc[0]) * z_copy[0].stats.delta - # calculate source spectrum and get w0 and fc - specpara = DCfc(z_copy, mpickP, calcwin, iplot) - w0 = specpara.getw0() - fc = specpara.getfc() + # integrate to displacement + corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta) + # class needs stream object => build it + z_copy = zdat.copy() + z_copy[0].data = corintzdat + # largest detectable period == window length + # after P pulse for calculating source spectrum + winzc = (1 / bpz2[0]) * z_copy[0].stats.sampling_rate + impickP = mpickP * z_copy[0].stats.sampling_rate + wfzc = z_copy[0].data[impickP : impickP + winzc] + # calculate spectrum using only first cycles of + # waveform after P onset! + zc = crossings_nonzero_all(wfzc) + if np.size(zc) == 0: + print ("Something is wrong with the waveform, " \ + "no zero crossings derived!") + print ("Cannot calculate source spectrum!") + else: + calcwin = (zc[3] - zc[0]) * z_copy[0].stats.delta + # calculate source spectrum and get w0 and fc + specpara = DCfc(z_copy, mpickP, calcwin, iplot) + w0 = specpara.getw0() + fc = specpara.getfc() print ("autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, " \ "Polarity: %s" % (Pweight, SNRP, SNRPdB, FM)) @@ -560,7 +560,7 @@ def autopickstation(wfstream, pickparam): hdat += ndat h_copy = hdat.copy() [cordat, restflag] = data.restituteWFData(invdir, h_copy) - # calculate WA-peak-to-peak amplitude + # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude if restflag == 1: if Sweight < 4: @@ -591,7 +591,7 @@ def autopickstation(wfstream, pickparam): h_copy = hdat.copy() [cordat, restflag] = data.restituteWFData(invdir, h_copy) if restflag == 1: - # calculate WA-peak-to-peak amplitude + # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude wapp = WApp(cordat, mpickP, mpickP + sstop + (0.5 * (mpickP \ + sstop)), iplot) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 16fd2cec..61387478 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # # -*- coding: utf-8 -*- """ @@ -91,7 +92,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length # T0/4 is assumed as time difference between most likely and earliest possible pick! EPick = Pick1 - T0 / 2 - + # get symmetric pick error as mean from earliest and latest possible pick # by weighting latest possible pick two times earliest possible pick @@ -493,9 +494,9 @@ def wadaticheck(pickdic, dttolerance, iplot): if len(SPtimes) >= 3: - # calculate slope - p1 = np.polyfit(Ppicks, SPtimes, 1) - wdfit = np.polyval(p1, Ppicks) + # calculate slope + p1 = np.polyfit(Ppicks, SPtimes, 1) + wdfit = np.polyval(p1, Ppicks) wfitflag = 0 # calculate vp/vs ratio before check @@ -532,40 +533,40 @@ def wadaticheck(pickdic, dttolerance, iplot): pickdic[key]['S']['marked'] = marker if len(checkedPpicks) >= 3: - # calculate new slope - p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) - wdfit2 = np.polyval(p2, checkedPpicks) + # calculate new slope + p2 = np.polyfit(checkedPpicks, checkedSPtimes, 1) + wdfit2 = np.polyval(p2, checkedPpicks) - # calculate vp/vs ratio after check - cvpvsr = p2[0] + 1 - print ("wadaticheck: Average Vp/Vs ratio after check: %f" % cvpvsr) - print ("wadatacheck: Skipped %d S pick(s)" % ibad) + # calculate vp/vs ratio after check + cvpvsr = p2[0] + 1 + print ("wadaticheck: Average Vp/Vs ratio after check: %f" % cvpvsr) + print ("wadatacheck: Skipped %d S pick(s)" % ibad) else: - print ("###############################################") - print ("wadatacheck: Not enough checked S-P times available!") - print ("Skip Wadati check!") + print ("###############################################") + print ("wadatacheck: Not enough checked S-P times available!") + print ("Skip Wadati check!") checkedonsets = pickdic else: - print ("wadaticheck: Not enough S-P times available for reliable regression!") + print ("wadaticheck: Not enough S-P times available for reliable regression!") print ("Skip wadati check!") wfitflag = 1 # plot results if iplot > 1: - plt.figure(iplot) - f1, = plt.plot(Ppicks, SPtimes, 'ro') + plt.figure(iplot) + f1, = plt.plot(Ppicks, SPtimes, 'ro') if wfitflag == 0: - f2, = plt.plot(Ppicks, wdfit, 'k') - f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') - f4, = plt.plot(checkedPpicks, wdfit2, 'g') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ - 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ - 'Reliable S-Picks', 'Wadati 2'], loc='best') + f2, = plt.plot(Ppicks, wdfit, 'k') + f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') + f4, = plt.plot(checkedPpicks, wdfit2, 'g') + plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ + 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) + plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ + 'Reliable S-Picks', 'Wadati 2'], loc='best') else: - plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) + plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) plt.ylabel('S-P Times [s]') plt.xlabel('P Times [s]') @@ -579,8 +580,8 @@ def wadaticheck(pickdic, dttolerance, iplot): def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): ''' Function to detect spuriously picked noise peaks. - Uses RMS trace of all 3 components (if available) to determine, - how many samples [per cent] after P onset are below certain + Uses RMS trace of all 3 components (if available) to determine, + how many samples [per cent] after P onset are below certain threshold, calculated from noise level times noise factor. : param: X, time series (seismogram) @@ -612,7 +613,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): print ("Checking signal length ...") if len(X) > 1: - # all three components available + # all three components available # make sure, all components have equal lengths ilen = min([len(X[0].data), len(X[1].data), len(X[2].data)]) x1 = X[0][0:ilen] @@ -639,7 +640,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): numoverthr = len(np.where(rms[isignal] >= minsiglevel)[0]) if numoverthr >= minnum: - print ("checksignallength: Signal reached required length.") + print ("checksignallength: Signal reached required length.") returnflag = 1 else: print ("checksignallength: Signal shorter than required minimum signal length!") @@ -649,7 +650,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): if iplot == 2: plt.figure(iplot) - p1, = plt.plot(t,rms, 'k') + p1, = plt.plot(t,rms, 'k') p2, = plt.plot(t[inoise], rms[inoise], 'c') p3, = plt.plot(t[isignal],rms[isignal], 'r') p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ @@ -729,27 +730,27 @@ def checkPonsets(pickdic, dttolerance, iplot): badjkmarker = 'badjkcheck' for i in range(0, len(goodstations)): # mark P onset as checked and keep P weight - pickdic[goodstations[i]]['P']['marked'] = goodmarker + pickdic[goodstations[i]]['P']['marked'] = goodmarker for i in range(0, len(badstations)): - # mark P onset and downgrade P weight to 9 - # (not used anymore) - pickdic[badstations[i]]['P']['marked'] = badmarker - pickdic[badstations[i]]['P']['weight'] = 9 + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badstations[i]]['P']['marked'] = badmarker + pickdic[badstations[i]]['P']['weight'] = 9 for i in range(0, len(badjkstations)): - # mark P onset and downgrade P weight to 9 - # (not used anymore) - pickdic[badjkstations[i]]['P']['marked'] = badjkmarker - pickdic[badjkstations[i]]['P']['weight'] = 9 + # mark P onset and downgrade P weight to 9 + # (not used anymore) + pickdic[badjkstations[i]]['P']['marked'] = badjkmarker + pickdic[badjkstations[i]]['P']['weight'] = 9 checkedonsets = pickdic if iplot > 1: - p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) + p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) p2, = plt.plot(igood, np.array(Ppicks)[igood], 'g*', markersize=14) p3, = plt.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', \ linewidth=2) for i in range(0, len(Ppicks)): - plt.text(i, Ppicks[i] + 0.2, stations[i]) + plt.text(i, Ppicks[i] + 0.2, stations[i]) plt.xlabel('Number of P Picks') plt.ylabel('Onset Time [s] from 1.1.1970') @@ -789,37 +790,37 @@ def jackknife(X, phi, h): g = len(X) / h if type(g) is not int: - print ("jackknife: Cannot divide quantity X in equal sized subgroups!") + print ("jackknife: Cannot divide quantity X in equal sized subgroups!") print ("Choose another size for subgroups!") return PHI_jack, PHI_pseudo, PHI_sub else: - # estimator of undisturbed spot check - if phi == 'MEA': - phi_sc = np.mean(X) + # estimator of undisturbed spot check + if phi == 'MEA': + phi_sc = np.mean(X) elif phi == 'VAR': - phi_sc = np.var(X) + phi_sc = np.var(X) elif phi == 'MED': - phi_sc = np.median(X) + phi_sc = np.median(X) - # estimators of subgroups + # estimators of subgroups PHI_pseudo = [] PHI_sub = [] for i in range(0, g - 1): - # subgroup i, remove i-th sample - xx = X[:] - del xx[i] - # calculate estimators of disturbed spot check - if phi == 'MEA': - phi_sub = np.mean(xx) - elif phi == 'VAR': - phi_sub = np.var(xx) - elif phi == 'MED': - phi_sub = np.median(xx) + # subgroup i, remove i-th sample + xx = X[:] + del xx[i] + # calculate estimators of disturbed spot check + if phi == 'MEA': + phi_sub = np.mean(xx) + elif phi == 'VAR': + phi_sub = np.var(xx) + elif phi == 'MED': + phi_sub = np.median(xx) - PHI_sub.append(phi_sub) - # pseudo values - phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) - PHI_pseudo.append(phi_pseudo) + PHI_sub.append(phi_sub) + # pseudo values + phi_pseudo = g * phi_sc - ((g - 1) * phi_sub) + PHI_pseudo.append(phi_pseudo) # jackknife estimator PHI_jack = np.mean(PHI_pseudo) @@ -899,17 +900,17 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): # vertical P-coda level must exceed horizontal P-coda level # zfac times encodalevel if zcodalevel < minsiglevel: - print ("checkZ4S: Maybe S onset? Skip this P pick!") + print ("checkZ4S: Maybe S onset? Skip this P pick!") else: print ("checkZ4S: P onset passes checkZ4S test!") returnflag = 1 if iplot > 1: - te = np.arange(0, edat[0].stats.npts / edat[0].stats.sampling_rate, + te = np.arange(0, edat[0].stats.npts / edat[0].stats.sampling_rate, edat[0].stats.delta) - tn = np.arange(0, ndat[0].stats.npts / ndat[0].stats.sampling_rate, + tn = np.arange(0, ndat[0].stats.npts / ndat[0].stats.sampling_rate, ndat[0].stats.delta) - plt.plot(tz, z / max(z), 'k') + plt.plot(tz, z / max(z), 'k') plt.plot(tz[isignal], z[isignal] / max(z), 'r') plt.plot(te, edat[0].data / max(edat[0].data) + 1, 'k') plt.plot(te[isignal], edat[0].data[isignal] / max(edat[0].data) + 1, 'r') diff --git a/pylot/core/read/__init__.py b/pylot/core/read/__init__.py index 8b137891..40a96afc 100644 --- a/pylot/core/read/__init__.py +++ b/pylot/core/read/__init__.py @@ -1 +1 @@ - +# -*- coding: utf-8 -*- diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 1c824ea1..01e7cd3b 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -208,8 +208,7 @@ class FilterOptions(object): def parseFilterOptions(self): if self.getFilterType(): - robject = {'type':self.getFilterType()} - robject['corners'] = self.getOrder() + robject = {'type': self.getFilterType(), 'corners': self.getOrder()} if len(self.getFreq()) > 1: robject['freqmin'] = self.getFreq()[0] robject['freqmax'] = self.getFreq()[1] diff --git a/pylot/core/util/__init__.py b/pylot/core/util/__init__.py index 2d4a37e4..128de997 100755 --- a/pylot/core/util/__init__.py +++ b/pylot/core/util/__init__.py @@ -1 +1,2 @@ +# -*- coding: utf-8 -*- from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/util/testUtils.py b/pylot/core/util/testUtils.py index 9913c940..07d64925 100644 --- a/pylot/core/util/testUtils.py +++ b/pylot/core/util/testUtils.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- ''' Created on 10.11.2014 @@ -23,4 +24,4 @@ class Test(unittest.TestCase): if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pylot/core/util/testWidgets.py b/pylot/core/util/testWidgets.py index f5582362..b20190e1 100644 --- a/pylot/core/util/testWidgets.py +++ b/pylot/core/util/testWidgets.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- ''' Created on 10.11.2014 @@ -15,4 +16,4 @@ class Test(unittest.TestCase): if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index 6b0342f7..7ce1513e 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import sys from PySide.QtCore import QThread, Signal diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 2d3b0e8b..f5c9b5fd 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # # -*- coding: utf-8 -*- diff --git a/testHelpForm.py b/testHelpForm.py index fe1d5a3e..2da58745 100755 --- a/testHelpForm.py +++ b/testHelpForm.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- import sys, time from PySide.QtGui import QApplication diff --git a/testPickDlg.py b/testPickDlg.py index e49f7675..5f8979b7 100755 --- a/testPickDlg.py +++ b/testPickDlg.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- import sys import matplotlib diff --git a/testPropDlg.py b/testPropDlg.py index d4c68871..b77de3b6 100755 --- a/testPropDlg.py +++ b/testPropDlg.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- import sys, time from PySide.QtGui import QApplication @@ -8,4 +9,4 @@ app = QApplication(sys.argv) win = PropertiesDlg() win.show() -app.exec_() \ No newline at end of file +app.exec_() diff --git a/testUIcomponents.py b/testUIcomponents.py index f422df81..920e9fd4 100755 --- a/testUIcomponents.py +++ b/testUIcomponents.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- + import sys, time from PySide.QtGui import QApplication @@ -9,7 +11,7 @@ dialogs = [FilterOptionsDialog, PropertiesDlg, HelpForm] app = QApplication(sys.argv) for dlg in dialogs: - win = dlg() - win.show() - time.sleep(1) - win.destroy() \ No newline at end of file + win = dlg() + win.show() + time.sleep(1) + win.destroy() From 4498c72c90d0a6b68bafc6b38092cecd8065c03b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Oct 2015 10:33:51 +0200 Subject: [PATCH 0622/1144] repick button for plot_traces --- pylot/core/active/seismicshot.py | 180 ++++++++++++++++++++++--------- 1 file changed, 131 insertions(+), 49 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 85d48e93..bb6d6cab 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -7,6 +7,8 @@ from pylot.core.pick.CharFuns import HOScf from pylot.core.pick.CharFuns import AICcf from pylot.core.pick.utils import getSNR from pylot.core.pick.utils import earllatepicker +import matplotlib.pyplot as plt +plt.interactive('True') class SeismicShot(object): ''' @@ -127,8 +129,15 @@ class SeismicShot(object): def getSourcefile(self): return self.paras['sourcefile'] - def getPick(self, traceID): - return self.pick[traceID]['mpp'] + def getPick(self, traceID, returnRemoved = False): + if not self.getFlag(traceID) == 0: + return self.pick[traceID]['mpp'] + if returnRemoved == True: + #print('getPick: Returned removed pick for shot %d, traceID %d' %(self.getShotnumber(), traceID)) + return self.pick[traceID]['mpp'] + + def getPickIncludeRemoved(self, traceID): + return self.getPick(traceID, returnRemoved = True) def getEarliest(self, traceID): return self.pick[traceID]['epp'] @@ -136,6 +145,12 @@ class SeismicShot(object): def getLatest(self, traceID): return self.pick[traceID]['lpp'] + def getSymmetricPickError(self, traceID): + pickerror = self.pick[traceID]['spe'] + if np.isnan(pickerror) == True: + print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) + return pickerror + def getPickError(self, traceID): pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) if np.isnan(pickerror) == True: @@ -209,7 +224,7 @@ class SeismicShot(object): :type: int ''' return HOScf(self.getSingleStream(traceID), self.getCut(), - self.getTmovwind(), self.getOrder()) + self.getTmovwind(), self.getOrder(), stealthMode = True) def getAICcf(self, traceID): ''' @@ -232,7 +247,7 @@ class SeismicShot(object): tr_cf = Trace() tr_cf.data = self.getHOScf(traceID).getCF() st_cf += tr_cf - return AICcf(st_cf, self.getCut(), self.getTmovwind()) + return AICcf(st_cf, self.getCut(), self.getTmovwind(), stealthMode = True) def getSingleStream(self, traceID): ########## SEG2 / SEGY ? ########## ''' @@ -291,11 +306,13 @@ class SeismicShot(object): def setEarllatepick(self, traceID, nfac = 1.5): tgap = self.getTgap() tsignal = self.getTsignal() - tnoise = self.getPick(traceID) - tgap + tnoise = self.getPickIncludeRemoved(traceID) - tgap - (self.pick[traceID]['epp'], self.pick[traceID]['lpp'], tmp) = earllatepicker(self.getSingleStream(traceID), - nfac, (tnoise, tgap, tsignal), - self.getPick(traceID)) + (self.pick[traceID]['epp'], self.pick[traceID]['lpp'], + self.pick[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), + nfac, (tnoise, tgap, tsignal), + self.getPickIncludeRemoved(traceID), + stealthMode = True) def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm = 0.6): ''' @@ -463,7 +480,7 @@ class SeismicShot(object): def setFlag(self, traceID, flag): 'Set flag = 0 if pick is invalid, else flag = 1' - self.pick[traceID]['flag'] = 0 + self.pick[traceID]['flag'] = flag def getFlag(self, traceID): return self.pick[traceID]['flag'] @@ -570,42 +587,89 @@ class SeismicShot(object): # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_upperb, ':k') def plot_traces(self, traceID, folm = 0.6): ########## 2D, muss noch mehr verbessert werden ########## - import matplotlib.pyplot as plt + from matplotlib.widgets import Button + + def onclick(event): + self.setPick(traceID, event.xdata) + self._drawStream(traceID, refresh = True) + self._drawCFs(traceID, folm, refresh = True) + fig.canvas.mpl_disconnect(self.traces4plot[traceID]['cid']) + plt.draw() + + def connectButton(event = None): + cid = fig.canvas.mpl_connect('button_press_event', onclick) + self.traces4plot[traceID]['cid'] = cid + + fig = plt.figure() + ax1 = fig.add_subplot(2,1,1) + ax2 = fig.add_subplot(2,1,2, sharex = ax1) + axb = fig.add_axes([0.15, 0.91, 0.05, 0.03]) + button = Button(axb, 'repick', color = 'red', hovercolor = 'grey') + button.on_clicked(connectButton) + + self.traces4plot = {} + if not traceID in self.traces4plot.keys(): + self.traces4plot[traceID] = {'fig': fig, + 'ax1': ax1, + 'ax2': ax2, + 'axb': axb, + 'button': button, + 'cid': None,} + + self._drawStream(traceID) + self._drawCFs(traceID, folm) + + def _drawStream(self, traceID, refresh = False): from pylot.core.util.utils import getGlobalTimes from pylot.core.util.utils import prepTimeAxis - + stream = self.getSingleStream(traceID) stime = getGlobalTimes(stream)[0] timeaxis = prepTimeAxis(stime, stream[0]) timeaxis -= stime - - plt.interactive('True') + ax = self.traces4plot[traceID]['ax1'] + + if refresh == True: + xlim, ylim = ax.get_xlim(), ax.get_ylim() + ax.clear() + if refresh == True: + ax.set_xlim(xlim) + ax.set_ylim(ylim) + + ax.set_title('Shot: %s, traceID: %s, pick: %s' + %(self.getShotnumber(), traceID, self.getPick(traceID))) + ax.plot(timeaxis, stream[0].data, 'k', label = 'trace') + ax.plot([self.getPick(traceID), self.getPick(traceID)], + [min(stream[0].data), + max(stream[0].data)], + 'r', label = 'mostlikely') + ax.legend() + + def _drawCFs(self, traceID, folm, refresh = False): hoscf = self.getHOScf(traceID) aiccf = self.getAICcf(traceID) + ax = self.traces4plot[traceID]['ax2'] - fig = plt.figure() - ax1 = plt.subplot(2,1,1) - plt.title('Shot: %s, traceID: %s, pick: %s' %(self.getShotnumber(), traceID, self.getPick(traceID))) - ax1.plot(timeaxis, stream[0].data, 'k', label = 'trace') - ax1.plot([self.getPick(traceID), self.getPick(traceID)], - [min(stream[0].data), - max(stream[0].data)], - 'r', label = 'mostlikely') - plt.legend() - ax2 = plt.subplot(2,1,2, sharex = ax1) - ax2.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label = 'HOS') - ax2.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label = 'AIC') - ax2.plot([self.getPick(traceID), self.getPick(traceID)], + if refresh == True: + xlim, ylim = ax.get_xlim(), ax.get_ylim() + ax.clear() + if refresh == True: + ax.set_xlim(xlim) + ax.set_ylim(ylim) + + ax.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label = 'HOS') + ax.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label = 'AIC') + ax.plot([self.getPick(traceID), self.getPick(traceID)], [min(np.minimum(hoscf.getCF(), aiccf.getCF())), max(np.maximum(hoscf.getCF(), aiccf.getCF()))], 'r', label = 'mostlikely') - ax2.plot([0, self.getPick(traceID)], + ax.plot([0, self.getPick(traceID)], [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], 'm:', label = 'folm = %s' %folm) - plt.xlabel('Time [s]') - plt.legend() - + ax.set_xlabel('Time [s]') + ax.legend() + def plot3dttc(self, step = 0.5, contour = False, plotpicks = False, method = 'linear', ax = None): ''' Plots a 3D 'traveltime cone' as surface plot by interpolating on a regular grid over the traveltimes, not yet regarding the vertical offset of the receivers. @@ -622,7 +686,6 @@ class SeismicShot(object): :param: method (optional), interpolation method; can be 'linear' (default) or 'cubic' :type: 'string' ''' - import matplotlib.pyplot as plt from scipy.interpolate import griddata from matplotlib import cm from mpl_toolkits.mplot3d import Axes3D @@ -636,8 +699,8 @@ class SeismicShot(object): y.append(self.getRecLoc(traceID)[1]) z.append(self.getPick(traceID)) - xaxis = np.arange(min(x)+1, max(x), step) - yaxis = np.arange(min(y)+1, max(y), step) + xaxis = np.arange(min(x), max(x), step) + yaxis = np.arange(min(y), max(y), step) xgrid, ygrid = np.meshgrid(xaxis, yaxis) zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) @@ -662,7 +725,7 @@ class SeismicShot(object): plotmethod[method](*args) - def matshow(self, step = 0.5, method = 'linear', ax = None, plotRec = False, annotations = False): + def matshow(self, ax = None, step = 0.5, method = 'linear', plotRec = True, annotations = True, colorbar = True): ''' Plots a 2D matrix of the interpolated traveltimes. This needs less performance than plot3dttc @@ -672,27 +735,32 @@ class SeismicShot(object): :param: method (optional), interpolation method; can be 'linear' (default) or 'cubic' :type: 'string' - :param: plotRec (optional), plot the receiver positions + :param: plotRec (optional), plot the receiver positions (colored scatter plot, should not be + deactivated because there might be receivers that are not inside the interpolated area) :type: 'logical' :param: annotations (optional), displays traceIDs as annotations :type: 'logical' ''' - import matplotlib.pyplot as plt from scipy.interpolate import griddata -# plt.interactive('True') - x = [] - y = [] - z = [] + x = []; xcut = [] + y = []; ycut = [] + z = []; zcut = [] + tmin, tmax = self.getCut() + for traceID in self.pick.keys(): if self.getFlag(traceID) != 0: x.append(self.getRecLoc(traceID)[0]) y.append(self.getRecLoc(traceID)[1]) z.append(self.getPick(traceID)) + if self.getFlag(traceID) == 0 and self.getPickIncludeRemoved(traceID) is not None: + xcut.append(self.getRecLoc(traceID)[0]) + ycut.append(self.getRecLoc(traceID)[1]) + zcut.append(self.getPickIncludeRemoved(traceID)) - xaxis = np.arange(min(x)+1, max(x), step) - yaxis = np.arange(min(y)+1, max(y), step) + xaxis = np.arange(min(x), max(x), step) + yaxis = np.arange(min(y), max(y), step) xgrid, ygrid = np.meshgrid(xaxis, yaxis) zgrid = griddata((x, y), z, (xgrid, ygrid), method='linear') @@ -700,14 +768,28 @@ class SeismicShot(object): fig = plt.figure() ax = plt.axes() - ax.imshow(zgrid, interpolation = 'none', extent = [min(x), max(x), min(y), max(y)]) - - if annotations == True: - for i, traceID in enumerate(self.pick.keys()): - if shot.picks[traceID] != None: - ax.annotate('%s' % traceID, xy=(x[i], y[i]), fontsize = 'x-small') + ax.matshow(zgrid, extent = [min(x), max(x), min(y), max(y)], origin = 'lower') + plt.text(0.45, 0.9, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes) + sc = ax.scatter(x, y, c = z, s = 30, label = 'picked shots', vmin = tmin, vmax = tmax, linewidths = 1.5) + sccut = ax.scatter(xcut, ycut, c = zcut, s = 30, edgecolor = 'm', label = 'cut out shots', vmin = tmin, vmax = tmax, linewidths = 1.5) + if colorbar == True: + plt.colorbar(sc) + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.plot(self.getSrcLoc()[0], self.getSrcLoc()[1],'*k', markersize = 15) # plot source location if plotRec == True: - ax.plot(x, y, 'k.') + ax.scatter(x, y, c = z, s = 30) + + if annotations == True: + for traceID in self.getTraceIDlist(): + if self.getFlag(traceID) is not 0: + ax.annotate(' %s' %traceID , xy = (self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), + fontsize = 'x-small', color = 'k') + else: + ax.annotate(' %s' %traceID , xy = (self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), + fontsize = 'x-small', color = 'r') plt.show() + + From 793e09910c2af9412e987bf1fc58725bc9ea6f02 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Oct 2015 10:34:45 +0200 Subject: [PATCH 0623/1144] *** empty log message *** --- pylot/core/active/surveyUtils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 4fbbe603..6c51b09d 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -20,7 +20,7 @@ def fitSNR4dist(shot_dict, shiftdist = 5): for traceID in shot.getTraceIDlist(): if shot.getSNR(traceID)[0] >= 1: dists.append(shot.getDistance(traceID)) - picks.append(shot.getPick_backup(traceID)) + picks.append(shot.getPickIncludeRemoved(traceID)) snrs.append(shot.getSNR(traceID)[0]) snr_sqrt_inv.append(1/np.sqrt(shot.getSNR(traceID)[0])) fit = np.polyfit(dists, snr_sqrt_inv, 1) From 0a7b02c04aed9e430103c34814a0a683df2d5847 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 19 Oct 2015 11:25:15 +0200 Subject: [PATCH 0624/1144] general clean-up 2.0 even more checks made and issues resolved --- QtPyLoT.py | 3 +- pylot/core/active/seismicshot.py | 2 +- pylot/core/analysis/magnitude.py | 2 +- pylot/core/pick/CharFuns.py | 18 +++++------ pylot/core/pick/autopick.py | 32 ++++++++++---------- pylot/core/pick/run_makeCF.py | 32 ++++++++++---------- pylot/core/pick/utils.py | 52 ++++++++++++++++---------------- pylot/core/read/io.py | 6 ++-- pylot/core/util/widgets.py | 5 +-- 9 files changed, 76 insertions(+), 76 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index cae007c4..2ba21c67 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -273,8 +273,7 @@ class MainWindow(QMainWindow): slot=self.autoPick, shortcut='Alt+Ctrl+A', icon=auto_icon, tip='Automatically pick' ' the entire dataset' - ' displayed!', - checkable=False) + ' displayed!') autoPickToolBar = self.addToolBar("autoPyLoT") autoPickActions = (auto_pick,) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 6bec9c70..522e4afc 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -437,7 +437,7 @@ class SeismicShot(object): if self.getDistance(traceID) == distance: traceID_list.append(traceID) if distancebin[0] >= 0 and distancebin[1] > 0: - if self.getDistance(traceID) > distancebin[0] and self.getDistance(traceID) < distancebin[1]: + if distancebin[0] < self.getDistance(traceID) < distancebin[1]: traceID_list.append(traceID) if len(traceID_list) > 0: diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 3beaac90..ab455762 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -185,7 +185,7 @@ class DCfc(Magnitude): [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [DCin, Fcin]) self.w0 = optspecfit[0] self.fc = optspecfit[1] - print ("DCfc: Determined DC-value: %e m/Hz, \n" \ + print ("DCfc: Determined DC-value: %e m/Hz, \n" "Determined corner frequency: %f Hz" % (self.w0, self.fc)) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index 0ffe9b52..e4c78ffd 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -165,12 +165,12 @@ class CharacteristicFunction(object): stop = min([len(self.orig_data[0]), len(self.orig_data[1])]) elif self.cut[0] == 0 and self.cut[1] is not 0: start = 0 - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ - len(self.orig_data[1])]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1])]) else: start = max([0, self.cut[0] / self.dt]) - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ - len(self.orig_data[1])]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1])]) hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() @@ -181,15 +181,15 @@ class CharacteristicFunction(object): elif len(self.orig_data) == 3: if self.cut[0] == 0 and self.cut[1] == 0: start = 0 - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ - len(self.orig_data[1]), len(self.orig_data[2])]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1]), len(self.orig_data[2])]) elif self.cut[0] == 0 and self.cut[1] is not 0: start = 0 stop = self.cut[1] / self.dt else: start = max([0, self.cut[0] / self.dt]) - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), \ - len(self.orig_data[1]), len(self.orig_data[2])]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1]), len(self.orig_data[2])]) hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() @@ -231,7 +231,7 @@ class AICcf(CharacteristicFunction): cumsumcf = np.cumsum(np.power(xnp, 2)) i = np.where(cumsumcf == 0) cumsumcf[i] = np.finfo(np.float64).eps - cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * \ + cf[k] = ((k - 1) * np.log(cumsumcf[k] / k) + (datlen - k + 1) * np.log((cumsumcf[datlen - 1] - cumsumcf[k - 1]) / (datlen - k + 1))) cf[0] = cf[1] inf = np.isinf(cf) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index b4da61b3..19d33b02 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -204,27 +204,27 @@ def autopickstation(wfstream, pickparam): if len(ndat) == 0 or len(edat) == 0: print ("One or more horizontal components missing!") print ("Signal length only checked on vertical component!") - print ("Decreasing minsiglengh from %f to %f" \ - % (minsiglength, minsiglength / 2)) + print ("Decreasing minsiglengh from %f to %f" + % (minsiglength, minsiglength / 2)) Pflag = checksignallength(zne, aicpick.getpick(), tsnrz, - minsiglength / 2, \ + minsiglength / 2, nfacsl, minpercent, iplot) else: # filter and taper horizontal traces trH1_filt = edat.copy() trH2_filt = ndat.copy() trH1_filt.filter('bandpass', freqmin=bph1[0], - freqmax=bph1[1], \ - zerophase=False) + freqmax=bph1[1], + zerophase=False) trH2_filt.filter('bandpass', freqmin=bph1[0], - freqmax=bph1[1], \ - zerophase=False) + freqmax=bph1[1], + zerophase=False) trH1_filt.taper(max_percentage=0.05, type='hann') trH2_filt.taper(max_percentage=0.05, type='hann') zne += trH1_filt zne += trH2_filt Pflag = checksignallength(zne, aicpick.getpick(), tsnrz, - minsiglength, \ + minsiglength, nfacsl, minpercent, iplot) if Pflag == 1: @@ -234,7 +234,7 @@ def autopickstation(wfstream, pickparam): print 'One or more horizontal components missing!' print 'Skipping control function checkZ4S.' else: - Pflag = checkZ4S(zne, aicpick.getpick(), zfac, \ + Pflag = checkZ4S(zne, aicpick.getpick(), zfac, tsnrz[3], iplot) if Pflag == 0: Pmarker = 'SinsteadP' @@ -331,7 +331,7 @@ def autopickstation(wfstream, pickparam): # waveform after P onset! zc = crossings_nonzero_all(wfzc) if np.size(zc) == 0: - print ("Something is wrong with the waveform, " \ + print ("Something is wrong with the waveform, " "no zero crossings derived!") print ("Cannot calculate source spectrum!") else: @@ -341,7 +341,7 @@ def autopickstation(wfstream, pickparam): w0 = specpara.getw0() fc = specpara.getfc() - print ("autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, " \ + print ("autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, " "Polarity: %s" % (Pweight, SNRP, SNRPdB, FM)) Sflag = 1 @@ -352,7 +352,7 @@ def autopickstation(wfstream, pickparam): Sflag = 0 else: - print ("autopickstation: No vertical component data available!, " \ + print ("autopickstation: No vertical component data available!, " "Skipping station!") if edat is not None and ndat is not None and len(edat) > 0 and len( @@ -593,8 +593,8 @@ def autopickstation(wfstream, pickparam): if restflag == 1: # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude - wapp = WApp(cordat, mpickP, mpickP + sstop + (0.5 * (mpickP \ - + sstop)), iplot) + wapp = WApp(cordat, mpickP, mpickP + sstop + (0.5 * (mpickP + + sstop)), iplot) Ao = wapp.getwapp() else: @@ -771,14 +771,14 @@ def autopickstation(wfstream, pickparam): # create dictionary # for P phase phase = 'P' - phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, \ + phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM} picks = {phase: phasepick} # add P marker picks[phase]['marked'] = Pmarker # add S phase phase = 'S' - phasepick = {'lpp': lpickS, 'epp': epickS, 'mpp': mpickS, 'spe': Serror, \ + phasepick = {'lpp': lpickS, 'epp': epickS, 'mpp': mpickS, 'spe': Serror, 'snr': SNRS, 'snrdb': SNRSdB, 'weight': Sweight, 'fm': None} picks[phase] = phasepick # add Wood-Anderson amplitude diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index d1748d67..2ecd636f 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -6,8 +6,8 @@ Only for test purposes! """ -from obspy.core import read -import matplotlib.pyplot as plt +from obspy.core import read +import matplotlib.pyplot as plt import numpy as np from pylot.core.pick.CharFuns import CharacteristicFunction from pylot.core.pick.Picker import AutoPicking @@ -56,7 +56,7 @@ def run_makeCF(project, database, event, iplot, station=None): st_copy = st.copy() #filter and taper data tr_filt = st[0].copy() - tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + tr_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) tr_filt.taper(max_percentage=0.05, type='hann') st_copy[0].data = tr_filt.data ############################################################## @@ -120,8 +120,8 @@ def run_makeCF(project, database, event, iplot, station=None): #filter and taper data trH1_filt = H[0].copy() trH2_filt = H[1].copy() - trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + trH2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) trH1_filt.taper(max_percentage=0.05, type='hann') trH2_filt.taper(max_percentage=0.05, type='hann') H_copy[0].data = trH1_filt.data @@ -167,9 +167,9 @@ def run_makeCF(project, database, event, iplot, station=None): All1_filt = AllC[0].copy() All2_filt = AllC[1].copy() All3_filt = AllC[2].copy() - All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) - All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) + All1_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All2_filt.filter('bandpass', freqmin=bph[0], freqmax=bph[1], zerophase=False) + All3_filt.filter('bandpass', freqmin=bpz[0], freqmax=bpz[1], zerophase=False) All1_filt.taper(max_percentage=0.05, type='hann') All2_filt.taper(max_percentage=0.05, type='hann') All3_filt.taper(max_percentage=0.05, type='hann') @@ -209,19 +209,19 @@ def run_makeCF(project, database, event, iplot, station=None): plt.ylim([-1.5, 1.5]) plt.xlabel('Time [s]') plt.ylabel('Normalized Counts') - plt.title('%s, %s, CF-SNR=%7.2f, CF-Slope=%12.2f' % (tr.stats.station, \ - tr.stats.channel, aicpick.getSNR(), aicpick.getSlope())) + plt.title('%s, %s, CF-SNR=%7.2f, CF-Slope=%12.2f' % (tr.stats.station, + tr.stats.channel, aicpick.getSNR(), aicpick.getSlope())) plt.suptitle(tr.stats.starttime) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) + plt.legend([p1, p2, p3, p4, p5], ['Data', 'HOS-CF', 'HOSAIC-CF', 'ARZ-CF', 'ARZAIC-CF']) #plot horizontal traces plt.figure(2) plt.subplot(2,1,1) - tsteph = tpredh / 4 + tsteph = tpredh / 4 th1data = np.arange(0, trH1_filt.stats.npts / trH1_filt.stats.sampling_rate, trH1_filt.stats.delta) th2data = np.arange(0, trH2_filt.stats.npts / trH2_filt.stats.sampling_rate, trH2_filt.stats.delta) tarhcf = np.arange(0, len(arhcf.getCF()) * tsteph, tsteph) + cuttimes[0] + tdeth +tpredh p21, = plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') - p22, = plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') + p22, = plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') p23, = plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') @@ -238,10 +238,10 @@ def run_makeCF(project, database, event, iplot, station=None): plt.ylabel('Normalized Counts') plt.title([trH1_filt.stats.station, trH1_filt.stats.channel]) plt.suptitle(trH1_filt.stats.starttime) - plt.legend([p21, p22, p23], ['Data', 'ARH-CF', 'ARHAIC-CF']) + plt.legend([p21, p22, p23], ['Data', 'ARH-CF', 'ARHAIC-CF']) plt.subplot(2,1,2) plt.plot(th2data, trH2_filt.data/max(trH2_filt.data), 'k') - plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') + plt.plot(arhcf.getTimeArray(), arhcf.getCF()/max(arhcf.getCF()), 'r') plt.plot(arhaiccf.getTimeArray(), arhaiccf.getCF()/max(arhaiccf.getCF())) plt.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'b') plt.plot([aicarhpick.getpick()-0.5, aicarhpick.getpick()+0.5], [1, 1], 'b') @@ -271,7 +271,7 @@ def run_makeCF(project, database, event, iplot, station=None): plt.ylabel('Normalized Counts') plt.title([tr.stats.station, tr.stats.channel]) plt.suptitle(trH1_filt.stats.starttime) - plt.legend([p31, p32], ['Data', 'AR3C-CF']) + plt.legend([p31, p32], ['Data', 'AR3C-CF']) plt.subplot(3,1,2) plt.plot(th1data, trH1_filt.data/max(trH1_filt.data), 'k') plt.plot(ar3ccf.getTimeArray(), ar3ccf.getCF()/max(ar3ccf.getCF()), 'r') diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 61387478..dd36e7da 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -110,7 +110,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): markersize=14) plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', - 'Zero Crossings'], \ + 'Zero Crossings'], loc='best') plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) @@ -183,10 +183,10 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): i = 0 for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): i = i + 1 - if xraw[j - 1] <= 0 and xraw[j] >= 0: + if xraw[j - 1] <= 0 <= xraw[j]: zc1.append(t[ipick][i]) index1.append(i) - elif xraw[j - 1] > 0 and xraw[j] <= 0: + elif xraw[j - 1] > 0 >= xraw[j]: zc1.append(t[ipick][i]) index1.append(i) if len(zc1) == 3: @@ -225,10 +225,10 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): i = 0 for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): i = i + 1 - if xfilt[j - 1] <= 0 and xfilt[j] >= 0: + if xfilt[j - 1] <= 0 <= xfilt[j]: zc2.append(t[ipick][i]) index2.append(i) - elif xfilt[j - 1] > 0 and xfilt[j] <= 0: + elif xfilt[j - 1] > 0 >= xfilt[j]: zc2.append(t[ipick][i]) index2.append(i) if len(zc2) == 3: @@ -263,15 +263,15 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): if P1 is not None and P2 is not None: if P1[0] < 0 and P2[0] < 0: FM = 'D' - elif P1[0] >= 0 and P2[0] < 0: + elif P1[0] >= 0 > P2[0]: FM = '-' - elif P1[0] < 0 and P2[0] >= 0: + elif P1[0] < 0 <= P2[0]: FM = '-' elif P1[0] > 0 and P2[0] > 0: FM = 'U' - elif P1[0] <= 0 and P2[0] > 0: + elif P1[0] <= 0 < P2[0]: FM = '+' - elif P1[0] > 0 and P2[0] <= 0: + elif P1[0] > 0 >= P2[0]: FM = '+' print ("fmpicker: Found polarity %s" % FM) @@ -286,7 +286,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) plt.legend([p1, p2, p3, p4], - ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ + ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], loc='best') plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) ax = plt.gca() @@ -563,8 +563,8 @@ def wadaticheck(pickdic, dttolerance, iplot): f4, = plt.plot(checkedPpicks, wdfit2, 'g') plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', \ - 'Reliable S-Picks', 'Wadati 2'], loc='best') + plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', + 'Reliable S-Picks', 'Wadati 2'], loc='best') else: plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) @@ -653,12 +653,12 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): p1, = plt.plot(t,rms, 'k') p2, = plt.plot(t[inoise], rms[inoise], 'c') p3, = plt.plot(t[isignal],rms[isignal], 'r') - p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], \ - [minsiglevel, minsiglevel], 'g', linewidth=2) + p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], + [minsiglevel, minsiglevel], 'g', linewidth=2) p5, = plt.plot([pick, pick], [min(rms), max(rms)], 'b', linewidth=2) - plt.legend([p1, p2, p3, p4, p5], ['RMS Data', 'RMS Noise Window', \ - 'RMS Signal Window', 'Minimum Signal Level', \ - 'Onset'], loc='best') + plt.legend([p1, p2, p3, p4, p5], ['RMS Data', 'RMS Noise Window', + 'RMS Signal Window', 'Minimum Signal Level', + 'Onset'], loc='best') plt.xlabel('Time [s] since %s' % X[0].stats.starttime) plt.ylabel('Counts') plt.title('Check for Signal Length, Station %s' % X[0].stats.station) @@ -747,15 +747,15 @@ def checkPonsets(pickdic, dttolerance, iplot): if iplot > 1: p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'r+', markersize=14) p2, = plt.plot(igood, np.array(Ppicks)[igood], 'g*', markersize=14) - p3, = plt.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', \ - linewidth=2) + p3, = plt.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', + linewidth=2) for i in range(0, len(Ppicks)): plt.text(i, Ppicks[i] + 0.2, stations[i]) plt.xlabel('Number of P Picks') plt.ylabel('Onset Time [s] from 1.1.1970') - plt.legend([p1, p2, p3], ['Skipped P Picks', 'Good P Picks', 'Median'], \ - loc='best') + plt.legend([p1, p2, p3], ['Skipped P Picks', 'Good P Picks', 'Median'], + loc='best') plt.title('Check P Onsets') plt.show() raw_input() @@ -916,13 +916,13 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): plt.plot(te[isignal], edat[0].data[isignal] / max(edat[0].data) + 1, 'r') plt.plot(tn, ndat[0].data / max(ndat[0].data) + 2, 'k') plt.plot(tn[isignal], ndat[0].data[isignal] / max(ndat[0].data) + 2, 'r') - plt.plot([tz[isignal[0]], tz[isignal[len(isignal) - 1]]], \ - [minsiglevel / max(z), minsiglevel / max(z)], 'g', \ - linewidth=2) + plt.plot([tz[isignal[0]], tz[isignal[len(isignal) - 1]]], + [minsiglevel / max(z), minsiglevel / max(z)], 'g', + linewidth=2) plt.xlabel('Time [s] since %s' % zdat[0].stats.starttime) plt.ylabel('Normalized Counts') - plt.yticks([0, 1, 2], [zdat[0].stats.channel, edat[0].stats.channel, \ - ndat[0].stats.channel]) + plt.yticks([0, 1, 2], [zdat[0].stats.channel, edat[0].stats.channel, + ndat[0].stats.channel]) plt.title('CheckZ4S, Station %s' % zdat[0].stats.station) plt.show() raw_input() diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index 926e3ca3..675aa150 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -73,8 +73,8 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): stations = [stat for stat in phases['stat'][0:-1:3]] - event = createEvent(eventDate, loccinfo, None, 'earthquake', eventNum, - authority_id) + event = createEvent(eventDate, loccinfo, etype='earthquake', resID=eventNum, + authority_id=authority_id) lat = float(loc['LAT']) lon = float(loc['LON']) @@ -130,7 +130,7 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): event.magnitudes.append(magnitude) return event - except AttributeError, e: + except AttributeError as e: raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ insufficient data!'.format(e, phasfn, locfn)) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 6e680d81..1a8ef94c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -814,9 +814,10 @@ class PropertiesDlg(QDialog): if values is not None: self.setValues(values) - def setValues(self, tabValues): + @staticmethod + def setValues(tabValues): settings = QSettings() - for setting, value in tabValues.iteritems(): + for setting, value in tabValues.items(): settings.setValue(setting, value) settings.sync() From 2dd36379a8b60a0cbb374c3d0b6ef23662376731 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Oct 2015 10:38:00 +0200 Subject: [PATCH 0625/1144] prepared application of stealth mode --- pylot/core/pick/CharFuns.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/CharFuns.py index f4b1230f..367841bc 100644 --- a/pylot/core/pick/CharFuns.py +++ b/pylot/core/pick/CharFuns.py @@ -62,7 +62,7 @@ class CharacteristicFunction(object): self.calcCF(self.getDataArray()) self.arpara = np.array([]) self.xpred = np.array([]) - self.stealthMode = stealthMode + self._stealthMode = stealthMode def __str__(self): return '''\n\t{name} object:\n @@ -136,6 +136,9 @@ class CharacteristicFunction(object): def getXCF(self): return self.xcf + def _getStealthMode(self): + return self._stealthMode() + def getDataArray(self, cut=None): ''' If cut times are given, time series is cut from cut[0] (start time) @@ -220,8 +223,8 @@ class AICcf(CharacteristicFunction): def calcCF(self, data): - if self.stealthMode is False: - print 'Calculating AIC ...' + #if self._getStealthMode() is False: + # print 'Calculating AIC ...' x = self.getDataArray() xnp = x[0].data nn = np.isnan(xnp) @@ -259,13 +262,13 @@ class HOScf(CharacteristicFunction): if len(nn) > 1: xnp[nn] = 0 if self.getOrder() == 3: # this is skewness - if self.stealthMode is False: - print 'Calculating skewness ...' + #if self._getStealthMode() is False: + # print 'Calculating skewness ...' y = np.power(xnp, 3) y1 = np.power(xnp, 2) elif self.getOrder() == 4: # this is kurtosis - if self.stealthMode is False: - print 'Calculating kurtosis ...' + #if self._getStealthMode() is False: + # print 'Calculating kurtosis ...' y = np.power(xnp, 4) y1 = np.power(xnp, 2) From 21ffbcabc8d25a2699b1ef294be4c6bb601e4583 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Oct 2015 10:52:05 +0200 Subject: [PATCH 0626/1144] deleted obsolete files --- pylot/core/active/picking_script.py | 107 --- pylot/core/active/seismicArrayPreperation.py | 666 ------------------- 2 files changed, 773 deletions(-) delete mode 100644 pylot/core/active/picking_script.py delete mode 100644 pylot/core/active/seismicArrayPreperation.py diff --git a/pylot/core/active/picking_script.py b/pylot/core/active/picking_script.py deleted file mode 100644 index 6a92b61b..00000000 --- a/pylot/core/active/picking_script.py +++ /dev/null @@ -1,107 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -from obspy import read -from obspy import Stream -from obspy import Trace -from datetime import datetime -import numpy as np - -from pylot.core.active import surveyUtils -from pylot.core.active import seismicshot -import activeSeismoPick -reload(seismicshot) -reload(surveyUtils) - -##################################################################################### -# parameter definitions:############################################################# -#traceslist = list(np.arange(1, 49, 1)) # traces (1-48) -#shotlist = list(np.arange(302, 352, 1)) # shot-files(.dat) (Paffrath: 302-352) (Hauburg: 353-401) (arange+1!) -cutwindow = (0, 0.2) # cut out a part of the trace [seconds] -tmovwind = 0.3 # size of the moving window -windowsize = (5, 0) # windowsize for AIC picker (indices around HOS picks!!) -pickwindow = cutwindow # for local max and threshold picker: fraction of the seismogram used (0...1) TO BE DONE: depends on cutwindow!!!! -folm = 0.6 - -rockeskyll = False -if rockeskyll == True: - receiverfile = "Geophone_interpoliert_rockes" - sourcefile = "Schusspunkte_rockes" - obsdir = "/rscratch/minos22/marcel/flachseismik/rockeskyll_200615_270615/" - filename = 'survey_rockes.pickle' -else: - receiverfile = "Geophone_interpoliert_GZB" - sourcefile = "Schusspunkte_GZB" - obsdir = "/rscratch/minos22/marcel/flachseismik/GZB_26_06_15_01/" - filename = 'survey_GZB.pickle' - -# SNR -tsignal = 0.03 -tgap = 0.0007 -snrthreshold = 1 -###################################################################################### - -vmin = 333 -vmax = 5500 -distBinSize = 2 - -########################################### -############# Settings: ################### -thresholdpick=True -localmaxpick=False - -if thresholdpick == True: pickmethod = "threshold" -if localmaxpick == True: pickmethod = "localmax" - -HosAic = 'hos' # can be 'hos' or 'aic' -########################################### - -starttime = datetime.now() - -print '\n--------------------Starting Script at %s -------------------\n' %starttime.time() -if thresholdpick == True: print 'Using treshold pickmethod!\n' -elif localmaxpick == True: print 'Using local maximum pick method!\n' -if HosAic == 'aic': print 'picking with AIC\n' -if HosAic == 'hos': print 'picking with HOS\n' - -survey = activeSeismoPick.Survey(obsdir, sourcefile, receiverfile, True) -surveyUtils.setFittedSNR(survey.getShotDict()) -print '\nDone after %s seconds!\n------------------------------------------------------------------------------\n' % (datetime.now() - starttime).seconds - -count = 0; tpicksum = starttime - starttime - -for shot in survey.data.values(): - tstartpick = datetime.now(); count += 1 - for traceID in shot.getTraceIDlist(): - distance = shot.getDistance(traceID) # receive distance - - pickwin_used = pickwindow # use pickwindow set in the parameter section - # for higher distances use a linear vmin/vmax to cut out late/early regions with high noise - if distance > 5.: - pwleft = distance/vmax ################## TEST - pwright = distance/vmin - if pwright > cutwindow[1]: - pwright = cutwindow[1] - pickwin_used = (pwleft, pwright) - - shot.setPickwindow(traceID, pickwin_used) - shot.pickTraces(traceID, windowsize, folm, HosAic) # picker - #shot.setManualPicks(traceID, picklist) # set manual picks if given (yet used on 2D only) - - # ++ TEST: set and check SNR before adding to distance bin ############################ - shot.setSNR(traceID) - #if shot.getSNR(traceID)[0] < snrthreshold: - if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): - shot.removePick(traceID) - # -- TEST: set and check SNR before adding to distance bin ############################ - - if shot.getPick(traceID) is not None: - shot.setEarllatepick(traceID) - - tpicksum += (datetime.now() - tstartpick); tpick = tpicksum/count - tremain = (tpick * (len(survey.getShotDict()) - count)) - tend = datetime.now() + tremain - print 'shot: %s, est. time to be finished is %s:%s:%s' % (shot.getShotname(), tend.hour, tend.minute, tend.second) - -survey.saveSurvey(filename) -print '\n--- Finished script ---' -print 'Elapsed time:', datetime.now()-starttime diff --git a/pylot/core/active/seismicArrayPreperation.py b/pylot/core/active/seismicArrayPreperation.py deleted file mode 100644 index 0bd3b1ad..00000000 --- a/pylot/core/active/seismicArrayPreperation.py +++ /dev/null @@ -1,666 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -import numpy as np -from scipy.interpolate import griddata - -class SeisArray(object): - ''' - Can be used to interpolate missing values of a receiver grid, if only support points were measured. - Input file should contain in each line: ('traceID' 'receiverLineID' 'number of the geophone on recLine' 'X' 'Y' 'Z') - - Can be used to generate a velocity grid file (vgrids.in) for FMTOMO with a topography adapting gradient. - - Can be used to generate an interface file for FMTOMO (right now only interface.z used by grid3dg) for the topography. - - Supports vtk output for sources and receivers. - Note: Source and Receiver files for FMTOMO will be generated by the Survey object (because traveltimes will be added directly). - ''' - def __init__(self, recfile): - self._receiverlines = {} - self._receiverCoords = {} - self._measuredReceivers = {} - self._measuredTopo = {} - self._sourceLocs = {} - self._geophoneNumbers = {} - self._receiverlist = open(recfile, 'r').readlines() - self._generateReceiverlines() - self._setReceiverCoords() - self._setGeophoneNumbers() - - def _generateReceiverlines(self): - ''' - Connects the traceIDs to the lineIDs - for each receiverline in a dictionary. - ''' - for receiver in self._receiverlist: - traceID = int(receiver.split()[0]) - lineID = int(receiver.split()[1]) - if not lineID in self._receiverlines.keys(): - self._receiverlines[lineID] = [] - self._receiverlines[lineID].append(traceID) - - def _setReceiverCoords(self): - ''' - Fills the three x, y, z dictionaries with measured coordinates - ''' - for line in self._getReceiverlist(): - traceID = int(line.split()[0]) - x = float(line.split()[3]) - y = float(line.split()[4]) - z = float(line.split()[5]) - self._receiverCoords[traceID] = (x, y, z) - self._measuredReceivers[traceID] = (x, y, z) - - def _setGeophoneNumbers(self): - for line in self._getReceiverlist(): - traceID = int(line.split()[0]) - gphoneNum = float(line.split()[2]) - self._geophoneNumbers[traceID] = gphoneNum - - def _getReceiverlines(self): - return self._receiverlines - - def _getReceiverlist(self): - return self._receiverlist - - def getReceiverCoordinates(self): - return self._receiverCoords - - def _getXreceiver(self, traceID): - return self._receiverCoords[traceID][0] - - def _getYreceiver(self, traceID): - return self._receiverCoords[traceID][1] - - def _getZreceiver(self, traceID): - return self._receiverCoords[traceID][2] - - def _getXshot(self, shotnumber): - return self._sourceLocs[shotnumber][0] - - def _getYshot(self, shotnumber): - return self._sourceLocs[shotnumber][1] - - def _getZshot(self, shotnumber): - return self._sourceLocs[shotnumber][2] - - def _getReceiverValue(self, traceID, coordinate): - setCoordinate = {'X': self._getXreceiver, - 'Y': self._getYreceiver, - 'Z': self._getZreceiver} - return setCoordinate[coordinate](traceID) - - def _getGeophoneNumber(self, traceID): - return self._geophoneNumbers[traceID] - - def getMeasuredReceivers(self): - return self._measuredReceivers - - def getMeasuredTopo(self): - return self._measuredTopo - - def getSourceLocations(self): - return self._sourceLocs - - def _setXvalue(self, traceID, value): - self._checkKey(traceID) - self._receiverCoords[traceID][0] = value - - def _setYvalue(self, traceID, value): - self._checkKey(traceID) - self._receiverCoords[traceID][1] = value - - def _setZvalue(self, traceID, value): - self._checkKey(traceID) - self._receiverCoords[traceID][2] = value - - def _setValue(self, traceID, coordinate, value): - setCoordinate = {'X': self._setXvalue, - 'Y': self._setYvalue, - 'Z': self._setZvalue} - setCoordinate[coordinate](traceID, value) - - def _checkKey(self, traceID): - if not traceID in self._receiverCoords.keys(): - self._receiverCoords[traceID] = [None, None, None] - - def _checkTraceIDdirection(self, traceID1, traceID2): - if traceID2 > traceID1: - direction = +1 - return direction - if traceID2 < traceID1: - direction = -1 - return direction - print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) - - def _checkCoordDirection(self, traceID1, traceID2, coordinate): - ''' - Checks whether the interpolation direction is positive or negative - ''' - if self._getReceiverValue(traceID1, coordinate) < self._getReceiverValue(traceID2, coordinate): - direction = +1 - return direction - if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): - direction = -1 - return direction - print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) - - def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): - ''' - Returns the mean distance between two traceID's depending on the number of geophones in between - ''' - num_spaces = abs(self._getGeophoneNumber(traceID1) - self._getGeophoneNumber(traceID2)) - mean_distance = abs(self._getReceiverValue(traceID1, coordinate) - self._getReceiverValue(traceID2, coordinate))/num_spaces - return mean_distance - - def interpolateValues(self, coordinate): - ''' - Interpolates and sets all values (linear) for coordinate = 'X', 'Y' or 'Z' - ''' - for lineID in self._getReceiverlines().keys(): - number_measured = len(self._getReceiverlines()[lineID]) - for index, traceID1 in enumerate(self._getReceiverlines()[lineID]): - if index + 1 < number_measured: - traceID2 = self._getReceiverlines()[lineID][index + 1] - - traceID_dir = self._checkTraceIDdirection(traceID1, traceID2) - traceID_interp = traceID1 + traceID_dir - - coord_dir = self._checkCoordDirection(traceID1, traceID2, coordinate) - mean_distance = self._interpolateMeanDistances(traceID1, traceID2, coordinate) - - while (traceID_dir * traceID_interp) < (traceID_dir * traceID2): - self._setValue(traceID_interp, coordinate, - (self._getReceiverValue(traceID_interp - traceID_dir, coordinate) - + coord_dir * (mean_distance))) - traceID_interp += traceID_dir - - def addMeasuredTopographyPoints(self, filename): - ''' - Use more measured points for a higher precision of height interpolation. - Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') - ''' - topolist = open(filename, 'r').readlines() - for line in topolist: - line = line.split() - pointID = int(line[0]) - x = float(line[1]) - y = float(line[2]) - z = float(line[3]) - self._measuredTopo[pointID] = (x, y, z) - - def addSourceLocations(self, filename): - ''' - Use source locations for a higher precision of height interpolation. - Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') - - Source locations must be added before they can be written to vtk files. - ''' - topolist = open(filename, 'r').readlines() - for line in topolist: - line = line.split() - pointID = int(line[0]) - x = float(line[1]) - y = float(line[2]) - z = float(line[3]) - self._sourceLocs[pointID] = (x, y, z) - - def interpZcoords4rec(self, method = 'linear'): - ''' - Interpolates z values for all receivers. - ''' - measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() - - for traceID in self.getReceiverCoordinates().keys(): - if type(self.getReceiverCoordinates()[traceID]) is not tuple: - z = griddata((measured_x, measured_y), measured_z, (self._getXreceiver(traceID), self._getYreceiver(traceID)), method = method) - self._setZvalue(traceID, float(z)) - - def _getAngle(self, distance): - ''' - Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. - ''' - PI = np.pi - R = 6371. - angle = distance * 180 / (PI * R) - return angle - - def _getDistance(self, angle): - ''' - Function returns the distance [km] on a Sphere of the radius R = 6371 [km] for an angle. - ''' - PI = np.pi - R = 6371. - distance = angle / 180 * (PI * R) - return distance - - def getMeasuredReceiverLists(self): - ''' - Returns a list of all measured receivers known to SeisArray. - ''' - x = []; y = []; z = [] - for traceID in self.getMeasuredReceivers().keys(): - x.append(self.getMeasuredReceivers()[traceID][0]) - y.append(self.getMeasuredReceivers()[traceID][1]) - z.append(self.getMeasuredReceivers()[traceID][2]) - return x, y, z - - def getMeasuredTopoLists(self): - ''' - Returns a list of all measured topography points known to the SeisArray. - ''' - x = []; y = []; z = [] - for pointID in self.getMeasuredTopo().keys(): - x.append(self.getMeasuredTopo()[pointID][0]) - y.append(self.getMeasuredTopo()[pointID][1]) - z.append(self.getMeasuredTopo()[pointID][2]) - return x, y, z - - def getSourceLocsLists(self): - ''' - Returns a list of all measured source locations known to SeisArray. - ''' - x = []; y = []; z = [] - for pointID in self.getSourceLocations().keys(): - x.append(self.getSourceLocations()[pointID][0]) - y.append(self.getSourceLocations()[pointID][1]) - z.append(self.getSourceLocations()[pointID][2]) - return x, y, z - - def getAllMeasuredPointsLists(self): - ''' - Returns a list of all measured points known to SeisArray. - ''' - mtopo_x, mtopo_y, mtopo_z = self.getMeasuredTopoLists() - msource_x, msource_y, msource_z = self.getSourceLocsLists() - mrec_x, mrec_y, mrec_z = self.getMeasuredReceiverLists() - - x = mtopo_x + mrec_x + msource_x - y = mtopo_y + mrec_y + msource_y - z = mtopo_z + mrec_z + msource_z - return x, y, z - - def getReceiverLists(self): - ''' - Returns a list of all receivers (measured and interpolated). - ''' - x = []; y =[]; z = [] - for traceID in self.getReceiverCoordinates().keys(): - x.append(self.getReceiverCoordinates()[traceID][0]) - y.append(self.getReceiverCoordinates()[traceID][1]) - z.append(self.getReceiverCoordinates()[traceID][2]) - return x, y, z - - def _interpolateXY4rec(self): - ''' - Interpolates the X and Y coordinates for all receivers. - ''' - for coordinate in ('X', 'Y'): - self.interpolateValues(coordinate) - - def interpolateAll(self): - self._interpolateXY4rec() - self.interpZcoords4rec() - - def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, method = 'linear', filename = 'interface1.in'): - ''' - Interpolate Z values on a regular grid with cushion nodes to use it as FMTOMO topography interface. - Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). - - :param: nTheta, number of points in theta (NS) - type: integer - - :param: nPhi, number of points in phi (WE) - type: integer - - :param: thetaSN (S, N) extensions of the model in degree - type: tuple - - :param: phiWE (W, E) extensions of the model in degree - type: tuple - ''' - - surface = [] - elevation = 0.25 # elevate topography so that no source lies above the surface - - if filename is not None: - outfile = open(filename, 'w') - - print "Interpolating topography on regular grid with the dimensions:" - print "nTheta = %s, nPhi = %s, thetaSN = %s, phiWE = %s"%(nTheta, nPhi, thetaSN, phiWE) - print "method = %s, filename = %s" %(method, filename) - - thetaS, thetaN = thetaSN - phiW, phiE = phiWE - - measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() - - # need to determine the delta to add two cushion nodes around the min/max values - thetaDelta = (thetaN - thetaS) / (nTheta - 1) - phiDelta = (phiE - phiW) / (nPhi - 1) - - thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes - - nTotal = len(thetaGrid) * len(phiGrid); count = 0 - for theta in thetaGrid: - for phi in phiGrid: - xval = self._getDistance(phi) - yval = self._getDistance(theta) - z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = method) - # in case the point lies outside, nan will be returned. Find nearest: - if np.isnan(z) == True: - z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = 'nearest') - z = float(z) - surface.append((xval, yval, z)) - count += 1 - progress = float(count) / float(nTotal) * 100 - self._update_progress(progress) - - if filename is not None: - outfile.writelines('%10s\n'%(z + elevation)) - - return surface - - def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, - thetaSN = (-0.2, 1.2), phiWE = (-0.2, 1.2), - Rbt = (-62.0, 6.0), vbot = 5.5, filename = 'vgrids.in', - method = 'linear' ): - ''' - Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. - - :param: nTheta, number of points in theta (NS) - type: integer - - :param: nPhi, number of points in phi (WE) - type: integer - - :param: nR, number of points in depth - type: integer - - :param: thetaSN (S, N) extensions of the model in degree - type: tuple - - :param: phiWE (W, E) extensions of the model in degree - type: tuple - - :param: Rbt (bot, top) extensions of the model in km - type: tuple - - :param: vbot, velocity at the bottom of the model - type: real - ''' - - def getRad(angle): - PI = np.pi - rad = angle / 180 * PI - return rad - - def getZmax(surface): - z = [] - for point in surface: - z.append(point[2]) - return max(z) - - R = 6371 - vmin = 0.34 - decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) - outfile = open(filename, 'w') - - thetaS, thetaN = thetaSN - phiW, phiE = phiWE - rbot = Rbt[0] + R - rtop = Rbt[1] + R - - # need to determine the delta to add two cushion nodes around the min/max values - thetaDelta = abs(thetaN - thetaS) / (nTheta - 1) - phiDelta = abs(phiE - phiW) / (nPhi - 1) - rDelta = abs(rbot - rtop) / (nR - 1) - - # create a regular grid including +2 cushion nodes in every direction - thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes - rGrid = np.linspace(rbot - rDelta, rtop + rDelta, num = nR + 2) # +2 cushion nodes - - nTotal = len(rGrid) * len(thetaGrid) * len(phiGrid) - print "Total number of grid nodes: %s"%nTotal - - # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' %(1, 1)) - outfile.writelines('%10s %10s %10s\n' %(nR + 2, nTheta + 2, nPhi + 2)) - outfile.writelines('%10s %10s %10s\n' %(rDelta, getRad(thetaDelta), getRad(phiDelta))) - outfile.writelines('%10s %10s %10s\n' %(rbot - rDelta, getRad(thetaS - thetaDelta), getRad(phiW - phiDelta))) - - surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method, filename = None) - zmax = getZmax(surface) - - print "\nGenerating velocity grid for FMTOMO. Output filename = %s, interpolation method = %s"%(filename, method) - print "nTheta = %s, nPhi = %s, nR = %s, thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt) - count = 0 - for radius in rGrid: - for theta in thetaGrid: - for phi in phiGrid: - xval = self._getDistance(phi) - yval = self._getDistance(theta) - for point in surface: - if point[0] == xval and point[1] == yval: - z = point[2] - if radius > (R + z + 1): - vel = 0.0 - # elif radius > (R + z - 15): ########### TESTING - # vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * 1.0 + vmin ########################## - else: - vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * vbot + vmin ########################## - count += 1 - outfile.writelines('%10s %10s\n'%(vel, decm)) - - progress = float(count) / float(nTotal) * 100 - self._update_progress(progress) - - outfile.close() - - def exportAll(self, filename = 'interpolated_receivers.out'): - recfile_out = open(filename, 'w') - count = 0 - for traceID in self.getReceiverCoordinates().keys(): - count += 1 - x, y, z = self.getReceiverCoordinates()[traceID] - recfile_out.writelines('%5s %15s %15s %15s\n' %(traceID, x, y, z)) - print "Exported coordinates for %s traces to file > %s" %(count, filename) - recfile_out.close() - - def plotArray2D(self, plot_topo = False, highlight_measured = False, annotations = True): - import matplotlib.pyplot as plt - plt.interactive(True) - plt.figure() - xmt, ymt, zmt = self.getMeasuredTopoLists() - xsc, ysc, zsc = self.getSourceLocsLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xrc, yrc, zrc = self.getReceiverLists() - - plt.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') - plt.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') - - if plot_topo == True: - plt.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') - if highlight_measured == True: - plt.plot(xmr, ymr, 'ro', label = 'measured receivers') - - plt.xlabel('X [m]') - plt.ylabel('Y [m]') - plt.legend() - if annotations == True: - for traceID in self.getReceiverCoordinates().keys(): - plt.annotate(str(traceID), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small') - - def plotArray3D(self, ax = None): - import matplotlib.pyplot as plt - from mpl_toolkits.mplot3d import Axes3D - plt.interactive(True) - - if ax == None: - fig = plt.figure() - ax = plt.axes(projection = '3d') - - xmt, ymt, zmt = self.getMeasuredTopoLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xin, yin, zin = self.getReceiverLists() - - ax.plot(xmt, ymt, zmt, 'b*', markersize = 10, label = 'measured topo points') - ax.plot(xin, yin, zin, 'k.', markersize = 10, label = 'interpolated receivers') - ax.plot(xmr, ymr, zmr, 'ro', label = 'measured receivers') - ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') - ax.legend() - - return ax - - - def plotSurface3D(self, ax = None, step = 0.5, method = 'linear'): - from matplotlib import cm - import matplotlib.pyplot as plt - from mpl_toolkits.mplot3d import Axes3D - plt.interactive(True) - - if ax == None: - fig = plt.figure() - ax = plt.axes(projection = '3d') - - xmt, ymt, zmt = self.getMeasuredTopoLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - - x = xmt + xmr - y = ymt + ymr - z = zmt + zmr - - xaxis = np.arange(min(x)+1, max(x), step) - yaxis = np.arange(min(y)+1, max(y), step) - - xgrid, ygrid = np.meshgrid(xaxis, yaxis) - - zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) - - ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) - - ax.set_zlim(-(max(x) - min(x)/2),(max(x) - min(x)/2)) - ax.set_aspect('equal') - - ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') - ax.legend() - - return ax - - def _update_progress(self, progress): - sys.stdout.write("%d%% done \r" % (progress) ) - sys.stdout.flush() - - def receivers2VTK(self, filename = 'receivers.vtk'): - ''' - Generates vtk files from all receivers of the SeisArray object. - ''' - outfile = open(filename, 'w') - traceIDs = [] - - for traceID in self.getReceiverCoordinates(): - traceIDs.append(traceID) - - nPoints = len(traceIDs) - - # write header - print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Receivers with traceIDs\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) - - # write coordinates - print("Writing coordinates to VTK file...") - for traceID in traceIDs: - x = self._getXreceiver(traceID) - y = self._getYreceiver(traceID) - z = self._getZreceiver(traceID) - - outfile.writelines('%10f %10f %10f \n' %(x, y, z)) - - outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) - - # write indices - print("Writing indices to VTK file...") - for index in range(nPoints): - outfile.writelines('%10d %10d\n' %(1, index)) - - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - outfile.writelines('SCALARS traceIDs int %d\n' %(1)) - outfile.writelines('LOOKUP_TABLE default\n') - - # write traceIDs - print("Writing traceIDs to VTK file...") - for traceID in traceIDs: - outfile.writelines('%10d\n' %traceID) - - outfile.close() - print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) - return - - def sources2VTK(self, filename = 'sources.vtk'): - ''' - Generates vtk-files for all source locations in the SeisArray object. - ''' - outfile = open(filename, 'w') - shotnumbers = [] - - for shotnumber in self.getSourceLocations(): - shotnumbers.append(shotnumber) - - nPoints = len(shotnumbers) - - # write header - print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Shots with shotnumbers\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) - - # write coordinates - print("Writing coordinates to VTK file...") - for shotnumber in shotnumbers: - x = self._getXshot(shotnumber) - y = self._getYshot(shotnumber) - z = self._getZshot(shotnumber) - - outfile.writelines('%10f %10f %10f \n' %(x, y, z)) - - outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) - - # write indices - print("Writing indices to VTK file...") - for index in range(nPoints): - outfile.writelines('%10d %10d\n' %(1, index)) - - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - outfile.writelines('SCALARS shotnumbers int %d\n' %(1)) - outfile.writelines('LOOKUP_TABLE default\n') - - # write shotnumber - print("Writing shotnumbers to VTK file...") - for shotnumber in shotnumbers: - outfile.writelines('%10d\n' %shotnumber) - - outfile.close() - print("Wrote receiver grid for %d points to file: %s" %(nPoints, filename)) - return - - - def saveSeisArray(self, filename = 'seisArray.pickle'): - import cPickle - outfile = open(filename, 'wb') - - cPickle.dump(self, outfile, -1) - print('saved SeisArray to file %s'%(filename)) - - @staticmethod - def from_pickle(filename): - import cPickle - infile = open(filename, 'rb') - return cPickle.load(infile) From 2b3e40b3b65d7e921ce6cc3d5de350373c3302ee Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Oct 2015 13:30:55 +0200 Subject: [PATCH 0627/1144] commit after recover of scripts from .pyc: figure refreshing of plotAllPicks, cbar, survey.recover() --- pylot/core/active/activeSeismoPick.py | 56 +++++++++++++++------------ 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index c222c737..3dc97353 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -19,8 +19,8 @@ class Survey(object): self.setParametersForShots() self._removeAllEmptyTraces() self._updateShots() - self.setArtificialPick(0, 0) # artificial pick at source origin - + self.setArtificialPick(0, 0) + def _generateSurvey(self): from obspy.core import read @@ -29,8 +29,7 @@ class Survey(object): for shotnumber in shotlist: # loop over data files # generate filenames and read manual picks to a list obsfile = self._obsdir + str(shotnumber) + '_pickle.dat' - - if not obsfile in shot_dict.keys(): + if obsfile not in shot_dict.keys(): shot_dict[shotnumber] = [] shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) shot_dict[shotnumber].setParameters('shotnumber', shotnumber) @@ -168,7 +167,7 @@ class Survey(object): def countAllTraces(self): numtraces = 0 for shot in self.getShotlist(): - for rec in self.getReceiverlist(): + for rec in self.getReceiverlist(): ### shot.getReceiverlist etc. numtraces += 1 return numtraces @@ -326,7 +325,7 @@ class Survey(object): fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1, wspace = 0, hspace = 0) - def plotAllPicks(self, plotRemoved = False, colorByVal = 'log10SNR', ax = None, refreshPlot = False): + def plotAllPicks(self, plotRemoved = False, colorByVal = 'log10SNR', ax = None, cbar = None, refreshPlot = False): ''' Plots all picks over the distance between source and receiver. Returns (ax, region). Picks can be checked and removed by using region class (pylot.core.active.surveyPlotTools.regions) @@ -378,20 +377,20 @@ class Survey(object): spe.append(shot.getSymmetricPickError(traceID)) color = {'log10SNR': snrlog, - 'pickerror': pickerror, - 'spe': spe} - + 'pickerror': pickerror, + 'spe': spe} + self.color = color if refreshPlot is False: - ax = self.createPlot(dist, pick, color[colorByVal], label = '%s'%colorByVal) - region = regions(ax, self) + ax, cbar = self.createPlot(dist, pick, color[colorByVal], label='%s' % colorByVal) + region = regions(ax, cbar, self) ax.legend() - return ax, region - elif refreshPlot is True: - ax = self.createPlot(dist, pick, color[colorByVal], label = '%s'%colorByVal, ax = ax) + return (ax, region) + if refreshPlot is True: + ax, cbar = self.createPlot(dist, pick, color[colorByVal], label='%s' % colorByVal, ax=ax, cbar=cbar) ax.legend() return ax - def createPlot(self, dist, pick, inkByVal, label, ax = None): + def createPlot(self, dist, pick, inkByVal, label, ax = None, cbar = None): import matplotlib.pyplot as plt plt.interactive(True) cm = plt.cm.jet @@ -399,20 +398,27 @@ class Survey(object): print('Generating new plot...') fig = plt.figure() ax = fig.add_subplot(111) - fig = ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) - cbar = plt.colorbar(fig, fraction = 0.05) + sc = ax.scatter(dist, pick, cmap=cm, c=inkByVal, s=5, edgecolors='none', label=label) + cbar = plt.colorbar(sc, fraction=0.05) cbar.set_label(label) - plt.title('Plot of all Picks') - plt.xlabel('Distance [m]') - plt.ylabel('Time [s]') + ax.set_xlabel('Distance [m]') + ax.set_ylabel('Time [s]') + ax.text(0.5, 0.95, 'Plot of all picks', transform=ax.transAxes, horizontalalignment='center') else: - ax.scatter(dist, pick, cmap = cm, c = inkByVal, s = 5, edgecolors = 'none', label = label) - - return ax + sc = ax.scatter(dist, pick, cmap=cm, c=inkByVal, s=5, edgecolors='none', label=label) + cbar = plt.colorbar(sc, cax=cbar.ax) + cbar.set_label(label) + ax.set_xlabel('Distance [m]') + ax.set_ylabel('Time [s]') + ax.text(0.5, 0.95, 'Plot of all picks', transform=ax.transAxes, horizontalalignment='center') + return (ax, cbar) def _update_progress(self, shotname, tend, progress): - sys.stdout.write("Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r" - %(shotname, tend.hour, tend.minute, tend.second, progress)) + sys.stdout.write('Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r' % (shotname, + tend.hour, + tend.minute, + tend.second, + progress)) sys.stdout.flush() def saveSurvey(self, filename = 'survey.pickle'): From 52e304b0af58cc93390396a792f7fd55d843f249 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Oct 2015 13:33:11 +0200 Subject: [PATCH 0628/1144] commit after recover of scripts from .pyc: repick button for .plot_traces implemented --- pylot/core/active/seismicshot.py | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 8adc03ae..07cd2b43 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -34,7 +34,8 @@ class SeismicShot(object): self.snr = {} self.snrthreshold = {} self.timeArray = {} - self.paras = {'shotname': obsfile} + self.paras = {} + self.paras['shotname'] = obsfile def removeEmptyTraces(self): traceIDs = [] @@ -262,23 +263,19 @@ class SeismicShot(object): traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] if len(traces) == 1: return Stream(traces) - else: - self.setPick(traceID, None) - print('Warning: ambigious or empty traceID: %s' % traceID) + self.setPick(traceID, None) + print 'Warning: ambigious or empty traceID: %s' % traceID #raise ValueError('ambigious or empty traceID: %s' % traceID) - def pickTraces(self, traceID, pickmethod, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## + def pickTraces(self, traceID, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## # LOCALMAX NOT IMPLEMENTED! ''' Intitiate picking for a trace. :param: traceID :type: int - - :param: pickmethod, use either 'threshold' or 'localmax' method. (localmax not yet implemented 04_08_15) - :type: string - + :param: cutwindow (equals HOScf 'cut' variable) :type: tuple @@ -301,15 +298,7 @@ class SeismicShot(object): aiccf = self.getAICcf(traceID) self.timeArray[traceID] = hoscf.getTimeArray() - - if pickmethod == 'threshold': - aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) - - #setpick = {'threshold':self.threshold, - # 'localmax':self.localmax} - - #aiccftime, hoscftime = setpick[pickmethod](hoscf, aiccf, windowsize, pickwindow) - + aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) setHosAic = {'hos': hoscftime, 'aic': aiccftime} @@ -621,13 +610,13 @@ class SeismicShot(object): button.on_clicked(connectButton) self.traces4plot = {} - if not traceID in self.traces4plot.keys(): + if traceID not in self.traces4plot.keys(): self.traces4plot[traceID] = {'fig': fig, 'ax1': ax1, 'ax2': ax2, 'axb': axb, 'button': button, - 'cid': None,} + 'cid': None} self._drawStream(traceID) self._drawCFs(traceID, folm) From eb873fd69fa1efffd73b78923a7a8182e895cf30 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Oct 2015 13:33:47 +0200 Subject: [PATCH 0629/1144] commit after recover of scripts from .pyc: code cleanup after transfer of functions to survey methods --- pylot/core/active/surveyUtils.py | 123 +------------------------------ 1 file changed, 1 insertion(+), 122 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 7cfbeae8..d6fb1d15 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -1,52 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from pylab import * -startpos = [] -endpos = [] - -def generateSurvey(obsdir, shotlist): - from obspy.core import read - from pylot.core.active import seismicshot - - shot_dict = {} - for shotnumber in shotlist: # loop over data files - # generate filenames and read manual picks to a list - obsfile = obsdir + str(shotnumber) + '_pickle.dat' - #obsfile = obsdir + str(shotnumber) + '.dat' - - if not obsfile in shot_dict.keys(): - shot_dict[shotnumber] = [] - shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) - shot_dict[shotnumber].setParameters('shotnumber', shotnumber) - - return shot_dict - -def setParametersForShots(cutwindow, tmovwind, tsignal, tgap, receiverfile, sourcefile, shot_dict): - for shot in shot_dict.values(): - shot.setCut(cutwindow) - shot.setTmovwind(tmovwind) - shot.setTsignal(tsignal) - shot.setTgap(tgap) - shot.setRecfile(receiverfile) - shot.setSourcefile(sourcefile) - shot.setOrder(order = 4) - -def removeEmptyTraces(shot_dict): - filename = 'removeEmptyTraces.out' - filename2 = 'updateTraces.out' - outfile = open(filename, 'w') - outfile2 = open(filename2, 'w') - for shot in shot_dict.values(): - del_traceIDs = shot.updateTraceList() - removed = shot.removeEmptyTraces() - if removed is not None: - outfile.writelines('shot: %s, removed empty traces: %s\n' %(shot.getShotnumber(), removed)) - outfile2.writelines('shot: %s, removed traceID(s) %s because they were not found in the corresponding stream\n' %(shot.getShotnumber(), del_traceIDs)) - print '\nremoveEmptyTraces, updateTraces: Finished! See %s and %s for more information of removed traces.\n' %(filename, filename2) - outfile.close() - outfile2.close() - +import numpy as np def readParameters(parfile, parameter): from ConfigParser import ConfigParser @@ -104,76 +56,6 @@ def setFittedSNR(shot_dict, shiftdist = 5, p1 = 0.004, p2 = -0.004): shot.setSNRthreshold(traceID, 1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) ### s.o. print "setFittedSNR: Finished setting of fitted SNR-threshold" -#def linearInterp(dist_med, dist_start - -def exportFMTOMO(shot_dict, directory = 'FMTOMO_export', sourcefile = 'input_sf.in', ttFileExtension = '.tt'): - count = 0 - fmtomo_factor = 1000 # transforming [m/s] -> [km/s] - LatAll = []; LonAll = []; DepthAll = [] - srcfile = open(directory + '/' + sourcefile, 'w') - srcfile.writelines('%10s\n' %len(shot_dict)) # number of sources - for shotnumber in getShotlist(shot_dict): - shot = getShotForShotnumber(shot_dict, shotnumber) - ttfilename = str(shotnumber) + ttFileExtension - (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) - srcfile.writelines('%10s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z)) # lat, lon, depth - LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) - srcfile.writelines('%10s\n' %1) # - srcfile.writelines('%10s %10s %10s\n' %(1, 1, ttfilename)) - ttfile = open(directory + '/' + ttfilename, 'w') - traceIDlist = shot.getTraceIDlist() - traceIDlist.sort() - ttfile.writelines(str(countPickedTraces(shot)) + '\n') - for traceID in traceIDlist: - if shot.getPick(traceID) is not None: - pick = shot.getPick(traceID) * fmtomo_factor - delta = shot.getPickError(traceID) * fmtomo_factor - (x, y, z) = shot.getRecLoc(traceID) - ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) - LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) - count += 1 - ttfile.close() - srcfile.close() - print 'Wrote output for %s traces' %count - print 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems to take Lat, Lon, Depth and creates output for FMTOMO as Depth, Lat, Lon' - print 'Dimensions of the seismic Array, transformed for FMTOMO, are Depth(%s, %s), Lat(%s, %s), Lon(%s, %s)'%( - min(DepthAll), max(DepthAll), min(LatAll), max(LatAll), min(LonAll), max(LonAll)) - -def getShotlist(shot_dict): - shotlist = [] - for shot in shot_dict.values(): - shotlist.append(shot.getShotnumber()) - shotlist.sort() - return shotlist - -def getShotForShotnumber(shot_dict, shotnumber): - for shot in shot_dict.values(): - if shot.getShotnumber() == shotnumber: - return shot - -def getAngle(distance): - ''' - Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. - ''' - import numpy as np - PI = np.pi - R = 6371. - angle = distance * 180 / (PI * R) - return angle - -def countPickedTraces(shot): - numtraces = 0 - for traceID in shot.getTraceIDlist(): - if shot.getPick(traceID) is not None: - numtraces += 1 - print "countPickedTraces: Found %s picked traces in shot number %s" %(numtraces, shot.getShotnumber()) - return numtraces - -def countAllPickedTraces(shot_dict): - traces = 0 - for shot in shot_dict.values(): - traces += countPickedTraces(shot) - return traces def findTracesInRanges(shot_dict, distancebin, pickbin): ''' @@ -199,6 +81,3 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): shots_found[shot.getShotnumber()].append(traceID) return shots_found - - - From 0223869df6224678955f1949a103d98031c374c3 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Oct 2015 13:35:02 +0200 Subject: [PATCH 0630/1144] commit after recover of scripts from .pyc: implementation of buttons for plotAllPicks/region object, printOutput to figure window, refreshing now with SNR, PE, SPE --- pylot/core/active/surveyPlotTools.py | 305 ++++++++++++++++++--------- 1 file changed, 203 insertions(+), 102 deletions(-) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index 79880445..9a63c21e 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -12,22 +12,29 @@ class regions(object): regions.chooseRectangles(): - lets the user choose several rectangular regions in the plot - - regions.plotTracesInRegions(): + + regions.plotTracesInActiveRegions(): - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) - - regions.setActiveRegionsForDeletion(): + + regions.setAllActiveRegionsForDeletion(): - highlights all shots in a the active regions for deletion - - regions.deleteMarkedPicks(): + + regions.deleteAllMarkedPicks(): - deletes the picks (pick flag set to 0) for all shots set for deletion regions.deselectSelection(number): - deselects the region of number = number - + ''' - def __init__(self, ax, survey): + + def __init__(self, ax, cbar, survey): self.ax = ax + self.cbar = cbar + self.cbv = 'log10SNR' + self._xlim0 = self.ax.get_xlim() + self._ylim0 = self.ax.get_ylim() + self._xlim = self.ax.get_xlim() + self._ylim = self.ax.get_ylim() self.survey = survey self.shot_dict = self.survey.getShotDict() self._x0 = [] @@ -36,31 +43,55 @@ class regions(object): self._y1 = [] self._polyx = [] self._polyy = [] + self.buttons = {} self._allpicks = None self.shots_found = {} self.shots_for_deletion = {} self._generateList() + self._addButtons() + self.addTextfield() + self.drawFigure() + + def _generateList(self): + allpicks = [] + for shot in self.shot_dict.values(): + for traceID in shot.getTraceIDlist(): + allpicks.append((shot.getDistance(traceID), + shot.getPickIncludeRemoved(traceID), + shot.getShotnumber(), + traceID, + shot.getFlag(traceID))) + + allpicks.sort() + self._allpicks = allpicks + + def getShotDict(self): + return self.shot_dict + + def getShotsForDeletion(self): + return self.shots_for_deletion def _onselect_clicks(self, eclick, erelease): - 'eclick and erelease are matplotlib events at press and release' - #print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) - #print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) - print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)'%(eclick.xdata, eclick.ydata, erelease.xdata, erelease.ydata) + '''eclick and erelease are matplotlib events at press and release''' + print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)' % (eclick.xdata, + eclick.ydata, + erelease.xdata, + erelease.ydata) x0 = min(eclick.xdata, erelease.xdata) x1 = max(eclick.xdata, erelease.xdata) y0 = min(eclick.ydata, erelease.ydata) y1 = max(eclick.ydata, erelease.ydata) shots, numtraces = self.findTracesInShotDict((x0, x1), (y0, y1)) - print('Found %d traces in rectangle: %s' %(numtraces, shots)) - + self.printOutput('Found %d traces in rectangle: %s' % (numtraces, shots)) key = self.getKey() self.shots_found[key] = {'shots': shots, 'selection': 'rect', 'xvalues': (x0, x1), 'yvalues': (y0, y1)} self.markRectangle((x0, x1), (y0, y1), key) - + self.disconnectRect() + def _onselect_verts(self, verts): x = verts[0][0] y = verts[0][1] @@ -72,6 +103,51 @@ class regions(object): def _onpress(self, event): if event.button == 3: self.disconnectPoly() + self.printOutput('Disconnected polygon selection') + + def addTextfield(self, xpos = 0, ypos = 0.95, width = 1, height = 0.03): + self.axtext = self.ax.figure.add_axes([xpos, + ypos, + width, + height]) + self.axtext.xaxis.set_visible(False) + self.axtext.yaxis.set_visible(False) + + def writeInTextfield(self, text = None): + self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) + self.axtext.clear() + self.axtext.text(0.01, 0.5, text, verticalalignment='center', horizontalalignment='left') + self.drawFigure() + + def _addButtons(self): + xpos1 = 0.13 + xpos2 = 0.6 + dx = 0.06 + self.addButton('Rect', self.chooseRectangles, xpos=xpos1, color='white') + self.addButton('Poly', self.choosePolygon, xpos=xpos1 + dx, color='white') + self.addButton('Plot', self.plotTracesInActiveRegions, xpos=xpos1 + 2 * dx, color='yellow') + self.addButton('SNR', self.refreshLog10SNR, xpos=xpos1 + 3 * dx, color='cyan') + self.addButton('PE', self.refreshPickerror, xpos=xpos1 + 4 * dx, color='cyan') + self.addButton('SPE', self.refreshSPE, xpos=xpos1 + 5 * dx, color='cyan') + self.addButton('DesLst', self.deselectLastSelection, xpos=xpos2 + dx, color='green') + self.addButton('SelAll', self.setAllActiveRegionsForDeletion, xpos=xpos2 + 2 * dx) + self.addButton('DelAll', self.deleteAllMarkedPicks, xpos=xpos2 + 3 * dx, color='red') + + def addButton(self, name, action, xpos, ypos = 0.91, color = None): + from matplotlib.widgets import Button + self.buttons[name] = {'ax': None, + 'button': None, + 'action': action, + 'xpos': xpos} + ax = self.ax.figure.add_axes([xpos, + ypos, + 0.05, + 0.03]) + button = Button(ax, name, color=color, hovercolor='grey') + button.on_clicked(action) + self.buttons[name]['ax'] = ax + self.buttons[name]['button'] = button + self.buttons[name]['xpos'] = xpos def getKey(self): if self.shots_found.keys() == []: @@ -81,16 +157,20 @@ class regions(object): return key def drawPolyLine(self): + self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) x = self._polyx y = self._polyy if len(x) >= 2 and len(y) >= 2: - plt.plot(x[-2:], y[-2:], 'k') + self.ax.plot(x[-2:], y[-2:], 'k', alpha=0.1) + self.drawFigure() def drawLastPolyLine(self): + self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) x = self._polyx y = self._polyy if len(x) >= 2 and len(y) >= 2: - plt.plot((x[-1], x[0]), (y[-1], y[0]), 'k') + self.ax.plot((x[-1], x[0]), (y[-1], y[0]), 'k', alpha=0.1) + self.drawFigure() def finishPolygon(self): self.drawLastPolyLine() @@ -103,65 +183,70 @@ class regions(object): shots, numtraces = self.findTracesInPoly(x, y) self.shots_found[key] = {'shots': shots, - 'selection': 'poly', - 'xvalues': x, - 'yvalues': y} + 'selection': 'poly', + 'xvalues': x, + 'yvalues': y} + self.printOutput('Found %d traces in polygon: %s' % (numtraces, shots)) - print('Found %d traces in polygon: %s' %(numtraces, shots)) + def printOutput(self, text): + print text + self.writeInTextfield(text) - def markPolygon(self, x, y, key = None, color = 'grey', alpha = 0.1, linewidth = 1): - from matplotlib.patches import Polygon - poly = Polygon(np.array(zip(x, y)), color = color, alpha = alpha, lw = linewidth) - self.ax.add_patch(poly) - if key is not None: - self.ax.text((min(x) + (max(x) - min(x)) / 2), (min(y) + (max(y) - min(y)) / 2), str(key)) - self.drawFigure() - - def disconnectPoly(self): - self.ax.figure.canvas.mpl_disconnect(self._cid) - del self._cid - self.finishPolygon() - self._lasso.disconnect_events() - print('disconnected poly selection\n') - - def disconnectRect(self): - self.ax.figure.canvas.mpl_disconnect(self._cid) - del self._cid - self._rectangle.disconnect_events() - print('disconnected rectangle selection\n') - - def chooseRectangles(self): + def chooseRectangles(self, event = None): ''' Activates matplotlib widget RectangleSelector. ''' from matplotlib.widgets import RectangleSelector - - print('Select rectangle is active') - self._cid = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) + if hasattr(self, '_cidPoly'): + self.disconnectPoly() + self.printOutput('Select rectangle is active. Press and hold left mousebutton.') + self._cidRect = None + self._cidRect = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) self._rectangle = RectangleSelector(self.ax, self._onselect_clicks) return self._rectangle - def choosePolygon(self): + def choosePolygon(self, event = None): ''' Activates matplotlib widget LassoSelector. ''' from matplotlib.widgets import LassoSelector - - print('Select polygon is active') - self._cid = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) + if hasattr(self, '_cidRect'): + self.disconnectRect() + self.printOutput('Select polygon is active. Add points with leftclick. Finish with rightclick.') + self._cidPoly = None + self._cidPoly = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) self._lasso = LassoSelector(self.ax, self._onselect_verts) return self._lasso - - def deselectLastSelection(self): + + def disconnectPoly(self, event = None): + if not hasattr(self, '_cidPoly'): + self.printOutput('no poly selection found') + return + self.ax.figure.canvas.mpl_disconnect(self._cidPoly) + del self._cidPoly + self.finishPolygon() + self._lasso.disconnect_events() + print 'disconnected poly selection\n' + + def disconnectRect(self, event = None): + if not hasattr(self, '_cidRect'): + self.printOutput('no rectangle selection found') + return + self.ax.figure.canvas.mpl_disconnect(self._cidRect) + del self._cidRect + self._rectangle.disconnect_events() + print 'disconnected rectangle selection\n' + + def deselectLastSelection(self, event = None): if self.shots_found.keys() == []: - print('No selection found.') + self.printOutput('No selection found.') return key = max(self.shots_found.keys()) self.deselectSelection(key) def deselectSelection(self, key, color = 'green', alpha = 0.1): - if not key in self.shots_found.keys(): - print('No selection found.') + if key not in self.shots_found.keys(): + self.printOutput('No selection found.') return if color is not None: if self.shots_found[key]['selection'] == 'rect': @@ -175,27 +260,11 @@ class regions(object): key = key, color = color, alpha = alpha, linewidth = 1) value = self.shots_found.pop(key) - print('Deselected selection number %d'% key) - return - - def _generateList(self): - allpicks = [] - for shot in self.shot_dict.values(): - for traceID in shot.getTraceIDlist(): - allpicks.append((shot.getDistance(traceID), shot.getPickIncludeRemoved(traceID), - shot.getShotnumber(), traceID, shot.getFlag(traceID))) - allpicks.sort() - self._allpicks = allpicks - - def getShotDict(self): - return self.shot_dict - - def getShotsForDeletion(self): - return self.shots_for_deletion + self.printOutput('Deselected selection number %d' % key) def findTracesInPoly(self, x, y, picks = 'normal', highlight = True): def dotproduct(v1, v2): - return sum((a*b) for a, b in zip(v1, v2)) + return sum((a * b for a, b in zip(v1, v2))) def getlength(v): return math.sqrt(dotproduct(v, v)) @@ -205,7 +274,7 @@ class regions(object): def insidePoly(x, y, pickX, pickY): angle = 0 - epsilon = 10e-8 + epsilon = 1e-07 for index in range(len(x)): xval1 = x[index - 1]; yval1 = y[index - 1] xval2 = x[index]; yval2 = y[index] @@ -214,7 +283,7 @@ class regions(object): return True if len(x) == 0 or len(y) == 0: - print('No polygon defined.') + self.printOutput('No polygon defined.') return shots_found = {}; numtraces = 0 @@ -229,7 +298,7 @@ class regions(object): pickX = shot.getDistance(traceID) pickY = shot.getPick(traceID) if insidePoly(x, y, pickX, pickY): - if not shotnumber in shots_found.keys(): + if shotnumber not in shots_found.keys(): shots_found[shotnumber] = [] shots_found[shotnumber].append(traceID) if highlight == True: @@ -251,7 +320,7 @@ class regions(object): dist, pick, shotnumber, traceID, flag = line if flag == pickflag: continue ### IMPROVE THAT if (x0 <= dist <= x1 and y0 <= pick <= y1): - if not shotnumber in shots_found.keys(): + if shotnumber not in shots_found.keys(): shots_found[shotnumber] = [] shots_found[shotnumber].append(traceID) if highlight == True: @@ -271,10 +340,9 @@ class regions(object): self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s = 50, marker = 'o', facecolors = 'none', edgecolors = 'm', alpha = 1) if annotations == True: - self.ax.annotate(s = 's%s|t%s'%(shot.getShotnumber(), traceID), xy = (shot.getDistance(traceID), shot.getPick(traceID)), fontsize = 'xx-small') - self.ax.set_ylim(shot.getCut()) + self.ax.annotate(s='s%s|t%s' % (shot.getShotnumber(), traceID), xy=(shot.getDistance(traceID), shot.getPick(traceID)), fontsize='xx-small') - def highlightAllRegions(self): + def highlightAllActiveRegions(self): ''' Highlights all picks in all active regions. ''' @@ -284,7 +352,7 @@ class regions(object): self.highlightPick(self.shot_dict[shotnumber], traceID) self.drawFigure() - def plotTracesInRegions(self, keys = 'all', maxfigures = 20): + def plotTracesInActiveRegions(self, event = None, keys = 'all', maxfigures = 20): ''' Plots all traces in the active region or for all specified keys. @@ -293,7 +361,7 @@ class regions(object): :param: maxfigures, maximum value of figures opened :type: int - ''' + ''' count = 0 if keys == 'all': keys = self.shots_found.keys() @@ -312,9 +380,9 @@ class regions(object): break shot.plot_traces(traceID) else: - print('No picks defined in that region(s)') + self.printOutput('No picks defined in that region(s)') - def setActiveRegionsForDeletion(self): + def setAllActiveRegionsForDeletion(self, event = None): keys = [] for key in self.shots_found.keys(): keys.append(key) @@ -326,14 +394,16 @@ class regions(object): for key in keys: for shotnumber in self.shots_found[key]['shots'].keys(): - if not shotnumber in self.shots_for_deletion: + if shotnumber not in self.shots_for_deletion: self.shots_for_deletion[shotnumber] = [] for traceID in self.shots_found[key]['shots'][shotnumber]: - if not traceID in self.shots_for_deletion[shotnumber]: + if traceID not in self.shots_for_deletion[shotnumber]: self.shots_for_deletion[shotnumber].append(traceID) self.deselectSelection(key, color = 'red', alpha = 0.2) - print 'Set region(s) %s for deletion'%keys + self.deselectSelection(key, color='red', alpha=0.2) + + self.printOutput('Set region(s) %s for deletion' % keys) def markAllActiveRegions(self): for key in self.shots_found.keys(): @@ -350,20 +420,18 @@ class regions(object): Mark a rectangular region on the axes. ''' from matplotlib.patches import Rectangle - - self.ax.add_patch(Rectangle((x0, y0), (x1 - x0), (y1 - y0), - alpha = alpha, facecolor = color, linewidth = linewidth)) - if key is not None: - self.ax.text((x0 + (x1 - x0) / 2), (y0 + (y1 - y0) / 2), str(key)) + self.ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, alpha=alpha, facecolor=color, linewidth=linewidth)) + if key is not None: + self.ax.text(x0 + (x1 - x0) / 2, y0 + (y1 - y0) / 2, str(key)) self.drawFigure() - def refreshFigure(self): - print('Refreshing figure...') - self.ax.clear() - self.ax = self.survey.plotAllPicks(ax = self.ax, refreshPlot = True) - self.markAllActiveRegions() + def markPolygon(self, x, y, key = None, color = 'grey', alpha = 0.1, linewidth = 1): + from matplotlib.patches import Polygon + poly = Polygon(np.array(zip(x, y)), color=color, alpha=alpha, lw=linewidth) + self.ax.add_patch(poly) + if key is not None: + self.ax.text(min(x) + (max(x) - min(x)) / 2, min(y) + (max(y) - min(y)) / 2, str(key)) self.drawFigure() - print('Done!') def clearShotsForDeletion(self): ''' @@ -374,13 +442,13 @@ class regions(object): def getShotsForDeletion(self): return self.shots_for_deletion - - def deleteMarkedPicks(self): + + def deleteAllMarkedPicks(self, event = None): ''' Deletes all shots set for deletion. ''' if len(self.getShotsForDeletion()) is 0: - print('No shots set for deletion.') + self.printOutput('No shots set for deletion.') return for shot in self.getShotDict().values(): @@ -402,7 +470,40 @@ class regions(object): for traceID in shot.getTraceIDlist(): if shot.getFlag(traceID) is not 0: self.highlightPick(shot, traceID, annotations) + self.drawFigure() - def drawFigure(self): + def setXYlim(self, xlim, ylim): + self._xlim, self._ylim = xlim, ylim + + def refreshLog10SNR(self, event = None): + cbv = 'log10SNR' + self.refreshFigure(self, colorByVal=cbv) + + def refreshPickerror(self, event = None): + cbv = 'pickerror' + self.refreshFigure(self, colorByVal=cbv) + + def refreshSPE(self, event = None): + cbv = 'spe' + self.refreshFigure(self, colorByVal=cbv) + + def refreshFigure(self, event = None, colorByVal = None): + if colorByVal == None: + colorByVal = self.cbv + else: + self.cbv = colorByVal + self.printOutput('Refreshing figure...') + self.ax.clear() + self.ax = self.survey.plotAllPicks(ax=self.ax, cbar=self.cbar, refreshPlot=True, colorByVal=colorByVal) + self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) + self.markAllActiveRegions() + self.highlightAllActiveRegions() + self.drawFigure() + self.printOutput('Done!') + + def drawFigure(self, resetAxes = True): + if resetAxes == True: + self.ax.set_xlim(self._xlim) + self.ax.set_ylim(self._ylim) plt.draw() From 30680a782021715cb79671f925ab49fca0a9acf4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 22 Oct 2015 10:54:24 +0200 Subject: [PATCH 0631/1144] cosmetics for array plots (title, legend) --- pylot/core/active/seismicArrayPreparation.py | 41 +++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 7e7dd09a..4077a3d6 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -16,13 +16,14 @@ class SeisArray(object): Note: Source and Receiver files for FMTOMO will be generated by the Survey object (because traveltimes will be added directly). ''' def __init__(self, recfile): + self.recfile = recfile self._receiverlines = {} self._receiverCoords = {} self._measuredReceivers = {} self._measuredTopo = {} self._sourceLocs = {} self._geophoneNumbers = {} - self._receiverlist = open(recfile, 'r').readlines() + self._receiverlist = open(self.recfile, 'r').readlines() self._generateReceiverlines() self._setReceiverCoords() self._setGeophoneNumbers() @@ -472,28 +473,33 @@ class SeisArray(object): def plotArray2D(self, plot_topo = False, highlight_measured = False, annotations = True): import matplotlib.pyplot as plt plt.interactive(True) - plt.figure() + fig = plt.figure() + ax = plt.axes() xmt, ymt, zmt = self.getMeasuredTopoLists() xsc, ysc, zsc = self.getSourceLocsLists() xmr, ymr, zmr = self.getMeasuredReceiverLists() xrc, yrc, zrc = self.getReceiverLists() - plt.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') - plt.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') + if len(xrc) > 0: + ax.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') + if len(xsc) > 0: + ax.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') if plot_topo == True: - plt.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') + ax.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') if highlight_measured == True: - plt.plot(xmr, ymr, 'ro', label = 'measured receivers') + ax.plot(xmr, ymr, 'ro', label = 'measured receivers') - plt.xlabel('X [m]') - plt.ylabel('Y [m]') + plt.title('2D plot of seismic array %s'%self.recfile) + ax.set_xlabel('X [m]') + ax.set_ylabel('Y [m]') + ax.set_aspect('equal') plt.legend() if annotations == True: for traceID in self.getReceiverCoordinates().keys(): - plt.annotate((' ' + str(traceID)), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small', color = 'k') + ax.annotate((' ' + str(traceID)), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small', color = 'k') for shotnumber in self.getSourceLocations().keys(): - plt.annotate((' ' + str(shotnumber)), xy = (self._getXshot(shotnumber), self._getYshot(shotnumber)), fontsize = 'x-small', color = 'b') + ax.annotate((' ' + str(shotnumber)), xy = (self._getXshot(shotnumber), self._getYshot(shotnumber)), fontsize = 'x-small', color = 'b') @@ -508,11 +514,18 @@ class SeisArray(object): xmt, ymt, zmt = self.getMeasuredTopoLists() xmr, ymr, zmr = self.getMeasuredReceiverLists() - xin, yin, zin = self.getReceiverLists() + xrc, yrc, zrc = self.getReceiverLists() + xsc, ysc, zsc = self.getSourceLocsLists() - ax.plot(xmt, ymt, zmt, 'b*', markersize = 10, label = 'measured topo points') - ax.plot(xin, yin, zin, 'k.', markersize = 10, label = 'interpolated receivers') - ax.plot(xmr, ymr, zmr, 'ro', label = 'measured receivers') + plt.title('3D plot of seismic array %s'%self.recfile) + if len(xmt) > 0: + ax.plot(xmt, ymt, zmt, 'b.', markersize = 10, label = 'measured topo points') + if len(xrc) > 0: + ax.plot(xrc, yrc, zrc, 'k.', markersize = 10, label = 'all receivers') + if len(xmr) > 0: + ax.plot(xmr, ymr, zmr, 'ro', label = 'measured receivers') + if len(xsc) > 0: + ax.plot(xsc, ysc, zsc, 'b*', label = 'shot locations') ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') ax.legend() From 158da885239c90944acafeef4cf67a071dfa39b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Oct 2015 09:40:07 +0100 Subject: [PATCH 0632/1144] Marginal changes only. --- pylot/core/analysis/magnitude.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 9d24392e..ab49bf86 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -188,7 +188,9 @@ class DCfc(Magnitude): "Determined corner frequency: %f Hz" % (self.w0, self.fc)) - if self.getiplot() > 1: + #if self.getiplot() > 1: + iplot=2 + if iplot > 1: f1 = plt.figure() plt.subplot(2,1,1) # show displacement in mm From 69a023e04842a6d331799c01e6ee2b6a4c23383a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Oct 2015 09:41:02 +0100 Subject: [PATCH 0633/1144] Introduced new function for writing phases files for various kinds of location tools. --- pylot/core/pick/utils.py | 46 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 16fd2cec..233bb70d 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -7,7 +7,7 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ - +import pdb import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime @@ -74,7 +74,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): # if EPick stays NaN the signal window size will be doubled while np.isnan(EPick): if count > 0: - print("\nearllatepicker: Doubled signal window size %s time(s) " + print("earllatepicker: Doubled signal window size %s time(s) " "because of NaN for earliest pick." %count) isigDoubleWinStart = pis[-1] + 1 isignalDoubleWin = np.arange(isigDoubleWinStart, @@ -928,6 +928,48 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): return returnflag + +def writephases(arrivals, fformat, filename): + ''' + Function of methods to write phases to the following standard file + formats used for locating earthquakes: + + HYPO71, NLLoc, VELEST, HYPOSAT, HYPOINVERSE and hypoDD + + :param: arrivals + :type: dictionary containing all phase information including + station ID, phase, first motion, weight (uncertainty), + .... + + :param: fformat + :type: string, chosen file format (location routine), + choose between NLLoc, HYPO71, HYPOSAT, VELEST, + HYPOINVERSE, and hypoDD + + :param: filename, full path and name of phase file + :type: string + ''' + + + if fformat == 'NLLoc': + print ("Writing phases to %s for NLLoc" % filename) + fid = open("%s" % filename, 'w') + # write header + fid.write('# EQEVENT: Label: EQ001 Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n') + for key in arrivals: + if arrivals[key]['P']['weight'] < 4: + # NLLoc only knows weight 0 (do not use pick) + # and weight 1 (use pick) + NLLocweight = 1 + # write phase information to file + fid.write('%s \n' % key) + + fid.close() + + + + + if __name__ == '__main__': import doctest doctest.testmod() From b96f81553d49ba0e935b72e9b8263fdcef7e621b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Oct 2015 09:42:25 +0100 Subject: [PATCH 0634/1144] Implemented new function for writing phase files for various kinds of location tools. --- autoPyLoT.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index 3084f1ad..767881af 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -13,6 +13,7 @@ from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter from pylot.core.util.structure import DATASTRUCTURE from pylot.core.pick.autopick import autopickevent +from pylot.core.pick.utils import writephases __version__ = _getVersionString() @@ -85,6 +86,9 @@ def autoPyLoT(inputfile): # !automated picking starts here! picks = autopickevent(wfdat, parameter) + # write phases to NLLoc-phase file + writephases(wd_checked_onsets, 'NLLoc', phasefile) + print '------------------------------------------' print '-----Finished event %s!-----' % event print '------------------------------------------' @@ -100,6 +104,9 @@ def autoPyLoT(inputfile): # !automated picking starts here! picks = autopickevent(wfdat, parameter) + # write phases to NLLoc-phase file + writephases(wd_checked_onsets, 'NLLoc', phasefile) + print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' From 7029c0b5761acafba3230c14bfacf8431c7bab56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 10:00:16 +0100 Subject: [PATCH 0635/1144] Implemted writephases.py. --- autoPyLoT.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index af61bcb1..3fd83257 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -12,6 +12,7 @@ from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter from pylot.core.util.structure import DATASTRUCTURE from pylot.core.pick.autopick import autopickevent +from pylot.core.pick.utils import writephases from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -70,6 +71,13 @@ def autoPyLoT(inputfile): # get path to inventory or dataless-seed file with station meta data invdir = parameter.getParam('invdir') + # get path and name of phase file + phasefile = parameter.getParam('phasefile') + # get path and name of NLLoc-control file + locfile = parameter.getparam('locfile') + # path and pattern of NLLoc ttimes from location grid + ttpattern = parameter.getparam('ttpattern') + # multiple event processing # read each event in database @@ -85,6 +93,9 @@ def autoPyLoT(inputfile): # !automated picking starts here! picks = autopickevent(wfdat, parameter) + # write phases to NLLoc-phase file + writephases(picks, 'NLLoc', phasefile) + print '------------------------------------------' print '-----Finished event %s!-----' % event print '------------------------------------------' @@ -99,6 +110,13 @@ def autoPyLoT(inputfile): ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) + + # write phases to NLLoc-phase file + writephases(picks, 'NLLoc', phasefile) + + # create comment line for NLLoc-control file + locfiles = printf('LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpattern, NLLocoutfile)) + print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') From a3153844174184b386f216be8bea7a0d3344c047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 10:01:07 +0100 Subject: [PATCH 0636/1144] New function writephases.py for writing phases files for various kinds of location routines. Started with NLLoc-phase file. --- pylot/core/pick/utils.py | 43 +++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9feabbb8..34b0c207 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -961,11 +961,44 @@ def writephases(arrivals, fformat, filename): fid.write('# EQEVENT: Label: EQ001 Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n') for key in arrivals: if arrivals[key]['P']['weight'] < 4: - # NLLoc only knows weight 0 (do not use pick) - # and weight 1 (use pick) - NLLocweight = 1 - # write phase information to file - fid.write('%s \n' % key) + fm = arrivals[key]['P']['fm'] + onset = arrivals[key]['P']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 1 \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms)) + if arrivals[key]['S']['weight'] < 4: + fm = '?' + onset = arrivals[key]['S']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 1 \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms)) + fid.close() From 55d3692f33e83b729e90fea82832bc3d824aadb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 10:03:40 +0100 Subject: [PATCH 0637/1144] New parameters phasefile, locfile and ttpattern for auomatic modifying NLLoc-control file. --- autoPyLoT_local.in | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 84ae2137..a6fad4f8 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -3,7 +3,6 @@ %and picking are to be set here! %Parameters are optimized for local data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path @@ -12,10 +11,15 @@ e0019.048.13 #eventID# %event ID for single event pr /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything -AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file -AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#NLLoc settings +/home/ludger/NLLOC/Insheim/obs/AUTOPHASES.obs #phasefile# %name and full path of autoPyLoT-output + %phase file for NLLoc +/home/ludger/NLLOC/Insheim/run/Insheim_min1d2015.in #locfile# %name and full path of autoPyLoT-output + %control file for NLLoc +/home/ludger/NLLOC/Insheim/time/ttime #ttpattern# %path and pattern of NLLOC ttimes from grid +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities -HYPOSAT #locrt# %location routine used ("HYPOINVERSE" or "HYPOSAT") 6 #pmin# %minimum required P picks for location 4 #p0min# %minimum required P picks for location if at least %3 excellent P picks are found From f8cbdf7ff03be99a094c6d26a6cc377519ec96d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 15:26:25 +0100 Subject: [PATCH 0638/1144] Implemented location routine NLLoc. --- autoPyLoT.py | 69 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 3fd83257..3d84de3b 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -5,8 +5,9 @@ import os import argparse import glob - +import subprocess import matplotlib.pyplot as plt + from obspy.core import read from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter @@ -71,12 +72,23 @@ def autoPyLoT(inputfile): # get path to inventory or dataless-seed file with station meta data invdir = parameter.getParam('invdir') - # get path and name of phase file - phasefile = parameter.getParam('phasefile') - # get path and name of NLLoc-control file - locfile = parameter.getparam('locfile') - # path and pattern of NLLoc ttimes from location grid - ttpattern = parameter.getparam('ttpattern') + + # get NLLoc-root path + nllocroot = parameter.getParam('nllocroot') + # get path to NLLoc executable + nllocbin = parameter.getParam('nllocbin') + nlloccall = '%s/NLLoc' % nllocbin + # get name of phase file + phasef = parameter.getParam('phasefile') + phasefile = '%s/%s' % (nllocroot, phasef) + # get name of NLLoc-control file + locf = parameter.getParam('locfile') + locfile = '%s/run/%s' % (nllocroot, locf) + # patter of NLLoc ttimes from location grid + ttpat = parameter.getParam('ttpatter') + ttpatter = '%s/time/%s' % (nllocroot, ttpat) + # patter of NLLoc-output file + nllocoutpatter = parameter.getParam('outpatter') # multiple event processing @@ -96,6 +108,27 @@ def autoPyLoT(inputfile): # write phases to NLLoc-phase file writephases(picks, 'NLLoc', phasefile) + ########################################################## + # For locating the events we have to modify the NLLoc-control file! + # create comment line for NLLoc-control file + # NLLoc-output file + nllocout = '%s/loc/%s_%s' % (nllocroot, event, nllocoutpatter) + locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) + print ("Modifying NLLoc-control file %s ..." % locfile) + # modification of NLLoc-control file + filedata = None + nllfile = open(locfile, 'r') + filedata = nllfile.read() + # replace old command + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(locfile, 'w') + nllfile.write(filedata) + nllfile.close() + + # locate the event + subprocess.call([nlloccall, locfile]) + ########################################################## + print '------------------------------------------' print '-----Finished event %s!-----' % event print '------------------------------------------' @@ -114,8 +147,26 @@ def autoPyLoT(inputfile): # write phases to NLLoc-phase file writephases(picks, 'NLLoc', phasefile) - # create comment line for NLLoc-control file - locfiles = printf('LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpattern, NLLocoutfile)) + ########################################################## + # For locating the events we have to modify the NLLoc-control file! + # create comment line for NLLoc-control file NLLoc-output file + nllocout = '%s/loc/%s_%s' % (nllocroot, parameter.getParam('eventID'), nllocoutpatter) + locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) + print ("Modifying NLLoc-control file %s ..." % locfile) + # modification of NLLoc-control file + filedata = None + nllfile = open(locfile, 'r') + filedata = nllfile.read() + # replace old command + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(locfile, 'w') + nllfile.write(filedata) + nllfile.close() + + # locate the event + subprocess.call([nlloccall, locfile]) + ########################################################## + print '------------------------------------------' From fbaaac843523512379037a6bcdef5c61635ad9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 15:26:55 +0100 Subject: [PATCH 0639/1144] New parameters for default location routine NLLoc. --- autoPyLoT_local.in | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index a6fad4f8..0ec8e105 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -13,11 +13,16 @@ PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #NLLoc settings -/home/ludger/NLLOC/Insheim/obs/AUTOPHASES.obs #phasefile# %name and full path of autoPyLoT-output - %phase file for NLLoc -/home/ludger/NLLOC/Insheim/run/Insheim_min1d2015.in #locfile# %name and full path of autoPyLoT-output - %control file for NLLoc -/home/ludger/NLLOC/Insheim/time/ttime #ttpattern# %path and pattern of NLLOC ttimes from grid +/home/ludger/NLLOC #nllocbin# %path to NLLoc executable +/home/ludger/NLLOC/Insheim #nllocroot# %root of NLLoc-processing directory +AUTOPHASES.obs #phasefile# %name of autoPyLoT-output phase file for NLLoc + %(in nllocroot/obs) +Insheim_min1d2015.in #locfile# %name of autoPyLoT-output control file for NLLoc + %(in nllocroot/run) +ttime #ttpatter# %patter of NLLoc ttimes from grid + %(in nllocroot/times) +AUTOLOC_nlloc #outpatter# %patter of NLLoc-output file + %(returns e.g. 'eventID_outpatter') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities 6 #pmin# %minimum required P picks for location From f13dda9a0f41aab607b76881b3b5ae2d45d50c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 15:50:37 +0100 Subject: [PATCH 0640/1144] Cosmetics. --- autoPyLoT_local.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 0ec8e105..ae4556c0 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -19,10 +19,10 @@ AUTOPHASES.obs #phasefile# %name of autoPyLoT-output pha %(in nllocroot/obs) Insheim_min1d2015.in #locfile# %name of autoPyLoT-output control file for NLLoc %(in nllocroot/run) -ttime #ttpatter# %patter of NLLoc ttimes from grid +ttime #ttpatter# %pattern of NLLoc ttimes from grid %(in nllocroot/times) -AUTOLOC_nlloc #outpatter# %patter of NLLoc-output file - %(returns e.g. 'eventID_outpatter') +AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file + %(returns 'eventID_outpatter') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities 6 #pmin# %minimum required P picks for location From d5b3a7d40f7699cd337f741eb954e4cc5ea3021b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 16:26:40 +0100 Subject: [PATCH 0641/1144] Marginal changes. --- autoPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 3d84de3b..d073f4ea 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -80,7 +80,7 @@ def autoPyLoT(inputfile): nlloccall = '%s/NLLoc' % nllocbin # get name of phase file phasef = parameter.getParam('phasefile') - phasefile = '%s/%s' % (nllocroot, phasef) + phasefile = '%s/obs/%s' % (nllocroot, phasef) # get name of NLLoc-control file locf = parameter.getParam('locfile') locfile = '%s/run/%s' % (nllocroot, locf) @@ -148,7 +148,7 @@ def autoPyLoT(inputfile): writephases(picks, 'NLLoc', phasefile) ########################################################## - # For locating the events we have to modify the NLLoc-control file! + # For locating the event we have to modify the NLLoc-control file! # create comment line for NLLoc-control file NLLoc-output file nllocout = '%s/loc/%s_%s' % (nllocroot, parameter.getParam('eventID'), nllocoutpatter) locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) From 6676484a6142e891e270395505c5a696a93e5251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 16:51:42 +0100 Subject: [PATCH 0642/1144] Debuged: Avoids writing multiple LOCFILES-command lines if same event is processed several times. --- autoPyLoT.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index d073f4ea..9ae760f7 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -7,7 +7,7 @@ import argparse import glob import subprocess import matplotlib.pyplot as plt - +import pdb from obspy.core import read from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter @@ -157,11 +157,12 @@ def autoPyLoT(inputfile): filedata = None nllfile = open(locfile, 'r') filedata = nllfile.read() - # replace old command - filedata = filedata.replace('LOCFILES', locfiles) - nllfile = open(locfile, 'w') - nllfile.write(filedata) - nllfile.close() + if filedata.find(locfiles) < 0: + # replace old command + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(locfile, 'w') + nllfile.write(filedata) + nllfile.close() # locate the event subprocess.call([nlloccall, locfile]) From 43d243e0a1a1123802bd0b8dd7eda28c4e9eafdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Oct 2015 16:53:11 +0100 Subject: [PATCH 0643/1144] Same for multiple event processing. --- autoPyLoT.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 9ae760f7..77436b66 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -119,11 +119,12 @@ def autoPyLoT(inputfile): filedata = None nllfile = open(locfile, 'r') filedata = nllfile.read() - # replace old command - filedata = filedata.replace('LOCFILES', locfiles) - nllfile = open(locfile, 'w') - nllfile.write(filedata) - nllfile.close() + if filedata.find(locfiles) < 0: + # replace old command + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(locfile, 'w') + nllfile.write(filedata) + nllfile.close() # locate the event subprocess.call([nlloccall, locfile]) From fc319f7162a6c41a568f252cad8100e60fcc4c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 28 Oct 2015 09:13:13 +0100 Subject: [PATCH 0644/1144] Introduced option for running autoPyLoT without location routine, if parameter nllocbin in autoPyLoT.in is not set. --- autoPyLoT.py | 146 +++++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 70 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 77436b66..1e60e54d 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -6,8 +6,7 @@ import os import argparse import glob import subprocess -import matplotlib.pyplot as plt -import pdb + from obspy.core import read from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter @@ -20,7 +19,7 @@ __version__ = _getVersionString() def autoPyLoT(inputfile): - ''' + """ Determine phase onsets automatically utilizing the automatic picking algorithms by Kueperkoch et al. 2010/2012. @@ -32,7 +31,7 @@ def autoPyLoT(inputfile): .. rubric:: Example - ''' + """ print '************************************' print '*********autoPyLoT starting*********' print 'The Python picking and Location Tool' @@ -57,9 +56,9 @@ def autoPyLoT(inputfile): if parameter.hasParam('datastructure'): datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() - dsfields = {'root':parameter.getParam('rootpath'), - 'dpath':parameter.getParam('datapath'), - 'dbase':parameter.getParam('database')} + dsfields = {'root' :parameter.getParam('rootpath'), + 'dpath' :parameter.getParam('datapath'), + 'dbase' :parameter.getParam('database')} exf = ['root', 'dpath', 'dbase'] @@ -70,25 +69,28 @@ def autoPyLoT(inputfile): datastructure.modifyFields(**dsfields) datastructure.setExpandFields(exf) - # get path to inventory or dataless-seed file with station meta data - invdir = parameter.getParam('invdir') - - # get NLLoc-root path - nllocroot = parameter.getParam('nllocroot') - # get path to NLLoc executable - nllocbin = parameter.getParam('nllocbin') - nlloccall = '%s/NLLoc' % nllocbin - # get name of phase file - phasef = parameter.getParam('phasefile') - phasefile = '%s/obs/%s' % (nllocroot, phasef) - # get name of NLLoc-control file - locf = parameter.getParam('locfile') - locfile = '%s/run/%s' % (nllocroot, locf) - # patter of NLLoc ttimes from location grid - ttpat = parameter.getParam('ttpatter') - ttpatter = '%s/time/%s' % (nllocroot, ttpat) - # patter of NLLoc-output file - nllocoutpatter = parameter.getParam('outpatter') + # check if default location routine NLLoc is available + if parameter.hasParam('nllocbin'): + locflag = 1 + # get NLLoc-root path + nllocroot = parameter.getParam('nllocroot') + # get path to NLLoc executable + nllocbin = parameter.getParam('nllocbin') + nlloccall = '%s/NLLoc' % nllocbin + # get name of phase file + phasef = parameter.getParam('phasefile') + phasefile = '%s/obs/%s' % (nllocroot, phasef) + # get name of NLLoc-control file + locf = parameter.getParam('locfile') + locfile = '%s/run/%s' % (nllocroot, locf) + # patter of NLLoc ttimes from location grid + ttpat = parameter.getParam('ttpatter') + ttpatter = '%s/time/%s' % (nllocroot, ttpat) + # patter of NLLoc-output file + nllocoutpatter = parameter.getParam('outpatter') + else: + locflag = 0 + print ("!!No location routine available, autoPyLoT just picks the events without locating them!!") # multiple event processing @@ -97,37 +99,39 @@ def autoPyLoT(inputfile): if not parameter.hasParam('eventID'): for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) - print 'Working on event %s' %event + print 'Working on event %s' % event print data - wfdat = data.getWFData() # all available streams + wfdat = data.getWFData() # all available streams ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) - # write phases to NLLoc-phase file - writephases(picks, 'NLLoc', phasefile) - ########################################################## - # For locating the events we have to modify the NLLoc-control file! - # create comment line for NLLoc-control file - # NLLoc-output file - nllocout = '%s/loc/%s_%s' % (nllocroot, event, nllocoutpatter) - locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) - print ("Modifying NLLoc-control file %s ..." % locfile) - # modification of NLLoc-control file - filedata = None - nllfile = open(locfile, 'r') - filedata = nllfile.read() - if filedata.find(locfiles) < 0: - # replace old command - filedata = filedata.replace('LOCFILES', locfiles) - nllfile = open(locfile, 'w') - nllfile.write(filedata) - nllfile.close() + # locating + if locflag == 1: + # write phases to NLLoc-phase file + writephases(picks, 'NLLoc', phasefile) - # locate the event - subprocess.call([nlloccall, locfile]) + # For locating the events we have to modify the NLLoc-control file! + # create comment line for NLLoc-control file + # NLLoc-output file + nllocout = '%s/loc/%s_%s' % (nllocroot, event, nllocoutpatter) + locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) + print ("Modifying NLLoc-control file %s ..." % locfile) + # modification of NLLoc-control file + filedata = None + nllfile = open(locfile, 'r') + filedata = nllfile.read() + if filedata.find(locfiles) < 0: + # replace old command + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(locfile, 'w') + nllfile.write(filedata) + nllfile.close() + + # locate the event + subprocess.call([nlloccall, locfile]) ########################################################## print '------------------------------------------' @@ -140,33 +144,35 @@ def autoPyLoT(inputfile): print 'Working on event ', parameter.getParam('eventID') print data - wfdat = data.getWFData() # all available streams + wfdat = data.getWFData() # all available streams ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) - # write phases to NLLoc-phase file - writephases(picks, 'NLLoc', phasefile) - ########################################################## - # For locating the event we have to modify the NLLoc-control file! - # create comment line for NLLoc-control file NLLoc-output file - nllocout = '%s/loc/%s_%s' % (nllocroot, parameter.getParam('eventID'), nllocoutpatter) - locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) - print ("Modifying NLLoc-control file %s ..." % locfile) - # modification of NLLoc-control file - filedata = None - nllfile = open(locfile, 'r') - filedata = nllfile.read() - if filedata.find(locfiles) < 0: - # replace old command - filedata = filedata.replace('LOCFILES', locfiles) - nllfile = open(locfile, 'w') - nllfile.write(filedata) - nllfile.close() + # locating + if locflag == 1: + # write phases to NLLoc-phase file + writephases(picks, 'NLLoc', phasefile) - # locate the event - subprocess.call([nlloccall, locfile]) + # For locating the event we have to modify the NLLoc-control file! + # create comment line for NLLoc-control file NLLoc-output file + nllocout = '%s/loc/%s_%s' % (nllocroot, parameter.getParam('eventID'), nllocoutpatter) + locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) + print ("Modifying NLLoc-control file %s ..." % locfile) + # modification of NLLoc-control file + filedata = None + nllfile = open(locfile, 'r') + filedata = nllfile.read() + if filedata.find(locfiles) < 0: + # replace old command + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(locfile, 'w') + nllfile.write(filedata) + nllfile.close() + + # locate the event + subprocess.call([nlloccall, locfile]) ########################################################## From 94448297bbc08b093ada5519c47149e1a751af36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 28 Oct 2015 09:13:30 +0100 Subject: [PATCH 0645/1144] Marginal changes. --- pylot/core/pick/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 34b0c207..28de1716 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -8,7 +8,7 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ -import pdb + import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime From 43f9e6fe0dd02b0988d63f900a98ce2cb224e168 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 30 Oct 2015 06:11:15 +0100 Subject: [PATCH 0646/1144] [added] __nonzero__ method for boolean tests on FilterOptions object --- pylot/core/read/inputs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 01e7cd3b..76b9bae9 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -206,6 +206,9 @@ class FilterOptions(object): order=self.getOrder()) return hrs + def __nonzero__(self): + return bool(self.getFilterType()) + def parseFilterOptions(self): if self.getFilterType(): robject = {'type': self.getFilterType(), 'corners': self.getOrder()} From 9b1f7541fd2abf91517b92ad477c363c90ff5ecd Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 30 Oct 2015 06:12:23 +0100 Subject: [PATCH 0647/1144] [added] isSorted function for iterables --- pylot/core/util/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index f5c9b5fd..bbfe4d4d 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -32,7 +32,8 @@ def runProgram(cmd, parameter=None): output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), shell = True) - +def isSorted(iterable): + return sorted(iterable) == iterable def fnConstructor(s): if type(s) is str: From 0cd427486c6c2dc40cd6d0bda2e5dfe4a8b18117 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 30 Oct 2015 06:16:00 +0100 Subject: [PATCH 0648/1144] [modified] updateUi method for the FilterOptionsDlg has been restructured and simplified --- pylot/core/util/widgets.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1a8ef94c..2fece9da 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -27,7 +27,7 @@ from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ getResolutionWindow from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ - demeanTrace + demeanTrace, isSorted def createAction(parent, text, slot=None, shortcut=None, icon=None, @@ -985,7 +985,7 @@ class FilterOptionsDialog(QDialog): """ super(FilterOptionsDialog, self).__init__() - if parent is not None: + if parent is not None and parent.getFilterOptions(): self.filterOptions = parent.getFilterOptions() elif filterOptions is not None: self.filterOptions = FilterOptions(filterOptions) @@ -1021,8 +1021,8 @@ class FilterOptionsDialog(QDialog): try: self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()) self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()) - except TypeError, e: - print e + except TypeError as e: + print(e) self.freqmaxSpinBox.setValue(1.) self.freqminSpinBox.setValue(.1) @@ -1037,6 +1037,7 @@ class FilterOptionsDialog(QDialog): self.selectTypeLabel.setText("Select filter type:") self.selectTypeCombo = QComboBox() self.selectTypeCombo.addItems(typeOptions) + self.selectTypeCombo.setCurrentIndex(typeOptions.index(self.getFilterOptions().getFilterType())) self.selectTypeLayout = QVBoxLayout() self.selectTypeLayout.addWidget(self.orderLabel) self.selectTypeLayout.addWidget(self.orderSpinBox) @@ -1071,30 +1072,28 @@ class FilterOptionsDialog(QDialog): self.buttonBox.rejected.connect(self.reject) def updateUi(self): - _enable = False - if self.selectTypeCombo.currentText() not in ['bandpass', 'bandstop']: - self.freqminLabel.setText("cutoff:") - self.freqmaxSpinBox.setValue(self.freqminSpinBox.value()) - else: - _enable = True - self.freqminLabel.setText("minimum:") - + type = self.selectTypeCombo.currentText() + _enable = type in ['bandpass', 'bandstop'] + freq = [self.freqminSpinBox.value(), self.freqmaxSpinBox.value()] self.freqmaxLabel.setEnabled(_enable) self.freqmaxSpinBox.setEnabled(_enable) - self.getFilterOptions().setFilterType( - self.selectTypeCombo.currentText()) - freq = [self.freqminSpinBox.value()] - if _enable: - if self.freqminSpinBox.value() > self.freqmaxSpinBox.value(): + if not _enable: + self.freqminLabel.setText("cutoff:") + self.freqmaxSpinBox.setValue(freq[0]) + freq.remove(freq[1]) + else: + self.freqminLabel.setText("minimum:") + if not isSorted(freq): QMessageBox.warning(self, "Value error", "Maximum frequency must be at least the " "same value as minimum frequency (notch)!") - self.freqmaxSpinBox.setValue(self.freqminSpinBox.value()) + self.freqmaxSpinBox.setValue(freq[0]) self.freqmaxSpinBox.selectAll() self.freqmaxSpinBox.setFocus() return - freq.append(self.freqmaxSpinBox.value()) + + self.getFilterOptions().setFilterType(type) self.getFilterOptions().setFreq(freq) self.getFilterOptions().setOrder(self.orderSpinBox.value()) From 809b6bea61fc241d4a4da6f8582a5861897348a4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 30 Oct 2015 08:33:05 +0100 Subject: [PATCH 0649/1144] make use of the new __nonzero__ method --- pylot/core/read/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index 76b9bae9..e6d609e3 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -210,7 +210,7 @@ class FilterOptions(object): return bool(self.getFilterType()) def parseFilterOptions(self): - if self.getFilterType(): + if self: robject = {'type': self.getFilterType(), 'corners': self.getOrder()} if len(self.getFreq()) > 1: robject['freqmin'] = self.getFreq()[0] From a2047aa37b876c50e59139d85d2bbeeaea18311e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 30 Oct 2015 08:37:00 +0100 Subject: [PATCH 0650/1144] [disabled] P and S buttons preliminary removed due to unclear functionality --- QtPyLoT.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 2ba21c67..e6dabc5b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -235,10 +235,10 @@ class MainWindow(QMainWindow): fileToolBar.setObjectName("FileTools") self.addActions(fileToolBar, fileToolActions) - phaseToolBar = self.addToolBar("PhaseTools") - phaseToolActions = (self.selectPAction, self.selectSAction) - phaseToolBar.setObjectName("PhaseTools") - self.addActions(phaseToolBar, phaseToolActions) + # phaseToolBar = self.addToolBar("PhaseTools") + # phaseToolActions = (self.selectPAction, self.selectSAction) + # phaseToolBar.setObjectName("PhaseTools") + # self.addActions(phaseToolBar, phaseToolActions) # create button group for component selection From 2201c3ea4d28472540c9c7ebb383d72c7c8e42ee Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 30 Oct 2015 08:39:51 +0100 Subject: [PATCH 0651/1144] [modified] restructured filterWaveformData method in order to make the GUI more intuitive --- QtPyLoT.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e6dabc5b..5c2cd677 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -544,18 +544,17 @@ class MainWindow(QMainWindow): return True return False - if self.filterAction.isChecked(): - kwargs = {} - freq = self.getFilterOptions().getFreq() - if freq is not None and len(freq) > 1: - kwargs['freqmin'] = freq[0] - kwargs['freqmax'] = freq[1] - elif freq is not None and len(freq) == 1: - kwargs['freq'] = freq + def pushFilterWF(kwdict): if hasfreq(kwargs): - kwargs['type'] = self.getFilterOptions().getFilterType() - kwargs['corners'] = self.getFilterOptions().getOrder() self.getData().filterWFData(kwargs) + + if self.getFilterOptions() and self.filterAction.isChecked(): + kwargs = self.getFilterOptions().parseFilterOptions() + pushFilterWF(kwargs) + elif self.filterAction.isChecked(): + self.adjustFilterOptions() + kwargs = self.getFilterOptions().parseFilterOptions() + pushFilterWF(kwargs) else: self.getData().resetWFData() self.plotWaveformData() From 84f1639e59bfea015b40bab6d9c89716dd25e02c Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 30 Oct 2015 11:56:45 +0100 Subject: [PATCH 0652/1144] added transformation to vgrids vtk with relative values using vgrids.in and vgridsref.in from FMTOMO --- pylot/core/active/fmtomo2vtk.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index 95b90705..63990852 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import numpy as np -def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): +def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'abs', inputfileref = 'vgridsref.in'): ''' Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in ''' @@ -19,6 +19,8 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): nTheta = int(vglines[1].split()[1]) nPhi = int(vglines[1].split()[2]) + print('readNumberOf Points: Awaiting %d grid points in %s' + %(nR*nTheta*nPhi, filename)) fin.close() return nR, nTheta, nPhi @@ -95,9 +97,24 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk'): outfile.writelines('LOOKUP_TABLE default\n') # write velocity - print("Writing velocity values to VTK file...") - for velocity in vel: - outfile.writelines('%10f\n' %velocity) + if absOrRel == 'abs': + print("Writing velocity values to VTK file...") + for velocity in vel: + outfile.writelines('%10f\n' %velocity) + elif absOrRel == 'rel': + velref = readVelocity(inputfileref) + if not len(velref) == len(vel): + print('ERROR: Number of gridpoints mismatch for %s and %s'%(inputfile, inputfileref)) + return + velrel = [vel - velref for vel, velref in zip(vel, velref)] + nR_ref, nTheta_ref, nPhi_ref = readNumberOfPoints(inputfileref) + if not nR_ref == nR and nTheta_ref == nTheta and nPhi_ref == nPhi: + print('ERROR: Dimension mismatch of grids %s and %s'%(inputfile, inputfileref)) + return + print("Writing velocity values to VTK file...") + for velocity in velrel: + outfile.writelines('%10f\n' %velocity) + print('Pertubations: min: %s, max: %s'%(min(velrel), max(velrel))) outfile.close() print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) From 422a76012e3be7b16461485cddb6f49c46adba79 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 30 Oct 2015 12:00:03 +0100 Subject: [PATCH 0653/1144] vert. exag for surface plot --- pylot/core/active/seismicArrayPreparation.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 4077a3d6..212f8495 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -470,7 +470,7 @@ class SeisArray(object): print "Exported coordinates for %s traces to file > %s" %(count, filename) recfile_out.close() - def plotArray2D(self, plot_topo = False, highlight_measured = False, annotations = True): + def plotArray2D(self, plot_topo = False, highlight_measured = False, annotations = True, pointsize = 10): import matplotlib.pyplot as plt plt.interactive(True) fig = plt.figure() @@ -481,14 +481,14 @@ class SeisArray(object): xrc, yrc, zrc = self.getReceiverLists() if len(xrc) > 0: - ax.plot(xrc, yrc, 'k.', markersize = 10, label = 'all receivers') + ax.plot(xrc, yrc, 'k.', markersize = pointsize, label = 'all receivers') if len(xsc) > 0: - ax.plot(xsc, ysc, 'b*', markersize = 10, label = 'shot locations') + ax.plot(xsc, ysc, 'b*', markersize = pointsize, label = 'shot locations') if plot_topo == True: - ax.plot(xmt, ymt, 'b', markersize = 10, label = 'measured topo points') + ax.plot(xmt, ymt, 'b.', markersize = pointsize, label = 'measured topo points') if highlight_measured == True: - ax.plot(xmr, ymr, 'ro', label = 'measured receivers') + ax.plot(xmr, ymr, 'r.', markersize = pointsize, label = 'measured receivers') plt.title('2D plot of seismic array %s'%self.recfile) ax.set_xlabel('X [m]') @@ -532,7 +532,7 @@ class SeisArray(object): return ax - def plotSurface3D(self, ax = None, step = 0.5, method = 'linear'): + def plotSurface3D(self, ax = None, step = 0.5, method = 'linear', exag = False): from matplotlib import cm import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D @@ -558,7 +558,8 @@ class SeisArray(object): ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) - ax.set_zlim(-(max(x) - min(x)/2),(max(x) - min(x)/2)) + if exag == False: + ax.set_zlim(-(max(x) - min(x)/2),(max(x) - min(x)/2)) ax.set_aspect('equal') ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') From 1b95ed0da7ff3e00d3fa264335f7ef74464c4569 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 30 Oct 2015 12:37:41 +0100 Subject: [PATCH 0654/1144] relative velocities in percent --- pylot/core/active/fmtomo2vtk.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index 63990852..10cbe879 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -106,7 +106,15 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a if not len(velref) == len(vel): print('ERROR: Number of gridpoints mismatch for %s and %s'%(inputfile, inputfileref)) return - velrel = [vel - velref for vel, velref in zip(vel, velref)] + #velrel = [((vel - velref) / velref * 100) for vel, velref in zip(vel, velref)] + velrel = [] + for velocities in zip(vel, velref): + v, vref = velocities + if not vref == 0: + velrel.append((v - vref) / vref * 100) + else: + velrel.append(0) + nR_ref, nTheta_ref, nPhi_ref = readNumberOfPoints(inputfileref) if not nR_ref == nR and nTheta_ref == nTheta and nPhi_ref == nPhi: print('ERROR: Dimension mismatch of grids %s and %s'%(inputfile, inputfileref)) From a31e1a21f0a70eb09b52d3cf7fbc22fb326f1fb3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 31 Oct 2015 00:07:24 +0100 Subject: [PATCH 0655/1144] [bugfix] now filtering in the main window works; filter parameters are not stored -> has to be checked again --- QtPyLoT.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 5c2cd677..92aa76ea 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -536,38 +536,31 @@ class MainWindow(QMainWindow): self.plotWaveformData() self.drawPicks() + def pushFilterWF(self, param_args): + self.getData().filterWFData(param_args) + def filterWaveformData(self): if self.getData(): - def hasfreq(kwdict): - for key in kwdict.keys(): - if not key.startswith('freq'): - return True - return False - - def pushFilterWF(kwdict): - if hasfreq(kwargs): - self.getData().filterWFData(kwargs) - if self.getFilterOptions() and self.filterAction.isChecked(): kwargs = self.getFilterOptions().parseFilterOptions() - pushFilterWF(kwargs) + self.pushFilterWF(kwargs) elif self.filterAction.isChecked(): self.adjustFilterOptions() - kwargs = self.getFilterOptions().parseFilterOptions() - pushFilterWF(kwargs) else: self.getData().resetWFData() self.plotWaveformData() def adjustFilterOptions(self): - filteroptions = self.getFilterOptions() fstring = "Filter Options ({0})".format(self.getSeismicPhase()) filterDlg = FilterOptionsDialog(titleString=fstring, - parent=self, - filterOptions=filteroptions) + parent=self) if filterDlg.exec_(): filteroptions = filterDlg.getFilterOptions() self.setFilterOptions(filteroptions) + if self.filterAction.isChecked(): + kwargs = self.getFilterOptions().parseFilterOptions() + self.pushFilterWF(kwargs) + self.plotWaveformData() def getFilterOptions(self): try: From bcc6c8a73d599606e8fae71beffdfd016eb5522c Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 3 Nov 2015 10:25:52 +0100 Subject: [PATCH 0656/1144] enabled use of parameter file "mygrid.in" for generation of a starting model, prepared generation of vgrid model from array dimensions --- pylot/core/active/seismicArrayPreparation.py | 80 +++++++++++++++----- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 212f8495..21a5c625 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -364,9 +364,9 @@ class SeisArray(object): return surface def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, - thetaSN = (-0.2, 1.2), phiWE = (-0.2, 1.2), - Rbt = (-62.0, 6.0), vbot = 5.5, filename = 'vgrids.in', - method = 'linear' ): + Rbt = (-62.0, 6.0), thetaSN = None, + phiWE = None, outfilename = 'vgrids.in', + method = 'linear', infilename = 'mygrid.in'): ''' Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. @@ -387,9 +387,12 @@ class SeisArray(object): :param: Rbt (bot, top) extensions of the model in km type: tuple - + :param: vbot, velocity at the bottom of the model type: real + + :param: method, interpolation method for topography + type: str ''' def getRad(angle): @@ -397,16 +400,48 @@ class SeisArray(object): rad = angle / 180 * PI return rad - def getZmax(surface): - z = [] - for point in surface: - z.append(point[2]) - return max(z) + def readMygridNlayers(filename): + infile = open(filename, 'r') + nlayers = len(infile.readlines()) / 2 + infile.close() + + return nlayers + + def readMygrid(filename): + ztop = []; zbot = []; vtop = []; vbot = [] + infile = open(filename, 'r') + nlayers = readMygridNlayers(filename) + + for index in range(nlayers): + line1 = infile.readline() + line2 = infile.readline() + ztop.append(float(line1.split()[0])) + vtop.append(float(line1.split()[1])) + zbot.append(float(line2.split()[0])) + vbot.append(float(line2.split()[1])) + + if not ztop[0] == 0: + print('ERROR: there must be a velocity set for z = 0 in the file %s'%filename) + print('e.g.:\n0 0.33\n-5 1.0\netc.') + + infile.close() + return ztop, zbot, vtop, vbot R = 6371 vmin = 0.34 + cushionfactor = 0.1 # add some extra space to the model decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) - outfile = open(filename, 'w') + outfile = open(outfilename, 'w') + + # generate dimensions of the grid from array + if thetaSN is None and phiWE is None: + x, y, z = self.getAllMeasuredPointsLists() + phi_min, phi_max = (self._getAngle(min(x)), self._getAngle(max(x))) + theta_min, theta_max = (self._getAngle(min(y)), self._getAngle(max(y))) + cushionPhi = abs(phi_max - phi_min) * cushionfactor + cushionTheta = abs(theta_max - theta_min) * cushionfactor + phiWE = (phi_min - cushionPhi, phi_max + cushionPhi) + thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) thetaS, thetaN = thetaSN phiW, phiE = phiWE @@ -433,11 +468,14 @@ class SeisArray(object): outfile.writelines('%10s %10s %10s\n' %(rbot - rDelta, getRad(thetaS - thetaDelta), getRad(phiW - phiDelta))) surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method, filename = None) - zmax = getZmax(surface) - print "\nGenerating velocity grid for FMTOMO. Output filename = %s, interpolation method = %s"%(filename, method) + print "\nGenerating velocity grid for FMTOMO. Output filename = %s, interpolation method = %s"%(outfilename, method) print "nTheta = %s, nPhi = %s, nR = %s, thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt) count = 0 + + nlayers = readMygridNlayers(infilename) + ztop, zbot, vtop, vbot = readMygrid(infilename) + for radius in rGrid: for theta in thetaGrid: for phi in phiGrid: @@ -445,19 +483,27 @@ class SeisArray(object): yval = self._getDistance(theta) for point in surface: if point[0] == xval and point[1] == yval: - z = point[2] - if radius > (R + z + 1): + topo = point[2] + z = -(R + topo - radius) + if z > (topo + 1): vel = 0.0 - # elif radius > (R + z - 15): ########### TESTING - # vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * 1.0 + vmin ########################## + elif (topo + 1) >= z > (topo): # cushioning around topography + vel = vtop[0] else: - vel = (radius - z - R) / (Rbt[0] - rDelta - zmax) * vbot + vmin ########################## + for index in range(nlayers): + if (topo + ztop[index]) >= z > (topo + zbot[index]): + vel = (z - topo) / (zbot[index] - topo) * (vbot[index] - vtop[index]) + vtop[index] + break + if not (topo + ztop[index]) >= z > (topo + zbot[index]): + print('ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile'%(z - topo)) + return count += 1 outfile.writelines('%10s %10s\n'%(vel, decm)) progress = float(count) / float(nTotal) * 100 self._update_progress(progress) + print('Wrote %d points to file %s for %d layers'%(count, outfilename, nlayers)) outfile.close() def exportAll(self, filename = 'interpolated_receivers.out'): From 1c08152e65de55e41067fdaf5ad1a8cdf9a393fe Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 3 Nov 2015 10:27:06 +0100 Subject: [PATCH 0657/1144] vkt file "rel" changed to changes in percent --- pylot/core/active/fmtomo2vtk.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index 10cbe879..59c0968e 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -93,7 +93,10 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a outfile.writelines('SPACING %f %f %f\n' %(dX, dY, dZ)) outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - outfile.writelines('SCALARS velocity float %d\n' %(1)) + if absOrRel == 'abs': + outfile.writelines('SCALARS velocity float %d\n' %(1)) + elif absOrRel == 'rel': + outfile.writelines('SCALARS velChangePercent float %d\n' %(1)) outfile.writelines('LOOKUP_TABLE default\n') # write velocity From c28641d62902cbf3f34a80d3a01a51f29aef2ed1 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 3 Nov 2015 10:29:09 +0100 Subject: [PATCH 0658/1144] changed plot traces for presentation (SNR, _drawStream etc.) --- pylot/core/active/seismicshot.py | 56 +++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 07cd2b43..58267dd5 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -588,6 +588,23 @@ class SeismicShot(object): # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_lowerb, ':k') # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_upperb, ':k') + def plotTrace(self, traceID, plotSNR = True, lw = 1): + fig = plt.figure() + ax = fig.add_subplot(111) + ax = self._drawStream(traceID, ax = ax) + + tgap = self.getTgap() + tsignal = self.getTsignal() + pick = self.getPick(traceID) + tnoise = pick - tgap + snr, snrdb, noiselevel = self.getSNR(traceID) + + ax.plot([0, tnoise], [noiselevel, noiselevel], 'm', linewidth = lw, label = 'noise level') + ax.plot([tnoise, pick], [noiselevel, noiselevel], 'g:', linewidth = lw, label = 'gap') + ax.plot([tnoise + tgap, pick + tsignal], [noiselevel * snr, noiselevel * snr], 'b', linewidth = lw, label = 'signal level') + ax.legend() + ax.text(0.05, 0.95, 'SNR: %s' %snr, transform = ax.transAxes) + def plot_traces(self, traceID, folm = 0.6): ########## 2D, muss noch mehr verbessert werden ########## from matplotlib.widgets import Button @@ -621,7 +638,7 @@ class SeismicShot(object): self._drawStream(traceID) self._drawCFs(traceID, folm) - def _drawStream(self, traceID, refresh = False): + def _drawStream(self, traceID, refresh = False, ax = None): from pylot.core.util.utils import getGlobalTimes from pylot.core.util.utils import prepTimeAxis @@ -630,7 +647,8 @@ class SeismicShot(object): timeaxis = prepTimeAxis(stime, stream[0]) timeaxis -= stime - ax = self.traces4plot[traceID]['ax1'] + if ax is None: + ax = self.traces4plot[traceID]['ax1'] if refresh == True: xlim, ylim = ax.get_xlim(), ax.get_ylim() @@ -645,8 +663,9 @@ class SeismicShot(object): ax.plot([self.getPick(traceID), self.getPick(traceID)], [min(stream[0].data), max(stream[0].data)], - 'r', label = 'mostlikely') + 'r', label = 'most likely') ax.legend() + return ax def _drawCFs(self, traceID, folm, refresh = False): hoscf = self.getHOScf(traceID) @@ -665,7 +684,7 @@ class SeismicShot(object): ax.plot([self.getPick(traceID), self.getPick(traceID)], [min(np.minimum(hoscf.getCF(), aiccf.getCF())), max(np.maximum(hoscf.getCF(), aiccf.getCF()))], - 'r', label = 'mostlikely') + 'r', label = 'most likely') ax.plot([0, self.getPick(traceID)], [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], 'm:', label = 'folm = %s' %folm) @@ -745,11 +764,12 @@ class SeismicShot(object): :type: 'logical' ''' from scipy.interpolate import griddata + from matplotlib import cm + cmap = cm.jet x = []; xcut = [] y = []; ycut = [] z = []; zcut = [] - tmin, tmax = self.getCut() for traceID in self.pick.keys(): if self.getFlag(traceID) != 0: @@ -761,6 +781,9 @@ class SeismicShot(object): ycut.append(self.getRecLoc(traceID)[1]) zcut.append(self.getPickIncludeRemoved(traceID)) + tmin = 0.8 * min(z) # 20% cushion for colorbar + tmax = 1.2 * max(z) + xaxis = np.arange(min(x), max(x), step) yaxis = np.arange(min(y), max(y), step) xgrid, ygrid = np.meshgrid(xaxis, yaxis) @@ -770,19 +793,28 @@ class SeismicShot(object): fig = plt.figure() ax = plt.axes() - ax.matshow(zgrid, extent = [min(x), max(x), min(y), max(y)], origin = 'lower') + count = 0 + ax.imshow(zgrid, extent = [min(x), max(x), min(y), max(y)], vmin = tmin, vmax = tmax, cmap = cmap, origin = 'lower', alpha = 0.85) plt.text(0.45, 0.9, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes) - sc = ax.scatter(x, y, c = z, s = 30, label = 'picked shots', vmin = tmin, vmax = tmax, linewidths = 1.5) - sccut = ax.scatter(xcut, ycut, c = zcut, s = 30, edgecolor = 'm', label = 'cut out shots', vmin = tmin, vmax = tmax, linewidths = 1.5) + sc = ax.scatter(x, y, c = z, s = 30, label = 'picked shots', vmin = tmin, vmax = tmax, cmap = cmap, linewidths = 1.5) + for xyz in zip(xcut, ycut, zcut): + x, y, z = xyz + label = None + if z > tmax: + count += 1 + z = 'w' + if count == 1: + label = 'cut out shots' + ax.scatter(x, y, c = z, s = 30, edgecolor = 'm', label = label, vmin = tmin, vmax = tmax, cmap = cmap, linewidths = 1.5) if colorbar == True: - plt.colorbar(sc) + cbar = plt.colorbar(sc) + cbar.set_label('Time [s]') + + ax.legend() ax.set_xlabel('X') ax.set_ylabel('Y') ax.plot(self.getSrcLoc()[0], self.getSrcLoc()[1],'*k', markersize = 15) # plot source location - if plotRec == True: - ax.scatter(x, y, c = z, s = 30) - if annotations == True: for traceID in self.getTraceIDlist(): if self.getFlag(traceID) is not 0: From dc4d19ba884202e912f66b33cd4131fb7d17e90a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 4 Nov 2015 10:04:05 +0100 Subject: [PATCH 0659/1144] bugfixes: formula for gradients in multiple layers for vgrid corrected, fixed issue with integer division that would lead to a wrong spacing --- pylot/core/active/seismicArrayPreparation.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 21a5c625..c0c1d99b 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -449,9 +449,9 @@ class SeisArray(object): rtop = Rbt[1] + R # need to determine the delta to add two cushion nodes around the min/max values - thetaDelta = abs(thetaN - thetaS) / (nTheta - 1) - phiDelta = abs(phiE - phiW) / (nPhi - 1) - rDelta = abs(rbot - rtop) / (nR - 1) + thetaDelta = abs(thetaN - thetaS) / float((nTheta - 1)) + phiDelta = abs(phiE - phiW) / float((nPhi - 1)) + rDelta = abs(rbot - rtop) / float((nR - 1)) # create a regular grid including +2 cushion nodes in every direction thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes @@ -487,17 +487,19 @@ class SeisArray(object): z = -(R + topo - radius) if z > (topo + 1): vel = 0.0 - elif (topo + 1) >= z > (topo): # cushioning around topography + elif (topo + 1) >= z > topo: # cushioning around topography vel = vtop[0] else: for index in range(nlayers): if (topo + ztop[index]) >= z > (topo + zbot[index]): - vel = (z - topo) / (zbot[index] - topo) * (vbot[index] - vtop[index]) + vtop[index] + vel = (z - ztop[index]) / (zbot[index] - ztop[index]) * (vbot[index] - vtop[index]) + vtop[index] break if not (topo + ztop[index]) >= z > (topo + zbot[index]): print('ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile'%(z - topo)) return count += 1 + if vel < 0: + print('ERROR, vel <0; z, topo, zbot, vbot, vtop:', z, topo, zbot[index], vbot[index], vtop[index]) outfile.writelines('%10s %10s\n'%(vel, decm)) progress = float(count) / float(nTotal) * 100 From d8a59ac81dace573f002c7532909566f2c082ae5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 4 Nov 2015 10:13:52 +0100 Subject: [PATCH 0660/1144] bugfix: not yet done: problem with cusioning around topography --- pylot/core/active/seismicArrayPreparation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index c0c1d99b..58d021be 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -486,7 +486,7 @@ class SeisArray(object): topo = point[2] z = -(R + topo - radius) if z > (topo + 1): - vel = 0.0 + vel = vtop[0] elif (topo + 1) >= z > topo: # cushioning around topography vel = vtop[0] else: From 223902f2d4a9fe992730bb1ba925bfa432f26550 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 4 Nov 2015 13:11:48 +0100 Subject: [PATCH 0661/1144] reformatted the file to avoid usage of tabs and spaces and made a more pythonic splash message than multiple calls to print --- autoPyLoT.py | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 1e60e54d..e7f79057 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -32,19 +32,17 @@ def autoPyLoT(inputfile): .. rubric:: Example """ - print '************************************' - print '*********autoPyLoT starting*********' - print 'The Python picking and Location Tool' - print ' Version ', _getVersionString(), '2015' - print ' ' - print 'Authors:' - print 'S. Wehling-Benatelli' - print ' Ruhr-Universität Bochum' - print 'L. Küperkoch' - print ' BESTEC GmbH, Landau (Pfalz)' - print 'K. Olbert' - print ' Christian-Albrechts Universität Kiel' - print '************************************' + splash = '''************************************\n + *********autoPyLoT starting*********\n + The Python picking and Location Tool\n + Version {version} 2015\n + \n + Authors:\n + S. Wehling-Benatelli (Ruhr-Universität Bochum)\n + L. Küperkoch (BESTEC GmbH, Landau i. d. Pfalz)\n + K. Olbert (Christian-Albrechts Universität zu Kiel)\n + ***********************************'''.format(version=_getVersionString()) + print(splash) # reading parameter file @@ -77,7 +75,7 @@ def autoPyLoT(inputfile): # get path to NLLoc executable nllocbin = parameter.getParam('nllocbin') nlloccall = '%s/NLLoc' % nllocbin - # get name of phase file + # get name of phase file phasef = parameter.getParam('phasefile') phasefile = '%s/obs/%s' % (nllocroot, phasef) # get name of NLLoc-control file @@ -99,8 +97,8 @@ def autoPyLoT(inputfile): if not parameter.hasParam('eventID'): for event in [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)]: data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) - print 'Working on event %s' % event - print data + print('Working on event %s' % event) + print(data) wfdat = data.getWFData() # all available streams ########################################################## @@ -148,7 +146,7 @@ def autoPyLoT(inputfile): ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) - + ########################################################## # locating if locflag == 1: @@ -166,15 +164,15 @@ def autoPyLoT(inputfile): filedata = nllfile.read() if filedata.find(locfiles) < 0: # replace old command - filedata = filedata.replace('LOCFILES', locfiles) - nllfile = open(locfile, 'w') - nllfile.write(filedata) - nllfile.close() + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(locfile, 'w') + nllfile.write(filedata) + nllfile.close() # locate the event subprocess.call([nlloccall, locfile]) ########################################################## - + print '------------------------------------------' From b8c26a6588dde1e6fb9aca120adfb3d912256bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 4 Nov 2015 16:52:39 +0100 Subject: [PATCH 0662/1144] Implemented extended function writephases for writing phases to HYPO71-input phase file. --- autoPyLoT.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 1e60e54d..06a3bdef 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -113,7 +113,7 @@ def autoPyLoT(inputfile): # write phases to NLLoc-phase file writephases(picks, 'NLLoc', phasefile) - # For locating the events we have to modify the NLLoc-control file! + # For locating the event the NLLoc-control file has to be modified! # create comment line for NLLoc-control file # NLLoc-output file nllocout = '%s/loc/%s_%s' % (nllocroot, event, nllocoutpatter) @@ -155,7 +155,7 @@ def autoPyLoT(inputfile): # write phases to NLLoc-phase file writephases(picks, 'NLLoc', phasefile) - # For locating the event we have to modify the NLLoc-control file! + # For locating the event the NLLoc-control file has to be modified! # create comment line for NLLoc-control file NLLoc-output file nllocout = '%s/loc/%s_%s' % (nllocroot, parameter.getParam('eventID'), nllocoutpatter) locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) @@ -174,9 +174,13 @@ def autoPyLoT(inputfile): # locate the event subprocess.call([nlloccall, locfile]) ########################################################## + # write phase files for various location routines + # HYPO71 + hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.getParam('eventID')) + writephases(picks, 'HYPO71', hypo71file) + - print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' From 3fa4a3197829a9f2ea7c9f8ce0bd7835daa81358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 4 Nov 2015 16:53:03 +0100 Subject: [PATCH 0663/1144] Extended function writephases for writing phases to HYPO71-input phase file. --- pylot/core/pick/utils.py | 77 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 28de1716..8319d90a 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -8,7 +8,7 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ - +import pdb import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime @@ -937,7 +937,7 @@ def writephases(arrivals, fformat, filename): Function of methods to write phases to the following standard file formats used for locating earthquakes: - HYPO71, NLLoc, VELEST, HYPOSAT, HYPOINVERSE and hypoDD + HYPO71, NLLoc, VELEST, HYPOSAT, and hypoDD :param: arrivals :type: dictionary containing all phase information including @@ -999,11 +999,80 @@ def writephases(arrivals, fformat, filename): mm, ss_ms)) - fid.close() + elif fformat == 'HYPO71': + print ("Writing phases to %s for HYPO71" % filename) + fid = open("%s" % filename, 'w') + # write header + fid.write(' EQ001\n') + for key in arrivals: + if arrivals[key]['P']['weight'] < 4: + Ponset = arrivals[key]['P']['mpp'] + Sonset = arrivals[key]['S']['mpp'] + pweight = arrivals[key]['P']['weight'] + sweight = arrivals[key]['S']['weight'] + fm = arrivals[key]['P']['fm'] + if fm is None: + fm = '-' + Ao = arrivals[key]['S']['Ao'] + if Ao is None: + Ao = '' + else: + Ao = str('%7.2f' % Ao) + year = Ponset.year + if year >= 2000: + year = year -2000 + else: + year = year - 1900 + month = Ponset.month + day = Ponset.day + hh = Ponset.hour + mm = Ponset.minute + ss = Ponset.second + ms = Ponset.microsecond + ss_ms = ss + ms / 1000000.0 + if pweight < 2: + pstr = 'I' + elif pweight >= 2: + pstr = 'E' + if arrivals[key]['S']['weight'] < 4: + Sss = Sonset.second + Sms = Sonset.microsecond + Sss_ms = Sss + Sms / 1000000.0 + Sss_ms = str('%5.02f' % Sss_ms) + if sweight < 2: + sstr = 'I' + elif sweight >= 2: + sstr = 'E' + fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s%sS %d %s\n' % (key, + pstr, + fm, + pweight, + year, + month, + day, + hh, + mm, + ss_ms, + Sss_ms, + sstr, + sweight, + Ao)) + else: + fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s\n' % (key, + pstr, + fm, + pweight, + year, + month, + day, + hh, + mm, + ss_ms, + Ao)) - + fid.close() if __name__ == '__main__': From 686395defc9404f807861ed1e32af5e1f71bd23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 5 Nov 2015 08:49:44 +0100 Subject: [PATCH 0664/1144] Marginal changes --- autoPyLoT.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 1d6fb6cf..ecef9770 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -172,17 +172,12 @@ def autoPyLoT(inputfile): # locate the event subprocess.call([nlloccall, locfile]) ########################################################## -<<<<<<< HEAD # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.getParam('eventID')) writephases(picks, 'HYPO71', hypo71file) -======= - ->>>>>>> 223902f2d4a9fe992730bb1ba925bfa432f26550 - print '------------------------------------------' print '-------Finished event %s!-------' % parameter.getParam('eventID') print '------------------------------------------' From d74aeb237c86a0fc2983362112eaa629043b5d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 5 Nov 2015 08:51:50 +0100 Subject: [PATCH 0665/1144] Implemented phase writing in multiple event processing mode. --- autoPyLoT.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index ecef9770..71a8031f 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -131,6 +131,10 @@ def autoPyLoT(inputfile): # locate the event subprocess.call([nlloccall, locfile]) ########################################################## + # write phase files for various location routines + # HYPO71 + hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, eventID) + writephases(picks, 'HYPO71', hypo71file) print '------------------------------------------' print '-----Finished event %s!-----' % event From d69db63132e920ae3e6aec4ed2c28cd4ae827835 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 5 Nov 2015 09:14:36 +0100 Subject: [PATCH 0666/1144] [new] started to implement a new package containing modules for location purposes featuring different external location programs like NonLinLoc --- pylot/core/loc/__init__.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 pylot/core/loc/__init__.py diff --git a/pylot/core/loc/__init__.py b/pylot/core/loc/__init__.py new file mode 100644 index 00000000..faa18be5 --- /dev/null +++ b/pylot/core/loc/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- From 0f8b228931e0ba43d83e0a7d1348e611966ae0f1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 5 Nov 2015 09:16:11 +0100 Subject: [PATCH 0667/1144] [new] module "nll" containing utility function to handle NonLinLoc input and output as well as execution of the external program from python --- pylot/core/loc/nll.py | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 pylot/core/loc/nll.py diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py new file mode 100644 index 00000000..f411906c --- /dev/null +++ b/pylot/core/loc/nll.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import subprocess +from obspy.core.event import readEvents +from pylot.core.pick.utils import writephases + +def picksExport(picks, phasefile): + ''' + Take dictionary and exports picking data to a NLLOC-obs without creating an ObsPy event object. + :param picks: picking data dictionary + :type picks: dict + :param phasefile: complete path to the exporting obs file + :type phasefile: str + ''' + # write phases to NLLoc-phase file + writephases(picks, 'NLLoc', phasefile) + +def modfiyInputFile(fn, root, outpath, phasefile, tttable): + ''' + + :param fn: + :param root: + :param outpath: + :param phasefile: + :param tttable: + :return: + ''' + # For locating the event we have to modify the NLLoc-control file! + # create comment line for NLLoc-control file NLLoc-output file + print ("Modifying NLLoc-control file %s ..." % fn) + nllocout = '%s/loc/%s_%s' % (root, outpath) + locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, tttable, nllocout) + + # modification of NLLoc-control file + nllfile = open(fn, 'r') + filedata = nllfile.read() + if filedata.find(locfiles) < 0: + # replace old command + filedata = filedata.replace('LOCFILES', locfiles) + nllfile = open(fn, 'w') + nllfile.write(filedata) + nllfile.close() + +def locate(call, fnin): + ''' + Takes paths to NLLoc executable and input parameter file and starts the location calculation. + :param call: full path to NLLoc executable + :type call: str + :param fnin: full path to input parameter file + :type fnin: str + ''' + # locate the event + subprocess.call([call, fnin]) + +def readLocation(fn): + pass + +if __name__=='__main__': + pass From afb7a189adc2587683d48aa94371f5e1e90a5566 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 5 Nov 2015 09:17:20 +0100 Subject: [PATCH 0668/1144] [new] preparing new modules for location routines planned to be implemented --- pylot/core/loc/hsat.py | 2 ++ pylot/core/loc/velest.py | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 pylot/core/loc/hsat.py create mode 100644 pylot/core/loc/velest.py diff --git a/pylot/core/loc/hsat.py b/pylot/core/loc/hsat.py new file mode 100644 index 00000000..faa18be5 --- /dev/null +++ b/pylot/core/loc/hsat.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- diff --git a/pylot/core/loc/velest.py b/pylot/core/loc/velest.py new file mode 100644 index 00000000..faa18be5 --- /dev/null +++ b/pylot/core/loc/velest.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- From f2fc5e50712f947d07a700aa348067be663b9d4a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 5 Nov 2015 10:13:04 +0100 Subject: [PATCH 0669/1144] working on implementing of gradients for different layers --- pylot/core/active/seismicArrayPreparation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 58d021be..c56ee29d 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -223,7 +223,7 @@ class SeisArray(object): ''' PI = np.pi R = 6371. - angle = distance * 180 / (PI * R) + angle = distance * 180. / (PI * R) return angle def _getDistance(self, angle): @@ -232,7 +232,7 @@ class SeisArray(object): ''' PI = np.pi R = 6371. - distance = angle / 180 * (PI * R) + distance = angle / 180. * (PI * R) return distance def getMeasuredReceiverLists(self): @@ -486,13 +486,13 @@ class SeisArray(object): topo = point[2] z = -(R + topo - radius) if z > (topo + 1): - vel = vtop[0] + vel = 0.0 elif (topo + 1) >= z > topo: # cushioning around topography vel = vtop[0] else: for index in range(nlayers): if (topo + ztop[index]) >= z > (topo + zbot[index]): - vel = (z - ztop[index]) / (zbot[index] - ztop[index]) * (vbot[index] - vtop[index]) + vtop[index] + vel = (z - ztop[index] - topo) / (zbot[index] - ztop[index]) * (vbot[index] - vtop[index]) + vtop[index] break if not (topo + ztop[index]) >= z > (topo + zbot[index]): print('ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile'%(z - topo)) From 06337b4d66c7858451d41e3edc8bc2e4b9711d77 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 5 Nov 2015 12:16:08 +0100 Subject: [PATCH 0670/1144] [bugsearch] tried to figure out why topography correction did not work for fmtomo model output --- pylot/core/active/seismicArrayPreparation.py | 35 +++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index c56ee29d..cf272135 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -387,10 +387,10 @@ class SeisArray(object): :param: Rbt (bot, top) extensions of the model in km type: tuple - + :param: vbot, velocity at the bottom of the model type: real - + :param: method, interpolation method for topography type: str ''' @@ -419,7 +419,7 @@ class SeisArray(object): vtop.append(float(line1.split()[1])) zbot.append(float(line2.split()[0])) vbot.append(float(line2.split()[1])) - + if not ztop[0] == 0: print('ERROR: there must be a velocity set for z = 0 in the file %s'%filename) print('e.g.:\n0 0.33\n-5 1.0\netc.') @@ -427,7 +427,7 @@ class SeisArray(object): infile.close() return ztop, zbot, vtop, vbot - R = 6371 + R = 6371. vmin = 0.34 cushionfactor = 0.1 # add some extra space to the model decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) @@ -435,7 +435,7 @@ class SeisArray(object): # generate dimensions of the grid from array if thetaSN is None and phiWE is None: - x, y, z = self.getAllMeasuredPointsLists() + x, y, _ = self.getAllMeasuredPointsLists() phi_min, phi_max = (self._getAngle(min(x)), self._getAngle(max(x))) theta_min, theta_max = (self._getAngle(min(y)), self._getAngle(max(y))) cushionPhi = abs(phi_max - phi_min) * cushionfactor @@ -459,7 +459,7 @@ class SeisArray(object): rGrid = np.linspace(rbot - rDelta, rtop + rDelta, num = nR + 2) # +2 cushion nodes nTotal = len(rGrid) * len(thetaGrid) * len(phiGrid) - print "Total number of grid nodes: %s"%nTotal + print("Total number of grid nodes: %s"%nTotal) # write header for velocity grid file (in RADIANS) outfile.writelines('%10s %10s \n' %(1, 1)) @@ -469,8 +469,10 @@ class SeisArray(object): surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method, filename = None) - print "\nGenerating velocity grid for FMTOMO. Output filename = %s, interpolation method = %s"%(outfilename, method) - print "nTheta = %s, nPhi = %s, nR = %s, thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt) + print("\nGenerating velocity grid for FMTOMO. " + "Output filename = %s, interpolation method = %s"%(outfilename, method)) + print("nTheta = %s, nPhi = %s, nR = %s, " + "thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt)) count = 0 nlayers = readMygridNlayers(infilename) @@ -484,22 +486,23 @@ class SeisArray(object): for point in surface: if point[0] == xval and point[1] == yval: topo = point[2] - z = -(R + topo - radius) - if z > (topo + 1): + break + depth = -(R + topo - radius) + if depth > 1: vel = 0.0 - elif (topo + 1) >= z > topo: # cushioning around topography + elif 1 >= depth > 0: # cushioning around topography vel = vtop[0] else: for index in range(nlayers): - if (topo + ztop[index]) >= z > (topo + zbot[index]): - vel = (z - ztop[index] - topo) / (zbot[index] - ztop[index]) * (vbot[index] - vtop[index]) + vtop[index] + if (ztop[index]) >= depth > (zbot[index]): + vel = (depth - ztop[index]) / (zbot[index] - ztop[index]) * (vbot[index] - vtop[index]) + vtop[index] break - if not (topo + ztop[index]) >= z > (topo + zbot[index]): - print('ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile'%(z - topo)) + if not (ztop[index]) >= depth > (zbot[index]): + print('ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile'%(depth-topo)) return count += 1 if vel < 0: - print('ERROR, vel <0; z, topo, zbot, vbot, vtop:', z, topo, zbot[index], vbot[index], vtop[index]) + print('ERROR, vel <0; z, topo, zbot, vbot, vtop:', depth, topo, zbot[index], vbot[index], vtop[index]) outfile.writelines('%10s %10s\n'%(vel, decm)) progress = float(count) / float(nTotal) * 100 From c28e2bda2a35914e5b696ad3292f7ec28c635eea Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 5 Nov 2015 12:19:56 +0100 Subject: [PATCH 0671/1144] added surface2VTK --- pylot/core/active/seismicArrayPreparation.py | 49 +++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index c56ee29d..a7a181a6 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -619,9 +619,54 @@ class SeisArray(object): sys.stdout.write("%d%% done \r" % (progress) ) sys.stdout.flush() + def surface2VTK(self, surface, filename = 'surface.vtk'): + ''' + Generates a vtk file from all points of a surface as generated by interpolateTopography. + ''' + outfile = open(filename, 'w') + + nPoints = len(surface) + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Surface Points\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + print("Writing coordinates to VTK file...") + for point in surface: + x = point[0] + y = point[1] + z = point[2] + + outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + + outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + + # write indices + print("Writing indices to VTK file...") + for index in range(nPoints): + outfile.writelines('%10d %10d\n' %(1, index)) + + # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + # outfile.writelines('SCALARS traceIDs int %d\n' %(1)) + # outfile.writelines('LOOKUP_TABLE default\n') + + # # write traceIDs + # print("Writing traceIDs to VTK file...") + # for traceID in traceIDs: + # outfile.writelines('%10d\n' %traceID) + + outfile.close() + print("Wrote %d points to file: %s" %(nPoints, filename)) + return + def receivers2VTK(self, filename = 'receivers.vtk'): ''' - Generates vtk files from all receivers of the SeisArray object. + Generates a vtk file from all receivers of the SeisArray object. ''' outfile = open(filename, 'w') traceIDs = [] @@ -670,7 +715,7 @@ class SeisArray(object): def sources2VTK(self, filename = 'sources.vtk'): ''' - Generates vtk-files for all source locations in the SeisArray object. + Generates a vtk-file for all source locations in the SeisArray object. ''' outfile = open(filename, 'w') shotnumbers = [] From 21bb39d528550ed457bbc85f0a8e08a2bbe8d1c8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 6 Nov 2015 08:20:08 +0100 Subject: [PATCH 0672/1144] [new] preparing MainWindow for location button --- QtPyLoT.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 92aa76ea..84702d28 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -759,6 +759,16 @@ class MainWindow(QMainWindow): "PyLoT - seismic processing the python way[*]") self.setWindowModified(self.dirty) + def check4Loc(self): + return self.picksNum() > 4 + + def picksNum(self): + num = 0 + for phases in self.getPicks().values(): + num += len(phases) + return num + + def tutorUser(self): self.updateStatus('select trace to pick on station ...', 10000) From 9c7fbc47254555cfa1da7f42ca7b3021f7d1d477 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 6 Nov 2015 15:40:21 +0100 Subject: [PATCH 0673/1144] [new] introduced new attribute loc in order to control localization relevant GUI elements depending on the number of available phase onsets --- QtPyLoT.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 84702d28..f3f4409a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -57,6 +57,7 @@ import icons_rc class MainWindow(QMainWindow): __version__ = _getVersionString() + __slots__ = ['loc'] closing = Signal() def __init__(self, parent=None): @@ -89,6 +90,7 @@ class MainWindow(QMainWindow): self.filteroptions = {} self.pickDlgs = {} self.picks = {} + self.locflag(False) # UI has to be set up before(!) children widgets are about to show up self.setupUi() @@ -635,6 +637,10 @@ class MainWindow(QMainWindow): self.drawPicks(station) else: self.updateStatus('picks discarded ({0})'.format(station)) + if not self.locflag() and self.check4Loc(): + self.locflag(True) + elif self.locflag() and not self.check4Loc(): + self.locflag(False) def autoPick(self): list = QListWidget() @@ -768,6 +774,13 @@ class MainWindow(QMainWindow): num += len(phases) return num + @property + def locflag(self): + return self.loc + + @locflag.setter + def locflag(self, value): + self.loc = value def tutorUser(self): self.updateStatus('select trace to pick on station ...', 10000) From 8ae692db702d9d613806fd9884a9ea5957d03e33 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 9 Nov 2015 08:53:26 +0100 Subject: [PATCH 0674/1144] [new] added a locate button to the toolbar in the GUI (not working yet) --- QtPyLoT.py | 39 +++++++++++++++++++++++++++------------ icons.qrc | 1 + icons/locate.png | Bin 0 -> 7252 bytes icons_rc.py | 8 ++++---- 4 files changed, 32 insertions(+), 16 deletions(-) create mode 100755 icons/locate.png diff --git a/QtPyLoT.py b/QtPyLoT.py index f3f4409a..57bd17fe 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -162,6 +162,8 @@ class MainWindow(QMainWindow): e_icon.addPixmap(QPixmap(':/icons/key_E.png')) auto_icon = QIcon() auto_icon.addPixmap(QPixmap(':/icons/sync.png')) + locate_icon = QIcon() + locate_icon.addPixmap(QPixmap(':/icons/locate.png')) newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, @@ -286,6 +288,16 @@ class MainWindow(QMainWindow): # pickToolBar.setObjectName("PickTools") # self.addActions(pickToolBar, pickToolActions) + locateEvent = self.createAction(parent=self, text='locateEvent', + slot=self.locateEvent, shortcut='Alt+Ctrl+L', + icon=locate_icon, tip='Locate the event using ' + 'the picked arrivals.') + + locationToolBar = self.addToolBar("LocationTools") + locationToolActions = (locateEvent,) + locationToolBar.setObjectName("LocationTools") + self.addActions(locationToolBar, locationToolActions) + self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) status = self.statusBar() @@ -752,18 +764,8 @@ class MainWindow(QMainWindow): [mpp + spe, mpp + spe], ylims, colors[1]) self.draw() - def updateStatus(self, message, duration=5000): - self.statusBar().showMessage(message, duration) - if self.getData() is not None: - if not self.getData().isNew(): - self.setWindowTitle( - "PyLoT - processing event %s[*]" % self.getData().getID()) - elif self.getData().isNew(): - self.setWindowTitle("PyLoT - New event [*]") - else: - self.setWindowTitle( - "PyLoT - seismic processing the python way[*]") - self.setWindowModified(self.dirty) + def locateEvent(self): + pass def check4Loc(self): return self.picksNum() > 4 @@ -782,6 +784,19 @@ class MainWindow(QMainWindow): def locflag(self, value): self.loc = value + def updateStatus(self, message, duration=5000): + self.statusBar().showMessage(message, duration) + if self.getData() is not None: + if not self.getData().isNew(): + self.setWindowTitle( + "PyLoT - processing event %s[*]" % self.getData().getID()) + elif self.getData().isNew(): + self.setWindowTitle("PyLoT - New event [*]") + else: + self.setWindowTitle( + "PyLoT - seismic processing the python way[*]") + self.setWindowModified(self.dirty) + def tutorUser(self): self.updateStatus('select trace to pick on station ...', 10000) diff --git a/icons.qrc b/icons.qrc index 010efb1b..8b2065fc 100644 --- a/icons.qrc +++ b/icons.qrc @@ -2,6 +2,7 @@ icons/pylot.ico icons/pylot.png + icons/locate.png icons/printer.png icons/delete.png icons/key_E.png diff --git a/icons/locate.png b/icons/locate.png new file mode 100755 index 0000000000000000000000000000000000000000..6e628e44b6397e2a77a44b0e626b79110c112193 GIT binary patch literal 7252 zcmeI1S5#Bmx5fjZi9xD`9-5$1LWd9tReDE2loFBvffNiux*#ARARQ6uMG-_0r3wg0 zktSW5AYF<`_lUGyEdO)8FZYc5aNo`zV~;i0Uf=xY+`qLS))@P)iIFw~JqJAi0ASG7 z(KI_AAwO<98UTO-Ox?kJJYgqkS(D5OP9!e`5e-m75ggHAT^zz0ZH7jmd~S50VE_RA z493EmWNl~wMG|o0h#wkpZ=CyaHUI#Fd%Gi$ZfFwN5$%k@EAy|{Ht>TnC}n;tSwo1S zy9U|?qvK0NoBJABAbs7CiYR`#3O&pldTfA0lMrBU92V~Z^;YKp=?guMe>|4p2mh3i z+?4r$HDzsR0@ff9(O?;INiifuS`I8DCoUx~CocsN1sf9Z;LB)7u%seb3IdS_OG!Z` zrJ*v?;NKU&3OyJ`L^(mtG%x?Icsx_)cOjA7p%M~aUS8r}(&7Z7vxKCgqM`&uN{ub@;e#h3yv6Vy0zWK%d1#_NkVK3-33FWZhbO|3;7L;E=RaNt|29V<|MqtGBw~L$ zqmUA4EE|yMx%x${hIwEg~j~f0mUNl z&dU7WVkoo|!V^p4SNXqm3X}N37>oH`<>%}_WndEjqlsVAKep#M=hw62Z9o3#B>w*D zemD6Cum6wmCzt;Z<3BzBE%GOge~kad^^b`D6#R?pPa6Li|BLG%5&bFn7uTOO{xSX+ z*FPfqQ}DmxqW|x6D;j@%cJewtmFgDBOalPSCv-K{EWF1Z56EOVP_B^@>0jG+KwfJm zg9x`IAqsnBBnq%>l7QUhJ=?NN9!5MSQ`UjVb0O?U(!qkeKI~)<4@rpVgSIytTi*l2 z7LAW2gFMqG+jq%>=uhNNVIZ=6x6@vro5Wn;b1J$5Ey$5o90E`?507AEQjM;@-b%&a zgLgV2?|}x%?^KbaK2&zMGrBH_+CI6o9Xm74aVCs$iY(cF_ZAzq+squ|pqY`B8%fzr zey4x+$QbKz(90+28APEYGO5(Q+W?C?f!*x*8tu^4Bwi#o>o zMRv%{jAsRaoQ&&pUZpA>w-4VnwSJFDoe2liDw&jlfFT9a(i)d3fg$%s>}do7XlN|6 z%T`6BL2jpXD?@M_UdY2w>l@}KL9w^hjPA#)>89T0lR1=c9!Tv55n0IW3aMo2=Tbmf z1<8rv+Gq8Vi(_x249HJkvXg0Rqz|mFpmuvZJ2x60MQoBgb)!d>8K}5ai{G|Myh^e) znU&)#HIa`i4J)Pr113@1394*bo7<#vsWf(0PIv;{GQ3YKlu7f>=Lzb5emeTxD`JtB zV&l}$u79O1H+C81cVX6x%9f$e5p2~o+z#nW9q|lp3Z)4{tkrJ!)cN(S+pizh8id?= zq-9lP$6Qh6TxeKn$56saUw)2oMnz!CnB(G=7%WeF9k7-DEuvd#@M1t62mMf81xt@s z<;WE}C%tHD6HJyX_o%S_f!W$ZZ17Z9wF|93J%m#R;RW>80dAWzB;p(SC~%|ng{0ND2}VGw1HArEX|lgRf2%#(>qz+%K$+ojaj?aYS6?- z=4O>sXWwf)&D4B4q*2O=(U2Z{a+gZ zUF4SCE#wFLq_3L$ZD%88(>dD{?9Z`Z<4j1-zmi$~_#4w?&rzm5g^O5V-{V|JWY#yk zheLJwLOATvP%v4Q_CoF{aWxS&9If!_9HZF*t)hrC!pu}+la95zv?-^Si?Xwq986vE zlT6Ar-Y38faRVZfl|DWr9_xdSxJx9+h8lQ1V5)v`KSQJP-M~%wo+W%kFXi$YEm7vq z^|&V}hIqr}*+YHX?$__N3RTUUn3i=@reBs?GmxYE;T&+I>%Cr6OQ&~X+J{auLp5`! z+n#EDvAn89gehNmZb7NypYLP!qRfWj>Eo$dms()>6CXqfdr~&HlgiC`za?{;jkE+P z1MXR4J%uepp#`;`VEG1h`~2-iR!YXVRNdS#>pWm~R(tQ6+z>9J$0R_SDIIfDg1cNa z+&a}*iueW>S{4%TL8CB^aVT>^;(5+-6bi^vQ-uVL%+<|3<^dsF2L~-IJ_^>$H9|m< z4wtx3ytue*Az>_>Gg(Eu6v(kRSgl0|e;`L4YqY%3J#JABjq z%L~&Flj0ot!!OFhQ4f;}3$JRpog}af_wjh)eN3uA?mXQz3N-Q}L!A#TXiD~+gu1TdmJcH$k)v{NX{)X+@1wy%Ba{I8$q|r3C>TyuD z6q`>|n{ArYUuT>+6>ervOu1UBvlz4IO5I1tSqAoT-pAJ*An3!)vDe3o-v-7;W>GTP zd*PHi`zc0CA2u_9?P%IF2tqJ2Q;i7 z`eZKFldN|ds8kvDbSeq!?Vmgv&bz&T@wE4yXQ+Lw)0yF^@qp;Pm0aeB)B`MejcrBF zF#jDA?qk2lpk6PRneR&W0Nfb5`>`Lfv=l!xuX}LQ?Gjw1dZsG3@sULfl}|1jQ0HBE$#P|X{-mAl2bo4YZ^J0g+eQ%zvy@OS9bJ^A zYusPFynStoH9@^@B`msCXR|Qu;59a0l5Lt5G&9dt4g3iP{>q#(-Lkp5ShlXNc&$8L z?Te-;H1=AlQHpfnmlC0ho482{XB|O(Ett8B0}~}~>JctTWcyJ*hw9WC>+(CBt{@Vf zFNiegsdFWF&qZ1|*24d!UAN@-#&0;aCSjJSJ6pp%LITZ}=kXI9?P6}vb{t);1s)~) z*iq=`@dm)L0Z}*BOq-O8h_rHi}Jl!alrE;oSqcUe& z*>)Ur3KI3TQ6UdmDNs`_kRy0^;xE#r<<`63ZWYa!?5{|9v?L0iVpCJ$HiLQGEs+{p zei5MDpxC&$oCIrREmehU=UD~|nwGQe?!I8l*PTe58@ysZ)N$d-mZ&bgk)}npxW3IK z_g;KS0Bzo#ijb}kI~kR71s^k5@mm2QzZh&c`W#KsI1NiJ=rYY`IsQPr{Zs$Qs zJvZ>WoOox)lecL~S)I5&#KmYPxgM4jj%n>AA&CbLo-cV3wrjgMY;J&Zde+`Zek}L_ zU<^K*ReK|c%i92$k=Akf#tE_8tX)23Jyz4Zt>og1NoKa!!yR;s$G$~t z@Z2@GezsaFaX*H*9=b47K-5Kb@lul9o|TzenC?!bj(t%X$DO3amE|xq9_7j$H2gw$1q62NyKW=m3tMu78~PtX8!gc!#0 zwWVt?*i^mOD~(5YSay+#FQscgeDv$7S=n=@H%-9AU61$C7xmF^CDOhbd zIbd{I672?bFQG*!e~*1lwG%xHumaOTj;`@;)#)DU}K>_CezpJ zTYg(>;mqmukpe}g?&sQrYo~p;bcy3Yg$oRQof`g+_(bk6W3$~?dW{a6$~=4W1cbj! z-{OrqZJ+B-$yeEu70NIMP#&>{SLV76NAFoIRDS@4WoKf)^s6@eJyqkR0G2b?N89C7 zaB%AHB${>>m=zi20;dm+!6kU$>x?x3Q&FKivu>8j~p>vxdDmzzW; z1VGd8XBJK!b_#92z7U+5Dd2JxC8rEcbJZmeY>z;{-bzTOHuvDCB(<>$uBAIMgx(0qU$60 zSbG$A6@v?ai9cn)I5sn~UNKKac}uGGt8}qRHm|DsgUEIcacfL?<*H@HQC7Zw%8@0_ zG@;4sZ2hM`uJhe5d1Z!xPa@!EpDGMW3A};!_c z^6BJV;IgSDe7(y_z%SS#A@7X*sPekJ()H3i#rw+2C4#{n`JDoS=ig9=t$ozqIcYwF zJ_$N~Q=v7ti_=EIVhK1(=#4FlTE~h^cE5VYPJhN`Z(t&`%_{3lNo8@i)lPJa$&Nzu z)>6p5B3{6hNP06?&Nk?j%25+$XGJf>=(S^EobVhY^}JvYoEg{R?5E#@8@qPEbEyH} z?!qSAlw!y$d-MIfj7;_!d6|Z>Rrq{AW4Me?*ONsbZ>(VQyEA|^&AbQi+(K8DSngI> z%7_T`jWHXUjRxHxcF`W}k!lR5ZgRFB(Yhd;kGH9TDPsCrm+3{yR#;0JPn~T^q%V-Z z-Ai;GUAz=)L)fpd15z5i&V9w?epC@y(ZFW%YR1*Xw>{ljW7TJ)Gf62C`Swb|MRr~K zDJD>+)cA+jvjCFFBM6um(%4TEWPLS$R_@Y$h33!r*1L>U83cvGk>~rm_pdhV5v$JU z)@Zgr7ntzm7oWTE;pT!sHTGlsB412$h4Q7(k-37YT8rP;gT6LYvlzU_(9rR1lrlUh zXmbcTvZ5{DeSStiiuoL_#s(`^Z=(v4jO`F<*Dk#MNt*njth3>PkN@a~?Qrdn|9rYy LMw%t+4#EEg!IM;; literal 0 HcmV?d00001 diff --git a/icons_rc.py b/icons_rc.py index 772daf1a..e71d2f97 100644 --- a/icons_rc.py +++ b/icons_rc.py @@ -2,16 +2,16 @@ # Resource object code # -# Created: Fr. Sep. 25 09:00:19 2015 +# Created: Mo. Nov. 9 08:50:04 2015 # by: The Resource Compiler for PySide (Qt v4.8.6) # # WARNING! All changes made in this file will be lost! from PySide import QtCore -qt_resource_data = "\x00\x00\x9e\x04\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x80\x00\x00\x01\x00\x08\x06\x00\x00\x00#\x95\xc7f\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x17\x12\x00\x00\x17\x12\x01g\x9f\xd2R\x00\x00\x00\x07tIME\x07\xdf\x07\x07\x090\x11\xbal\xcd}\x00\x00 \x00IDATx\xda\xec}w\x94\x5c\xe5y\xfe3w\xee\xf4\x99\x9d\xb2\xb3;\xdb\x9bV\xbb*+\xd1-\x0c\xa2\x83\xb0\x0d\x1cSbL\xe28v0v|lp\x0eq\x09\x04\x88c\x1cl\x9c\xe08\x80K\x82c\xe3\xb8!sL\x5c\xe4P\x02\x18\x04\x085TWm{/\xb3\xd3{\xbf\xf3\xfbC\xbf\xf7\xf3\x9d\xbb\xb3\xb3\xbb\xd2\xae\xb4\xda\xfd\x9est\xa4\x1d\xcd\xcc\xce|\xed}\xbe\xe7m\xaa|>\x9f\x07\x07\x07\x07\x07\x07\xc79\x8c|>\x8fT*\x85\xed\xdb\xb7#\x95J!\x97\xcb\xe1\xb6\xdbn\x83$I\x10\x04\x81\x0f\x10\x07\x87\x02|Wppppp\x9c\xf3P\xa9T\xc8\xe7\xf3\xc8\xe7\xf3P\xa9T\xc8\xe5r|P888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0\x0e-\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0* \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqppp\x02\xc8\xc1\xc1\xc1\xc1\xc1I \x07\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqpp\x02\xc8\xc1\xc1\xc1\xc1\xc1\xc1\x09 \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x9c\x00rppppp,%P!h\x0e\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\xe3\x9c\x07w\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xc1\x01\x95J\xc5\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\xe5\x0aI\x92\xb8\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7J\x05W\xfe888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83c\xb9A\xe9\xfe\x95$\x89\x0f\x0a\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\xcb\x9d\x00\xcaI\x1f\x8f\x07\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x10@\xe5\xcf<\x16\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\x02\xb9\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a \x7fr\xc5\x8f\x13@\x0e\x0eN\x0098888V\x08\x09\xe4\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\xfe\xe4\xa4\x8fg\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x08 A\xa5Rq\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95F\x029\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x91?N\x00988\x01\xe4\xe0\xe0\xe0\xe0X!\x04\x90\xc7\x00rpp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x09 \x95\x82\xe1\x0a \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0cR\x9e+\x80\x1c\x1c\x9c\x00rpppp\xac(p\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15F\xfc\xf2\x12'\x80\x1c\x1c\x9c\x00rpppp\xacH\x22\xc8\xc1\xc1\xc1\x09 \x07\x07\x07\x07\x07'\x80\x1c\x1c\x9c\x00\xf2!\xe0\xe0\xe0\xe0\xe0Xn\xe0\x9d@888\x01\xe4\xe0\xe0\xe0\xe0X\x81\xe0\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x86\x5c.\xc7\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eN\x0098888\x96-x+8\x0e\x0eN\x0098888V\x18x\x0c \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0c\x5c\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x85\x81+\x80\x1c\x1c\x9c\x00rpppp\xac \xf0:\x80\x1c\x1c\x9c\x00rpppp\xac@p\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a\x03\x8f\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\x13@\x0e\x0e\x0eN\x0098888\x963\xb8\x0b\x98\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x04\x95J\x85L&\xc3\x07\x82\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95\x04\xde\x0b\x98\x83cf\x88\x8b\xf5\xc6$\xbds\x09~\xf1o\xb9\xf2\xbf\xf3\xf9<\xfb7\x07\xc7b\xedm\xf9:+\xb5\xc7\xe9y\xf4\xb7r\xbdrpp\xfc\x09\x14\xb3\xc8K\xd8,\xdf\xb3S~n\xe6\xf3y\xa8\xd5\xeai\xf6\xfc\x9c$\x80\xf9|\x1e\xd9l\x16*\x95\x0a\x92$A\x92\xa4i\xc6\x82c\xe1\x17\x92\xdc\xb0\x0a\x82P\xf0X\xb1\xe7pp\x9c*\xe9\xa3}M\xc6\xaa\xd4\xfe\xce\xe7\xf3\xc8\xe5r\x10\x04\x81\xad?A\x10\xa0V\xab\xa1R\xa9\x0a\x0e>\x0e\x8e\x85\x14\x1e\x94\x84j\xa9\x93>I\x92\x98ZI{\x8a\xdb\xcd\xe5\xb76%I\x82J\xa5*8\x17\xe9L\x14\x04\x81\xfd9\xe7\x08`6\x9bE6\x9b\x85$I\xc8d2H\xa5R\xecg2\x14\x1c\x0b\x07Z\xb8$IB:\x9d\x86$Il\x7fg\xb3Y\xf6\xff\xc5@\x97\xc1|>\x0f\xadV\x0b\xbd^\x0f\xbd^\x0f\xadV\x0b\x00\x9c\x04r,\x0a\x01\xa4\xbf\x97\xba\x0b8\x9b\xcd\x02\x002\x99\x0c\xb2\xd9,2\x99\x0c\xd2\xe94r\xb9\x5c\xc1e\x8bcy\x81\xe6W\xa3\xd1@\x14E\x98\xcdfh\xb5Zh\xb5\xda3J\xfc\xc5\x85\xfa2\xf4'\x1e\x8f#\x1c\x0e#\x1c\x0e#\x16\x8b!\x99L\x22\x97\xcbq\x02\xb8\xc0 \x05EN\xfch1i\xb5Zh4\x1ah4\x1ah\xb5Z\xf6\x98(\x8aP\xab\xd5\x10E\x11*\x95\x0a\x1a\x8d\xa6@1\xe4\xe0(\xa5R\xa4\xd3i\xa4\xd3i\xb6\xbf#\x91\x08\x92\xc9$\xbb\xe8\x15\x83Z\xadfF\xd8h4\xc2n\xb7\xa3\xbc\xbc\x1cv\xbb\x1d\x1a\x8d\x86\x0f,\xc7\xa2\x13\xac\x5c.\xb7\xa4/\x1a\x99L\x06\x89D\x02\x89D\x02\xa1P\x08\xe1p\x18\x89D\x02\x99L\x86\x13\xc0eL\x00\xb3\xd9,DQ\x84\xd5jEyy9\x9cN',\x16\x0b4\x1a\xcd\x19[\xaf\xe2B~\xa1X,\x86`0\x88\xee\xeenx\xbd^\xf6E\xe4j\x15\xc7\xc2\x19\xe4h4\xca\x16\x92\x5ce\x15E\x11\x06\x83\x01&\x93\x09v\xbb\x1dF\xa3\x11F\xa3\x11z\xbd\x1e:\x9d\x0ez\xbd\x9e\x91C\xadV\x0bA\x10\x18)\xe4\xf3\xc4QLU\xc9\xe5rH\xa7\xd3\x08\x06\x83\xe8\xeb\xeb\xc3\xe8\xe8(\xbbP\x90\xb2\x5c\x0c\xf1x\x1c{\xf7\xeeE,\x16Css3\xd6\xaf_\x8f\x96\x96\x16\x08\x82\x00\xbb\xdd^\xf2\xb5\x1c\x1c\xa7z9\x96\x9f\x93K\x95\x00\x92r\x9eN\xa7\x11\x8dF1>>\x8e\xae\xae.\xe6\xa5!\xdb\xc9\xb1\xfc\xce\xd2\xfd\xfb\xf7cxx\x18v\xbb\x1d\xabW\xaf\xc6\xfa\xf5\xeb!\x08\x02\x13e\xce\x19\x02H_(\x9b\xcd\x22\x16\x8badd\x04\x95\x95\x95\xb8\xfa\xea\xab\x0bT*\x8e\x85_Hr\xf7\x1b\x11\xc0L&\x83\x89\x89\x09\xc4\xe3qLNN\xe2\xd8\xb1c\xc8f\xb30\x1a\x8dp:\x9d\xb0\xdb\xed0\x18\x0c\xd0\xeb\xf50\x99L0\x9b\xcd0\x18\x0c\xd0\xe9t\xd0h4|\xce8f\xdc\xe3\xf1x\x1c\x13\x13\x13p8\x1c\xd8\xb4i\x13[+\xa5\x0e\xabl6\x8b\x1bo\xbc\x11\xdb\xb7o\xc7\x0f~\xf0\x03\x08\x82\x00\xadV\x0b\x9b\xcd\x06\x8b\xc5\xc2\x14i\x0e\x8e\x85Z\xab\xca\x8b\xf2RU\xd1\xc8\x0d\x98L&\x11\x0c\x06\x11\x0e\x87q\xe7\x9dw2e\x88\x0b'\xcbw\x8dn\xd9\xb2\x05G\x8e\x1c\xc1\xd3O?\x8d#G\x8e \x16\x8b\xa1\xac\xac\x0cF\xa3\x11:\x9d\xee\x8c\xb9\x81O\xfb\xe4\xa5\xa0\xc6t:\x8dx<\x8e\xa9\xa9)\xe8\xf5z\x98\xcdf\xee\xf6]dP\x1c\x95r\x9c+++\x0b~\x0e\x06\x83p\xbb\xdd\xd8\xbf\x7f?\xba\xba\xba \x08\x02\x0c\x06\x03l6\x1b\x1c\x0e\x07\xacV+\xca\xca\xca`6\x9ba4\x1a\xd9\xe2#U\x90\x83\x1fX\x14\xd7\xeb\xf3\xf9PYY\x09\x8b\xc52\xeb\xfe\xa6\xb8\xbf\xae\xae.<\xf7\xdcs\xe8\xe9\xe9\xc1\xaaU\xabP__\x8fh4\x0a\xbb\xdd\xce\xd60\x07\xc7b`\xa9\xc7\xd1e\xb3Y$\x93I\x84\xc3a\xf8\xfd~\x18\x0c\x06fS9\x96'T*\x15\xa6\xa6\xa6\xf0\x9b\xdf\xfc\x06===\x98\x9a\x9a\x82\xdb\xedF0\x18\x84\xcb\xe5bb\xce9A\x00\x05A@:\x9dF2\x99D,\x16C\x22\x91\x80N\xa7c_\x94\xe3\xcc,\xa8b\xb7`\xca$\xb3\xd9l\xb0\xd9lhooG>\x9fGgg'zzz\xb0o\xdf>\x08\x82\x80\xd6\xd6VTTT\xc0\xe9t\xc2\xe1p\xb0\x9b\x08\xbd7w\xd3\xadl\xe4r9d2\x19\xc4\xe3q\xc4b1\xa4\xd3\xe9Y\xf7w>\x9f\x87 \x08\x18\x1d\x1d\xc57\xbf\xf9M\x9c8q\x02\x1e\x8f\x07\x91H\x04\xf1x\x1c\x99L\xa6d\xf2\x08\x07\xc7\xa9^V\x94?/U!\x82\x12&\x13\x89\x04\xdb[\xb4\xaf\xf8y\xbb<\xd7\xa5J\xa5B8\x1c\xc6/~\xf1\x0b\xec\xdc\xb9\x13###,|.\x1e\x8f\xb3\x98\xd53\x85\x05q\x01\x93\x8b\x88\xbe\xc4\x5c\x82\xbb\xb9:\xb8p\xe4Oy[\x98\xa9\xd6\x1a=o\xe3\xc6\x8d\xd8\xb8q#6o\xde\x8c\xb7\xdez\x0b/\xbe\xf8\x22\xea\xea\xea\xd0\xda\xda\x0a\x97\xcb\x85\xaa\xaa*8\x9dN\xe6\x1e\xa6\x0cb\xee\x16^\xb9kL\x92$\xc4\xe3qD\xa3\xd1Y\x89\x1f\x91\xbf}\xfb\xf6\xe1\xa7?\xfd)\x0e\x1e<\x08\xaf\xd7\x8bL&\xc3\xfePl\x167t\x1c\x8bmx\x97\xaa\xad!\x17o&\x93A \x10(i7\x17\xab\xae\xee|\xf6\xdfB\xfe\xee\x95\xb4\xef\xe5\x84\xde\xeb\xf5\xe2\xfb\xdf\xff>\xde|\xf3M\xf4\xf7\xf7#\x16\x8bA\xadV\xb3\x90\xae3\x9d0\xbb \x04\x90\x0cD4\x1a\xc5\xd8\xd8\x18\xbc^/_\x00gx\x81\x9d\xca\xf3\x5c.\x17\xee\xb8\xe3\x0e\xdcz\xeb\xad\xf8\xe7\x7f\xfeg\xbc\xf2\xca+hkkCss3\x9a\x9b\x9bQUU\x05\x87\xc3\x01\xb3\xd9\xcc\xe6\x9a\x97\xedX\xb9F4\x99Lbjj\x0aUUU%\xd7X<\x1e\xc7[o\xbd\x85\x17^x\x01\x87\x0f\x1f\x86\xdf\xefG<\x1e\x9fv\x18\xf23\x80c\xb1\x94\x16\xc2Rv\x01+\xe3\xe7\xa7\xa6\xa6f=\xbb\x17\xf2\xbb\x9c\xa9\xb1Q\xce\x89\xbc4\xd4\xd9\xba\xcc\x9e\xa95\xa1R\xa9\x90L&\x11\x8dF\xd1\xdd\xdd\x8d\xd7_\x7f\x1dG\x8f\x1eEWW\x17b\xb1\x18KP\x22q\xe5L\x8b,\xe2B.\xa6\x5c.\x87H$R\x92\x00F\x22\x11\xe4\xf3yLNN2\xe6;\xd3\xfbQ\x8d\xa4RFi)\xdd\xeeN\xf5\xf3\x9c\xcakT*\x15R\xa9\x14\xaa\xaa\xaa\x10\x0e\x87a\xb5Z!I\x12\xca\xca\xca\x90\xcb\xe5`6\x9b\xa1R\xa9\xa0\xd5jg,\xd2+7\xc4_\xfb\xda\xd7\xb0{\xf7n\xfc\xe4'?A\x7f\x7f?.\xb8\xe0\x02\xb4\xb6\xb6\xa2\xbe\xbe\x1e.\x97\x0b6\x9b\x8d\x05\xebs\x12\xb8\xf2@\xfb;\x1e\x8f\xc3\xe7\xf3\x15}N.\x97\xc3\xc0\xc0\x00^y\xe5\x15\xec\xd9\xb3\x07'N\x9c\xc0\xc8\xc8\x08b\xb1\x18;p)\xcb\x8d\x07\xb8s\xccv\x86.\xc4\x85s\xa9\x87\x19\x90\x1b8\x1e\x8fc||\xbc\xe8\xf9\x1c\x8b\xc50<<\x0c\xb7\xdb\x8d\xde\xde\xde\x19\xc9\xd3\x5c\xec\x88\xdc\x15y*\x9f\xf5T^W,\xc9\xebTm\xe5b\xb8H\xe7\xf3YJ=\xaf\x98\xc7M\x10\x04D\xa3Q\x04\x02\x01\x8c\x8f\x8fcdd\x04\x83\x83\x83\x88F\xa3,\x0c\x86\x88\xdf\xd9h\xda .\xd4\x00\x92\x01\x88\xc7\xe3,FH\x8e\xd1\xd1Q\xf4\xf7\xf7\xa3\xbb\xbb\x1b\xf1x\x1c\x1e\x8f\xa7(\xdb\xa5\x85\x9fJ\xa5\x90N\xa7Y\x11c\xe5\x00+[K-$\xb9:]\xe26\xd7\x00\xce\xf9\xc8\xfa\xb4\xf9\xe4\xef-\xaf\x01HcAn[\x8b\xc5\x82\xf2\xf2r\x18\x8dF\x94\x95\x95\xa1\xac\xac\x0cv\xbb\x1dv\xbb\x1d:\x9d\x8e\x1d\xac\xf4\x1e\xd9l\x16\x9b6m\xc2\xea\xd5\xab\xf1\xf4\xd3O\xe3\xc5\x17_\xc4\xfb\xdf\xff~\x04\x83A\xa4\xd3id\xb3Y\xf6ZN\x02W.\xb2\xd9,\x82\xc1`\x01\xe9K\xa7\xd3\x98\x9a\x9a\xc2\xfe\xfd\xfb\xb1\x7f\xff~\xf4\xf6\xf6bhh\x08n\xb7\x1b\x89D\xa2\xc0\xad!\xdf\xb3<\x0c\x84C\xde\x05\x83J\x9f(\xcf\xceP(\x04\xb7\xdb\x8d@ P\xd0UF\xaf\xd7\xa3\xbd\xbd\xbd\x80`\xccG\x01<\x1d2\xb4P \xfb\x96\xc9d\x0ab\x00\x01\xc0\xef\xf7\xa3\xbf\xbf\x1f;v\xec@\x7f\x7f?\xc2\xe10\x82\xc1`A\xfd\xd6S\xb5u\x0bE\x8a\xe5cX\xec\xf7SR\x8b\xb2\xc5\x9d\xb2\x1d\x9arLf\xfa^D\x98\x8b\xa9\x8a\xa5~.5N\x0b\xd5v\xaf\xd8{\xe4r9$\x12\x09V?\xd5\xe7\xf31\x8e$O\xf6\xa0j\x0ag\xfaL\x5c\xd0\xfa\x0b\xc4\xce'&&\xd8\x97\x0f\x06\x83\xe8\xe9\xe9\xc1\xc1\x83\x071::\x0a\x9f\xcf\x87d2\x09\xaf\xd7[@\xee\x94\x93F\x93\x12\x8f\xc7\xd9\x82 \xb9\x5c.\x87'\x93\xc9\x05'p\x8bI\x0eO\xf5\x90\xa0X<\xe5\xe3t\xf8i4\x1a\xe8t:\x18\x0c\x06$\x12\x098\x1c\x0eH\x92\x04\xb3\xd9\x0c\x93\xc9\xc4J\xc0TVV\xc2f\xb3\xc1n\xb7\xa3\xa2\xa2\x02&\x93\x09\x06\x83\x01\x82 \x97\xcb!\x95J\xe1\xc6\x1bo\x84\xc5b\xc1\x0b/\xbcPP\xec7\x97\xcb\xc1\xe1p\xc0h4\xb2R\x05\x1c+\x03tI\xc8f\xb3\xf0z\xbdH&\x93\xd0\xe9tH&\x93\xe8\xec\xec\xc4\xee\xdd\xbb\xd1\xdb\xdb\x8b\x91\x91\x11LNN\xc2\xe7\xf3!\x12\x89\xb0RE\xb4v\xc9\xc0s\xf2\xc7\x89\x1f)$\xa2(B\x14Ex<\x1e\xbc\xfd\xf6\xdbx\xeb\xad\xb7\xf0\xf6\xdboc``\xa0 ^T\xbefr\xb9\x1cZ[[\xf1\xd6[o\xc1\xe9t\xce\xe8:+\xe5j\xccI9\xa8p\xf6[\x12\x92]\x8bF\xa3\xe8\xec\xec\x84 \x08\x18\x1f\x1fGoo/\x06\x06\x06\xd0\xdd\xdd\x8d@ \x80d2\x89D\x22\x01\xe0d\xf1\xe8\xb3I\x5c\xf3\xf9<#\xecr\xb5\x8bHL>\x9f\x87\xc9d\x9aF\xc0\x95s\xa1\xd5j\x0b\xc6\x9fBMJ\xa9k\xc5\xbc\x834\x1er\x01\xe9L\x9d1\xf4{\x8b\xfd>\xba\xacd\xb3YV\xec;\x95JM[\xcf\xf2\x8bM\xb1\xb8\xfe%O\x00I\xae\xcf\xe7\xf3\xac\x04L:\x9df$.\x9dN#\x16\x8b\xc1\xe7\xf3\xb1\xfaG\xca~\x87$\x8bR\xab\xa9L&\x03\x8dF\xc3\xda\xe3H\x92\x04A\x10\x90J\xa5\xd8k\xa8v\xddb\xa8t\xa7\xb3\x80\x16r\xf2h,\x09T`\x9bz\x0aR\xc9\x16Q\x14!I\x12L&\x13<\x1e\x0f4\x1a\x0d\xab+\xd4\xd3\xd3\x03\x87\xc3\x01\xbd^\x8f\xf2\xf2r\xd4\xd7\xd7\xc3j\xb5\xa2\xb6\xb6\x16N\xa7\x13\xa2(\xb2[h:\x9d\x86F\xa3\xc1\xc5\x17_\x8c\xbd{\xf7\x16\x14\x9c&\xc5P\xa7\xd3-\xf9\x0a\xfb\x1c\x0b{\xc0\xc9\x0f)\xba|%\x12\x09$\x93I\xa6\x12+[X)\x0fF\xa5r\xcf\x13\x8bV\x06\x94\xe7m,\x16\x83\xc7\xe3\xc1;\xef\xbc\x83_\xfd\xeaWx\xe9\xa5\x97\x0a\xd6\x09u6\x92\xaf!\xe5:\x9a\xcb\x99-\xef\xb1+\x7fN\x22\x91\xc0\x1f\xff\xf8G\xd4\xd4\xd4\xa0\xa3\xa3cV;\xb2\xd8\xe3\x92\xcb\xe5\x10\x8dF\xf1\xf2\xcb/#\x16\x8b!\x93\xc9\xa0\xaf\xaf\x0f\x1e\x8f\x07^\xaf\x97u\x06\xc9d2KfNi\xaf\x13\x91#\x11\x81:R%\x12\x09&\x14P\xe7)R\x04\xe9\x5c\xa1\xccW\xf9x(\xc3\x96H\x80\x90\x17\xa4\xa7\x0eC\xc4\x17\xe8w\x93\xa7\xecL\xf7\xd4-\xc5\x15\xe8<$\x1e\x93\xc9d\x0a\xfa>\xcb/\xc6g\x1a\x0bF\x00\xc9(\x00@\x7f\x7f?\xf6\xec\xd9\x03I\x92\xe0\xf3\xf9\xd0\xdf\xdf\x8f\xfe\xfe~LNN\x22\x1c\x0e#\x9b\xcd\xb2\xa0pb\xef\xc4\xe0\x05A`\x841\x97\xcb\xb1Vr\xc4\xa2\xcf\x04\xab_\xca\xea\x04\xa9n\xb4\x80\xa8`(\x19g\xb5Z\x8dX,\x06\x95J\xc5\x5c\xbd\x1a\x8d\x06\x16\x8b\x05\x81@\x00eee\x98\x9c\x9cD__\x1f\xecv;\x1c\x0e\x07\x0c\x06\x03V\xaf^\xcdnc\xe1p\x18\xa2(B\xaf\xd7\xa3\xb5\xb5\x15G\x8f\x1ee\xad\xfeh\xa1\x92;\x98\xc7r\xad\x1c\xd0\x9as\xbb\xdd8r\xe4\x08\x04A\xc0\xd4\xd4\x14\x06\x07\x071<<\x8c\xf1\xf1q\xf8|>D\xa3\xd1\x02\xd5X\x0ey\xb8\xc2R\xdfk\x1c\x0b\xb3fH-\x02\x80\xd7_\x7f\x1d\xdb\xb6m\xc3\xee\xdd\xbb\xb1k\xd7\xae\x82K-\x11\x05\xf9e\xa2\x14\xc6\xc6\xc6fue\x16S\xa0T*\x15N\x9c8\x81t:\x8d\xf1\xf1q477\xc3n\xb7\x9fUrL\xf5\x00\xdf|\xf3M\x18\x0c\x06\xa6\xa2\xc7b1\xa4R)\xa4R\xa9\xb3\x9681\x1b\x04A`\xaa\x1d\xa9\x80\x94\xd8\xa0\xd5j\x19\x09$\xe2M\x82\x05\x915\xa5\xfd \x92K]R\xe89\xd4\x8aR\x9e<#\x9fc\xb9r\xb6XY\xd3\xf3\x15|\xe8\xf7\xcb\x9b5\x14\x9b\xc7\xb3U\xfaG\x5c\x8cA\x10E\x91)D\xd4aB\xde\xda\x86\x9e\xa7V\xab\x11\x8f\xc7\xd9\xe4\x91\x9f\x0f\xe5\xe5\xe5\xf0\xf9|0\x9b\xcd\xa8\xa8\xa8\x00\x00x<\x1ev\x135\x9b\xcd\xe8\xec\xecd]\x1bh.m6\x1b\xdb\xcc\x1c+\x07t\xa9 %\x98\x0es\xba\x8c\xc8\xd7\xecL5*9\x96\xbf\xe2\x97\xcdf\x99\x8a\xf7\xf4\xd3O\xe3{\xdf\xfb\x1e\x06\x07\x07\x91J\xa5\xd8\xda\xa0\xb3C\xaenY\xadV\x5cs\xcd5hkkCCC\x03\xfbc\xb5Z\x0b\xde_\xa3\xd1\xa0\xa2\xa2\xa2\xc0\x16\xccu}%\x93I\xf6\xbaR\xc9\x86gb\x9c\x88\x10\xe4r9\xf4\xf5\xf5!\x99L\x22\x95J!\x99L2\xb5\x88D\x10\xb9z\xb6\x14m\x14\x8d)\x9d\x11\xe9t\x1a:\x9d\x0e\xd9l\x96\xf5\xa8'\x8f\x16u\xbc(f\xd3\x88\xf0\xd2\xbf3\x99\x0c\xb2\xd9,\xe3\x04t\xb1P\xd6\xbc\x95\x7f\x9e\xa5r\xd6(\x95k%9%\xe1\xe6L\xb9}\x17\x8d\x00\xcewP\x22\x91\x08\x93ri\xc1\xd3m\x87\xfc\xe4rW\x92\xfc\xef3Y,q\xa9\x93Be\xf6\x10-(\x22\xe3Z\xad\x96\xf5\x07\xa6q\xa6\x9f\xa9\x83\x8b\xc9d\x82\xcb\xe5B(\x14b\xad\xe2\xd4j5\x8cF#\xdb|\x07\x0f\x1edq\x86\xd4K\x98nv\x5c\x05\xe4\x06\x9f\x83C~&\x85\xc3a<\xf5\xd4Sx\xf4\xd1G\xd9Y\xa44\x86z\xbd\x1e\x95\x95\x95\xb8\xf3\xce;q\xc3\x0d7\xe0\xf2\xcb/\x9f\xd6\x1d\x86\xc8Q\xb15\xb6\x10Y\xc2\x14^s6\xce1y\x22\x9f \x08\x18\x1b\x1b\x83Z\xadf\x1e0y\xa8\xd4R\xdc\xefJ;$'\xaa\xe4\x0e&\xb2N\xa4\x8cD\x84L&3\xcdU+O\xf2 \xc50\x9b\xcd2AH9&\xa7\x9b\x88y6\xc6l\xa6X\xfe\xb3\xa1\x02\x8a\x8b\xb5\xf9U*\x15\xeb\x0c\x92L&\xd9d\xc9\xcbI\xe8t:\xa4\xd3\xe9\x82\xe0H\x8au(\x15GT,\x93h\xa5\xa9\x7f\xf2\x9b\xa0|\x1c\xe4A\xa7\x14\x17\x91\xc9d \x8a\x22\x0b\xdc\xcff\xb3\xcc}\xeb\xf5z\xa1\xd7\xeb\xd9\x1cP\x1b\xbfd2\x09\x8dF\x83p8\xcc$\xfd`0\x88}\xfb\xf6\x15\xf4\x10\xa6x\x0d\xde6n\xf9\x13f\xf2\x18\x8b\x5c.\xc7\x08!\x05\xd2\x92{\x97\xdc\xec\x14\x9bID\x906\xb3Z\xadF&\x93\x81Z\xad\xc6\xd0\xd0\x10\x8e\x1e=\xcaJ\xcb\x18\x8dFF\xfe\xb8+\x98+\x7f\x1c+\x0ft~h\xb5Z<\xf2\xc8#\xf8\xe1\x0f\x7f\x08\xb7\xdb\x0d\x00,\x06L\x92$\xacY\xb3\x06\x8f<\xf2\x086o\xde\x8c\x86\x86\x86\xa2kh\xb1\xcf\x10eAe\x95J\x05)/\xcd\xba\xc6\xe5\xee\xec\x85T\xff\xc8\xcbb\xb5ZY\xf8\x8d\xd2\xce\xcd&\x06\x9cm\x05\xb0X\xfb=\xa5r)\x0f\xfb\x22\xc5\x8b\xc2Fh.\x94\xe4\x8f\x04!\xb2M\xc5\xde\x93\x88\xe5\xb9$>(\xe7\xd4`0\xc0f\xb3\xc1h4N\xcb\x8a>\xa7\x08\xa0\xbc\xae\x8d\xdb\xedFUU\x15\xe2\xf18K@ \xd7$\x11\x0br\xf5\xc6b\xb1\x82x\x07\xb9\xaa\xa0\xcc>,\xa64\xae$\x05p\xa6\x1aF\xc5T\x18\xf9\xd8\x90\x22H\xb7(\x9dN\xc7\xe4v\x8b\xc5\x02\x95J\x85H$\x02\xbd^\xcfn\xc5\x92$\xb1\x22\xbe\xf2\xf2\x1d\x9d\x9d\x9dp:\x9d\xacw0\x91@\xee\x0a\xe6d\x90c\xe5\x81\x5c\x97\xd7]w\x1d\x06\x06\x06\x0a\x5cz\xe9t\x1a\x97]v\x19\xbe\xff\xfd\xef\xe3\xbc\xf3\xce\x9b\xb1T\xc6\x99\x5c\x83rwr>\x9fG^*}\xc9>r\xe4\x08\xc6\xc6\xc6\xb0a\xc3\x06\xd4\xd6\xd6.\xd89G\x99\xb1F\xa3\x11.\x97\x0b\xf5\xf5\xf5\x05\x04j&\x22\xb8\x14I\x8c2\x16Oi\x93\xc8\x0bEv%\x95J\xb1\xd2f\xa4\xd6\x91:H\xc9\x1f\x14\x06&O\x08*V6\xee\x5c%\x7f\x04\xb5Z\x0d\x97\xcb\x05\x8b\xc5rn\x13@\x22\x81\x82 \xa0\xa1\xa1\x81\x05\xd7\xca\xbf4e\xf1\xa4R)\xa6\x14\x92\x8f_\x1e\xf3\x07\x00z\xbd\x1e\xf5\xf5\xf5X\xb5j\x15+[B\x81\xe6\xf2E\xb7\x9cI\x87|<\xe8\xdf\xd9l\x16\xa1P\x88e[\xd2f\x19\x1d\x1dE4\x1aE8\x1cf\x8f\xd1\xa6Q\xb6\xdf\xa1C\x862\xb8r\xb9\x1cL&\x13{\x1e5)'\xd7\xb1^\xafg\x85J\x83\xc1 \x0e\x1f>\x8c\xea\xeajTWW\xc3b\xb1\xb0\x98A^\x16fe+A\x8be\xd89\x96\x16\xc8\xdd\x9bJ\xa5\xf0\x9d\xef|\x07\x0f>\xf8 \x04A`e\xbb\x00\xe0\xaa\xab\xae\xc2?\xfc\xc3?`\xcb\x96-,\xd6\xeel\x94[Q\xaeKe\xd8B)\x050\x99Lb||\x9c\x950\xab\xad\xad]P2 \x08\x02\xccf3jkkq\xe9\xa5\x97\xe2\xa7?\xfd)\xc6\xc7\xc7\x0bB\xa1\xe4b\xc7R =r\xd7\xab<\xb35\x93\xc9 \x99Lbxx\x18\x03\x03\x03\x18\x19\x19A$\x12a\x17\x82L&\xc3\x1a\x09P9\x18\x22\x85\xc0\xc9Lp*\x1cO|\x80\x94?9\xf9\xd3h4\x8c\x17\xd4\xd5\xd5\xa1\xbc\xbc\x9c%\xa5\x15k\x16q\xb6/\xc5\xca\x905\xb9\xdb\xdfn\xb7\xa3\xaa\xaa\x0av\xbb\x1dV\xab\x95y\xde\xcei\x17\xb0\xb2\xb1\xb1\xcdf\xc3\xc0\xc0\x00#\x17\x82 0\xb7/\xc9\xbdJ\xe5\xcf\xe5ra\xf3\xe6\xcdX\xb7n\x1d\x9cN'\x1b\x1cr7\xcao\x99+A\x01T\xc6\xaa\xd0x\x11y\xa6\x04\x9ah4\x0a\xb7\xdb\x8d`0\xc8Z\xcf\xf4\xf5\xf5\x15,D\xb9\x9cN\x8a \x118yk\x1aJ\xceQ\xab\xd5,k\x8e2\x81\xfb\xfa\xfa\xd0\xd5\xd5\x85\x9a\x9a\x1a8\x9dN\x94\x95\x95\xb1ZP\xdc\xf0\xaf\x1c\x92W\x8a\xf0qEpy\x93\xbf\xa3G\x8f\xe2\xe3\x1f\xff8\x0e\x1c8p\xd2\x98\x88\x22\xd2\xe94\xd6\xacY\x83'\x9f|\x127\xdcpCA\xf8\x892\xb9c1\x8cm1\xa3\xaf\xfcY^/\x0e\xc0\x8c\x0a\xa0\xbc$\x8d \x08\xec\x02\xbc\x90g\xbbZ\xadf\x09xZ\xad\x16eee\xa8\xac\xacd\xd51\x8ay\xc0\x96\xc2\xbeR\xc6\xda\x11YK\xa7\xd3X\xb7n\x1d&''\xd1\xdd\xdd\x8d\xfd\xfb\xf7\xa3\xbf\xbf\x9f}\x07\xaa\xe8\x91\xcdf\x99`@\xff&\xce@\xb1\x7fD\x80\xe5j\xa2\xcdf\xc3\xd5W_\x8d\x8e\x8e\x0eTVV\xc2j\xb5\xb2J#d\xb7\x96\xd2\x19\xa4\x8c\xcf\x97\xab~T-\xc5j\xb5\x16\x10\xc0sZ\x01\x94\x7fqb\xf8\xb4\x89\x08\xf2\xac\x1e\xba9\xc8\x0d\x8b\xddn\xc7\x07>\xf0\x01\xac[\xb7\x0e---p\xb9\x5c\x05\x04C\x9e\xf6\x7f\xb6\xea\xe7\x9c\xe9\x03\x97\xc6\x94\xfe-'p\x14\xcfG1\x13\xd1h\x14\xd1h\x14\xc1`\x10>\x9f\x0f\x13\x13\x138|\xf80:;;Y\x11m\xda\xb4\x82 \xb0\xc2\x9a\xc9d\xb2\xe0\xa6I%d\xe8\x00\x94\xbb\x81s\xb9\x1c\x0e\x1f>\xcc\xfa\x05;\x9dN\xe6\x0a\xe6*\xe0\xf2\xc5\xd9l\xe4\xceq\xf6A1\xc1/\xbf\xfc2\xfe\xec\xcf\xfe\x8c\x91\x22:\xe7\xef\xbf\xff~<\xf1\xc4\x13\x05\x86\xf8L\x9f\x07\xb3%&)\xeb\xc9\x96Z\xcbD\x16\x17\x83\x00\xd2\xb8Q\xd7\x0c\xadV\x0b\xab\xd5\xcaD\x11%i\x90\x17d_*kAN\x02\xc9\xae\x93\x08A\x84&\x9f\xcfctt\x94\xa9w\xe4\xe6\xa4\xf8P\xbd^\xcf:w\x10\x1f\x90\x8b\x14\xf4\xddM&\x13n\xbf\xfdv\xb4\xb7\xb7\xa3\xb9\xb9\x99\xb9M\xa9\xde\xadR%]*c\xa4\xbc\x84\xd0\xe5\x83l%%\x80P\x99\x9c39\xbf\x8b\x1aqK\x0bV\xde\xdaE\xb9`\xc8%,\x1f\xa4\xcd\x9b7c\xcd\x9a5X\xbf~=\x93x\x0d\x06\x03\x0b\x10V\xca\xe0\xcb\xd5\x18\xcd\x14`+\x1f/\xda(tsR\x12A\x9f\xcf\x07\x8f\xc7\x83\xa6\xa6&\x9cw\xdeyx\xe7\x9dw\xd0\xd7\xd7\xc7Z\xea\x91\xbb\x86\x12v\xe8\xbd\x89\x94S\x89\x04y\x0b>\xfa{jj\x0a===hllDuu5/\x0e\xcd\xc1\xb1\x8c\x89\x1f\x19\xd7\x1f\xfc\xe0\x07\xf8\xdc\xe7>W@\xec\x9a\x9b\x9b\xf1\xb3\x9f\xfd\x0c\x9b6mb\xc1\xfcg\x03\x92$\xa1\xaf\xaf\x0f555\x05IjJ\xdbS\xaa=Y\xb1\xf3V\x10\x04\xd6\x86m!]\x8bt\xd1&\x85\x94b\xb3g\x22\x0f\xf2\xdf\x7f\xb6\xec\x9e\xd2\xcd*\xff\x9b*|P\xd7)I\x92\x10\x0a\x85\xe0\xf5z\x0b\x08\x9a^\xaf\x07\x00\x18\x8d\xc6\x82\xf6v\xca,_\xb9\xbd\xdb\xb2e\x0bZZZ\xb0f\xcd\x1a455\xb1\xdfA\xea\xdfR\xb49J\x05P\xe9\x0a&[)o\xe8p&!.\xf6\xa1Q,iA\x1e\xd8)\x7f\x8e$I\xa8\xae\xae\xc6\xda\xb5k\xd1\xd8\xd8\x88\xba\xba:TUU\xc1b\xb1\xb0\xf6g\xf2\xc5\xb6\x12\xdaH\xcd\xa5\x06\x94\xfc@\xa3MDD\xb0\xba\xba\x1a\xc1`\x10\x1e\x8f\x07UUU\xa8\xae\xae\xc6\xae]\xbb\xb0w\xef^\x84B!6\x1f\xf4|Z\x94\xb4A\xe5E*S\xa9T\x81\x0b>\x16\x8b\xa1\xbf\xbf\x1f\xa3\xa3\xa3hjj\x82\xcb\xe5\x82\xc9d\xe2\xb1\x80+\x84\x10p\xac\x1c\x90\xd1\xfa\xd4\xa7>\x85\x1f\xff\xf8\xc7\xac\x96[.\x97\xc3=\xf7\xdc\x83g\x9ey\x86]H\xcf\x06\xf9#\x92v\xe4\xc8\x11<\xf0\xc0\x03\xa8\xab\xabCmm-V\xb5\xb4\xe0\xaf?\xf5)\x5c\x7f\xddu\x8c\xfc\xc9E\x07\x22-\xb3\xa9\x89\xe4)Y\xac\xb1%5H\xab\xd5\x16\xcdt]\xaa(\x16_Iud\xa9\x8e\xdf\xf8\xf88\xea\xea\xea055\x05\xa3\xd1XP9\x82TARe\x95\x8d\x1f\x08\xabW\xafFSS\x13\x9a\x9b\x9b\xd1\xd8\xd8\x88\xca\xcaJ\xa6\xfe)\xc3\xc1\xce\x15^\xa0$\x83g\x03\xe2\x99\xfe\xc2\xa4\x08*\xb3Wi\x00\xd6\xae]\x0b\xbb\xdd\x8e\xca\xcaJ\xd8l6\x98\xcd\xe6\x82\xfa8+\xf5\xe0-\x059\xd9\xd2j\xb5\x8c\x10f\xb3YX,\x16X\xadV\xd8l6\x94\x97\x97\xc3f\xb3A\xab\xd5\xa2\xbc\xbc\x1c\xff\xf7\x7f\xffW@\x02\xd3\xe94+\xd0I2}>\x9f/\x88\xd1\xa4\xc3\x8a\xc8\xe0\xe0\xe0 \xc6\xc7\xc7\xe1v\xbbY\xa2\x8e\x9c\xacs,_,\x85:[\x1cgf\x9eU*\x15\xee\xba\xeb.<\xff\xfc\xf3\xec\x02\x98L&\xf1\xcc3\xcf\xe0\xd3\x9f\xfe\xf4\x9f\x0c\xcaY*\x05E\xc6\xf4\x8f\x7f\xfc#\x00`tt\x14\xa3\xa3\xa3\xd8\xbd{7~\xfb\xbb\xdf\xa1\xa9\xa9\x09\x9f\xfb\xdc\xe7\xf0\xf9\xcf\x7f\x9e\x95\xb5\x9a\xcbE\x86\xce\xd23q\x9e\x9d\x8bY\xad\xcaq\xa1\xd86\xe0d(\x97\xcb\xe5b\x7f\x02\x81\x00\xb3\xe7\x0e\x87\x83%0R\xbc#\x91\xf1b\xed\xd2jjj\xe0r\xb9PUU\x05\x87\xc3\x01\x8b\xc5\x02\x83\xc1pN\xf3\x82\xa50\xcf\xe2\x99>H\xe80Q\xa6\xe2\x13(\xde\xcfl6\xc3d2\x9d\xb5&\xdd\xe7\xf2\xa2\x22\x05\x8e\xba\x80h4\x1a\xe8\xf5z\x18\x8dF\xe8\xf5z\xd6\xbe+\x9f\xcf\xe3\xa5\x97^b}\x99\x95-\x87\xe4\xe4\x9c$jy\xcb\x1aA\x10\x10\x0e\x87\xe1v\xbb\xe1\xf1x\x10\x0a\x85PQQ\xc1:\x85ppp\x9c\xdb\xa0K\xfa\xddw\xdf\xcd\xc8\x1f\xa9,o\xbf\xfd66o\xde\xccz\xbd.\x05\xec\xd8\xb1\xa3\xa0\xf6,p\xb2\xf2DOO\x0f\xee\xbd\xf7^\xdc\x7f\xff\xfdx\xf8\xe1\x87\xb1v\xedZ\xe8t\xbaY\x0b\x06\xcf\xd4\xb7\xb5\x94\x9a\xc3m\xd0I{a2\x99`\xb1X`\xb3\xd9\xd8\xdfv\xbb\x9d\xf5]\xa6\xb2dF\xa3\x91\xcd\x13\xcd\x87\xbc\xc4\x9bV\xab\x85\xc3\xe1`\xbc@\xa7\xd3\xb1\xe2\xe2\x1cK\x90\x00\xca'Q\xf98\xc5\xac\xc9\xff\xc8AA\xa2D\xfcVB\x92\xc7b\x93A\xea\xd8!\xef\x0dL\x99\xd8\xf1x\x1c\xaf\xbe\xfa*#\x7f\xca\xc0^j\xe5C\x89 D\x02\xe57\xfe\xa3G\x8f\xe2\xfc\xf3\xcfG0\x18D\x22\x91@YY\x19\x0b\x9c\xe6X\xfe\x04a>\x8fs\x9c{g\xc8_\xfe\xe5_b\xeb\xd6\xad,h\xbf\xac\xac\x0c\xef\xbe\xfb.\xd6\xad[\xc7\x12\xfdf[\x1f\x8b}\x86Sm\xd9\xef~\xf7\xbbx\xf1\xc5\x17\xd1\xdf\xdf\x8f\xde\xde^\x1c9r\x84\x91A\xbd^\x8fl6\x8b\xaf~\xf5\xab0\x9b\xcd\xb8\xe9\xa6\x9bp\xfb\xed\xb7\xb3\xb8\xbbb%D\x94\xa5\xc9\x94\xe7\x1a\xfdL\xf1\xd4\xfc\xe2\xfb\xa7\xf9 \x976u9\xb1\xdb\xed\xa8\xad\xad\x85\xc1``%\xc6\xa8\x12\x88<\xa9\xb1Xm[\x12/\xc8\x8eq^\xb0\x04\x09\xa0r\x03\x15K`\xa0\x85A\x05\xa1\x95\xc5\x9c\xc9}H$\x83O\xf2\xc2\x80\xb2\x8d\xe8\xdf\xf9|\x1e\xf1x\x1c\xe1p\x18\x93\x93\x938t\xe8\x10\x80\xc2\x12\x09\xf2lk\x22{\xf1x\xbc\xe0\xff\x04A\xc0\xe4\xe4$B\xa1\x10\x82\xc1 \xe2\xf18#\xf9\x9c\x00.op\x17\xf0\xf2\xc7'>\xf1\x09l\xdd\xba\x95\x15\xe9\xd5\xe9t8t\xe8\x10\x1a\x1b\x1bg\x8d\xb7\x92\xb7\x06;S\xeb\xb1\xaa\xaa\x0a\x97]v\x19\xae\xb9\xe6\x1aD\x22\x11\x04\x83A\x04\x02\x01\xfc\xe2\x17\xbf\xc0\x81\x03\x07\xd8%8\x1a\x8d\xe2W\xbf\xfa\x15^{\xed5<\xf0\xc0\x03\xf8\xd2\x97\xbe\xc4\xea\x9d\xceDdU*\x15|>\x1f\x92\xc9$\x02\x81\x00\x9a\x9b\x9ba\xb1X\xe0\xf1x\xf0\xf5\xaf\x7f\x1dO=\xf5\xd4\x92RC\x97\x0a\x11$\x8f\x13\x95\xbb\xa1\xf8Q\xaf\xd7\x8bL&\x03\xb7\xdb\xcdb3\x8b\xad\x17y\x1b9\xce\x07\x16x~\x16\xe3\xc6(\x872\xd1CN\x0e\x8b\xc9\xebt\xa8\xcc\xd4\xfd\x83\xe3\xf4H\xa0V\xab\x85^\xafGEE\x05\x1a\x1b\x1b\xb1j\xd5*\xb4\xb7\xb7\xa3\xbc\xbc\xbc\xb06\xd6\xff\x9f\x17\xbau\xa9\xd5j\xe4r\xb9\xa2\x1d?(\xf3.\x12\x890\x02\xc8\x15\xa0\xe5\x0f>\xc7\xcb{^\xbf\xfb\xdd\xef\xe2\xa7?\xfd);\xc7\x8dF#zzzPSS3\xa7\xcb\xb9 \x08x\xf5\xd5W\xb1k\xd7\xae3\xb6^\xa8\xb2D\x22\x91\x80Z\xad\x86\xd3\xe9\xc4\x1dw\xdc\x81\xfd\xfb\xf7\xe3\xbd\xf7\xde\xc3\xc6\x8d\x1bY\xfc\x1fp\xb2\xa8\xfd\x97\xbf\xfcel\xd9\xb2\xa5\xa4\x1d#\xbb\xb4o\xdf>\x1c?~\x1c}}}\x18\x1e\x1e\xc6\x81\x03\x07\xd0\xd6\xd6\x86\xef}\xef{x\xf2\xc9'9\xf9S\xcc?\x09=\x82 \xa0\xae\xae\x0e\xabV\xadB[[\x1b\xf3\xf0%\x12\x09X,\x16&P\xc8\xcb\xc5\x15\xe3\x16\xe4\xd5\xe2\xbc`\x89\x12\xc0\xb9lt\xb9\xebW9\x91\x9c\xf8-.\xa8\xf8\xa4\xd9lf$\xb0\xb9\xb9\x19\xf5\xf5\xf5Lu\xa59\xd0\xeb\xf5H&\x93\xac\x04\x0c\x91\xbfb\xca\xad\xc7\xe3A8\x1cF4\x1ae\xdd]8\x96\x0ff\xda\x8fs\xc9R\xe78w@1\xc0\xaf\xbf\xfe:\xee\xbb\xef>f\xbc\x8dF#v\xef\xde\x8d\xda\xda\xda\x92nN\xf9Zx\xea\xa9\xa7\xb0e\xcb\x16\x5cv\xd9ep\xbb\xddg\xa5t\x09)J\xd9l\x16\x17]t\x11\xf6\xed\xdb\x87\xe7\x9e{\x0e\xeb\xd7\xafg\xdfW\x14E\xbc\xfa\xea\xabhoo\xc7\xdbo\xbf\x0d\x00\x05$Q\x0e*\xb1%I\x12|~\x1fn\xbc\xf1F\x04\x83A\xe4\xf3y|\xe5+_\xc1\xce\x9d;\xf9\x22\x92\xd9y9\x014\x99L\xa8\xa9\xa9\x81\xd1hD}}=DQdmH\x95\xf6\xbf\xd8:\x91W\x0b\xe1g\xce\x12$\x80r\xf5h.\x13T\xcc\xa8PM;>\xc1\x8b\x07J\x10\xb1Z\xadp:\x9d\xa8\xad\xadEcccAp-\xb9J,\x16\x0b\x8cF#s\xf5\xd0\x86Vf\x7fMLL \x1e\x8f#\x91H\xb02\x0b|\x0e\x97\x8f\x224\x9f\xb9\xe4\xf3~n\x9f\x0d]]]\xb8\xf9\xe6\x9b\x99\xfbN\x92$\xfc\xcf\xff\xfc\x0f\xd6\xacY3\xeb\xdcR\xec\xf0\x87?\xfca\xfc\xed\xdf\xfe-T*\x15t:\x1d.\xbd\xf4R$\x12\x89E\xbf\xd8\x17\xf3(E\xa3\xd1\x82f\x04W\x5cq\x05\xbe\xf1\x8do\xe0\x8b_\xfc\x22\x8b\x0b\x14E\x11\xc3\xc3\xc3\xd8\xb2e\x0b\xfe\xe5_\xfe\x05\x1a\x8dfZ\xfc\x9f\xfc;\xaa\xd5j\xd8]\xe1/\x0f\x00\x00 \x00IDAT\xac6<\xf5\xd4S\xec2\x9cN\xa7q\xd3M7!\x1c\x0e\x17\xd4\xbe]\xa9{\x83\x92<\x88\xd4\x95\x97\x97\xa3\xa6\xa6\x06\xf9|\x1e\xc1`\xb0\xa0\x86\x9f\x92\x03\xcc\xa4\xc0r,a\x02(\xdf \xf2\x94\xf6\x996\xd1L\x87\x01/$\xbc\xf8j\x8e(\x8a\xd0\xeb\xf5(//\x87\xcb\xe5Buu5\x1c\x0e\x07{\xdcl6\x17\xa4\xe6\xd3\xebf:\xc0\xd2\xe94\xc2\xe10\xe2\xf18S\x009\x11X>\xeb\x85\xf6%\xc7\xf2\xc7G>\xf2\x11\x96\x91\x99H$\xf0\xd4SO\xe1\xc6\x1bo,j\x98\x8b\x91\xad\xcd\x9b7\xe3\xf7\xbf\xff=#\x94\xa9T\x0a\x7f\xfd\xd7\x7f\x0d\x83\xc1\xb0\xe8g\x82\xf2\xfd%I\xc2\xe0\xe0 \xdez\xeb-\xec\xdd\xbb\x97\xd53\x15\x04\x01W]u\x15~\xf2\x93\x9f\xa0\xbd\xbd\x9dy9R\xa9\x14\xfe\xfe\xef\xff\x1e\x0f=\xf4\x10\x8b{\x9ciO\xa4\xd3i\xdcu\xd7]\xb8\xef\xbe\xfb\x98\xda\x15\x08\x04p\xe5\x95W2b\xa9\xfc,+)n\x96\xba\xc6\xd0w\x0e\x85B\x08\x85B\xa8\xaa\xaaBCC\x03\xcb\xfe-v\xc9,\x96@\xca\xed\xc99B\x00\xe5\x13z*\x0b\xbeTaN\x8e\x85\xbb\x9dQ\x86\xb0\xddnGEE\x05\xacV+\xabF/\x8a\x22l6\x1bk\xe7C\xedvH\xceW\xcek<\x1e\x87\xcf\xe7\x9b\xd6\xf6o\xb6\xf5!/\x05\xa4|Ly\xa0\xf3\x03\xe0\xd4\xf7a\xb11\x9ei\xbc\x8b\xa9(3\xdd\xcc9\x96\xcf:\x01\x80\xc7\x1e{\x0c\x9d\x9d\x9d\xcc\x13\xf3\xc9O~\x12\xf7\xddw\x1f\x0b\xd0/\xf5Z\xaf\xd7\x8b\xcb.\xbb\x0c;w\xeed\xcf\xb5Z\xadx\xeb\xad\xb7\xf0\x8f\xff\xf8\x8f\x05\xdd\x84\xce\x04\xf9\x93?\x9e\xc9dX\xa9*\xf9z\x06\x80\x17^x\x01\x0f?\xfcpA\xd7\x8fo|\xe3\x1b\xf8\xf2\x97\xbf\xccb\x9fg\xda\x0f\x99L\x06O>\xf9$\xb6l\xd9\x82\x5c.\x07\x8dF\x83C\x87\x0e\xe1[\xdf\xfa\x16\x0b\x99\x01N\xba\x8e\xfb\xfa\xfap\xe8\xd0!\xd6\xf9b%\xd8\x18\xea\xf3\xabR\xa9PSS\x83\xb6\xb66\x96\x8c8\x1fn\xc0\x15\xc0s\x8c\x00\x16c\xf1\x9c\xc9/-UG\x1e\x0fh\xb3\xd9PQQ\x81|>\xcfZ(\xe9\xf5z\x16#Cs\xa9\x8c\x13\x94oPR\xff(\xad\x9f68\x15\xa5\xa6^\x91\xd4\x09F\xfe\x98\xfcg\x0a\xe4\xa6\xee$\xd45F\xdeA\x86\xa3\xf8!)o\xcc>\xdb\x18\xd3\xf8R_n\xf98+\xc30\x94\x8a\xfdL$\x91\xe3\xdcD&\x93A__\x1f\x1e~\xf8a\xf6X{{;\xfe\xf3?\xff\x93\xd5b+u\x96D\xa3Q\x5c}\xf5\xd5\xe8\xec\xecd\xc5\xe8\xdb\xda\xdap\xf4\xe8Q\x5cq\xc5\x15\xec\x1c9\x955M\xeb9\x9dN\x97\xdc\xfbs\x89S\x0d\x06\x83\xcc;A\xeei\xbf\xdf\x8f\xaf\x7f\xfd\xeb\xf8\xcdo~\x03\x00\xac+\xc7\x13O<\x81G\x1ey\x04v\xbb\xbd\xe8\xa5\x96\x08_:\x9d\xc6\xb6m\xdbPSS\x83L&\x03A\x10\xf0\xe8\xa3\x8f\xa2\xab\xab\x8b}\xa6@ \x80\xae\xae.\x0c\x0f\x0f#\x14\x0a\xad(;#o\x137\xd7\x02\xdc\xa5\xe6\x90c\xe1pFR\x96x\xb9\x88\xa5M\x02\x0d\x06\x03,\x16\x0b\xccf3\xacV+\xacV+\xeb\xb3\x98N\xa7\xe1\xf3\xf9\x10\x8b\xc5\xd8\x0d\xac\xd8A\x9b\xcb\xe5\x90L&\x19\xf9\x937\xf3&2A\x07@\xa9,\xf0b\x87\x87<\xfbK\xde\xa1\x84g\xdc\x15\xee1\x1ag\x22x\xf2\xdaZ\xb3\x1d\xac\xf22L\x1a\x8d\x06:\x9d\x8e\x198\xf9\xf3h.\xe4e~\xf2\xf9\xfcY\xeb\xfd\xca\xb10\xe4O\xab\xd5\xe2\xe6\x9bofk@\x92$|\xf7\xbb\xdf-I\xfc\x08\xa9T\x0a\x1f\xf8\xc0\x07p\xec\xd81\xd62\xed\xa2\x8b.\xc2\xae]\xbb\xd8\x1e\x9do'\x0d\xba@\x16\x8b7>U\x15\x10\x00\xa2\xd1\xe84%\x89>\xe3\xad\xb7\xde\x8a\x83\x07\x0f\xe2\xfc\xf3\xcfg$\xf6\xbf\xff\xfb\xbf\x99\x0b[\xa9\xdc\xd1\xfe\xa21\xfa\xe1\x0f\x7f\x88\x9bn\xba\x09\x92$!\x1e\x8f\xe3\xcb_\xfe2~\xfd\xeb_\xb3\xf2Y\xa4bq{\xc89\xc1\x8a\x22\x80\x1cK\x9b\x00\xeat:\x98L&\x98\xcdf\x88\xa2\x08\xbb\xdd^P\x0f\x8bj8\xe5r9v\x80)\x89C.\x97C,\x16c7u\xf9\xe3\x92$\xb1\xe4\x10\xca\xa2\xa3\xdb|\xa9\x9b\x9d\xbc96e\x22\xd2\xe7\xa4 m\xder\xeeO*\x09\x91py2\x0e\x11\xee\x99\x0e\x5c*\x08\x9e\xc9d\x0a\xaa\xf7\x93\x0a,\x8f\xff$\xe2\xa7\xd7\xeb\x0b\x94`y\xed\xb3\xd9\xdc4\xdc\x03\xb0\xf4\xd6\x8eF\xa3\xc1\x13O<\x81\x13'N@\xab\xd5\x22\x9dN\xe3\x9e{\xee\xc1\xf5\xd7__r\x8f\xd1\xff\xdds\xcf=\xd8\xb1c\x073\xec\x97\x5cr\x09\xf6\xec\xd9\xc3\x8a\xfd\x9e\x0ab\xb1\x18\x02\x81\x00\x8e\x1c9\x82\x1d;v`\xdf\xbe}\xe8\xea\xea\xc2\xc7?\xfeq<\xfa\xe8\xa3\xa7\xdcy#\x93\xc9L\xfb>\xf2\xb3j\xfd\xfa\xf5\xe8\xea\xeaB{{;[\xaf[\xb7nE}}=\xae\xbc\xf2\xca\x0276\x9d\x83\xf4\xf3\x87>\xf4!\xdcz\xeb\xad\xf8\xedo\x7f\x0b\xb5Z\x8dm\xdb\xb6\xe1\xd5W_\xc5\x87>\xf4!\xd6\xee\x8c\x8aK\xaf4;\xc3\xc3GV \x01\xe4\xedq\x96>\x04A`\xad\xe2L&\x13$IbE^EQD(\x14B.\x97c\x07\x98\xbc\xf2\xbfrn\xe5\xaa\x13\x1d\x90*\x95\x0a\xc9d\x12\xd1h\x14\xe1p\x18^\xaf\x17>\x9f\x0f\xa1P\x08\xc9drNY\x85\x00\xa0\xd3\xe9X\xb2\x0a\xf5\x89\xd6\xe9t+~\x8d\xc9\xdd\xbe\xd1h\x14\x81@\x00~\xbf\x1f\x13\x13\x13\xac3K)\xd0\xeb\xf2\xf9<\xccf3\xea\xea\xeaP[[\xcb\x02\xb8\xa9\xd9\xba\x9c\xf4'\x93I\xb6\x0e(K4\x95J1e\x96\xe3\xdc2\xce\xb1X\x0c\x8f?\xfe8#\x7f\x8d\x8d\x8d\xf8\xe1\x0f\x7fX\xb202\x91\xfe\xa7\x9f~\x1a?\xff\xf9\xcfY\xb0\x7fKK\x0b^{\xed5\xe4\xf3y\x18\x0c\x869}\x06\xf9\x05b\xe7\xce\x9d\xd8\xb6m\x1b^{\xed5\xec\xdd\xbbw\xdas\x87\x87\x87g,!6\x177a)\x22B%c\xda\xda\xda\xf0\xea\xab\xaf\xe2\x86\x1bn`\x17\x96'\x9f|\x12k\xd7\xae\x85\xd5je\x04R\xe9\xc1\x08\x06\x83\xb8\xfb\xee\xbb\xf1\xca+\xaf0\x97\xf1_\xfd\xd5_\xc1\xe7\xf3!\x1a\x8d\xb2D\x11^\x22\x8bcE\x10@~\xdb_\xfa\x87\xbf\xbc\xca:\xb9=jjj`\xb7\xdb\x11\x0c\x06\x11\x8b\xc5\x98\xb2D\x07d\xb1\x04\x0e:\x10)\xbe\x86\x0eiR\x04\x03\x81\x00\x04A\x80V\xab\x85\xc1`\x80\xc3\xe1`\x8d\xc1\x8b)\x0c\xa4j\xa5\xd3itww#\x14\x0a\xe1\xbd\xf7\xdeCkk+#\x1f\xd4\xd2n\xa5\x13\xc0l6\x8bD\x22\x01\x9f\xcf\x87\xb1\xb11\x0c\x0f\x0f\xa3\xbe\xbe\x9e5M'\xb7y1\x8c\x8f\x8f\xa3\xb7\xb7\x17{\xf6\xecA<\x1e\x87V\xab\xc5\x95W^\x09\xa3\xd1\x08\xb3\xd9\x5c\xe0\x02T\xb6f\xcc\xe7\xf3\x88D\x22\x10E\x11f\xb3\x99\xb9\x9e\xe7c\x909\xce>\xbe\xf5\xado\xc1\xe7\xf3\xb1y}\xf6\xd9ggu\xeb\x8b\xa2\x88]\xbbv\xe1\x0b_\xf8\x02\xdb\x8b\x00\xb0g\xcf\x1e\x94\x95\x95\xcd\xf9w\xa7\xd3ih\xb5Z\xec\xda\xb5\x0b\xf7\xde{/\xba\xbb\xbb\x11\x89D\x0a.\xa7\x94l\x01\x00###L\xad>\x15\xcc\xd4\x83^N\x02\x01\xe0\xfa\xeb\xaf\xc73\xcf<\x83\xcf|\xe63,\xce\xef\xa1\x87\x1e\xc2\x8f\x7f\xfcc\xc4\xe3q\x88\xa2X\xe0\xc1\xc8\xe5r\xd8\xb9s'4\x1a\x0d>\xfb\xd9\xcf\xe2;\xdf\xf9\x0e\xb4Z-\xfc~?\xfe\xf5_\xff\x15\xb7\xdcr\x0b\xc6\xc7\xc7\x91\xcf\xe7\xe1\xf1xPYY\x09\xa3\xd1\xb8b\xce..\x04-s\x02\xa8\xec!;\xdbm\x8bci\x90\x07r\xb3R\xdd?\x8dF\x83\xf7\xbd\xef}\xe8\xea\xeab\xaa[(\x14\x82\xc3\xe1\xc0\xc8\xc8H\xc1\x5c\x17K\x04\x90\xf7\xfe\xa4\xf5\x90N\xa7\x91L&\xd1\xdd\xdd\x8d\x8f}\xecc\xf3\xfe\x9c\x17^x!\xa2\xd1(\xee\xb9\xe7\x1e\xa6J\x10y\x5c\xc9\xbd7i\xbc\xd3\xe94\xa2\xd1(\xbc^/\xba\xbb\xbbq\xf3\xcd7\xa3\xae\xaenN\xef\xb1f\xcd\x1a\xc4b1x\xbd^tvv\xc2\xe7\xf3\xe1\xf2\xcb/G8\x1cFee%\xfb=\xf2\xf9T\x1e\xec\x82 \x14\x0a\xf1,\xbds\x0c\x92$abb\x02?\xfe\xf1\x8f\xd9<_{\xed\xb5\xb8\xf4\xd2Kg5\xda\x99L\x06w\xddu\x17DQ\x84$IH&\x93\xd8\xbbw/l6\xdb\xbcT\xf9h4\x8a\xbf\xf8\x8b\xbf\xc0+\xaf\xbc2MA\xa6z\xa5uuu\xd8\xbcy3\x1a\x1b\x1bq\xe1\x85\x17\xb2\x8b\xdfL{\xa2\xd4\xef\x9ek\x11\xe1\x5c.\x87O\x7f\xfa\xd3x\xf1\xc5\x17\x99Kwrr\x12\xff\xfe\xef\xff\x8e\xaf|\xe5+\x88D\x22\x05\x1e\x91\x91\x91\x11\xd6&\xf3\xba\xeb\xae\xc3\xd6\xad[111\x01Q\x14\xf1\xf8\xe3\x8f\xe3\xa2\x8b.b\x97\xb0\x89\x89\x09\xf8|>\xb4\xb5\xb5a\xd5\xaaU\xcb\xde\x8b\xc1\xfb\x85\xaf\x10\x05P\xbe\x88\x95\xea\x1f\x9f\xec\xa5y+#\xb7\x04\x05\x5c\x9b\xcdf\xb4\xb5\xb5\xa1\xae\xae\x0e\xdd\xdd\xdd\x88\xc7\xe3\xd0\xeb\xf5\xd3\x0e\xdc\x992\xbc\x8b)r\x94!\xec\xf3\xf9\xf0\xf6\xdbo\xe3\x8a+\xae\x98\xf3\xe5\x80n\xd7\xdb\xb6m\x83\xdf\xefG4\x1a\x85\xc3\xe1@EE\x05\xca\xca\xcaX\xab\xba\x95z\xd1\xc8f\xb3H\xa5R\x08\x04\x02\x18\x1c\x1c\x84\xddnG]]\x1d+\xbfP\x8a\xf8'\x93I\xfc\xe4'?\xc1\xb3\xcf>\x8b\xde\xde^\x04\x02\x01\x5cs\xcd5\xacN\x9a\xbc\xdb\x8b8\x96>\xf9S\xa9Tx\xe1\x85\x17066\xc6\xce\x80\xcf\x7f\xfe\xf3sr\xdd>\xfe\xf8\xe3\x18\x1a\x1ab??\xf6\xd8c\xb8\xf8\xe2\x8bY\x9c\xf0l{Z\xa5R\xe1\xd9g\x9f\xc5\xa7?\xfdi\xe4r9\x18\x0c\x06\x16\xae\xd0\xda\xda\x8a\x1bn\xb8\x017\xddt\x13\xae\xbf\xfe\xfa9'\x18\xcd\xc5\xc6\x90\x87b\xb6\xe7\xaa\xd5j\xc4b1|\xeb[\xdf\xc2\xde\xbd{166\x06A\x10\xf0\xc6\x1bo\xe0\xc6\x1boD{{{\xc1\xfb\xd0\x05\x88\x92`>\xf3\x99\xcf\xe0k_\xfb\x1a\xb2\xd9,\xfc~?\x9e}\xf6Y|\xecc\x1fC*\x95\x82J\xa5bY\xd7\xabV\xad\xe2\x8b\x91\xe3\xac\xe1\x8c\x97\x81\xe1\xd9?K\xebfFA\xc9j\xb5\x1a\xe9t\x9ae\xc0\xe9t:\xe6\xa6\xb0Z\xad\x88\xc7\xe3\xecfL%E\x94\x07)\x95O(X`\xff_]\xa4\xf7\xdf\xb6m\x1b\xb2\xd9\xec\xb4l8\xb9{Q\x99iz\xf0\xe0A\xec\xd8\xb1\x03^\xaf\x17\xbd\xbd\xbd\x18\x1c\x1c\xc4\xf8\xf88\x02\x81\x00\x8bG[\x89\xa0\xb9\x88D\x22\xf0x<\xf0x<\xb8\xe5\x96[\x0aJ\xf7(\xdd\xb64']]]x\xe0\x81\x07\xf0o\xff\xf6o8~\xfc8\xc2\xe10\x0c\x06\x03\x9a\x9b\x9ba\xb5Za4\x1a\xa7\xc5z*/x\x84p8<\xef\x0b^\xb1\xf7\xe48s\xc8d2P\xa9Tx\xe4\x91G\xd8\xfa\xb8\xf0\xc2\x0bq\xfb\xed\xb7\x97\x9c\xcbl6\x8b\xf1\xf1q<\xfa\xe8\xa3\xec\xb1\xf7\xbd\xef}x\xf0\xc1\x07Y\xdch)P7\x90?\xff\xf3?\xc7\xddw\xdf\x8d\x5c.\x07\xadV\x8bD\x22\x81\xc6\xc6F<\xf7\xdcs\xd8\xb1c\x07\xbe\xff\xfd\xef\xe3\xa6\x9bnb1\xa6\xf2\x18\xd4\xd3!\x81\xa9T\x8a%\xab\xcd\x16\xa2\xa4\xd3\xe9\x90\xc9d\xf0\x85/|\x81\x85\xc7\xa4\xd3i\xfc\xf2\x97\xbfD2\x99d\x09V\xf2\xefE\xfb\xeb\xe2\x8b/\xc6\xa6M\x9b\xd8{\xed\xd8\xb1\x03~\xbf\xbf\xa0\xd6 \x9d\xa9\x00\x10\x89D\xd0\xdf\xdf\x0f\xaf\xd7\xcb\xc5\x12\x8e\xe5A\x00y\x0c\xd0\xd2W\x00Ia\xa3@\xfe\x8d\x1b7\x02\x00\x0c\x06\x03k\x17\xe7\xf5z\x91\xcdf\xa1\xd7\xeb\x0b\x5c\xbcJ\x90\x0b\x99\x0a\x7f\xca{\x0aS\xad\xc1\x81\x81\x01\xfc\xd3?\xfd\x13N\x9c81k\xdfgA\x10p\xe4\xc8\x11\xec\xde\xbd\x1b]]]\x88D\x22\xf0\xfb\xfd\xe8\xe9\xe9\xc1\xd8\xd8\x18|>\x1f\xe2\xf1x\xc1A\xbc\x92\xc8;eW\x87\xc3a\x0c\x0f\x0fc\xc3\x86\x0d\x05m\xfb\x8a\xcd\xb7\xdf\xef\xc7\xcf\x7f\xfes<\xf8\xe0\x83\xf8\xedo\x7f\x8b\xd1\xd1Qf\x10\x9b\x9a\x9a\xd0\xd4\xd4\x04\xbb\xdd\x0e\x93\xc9\xc4\xe6\xb2\xd8\xd8\xcac\xfd\x94*!\xc7\xd2_;:\x9d\x0e\xbf\xff\xfd\xef\x11\x0e\x87\xa1\xd3\xe9\x90\xcdf\xf1\xd5\xaf~uV\xf5L\x14E|\xf1\x8b_d\xff\xd6h4x\xec\xb1\xc7\xe6\xd4\xbf]\x92$\x18\x0c\x06\x9c\x7f\xfe\xf9\xd8\xbau+{~:\x9d\xc6\xc3\x0f?\x8c\x81\x81\x01|\xf4\xa3\x1fe\xa1\x07\xf2s`.=\xe2\xe7\x12s.I\x12FGG\xe7\x1c\x9f.I\x12:::p\xf5\xd5W\xb3\xdf\xf1\xde{\xef\xa1\xab\xabkZ\xc2\x9b\x1ceee\xf8\xc8G>\xc2>\xfb\xc0\xc0\x00\xc6\xc6\xc6\x8a\xc6\x1f\xe6\xf3y\x0c\x0c\x0c\xe0\xc4\x89\x13\xe8\xef\xefG:\x9d\xe6\x8b\x94\xe3\x8c\x80\x97\x81Y\xe1\x86\x80n\xd7\xa4&Q\x00\xb7$I\xa8\xa8\xa8@6\x9b\x85\xd9l\x86Z\xadF&\x93\x99V\xdbO~\x90\xe9\xf5z\x88\xa2\xc8\x12\x07\xa8\xcc\x8cV\xab\x85\xc5bAee%\x9a\x9b\x9b\xf1\xc6\x1bo`\xff\xfe\xfdhoo\xc7\xb5\xd7^\x8b\x86\x86\x06TUU\xc1h4\x22\x9dN#\x9dN#\x91H`hh\x08\xef\xbd\xf7\x1e\x8e\x1f?\x8e@ \x80x<\x8et:\x8d\xfe\xfe~\x8c\x8c\x8c\xa0\xa9\xa9\x09\x15\x15\x15\xac|\xcdJ\xaa\x0bH\xf3E\xf1{\xa9T\x0a\x1b6l(J\xce\xf3\xf9<\xbc^/\x8e\x1d;\x86\xe7\x9f\x7f\x1e{\xf6\xec\xc1\xd0\xd0\x10\x22\x91\x08S\x82DQ\xc4E\x17]\x84\xea\xeaj\xb8\x5c.F\x00\xe5FWi\xe4\x94\xe5,\xb8\x9awn\x80\xc2>\x1ey\xe4\x11\xb6\xaf\xadV+n\xb9\xe5\x16\x96\x94Q\x0cj\xb5\x1a\x13\x13\x13\xd8\xbau+\xf4z=\x92\xc9$n\xbb\xed\xb6Y\xcb\xc5\xc8\x09\xe4e\x97]\x86\xc3\x87\x0fC\x14Ed\xb3Y\xb4\xb4\xb4\xe0\x97\xbf\xfc%6m\xda\x84t:}Z1\xbdsq\xeb\x0e\x0e\x0e\xe2\xfe\xfb\xefGMM\x0d\xbe\xf9\xcdo\x96\xfc}*\x95\x8a\x85X\xdc}\xf7\xdd\xd8\xb1c\x07+L\xff\xcc3\xcf\xe0G?\xfa\xd1\x8c%]\xe2\xf18>\xfa\xd1\x8f\xe2\xf1\xc7\x1fG(\x14\x82$I\xf8\xfd\xef\x7f\x8f\x07\x1ex\x80e\xcc\xcb?w8\x1cF6\x9bE0\x18D2\x99\x5c\xb6u5\x8b\x85\x85\xf1K\xe32!\x80|\x22\xcf]\x02H\x99u\xf2\xfaW\xf9|\x1eF\xa3\x91\xd5\x0a\xa3\x8e\x11\xf2b\xce\xf4>\x1a\x8d\x06\x16\x8b\xa5\xa0e\x1c\xbd\x8fF\xa3AYY\x19\xaa\xab\xab\xd1\xdc\xdc\x8c\x9e\x9e\x1e\x1c\x8f\x91\x91\x11\x84B!LNN\x22\x99L\xb2\xcf499\x89\xbe\xbe>444\xc0\xe5r\xc1f\xb3\x15(\x8f+\xc1\x80Sfv(\x14\xc2\xd4\xd4\x14\xaa\xaa\xaa\xa6)'4?\x07\x0f\x1e\xc4K/\xbd\x84\x1d;v\xa0\xaf\xaf\x0f^\xaf\x17\x91H\xa4\xa0\xd9}[[\x1b:::P]]\x0d\x9b\xcd\x06\x83\xc1\xc0\xc63\x97\xcbM\xeb\xef]l\xbf\xf3\xf0\x8es\x03j\xb5\x1a\xfb\xf7\xef\xc7\xe1\xc3\x87\xa1\xd3\xe9\x90J\xa5\xf0\xf8\xe3\x8f\x9f4\x083\x5c\xa2(\x1c\xe3S\x9f\xfaTA\xf8\xc6\xcf~\xf63d2\x99\x92D\x8aH\xe5\xad\xb7\xde\x8a}\xfb\xf61bu\xfd\xf5\xd7\xe3\xb9\xe7\x9e\x83\xd3\xe9D6\x9b\x9dS\xd1\xe9\xd3\xd93^\xaf\x17\xf7\xde{/\xd2\xe94zzz\xf0\xd8c\x8f\x15t>Q\x92\xd5\xae\xae.LLL\x00\x00\xca\xcb\xcbq\xe7\x9dw\xe2\xd9g\x9f\x85V\xab\xc5\xe0\xe0 v\xef\xde\x8d\xcb/\xbf|\xc6\xd7\xd7\xd5\xd5\xe1\xd6[o\xc5\x7f\xfd\xd7\x7fA\x14E\xbc\xf3\xce;\x88D\x22\xd0j\xb5E\xdb/R\x9f\xe2\x85\xaa\x13\xc8\xcb\xb0q\xcc\x86\x05s\x01\x173\x06|\xf1-}\x15I\xde\xb5C\xa3\xd1\xa0\xad\xad\x8d\x110jh\x1e\x8f\xc7Y\xbc\x9d\xb2\x06\x96<\x19\xc0`0L\xeb\x19L\x06\xc7h4\xc2\xe1p\xa0\xbe\xbe\x1e\xad\xad\xad\xb0\xd9l\x98\x9a\x9a\xc2\xf0\xf00\x0e\x1e<\x887\xdex\x03\xbbw\xef\xc6\x9e={\xb0\x7f\xff~\xec\xdc\xb9\x13\x03\x03\x03\x08\x87\xc3\x88\xc5b\x05\xbf+\x9dN\xa3\xb7\xb7\x17\xa3\xa3\xa3p\xbb\xdd\x08\x85B\xb3\xb6\x89Z\x8e\xea\x1f\xd5\xfd\xf3z\xbd\xb8\xe2\x8a+\x8a\x1a\xde]\xbbv\xe1\xe7?\xff9\xdex\xe3\x0d\xf4\xf6\xf6\xc2\xe3\xf1 \x12\x89\xb09'R}\xfd\xf5\xd7\xc3\xe9t\xa2\xb2\xb2\x92\x95\x8eQ\xba\x92\x95Y\xfe3\xa9&\x1cK\x1f?\xfb\xd9\xcf\x00\x9c\x8c\x89\xd3\xe9t\xb8\xe3\x8e;X\x1b\xb3\xa2\x86B\x10p\xfc\xf8ql\xdf\xbe\x9d\xc5\xf3\xde{\xef\xbd0\x99L%\xe7\x9cb\xfc\xbe\xf4\xa5/\xe1\xa5\x97^b\xcf\xbd\xf2\xca+\xf1\xd2K/\xc1\xe9t\x96$\x9e\x0b)BX\xadV\xac_\xbf\x9e\xfd|\xe0\xc0\x01<\xff\xfc\xf3E_322\x82\xbe\xbe>6&\xa1P\x08w\xdcq\x07\x8b\x0bT\xab\xd5\xf8\x8f\xff\xf8\x0fx\xbd^$\x12\x09\xa6\xa4\xcb\xf7\xc1\xa4{\x12\xdf\xf8\xc67\x0a\xbe\xdfK/\xbd\xc4.\xd5r[\xa9\x8c\x87>\x1d\xc4\xe3qvy^\x0a\xe0g\xc2\x0a \x80\xf31\x12\xa5\x16\x87\xbc\x84\x0c\xc7\xe2\xaa\x7fD\x12R\xa9\x14\xfc~?\xee\xb8\xe3\x0eF\xd8\x08\xe1p\x98\xa9x\x94\xe1V\xac\x9f\xa3N\xa7\x83\xddng\xc9\x03r\x02(W\x01].\x17\x9a\x9b\x9b\xd1\xde\xde\xce\x8a\x10\x87\xc3a\xf8\xfd~x\xbd^\x8c\x8f\x8fcbb\x02\xb1X\x8c\xd5\x0fL&\x93\xec}(\xeehhh\x08\xc3\xc3\xc3\x18\x1d\x1den\x13e\xff\xda\xe5:oT\x1f1\x1c\x0ec||\x1c\xd5\xd5\xd5(//\x9f\xb6gz{{\xf1\xe2\x8b/\xe2\xe0\xc1\x83\x18\x19\x19\x81\xc7\xe3A\x22\x91(p\xdfK\x92\x84\x0b/\xbc\x10UUU\xa8\xaa\xaabsH\xf3]l\xaf\x16#\x86\x1c\xe7\x0e$I\xc2\x9e={\xd8\xcf\x1f\xfe\xf0\x87a\xb3\xd9fu\xe1n\xdd\xba\x95\xc5\xdc\x02\xc0\xfd\xf7\xdf_\xb2\xc7/%\x85l\xdf\xbe\x1d\xdf\xfe\xf6\xb7Y\x0cq[[\x1b\xb6o\xdf~F3\xc7\xa9\x06\xe9\x83\x0f>\x88\xd6\xd6Vv\xce\xfd\xeaW\xbf\xc2\xb1c\xc7\xa6\xadc\xea\x19L \xd2{\xf7\xddw\xb3\xe7MMM\xe1\xd7\xbf\xfe5\x0e\x1c8P\x90\xd0A\xc8\xa43\xa8\xa8\xa8\xc0\xa6M\x9b\xd8\xe5\xf9\xcd7\xdf\x84\xc1`(8\x7f\x95\x04\xf0t\xf7S\x7f\x7f?\xf6\xee\xdd\x8b\xce\xce\xce%aG\xb9\xabw\x05\x11@9\xa1S.>\xf9\x02\x98)\xb0w\xa6zc\x1c\x0bo\x04\x88H\x84B!\xc4\xe3q\xd4\xd7\xd7\x17(z\xc1`\x10\xe9t\x1a\xc1`\x10\xe1p\x98e\xff\xca\xfb\xce\x12\x0c\x06\x03*++\xa1\xd5j\xa7\xa9G\xd4\xc7\xd7h4\xa2\xbc\xbc\x1c\xf5\xf5\xf5hllDKK\x0b\xe2\xf18#\x81\xe1p\x18\xd1h\x94\x156\xa6vfT\x80\x9a\x0aVS\xfb\xb2c\xc7\x8eatt\x14\xe3\xe3\xe3\x88D\x22\xec0]\x09\xf3\x16\x8dFY\xe1\xe7\x0f~\xf0\x83\x05\xbdy\x01`ll\x0c\xbf\xfb\xdd\xef\xb0\x7f\xff~\xf8\xfd~6>\xcan-j\xb5\x1a\x17_|1jkk\x0b\xd4?\xb9\xd2[\xea`\xa7B\xd4\x1c\xe7\x0e\x8e\x1f?\xceH\x0f\x00\x5c}\xf5\xd5E\x09\xbf\x1c\x89D\x02\xaf\xbf\xfe:\xfb\xf9\xb6\xdbnCccc\xc9\xd7\xa8T*V\xe7O\xab\xd5\x22\x99L\xc2j\xb5\xe2\xddw\xdfea\x05\x8bE6f\xfa?\xa3\xd1\x88\x87\x1ez\x88\x95.\x12E\x11_\xfa\xd2\x97\x90\xcf\xe7\x91\xc9d\xe0\xf7\xfb155\x85X,V4\xeb\xfd\xe2\x8b/\x86^\xafg!\x18;v\xec@8\x1c\x9e\x96\xb8!\xb7a\x9f\xf8\xc4'\xd8\x99:66\x86\x91\x91\x11F\x9a\xc9s1W\x028[\xa2\x15\x95\x9d\x11E\x11\xd1h\x14\xd1htI]^g\xeb\xfd\xce\xb1\x0c\x14\xc0\xb9L\xba\xbc\x91|\xb1\xd7s\x17\xf2\xe2\x92\x08\xca\xfa\x0d\x87\xc3\x98\x9a\x9aBEE\x05\xaa\xab\xab\x0b6\x22\x95|\xa0\x96mD\xb0\x8a\x19\xfc\x8a\x8a\x0a\x18\x8dFV\x97\x8f\x12\x08\xe4\xf3-\x8a\x22\xca\xca\xcaPUU\x85\x86\x86\x06\xacZ\xb5\x0aF\xa3\x11\x99L\x86\xf5\x08\xa6^\xb6Tf\x81\x0eF\xeaTB\x17\x07\x9dN\x87\xc1\xc1A\x0c\x0d\x0da||\x9c\xb9b\x96\xb3\x0aH\xea\x1f\xc5\xfe\x0d\x0f\x0fc\xe3\xc6\x8d0\x99L\xd3\x9e\xb3\x7f\xff~\xf4\xf7\xf7#\x14\x0a1\x82\x9f\xcdf\xa7\x8dOGG\x07\x9a\x9a\x9a\x98\xfaG\x89<\xc5\x0c\xd9L\xfbu\xa6va\x1cK\x13\x87\x0f\x1fF0\x18\x84J\xa5\x82\xcdfc\x85\x9fK\xad\xbb\x9e\x9e\x1e\xd6\xef\x17\x00n\xbc\xf1\xc69\xc5\xdc>\xf1\xc4\x13\x18\x1f\x1fg\x04\xe9\x97\xbf\xfc%\x1c\x0e\xc7Y\xeb\xe0\x93\xc9\x9cT\xe5\x1e~\xf8a\xe6\xb6\x0d\x87\xc3\xb8\xef\xbe\xfb\xa0\xd1h\xb0w\xef^\xbc\xf7\xde{\xacs\x91\x92\xa0\x94\x97\x97\xe3\x82\x0b.`\x8f\x1dD\x22\x11f\x8c\xe4c\xe4\xf7\xfbq\xf8\xf0aLLL \x10\x080w\xba\xb2\x0b\x82N\xa7\xc3\xc6\x8d\x1bQ]]\x8d\x8a\x8a\x0a\x98L&F\xb2g2\xce\xc5B6\xf8E\xed\xdc\x00\xed\xa7W^y\x85\xcdeMMM\x01\xa1)\xb6\xe6T*\x15\xdez\xeb-\x00'c\xd9\xca\xcb\xcb\xf1\xa1\x0f}h\xd6\xdf511\x81\xef}\xef{\xec\xb1\xbb\xee\xba\x8b\xbdn\xb1\xd4\xbf\xb9\xf4\x16O\xa7\xd3\xe8\xe8\xe8\xc0u\xd7]\xc7\xe2\xf9\x9e\x7f\xfey\xbc\xfa\xea\xab\xac\x9da1\x12\xa6R\xa9\xa0\xd3\xe9p\xd1E\x17\xb1\xc7\xfa\xfb\xfb\xe1\xf7\xfbg\xfc<*\x95\x0a\x06\x83\x01UUUl\xff\x1d\x04\x83A\xdcv\xdbm,\xf1C\x8eP(\x84\xb1\xb11\x0c\x0e\x0e\x22\x1c\x0e#\x91H\xb0\xd8\x18\xb9\x92D\xf3q\xfe\xf9\xe7\xc3b\xb1\xc0f\xb317m\xa9\x182\xa5\x0aX[[\x8b\xe6\xe6f\xf4\xf6\xf6B\x14EV\x9a\x86\xe6]\x14E\xe6N\xa1\x9e\x9c\xf4\xb7(\x8a\x08\x87\xc3\xe8\xef\xefg\xad\xcc\x9cN'+G\xb3\x1c\x08\x09\x19\x85x<\x8e`0\x88\xc1\xc1A\x96Y(\x1f\xe7\xc9\xc9Itww#\x16\x8b\xb1\xb8\xa0T*UP\xb3\x91\x08\xde\xe5\x97_\x8e\x8a\x8a\x0a8\x9dN\x94\x95\x95\xb1q.\xa6\xd8\x92\xa23\x9b\xd2R,\xa4\x83c\xe9\xac!A\x10\x0a2y\x01`\xdd\xbau3v\xf1\xa0\xd7\xec\xdc\xb9\xb3\xe0\xf1\x8b.\xbah\xc6\xe2\xcf\xb4\xc6\x9ey\xe6\x19\x00'=:\xf5\xf5\xf5X\xb7n]\xc9.#\xb3|\xf8\xb90\x89y\xbfm*\x95\xc2e\x97]\x06\x8b\xc5\x82H$\x02\x00\xd8\xb9s'ZZZ\x98\x9d*v~\x85\xc3a\xdc\x7f\xff\xfd\xd0j\xb5\x88\xc7\xe3,\xfe\xae\x94\x22J\x9d\x8e(<\xc5\xe3\xf1\xa0\xbd\xbd\x9d\xb9h\xe5\xe5\xd3\x02\x81\x00\xd4j5\x12\x89\x04\xecv;\x9cN';\xc7i\x8c}>\x1f***\x10\x89DX8\xd5\xd8\xd8\xd8\xb4\xb9\xa40\xaa\x85\xdc\x97\xd4\x13Y\xa7\xd3!\x97\xcbadd\x04\x83\x83\x83\x00\x00\x8b\xc5\x82\xb6\xb66\xbe\xe18\x01\x9cy\xf1\x10\x01\xd4h4\xc8\xe5r0\x18\x0c,K\xd4\xedvcdd\x04\xfd\xfd\xfdp8\x1c,\xa1@\xd9\xe0\x9e\x03\xd3T\x1d\xfa\x9b\x5c\xa0\x99L\x86e\xd5\x86\xc3ax\xbd^\xf4\xf4\xf4\xc0n\xb7\xe3\xf6\xdboGUU\xd5\xb4\xb9\xc9d2\xd8\xb1c\x07v\xef\xde\x8d@ \xc0\x0a1\xa7R\xa9\x82Vp\xf4\xfb\xd7\xad[WP@X\x19\xffW\x0cD\xe4\xacV+\xaa\xab\xab\xd1\xd0\xd0\x80\xd5\xabWchh\x88\xa9}Db\xe5\xb1\x80\x14\x9fCa\x04\xf4\x1d\x0d\x06\x03:;;\xd1\xde\xde\x8e\x91\x91\x11\xe6\xd2\x9c\xcbg9W\xd4?r\xfb\x8c\x8c\x8c\xa0\xbe\xbe\x1eN\xa7\xb3\xe0`\x97$\x09\xfb\xf6\xedCww7+\xfbB\xea\x822\xcbp\xed\xda\xb5hjjBuu5\xecv;t:]\xc9\xd6Ss\xddo\xbc\x17\xf0\xd2?+\x0e\x1e<\xc8\xc8\x0f\xc5\xa5\xcd6g\x87\x0e\x1d\x02p2\xc1\xa0\xae\xaenN\x8a\xce\x1f\xfe\xf0\x07\xd6e\xe4o\xfe\xe6o\x0aT\xc7y\xe3\x14\xd6\xdf|\x5c\xc1\x9b7o\xc6\xb6m\xdb\x00\x00o\xbf\xfd6>\xf9\xc9O\xb2\xf2S3\xbd&\x1e\x8f\xb3R1\xb3\x91\xabL&\x83\xea\xeaj\x98\xcdf\x84\xc3a\xd6\xfb\xf7\xf2\xcb/G4\x1a-\x88\xe3U\xa9T\x18\x19\x19\xc1\xf0\xf00\xb2\xd9,\x9cN'6m\xda\xc4\xcef\x82\xd7\xebE\x7f\x7f?\xe2\xf18\xb3\x89>\x9fo\xda\xe7!\xaf\xcdlc_\xac\xc7{\xb1\xc7(\x16qdd\x04\xeb\xd6\xadCYY\x19\x02\x81\x00\xfb\x0cn\xb7\x9b\xd5YT\x8eY\xb19\xe1\xe7\xc5\x0a#\x80\xe4\x02\xa4\xd6?z\xbd\x1e\xa1P\x08\xa2(\xb2\x1e\x89\xc7\x8f\x1fg\xeeD\xca\x94\xa2 \xf5\xb3U>`)\x1e\xe8\xf2\x929D\xfa\xc8\xddKeU\xa2\xd1(\x82\xc1 <\x1e\x0fB\xa1\x10.\xbf\xfcr\x5ct\xd1EE\x8b\xf9\x0a\x82\x80\xbe\xbe>\xbc\xf3\xce;\x18\x1b\x1bC0\x18D4\x1ae\x99eJW\x82 \x08\xd8\xb0a\x03\xaa\xaa\xaa\xe0p8X\x06\xb0\xb2\x06\xa0r\x93\xd3!e4\x1aQQQ\x81\x86\x86\x06455\xa1\xae\xae\x0e\x03\x03\x03\x05\x87\x18)}r\x12(\xbfDP}\xb1L&\x83\xc3\x87\x0f\xa3\xa9\xa9\x09\xf5\xf5\xf5p8\x1c\xcc\xady.\xabR\xf2\xd8?\xaaQv\xfb\xed\xb7O\xcb\x8e\x9e\x9c\x9c\xc4\xd1\xa3G\xe1\xf1x\x98\xfb\xb7X\xe6\xaf(\x8a\xe8\xe8\xe8@MM\x0d*++QVV\x06\x9dN\xc7\x0e\xf0\xd3q\x17\x15\xdb\x97|\xaf.-\x02\xd8\xdf\xdf\xcf\xce\x0cy\xdb\xc7R\xaf9|\xf80\xbb\x84o\xdc\xb8\xb1\xe4kT*\x15\xc6\xc7\xc7Y\xf7\x1e\x00\xb8\xf9\xe6\x9b\xd9\xc5n\xde\x18\x0d\x03\xbf\xef\x06D\x01@\x91\xdfI\x0f]\xdb\x044\x9b\xe7E(T*\x15b\xb1\x18\xae\xb9\xe6\x1al\xdb\xb6\x0d:\x9d\x0e\xe3\xe3\xe3s\xda\x03\xf3\x11\x22\xb2\xd9,\xacVkA\xb9$\x9f\xcf\x07\x00\xac\x16k\xb1q\xa7\x18\xe7t:=\xed\x1c\xcdd2\x18\x19\x19\x99V[\xb7\x18\xf9\x94\xf2\x12\x90/>|\xf3\xdd\xbb\xb1X\x0c\x83\x83\x83\x88\xc7\xe3\xe8\xee\xeeFmm-s\xfb\x92::\x97\xb9^\x8c\xe4\x0f^?x\x09\x11@r\xdb\xc9'\x99\x8c~,\x16c\x1d\x1d\xa2\xd1(\xecv{A?Z\x8dF\x83\x13'N0\x22\x13\x8f\xc7QSS\xc32\xaf\xe6\xbb\x01\x97\x0b\x94\x07\x13);D\xfc\xa8TK,\x16+\x88\xa3\xd4h4hnn\xc6\x95W^9-XY\xbe\xd9\x8f\x1f?\x8em\xdb\xb6\xb1\xae\x11\x81@\xa0@IR\x06+\xd7\xd5\xd5a\xed\xda\xb5,\x8e\x8c\xdc\xbf\xc5H\xbf\x12\x94\xf9Ku\x01\xa90\xf4\xd8\xd8\xd84\xd7\xb5\xc1`(\xd8\xe0\x82 \xb0\x18R*\xe3@1<\xc3\xc3\xc3hhh`I)\xa2(\x9e\xb3\x8d\xd5\xe5\xb1\x7f\x91H\x04\xe3\xe3\xe3\xacc\x87RQ\xa7\xde\xca\xa4\xda\x16+\xfa\x0c\x00UUUX\xbf~=\x5c.\x17S\xffHe?\x9dCw9\x97\xdfYN\x04p``\x00Z\xad\x16\xe9t\x1a\x8d\x8d\x8dszMgg'\x8b\xdd\xa6Vj\xa5\xd6Kgg'K\xe42\x18\x0c(//?\xf5\x0f\x9e\xce\x01\x13Q@3\x03\x01\x14p\x92\xdc$\xb2\xa7\xe4\x06N\xa7\xd3\xd8\xb0aC\xc1c===hnn>\xed\xf5L\xaf\xa7s\xd3f\xb3\xb1\xc7\x02\x81@\xc1\xe5n&2\x94\xcb\xe5\x10\x8b\xc5X\x17\x95R\xf6`\xa6\xef'\xc3E\x87\xb0\x00\x00 \x00IDATe%\xe4uy\xa80s\xa2\xcf\xc0\xc0\x00\xb4:-\xaa\xab\xaaY\xd8\xc7\xc4\xc4\x04&''\xb1f\xcd\x1aX\xadVF\x5c\x93\xc9$k\x8fG\xb1\x88d\x8f\xb3\xd9,\x92\xc9$\xccf\xf3\xbc/\x80s\x1doyMZ\xb2gT\xab\x96B\x7ff\xb2\x9f\xc41\x96k\xac2\xc5\x89\xce\xf5\xb2%\x9e\xce\x22\x97\xdfJ\xe4\x7fK\x92\x84\xf2\xf2r\x8c\x8d\x8dM{>%#\x10,\x16\x0b3\xea:\x9d\x0e\xe5\xe5\xe50\x9b\xcdp:\x9d\x08\x87\xc3x\xf3\xcd7\x11\x08\x04P[[\x0b\xb3\xd9\xcc\x12\x00V2\x01\xa4\x80`\x22eJ\xa5\xc7\xe1p\xa0\xac\xac\x0c\xcd\xcd\xcd\xa8\xaa\xaaBmm-s\x9f*\x95#\x8a\x19\xe9\xed\xed\xc5\xb6m\xdbp\xe4\xc8\x11\x8c\x8c\x8c\xc0\xef\xf7#\x1a\x8d\xb2\xd2\x02\xf2\xa4\x0a*\x7f\xf0\xfe\xf7\xbf\x1f\x0d\x0d\x0d\xa8\xac\xac\x84\xd9l.(\xcf2\x97uE\xae\xdc\xf2\xf2r\xd4\xd5\xd5\xa1\xad\xad\x0dG\x8f\x1e\x85\xdf\xefg\xc9&\xa4l\xca\xe3\xcb\xe41\xa4\xa4&\x93[\xe6\xe8\xd1\xa3hhh@MM\x0dS\x8d\x89 \x9e\x8b\xf3M\xfb\xc5\xef\xf7#\x10\x08\xe0\xca+\xaf,\xd8\xdc*\x95\x0a^\xaf\x17\x9d\x9d\x9d\x98\x9a\x9ab\x074\x112\xf9\x9cK\x92\x84M\x9b6\xb1\xba\x7ff\xb3\xb9@\x95P\x92E\xa5\xda\x5c\xaa\xec\x07\xc5\xf0\xce\xe7f?\xdb\xfbr,<\x01\x9c\x9a\x9ab\x17\xaa\xe6\xe6\xe6\x92\xc6\x97\x1e\x0b\x87\xc3,\xde\xaf\xb6\xb6\xb6`\xee\x8a\xd9\x84\xd1\xd1QFz\xea\xeb\xeb\x0b.p\xf32\xf6*\x15TR\xfed\x0c\xa0<\xd9\xa9\xe0C\xaaNK\x05\x92$\x09f\xb3\x19f\xb3\x99\xd5\xcc\x1b\x1e\x1eF[[\x1b\x8bC\x9e\x8f=\xd4\xeb\xf5\xd3\xceK\xba\xa8;\x9d\xcei\x040\x91H`rr\xb2d\xdd\x5cj\xc7y*H\xa5R\xc8J\xd9\x821\x97'z\x09\x82\x80\x13'N\xa0\xab\xab\x0bZ\xad\x16\xa3#\xa30\x9b\xcd\x88F\xa3\xec\x1cN&\x93\xb8\xe4\x92K`4\x1a\xa7\x95\x95\x917\x05\xa0\xc7\xe3\x898\xca\xca\xca\x98m\x9a\xc9\xfd{\xaa\x9c\x83\x0a\xff\x93\xfd\xa0nV\xd9l\x16\xb5\xb5\xb5X\xbf~=\xcb\x19\xa0\xf5@\x9d\xa3B\xa1\x10\xf4z=.\xb8\xe0\x82\x82\x18\xd6\xb9\xac\x9bd2\xc9<\x5ct\xf1\x96$\x09\xb1X\x0c\x16\x8b\xa5\xe0}\x8a\xbd\x9f\xfc\xff\xe8\xbbP\x85\x0d\x9b\xcd6\xe3\xe5z\xa6\xcf\xa6t\xd1\x07\x02\x01\xf4\xf4\xf4@\x92$\xd4\xd4\xd4\xa0\xa1\xa1\x81\xbdvrr\x92\x09it\xe6\xe7\xf3y\x88\xa7K\xa2\xe4\x85\x9ciQi\xb5ZLNN\xa2\xa2\xa2\x02\xa9T\xaa\x80\x14P\xe0\xbe|Ah\xb5ZVC\xce\xe1p`\xcd\x9a5\xb8\xfd\xf6\xdbQYY\xc9\xcaY\xe4\xf3y\x84B!\x04\x83\xc1eY\xe6c\xae\xaa*\x15T\xb6\xdb\xed\x8c\x04\x91+\xddf\xb3\xc1d21uL9\xb7\xca\xf8\x0e\xbaA\xed\xde\xbd\x1b/\xbe\xf8\x22\xba\xbb\xbbY/\xdeh4\xca\x02\x9c)+\x8d\x16S.\x97\xc3\x86\x0d\x1bp\xc9%\x970\xf7/\x91\xf3\xf9\xb8[)\x09\xc8f\xb3\xb1X\xc0\xf3\xce;\x0f/\xbf\xfc2\x04A@*\x95b\xea\x94\xbc\x9c\x01\xb9\x82\xa9\x86\x17e\x03\xe7r9\x0c\x0e\x0ebpp\x10\xf5\xf5\xf5\xac\xbb\xc5\xe9(\x5cg\x1b\xe9t\x1a\x91H\x04>\x9f\x0f:\x9d\x0ek\xd7\xae\x9dv@P1l\x8f\xc7\xc3\x92?h\x8f\xc8\xc3\x04jjjX\xe1g\x87\xc31\xad\xed\xdbL\x87\x8d\xb2m\xa3\x92(\xca]\xf4\xb3\x19w\xba\xec\xc9K@q\xb7\xcd\xe2\x83\x92/.\xbc\xf0Bx\xbd^\x0c\x0f\x0f3\x028\xd3\xe5\xa8X\x92\x07)A\xa5\x14\x06\xb7\xdb\xcd.\xab.\x97\x0bf\xb3y\xda\xf3\xe7<\xe7\x1a\x0d \x08\x80\xa0\xa6E\xaf\xf8\xfb\xf4\xc6\x85\xca\xd4\xd4\xd4\xd4\xa0\xbb\xbb\x1b\xc0\xc9,\xddS\x09\x1bQ\xab\xd5\xa8\xa8\xa8\xc0\xd4\xd4T\x81}\xa2\xcb\xac\x5c\x09\x0d\x04\x02\x8c\xa0\x95\xb2e\x92$\xc1\xef\xf7\x17\x9d\x8b\xb9z\x10\x90GQ[\x90\xcf\xe7Y\xb9/\xba\xd4{<\x1ex<\x9e\x82\xe7\x06\x83A\x9c8q\x02k\xd7\xae\x85\xdb\xed\x9e\x95\x90\x0c\x0f\x0d#\x93>\x19\x92\x13\x08\x04\x10\x8dF\x17d\x8f\xabT*\x04\x83A\xb8\xddnv\x86\xc8\xc7\x98\xe2'\xa9\xbe%\xd9\xb8\x9e\x9e\x1e\x8c\x8e\x8e\x16(\xad\xe1p\x18v\xbb\x1d\xa2(\xa2\xb5\xb5\x15&\x93\x09\xbd\xbd\xbd\xe8\xe9\xe9\x81\xd1h\xc4\x05\x17\x5c\x80\xb2\xb22\xf6\x9d\x8e\x1f?\x8e\xc1\xc1A\xf6\xfc\x96\x96\x16\xf4\xf4\xf4```\x00\xd9l\x16:\x9d\x0e\x97^z)L&\x13\xb3\x95\x9d\x9d\x9d\x98\x98\x98\x80\xc1`@{{;\x5c.W\xc1\xda\x9f\x9c\x9c\xc4\x81\x03\x07X7\xa5\xf5\xeb\xd7#\x9b\xcd\xc2\xeb\xf52QDN`'''\x11\x89D \x8a\x22\xf3p\x112\x99\x0c:;;\xd9\xba\x9a\x9a\x9a\x82N\xa7\x83\xcb\xe5BWW\x17\xba\xbb\xbb\x91\xcf\xe71::\x8a\x0b/\xbc\x10f\xb3\xf9$\x97\x90W\x15\x9f\xaf\x14K\x03\x9f\xc9d\xe0v\xbb\x11\x0c\x06\x99\xf4O\x8bK\xe96\xa4/\x1b\x8f\xc7\x19Y$\x03m2\x99\x98q\x22bSYY\x89\x9a\x9a\x1a~\x82\xcfC!\x9cOl\xca\xeb\xaf\xbf\x8e\xed\xdb\xb7\xb3\x96jtS\xa0^\xbc\xf2\xac_\x9a?\x83\xc1\x80K/\xbd\x94\xd5\xa4\xa26l\xf3%\x80\xf4\x19\x92\xc9$\xa2\xd1(\x04A`\x89!\x1e\x8f\x07\x82 0\xb5\xd8h4N\xcb2%7\xb2N\xa7c\xaen\xbf\xdf\x8f\xee\xeenX\xadVh4\x1a\x84\xc3a\xd8l\xb6\x19\x13BJ\x8dU\xa9[9\xfb7\xf2,\xbeF\xe9bQ\x12ny(\x84\xf2&^p0#\x8f\x5c\xf6O1\x9d~\xbf\x1fCCC\xac\xa5\x94\xfc\xb9\xd1h\x14\x87\x0e\x1d\xc2\xe4\xe4$\x02\x81\x00\x8b\x87T\x96\xeb\xc9\xe7\xf3\xd8\xb8q#r\xb9\x1c\x22\x91\x08\x9bgRm\xe5\xdd\x08\xe4\xddz\xe8p\x09\x85B\x98\x9c\x9c,X\x17\xc5\xc2\x8f<\xf20\x8c%Q;\xe0A^#\x14\x84~X\xad\xd6\xe9$\xf4\x14\xd5\xe4\x5c.\x07\xbb\xdd\xce~\x8e\xc5b\xa7D\x00\xc9\x86)\xd73\xd9C\xa7\xd3\xc9\x88\x22e\x1d\xcf\xe5\xf3Ro\xf4\xf9|7\xaa\xd9(I\x12\xeb\x92\x14\x0e\x87Q^^\x0e\xadV\x8b\x9e\x9e\x1e\x0c\x0f\x0f\xcf\xd9;2<<\x8c\x91\x91\x919y\xde\xc6\xc7\xc7\xd9s\xb3\xd9,+F_\xcc^\x95R\xb8\x8a)p\xa4.[,\x16\x88\xa2\x88L&\x03\x87\xc3\x81\x96\x96\x16D\x22\x11\xf4\xf5\xf5\xb1\x8b\xb0\xd9d\xc6\xbb;\xdf\x85\xd7sR-t8\x1c\xec\xfc\xa2$\x1eI\x92011\x01\x8b\xc5\x02\x8f\xc7\x03\xb5Z\x8dP(\x84\xae\xae.ttt\xc0`0\xa0\xaf\xaf\x0f'N\x9c`cz\xf8\xf0atvv\x16x\xd3\xb2\xd9,v\xee\xdc\x89\xf6\xf6v\xf6\x9a\x89\x89\x09\xa8\xd5j\xa4R)\x1c8p\x00\xe7\x9dw\x1etz\x1db\xd1\x18&''\x0bzBG\xa3Q\xd6jQ\xadV\xa3\xaf\xaf\x0fn\xb7\x1b\xf5\xf5\xf5\x08\x87\xc38z\xf4h\x81\x08\xd2\xd9\xd9\x09\xb3\xd9\x8c\xc6\xc6F\xb8\x5c.\x0c\x0e\x0e\xb2\x5c\x0a\xaa\xa4\xf1\xce;\xef\xa0\xba\xba\x1a~\xbf\x9f\xed\xdf@ \x80\xbe\xbe>\xb4\xb4\xb4`jj\x0a\xe2{\xef\xbdw\xda\x12:\x19\x15\xb7\xdb] \x9b\x0b\x82\x80d29\xad\x03\x01\xb9\xecHB\xcdf\xb3L\xb1\x92+\x18\xf2N\x10\xa7BN\x973\x8a\xcdQ\xa9\x22\xbe\xf2\xc5\x9aH$\xd0\xdd\xdd\x8d\xd7^{\x0d]]]\xac\xf4\x8e\xcf\xe7C<\x1eg\xca\x1f\x91\x08e\xa0\xf1%\x97\x5c\xc2\xfaL\xba\xddnD\xa3\xd1\x02\xf7\xef|n\xfatK\x8dF\xa3H&\x93\x10E\x11\xb5\xb5\xb5\x98\x9c\x9cd\xe4\x93.\x15\xa4\x80\x12\x11$\x05\x93n\x83d\xb0\x8e\x1d;\x06\x9b\xcd\xc6n\x8c\x0e\x87\x03:\x9d\xee\xd4\xb3\x10K\xcd\xc3I\xe6\x87\x93\xb6?\xbf\xa0sKk?\x12\x89\xc0`0`\xcd\x9a5\xd3J:\xec\xdb\xb7\x0f\x03\x03\x03\xacc\x0b\x91?e1Y\xa7\xd3\x09\x8b\xc5\x82d2\x09\x9f\xcfW\x90}/\xff]\xc5\xe6-\x97\xcb!\x1e\x8f\xc3\xe3\xf1 \x91H\x14\xb8W(\x0bq\xae\xc9#\xd1h\x14\x1e\x8f\x07###\x88\xc5b\xacn\xe4B\xee\x83\xd39\xcf\x94\xef9/\x17 \xad\x85\x05\xfc\xfc\xf3\xfd\x0c\xb3\xc1\xe3\xf1\xb0\xf3\xa0T\xc1v\xb9\xaa%_GtY\xcbKy\xa8\x04\xd5\x8cdj\xcd\x9a5\xc8d2\xa8\xab\xabC2\x99\x9cV\x1ajN\xdfI\xa5\x82\x86B]\x00H\xb9?\x15\xa0\xcff\xb3\xa8\xac\xac<\xf9\x19\x16`\xdb\xc9\x09\xca\x5c\xd6\xa3<\xfb^\xfe\x98^\xaf\x9f\xf6\x7fd\xcb\xe4*\xde|\xe64\x9dN\xb3\xec\xe1\xb9\x90\x7f\xbd^\x0f\xab\xd5\x0a\xaf\xd7\x0bI\x92p\xe2\xc4\x09v\x8eR\xa1\xfeD\x22\xc1\xe2\x0a\xe5\x7f\x94\x97V\xf2\x10\xcc\xa7&/}\x7f:\xbf)1\xb1\x14\xf1\x9fK\x0b\xbfP(\x84d2\x09\xa7\xd3\x89\xd6\xd6V8\x1c\x0e\xc4b1\xb8\x5c.V\xcagdd\x04\x89D\x02CCC'\xd5\x5c\x9f\x1fz\xbd\x1eMMMhnn\x86\xd7\xeb\xc5\x91#G\x90H$\x98-\xa1\x84I\xb5Z\x8dL&\x03A\x10\xe0\xf3\xf90>>\x0e\xa3\xd1\x88\xbe\xbe>\x08\x82\x00\x83\xc1\xc0B\x8dr\xb9\x1c4\x1a\x0d\xca\xcb\xcbQQQ\x81\xf1\xf1q\xf8\xfd~\xec\xdb\xb7\x8fy\xc9\x8cF#\xf4z=\xc2\x91\x931\xd9\x9d\x9d\x9d\x90$\x09\xf1x\x9c\x952\xa3P\x1c\x9f\xcf\x87h4\xca\x08\x5c,\x16\xc3\xe8\xe8(\xc6\xc7\xc7Y\xc2lee%\x9cN'2\x99\x0c\xb3\xd7\xc7\x8f\x1f\xc7\xe8\xe8(\xa2\xd1(\xab\xefZQQ\xc1jwNMM1\xd2N\x89\xb6\x14\xe2\x15\x0a\x85 RP\xef)\x1dx*\x15rR\x0e\xb9l\x0eSSS\xac\xce\xd3L\x86DyP\x90A\xa6\xbaB\xe1p\x18Z\xad\x16\x1e\x8f\x07\xef\xbd\xf7\x1e\x1c\x0e\x07\x93T\xe9\xbd\xb8\xbbhn\xd2?\xfd-w\xdd\x05\x83Atuu\xe1\xd0\xa1Cl\xe1P\xcd\xc5X,\xc6b\xfe\xe4\x0a\x92R\xb5\xda\xbcy3n\xb8\xe1\x06\xac^\xbd\x1a\x8d\x8d\x8dp:\x9d\xd0\xeb\xf5,\x93t\x0eV\xb2P9P\x01\xd9\xcc\xc9\x1b\xae\xdb\xed\x86\xd3\xe9\x84(\x8a\x98\x9a\x9a\xc2\xc8\xc8\x08ssS\xbc#\x1d\xa2D\x00I)\xa6D\x0f\x8aY\x09\x04\x020\x9b\xcd\xac\xc4\x0c}Nf\xb4\xf2s$\x0bE\x94\xbd\x85\x9c\xa7bcF\xb13\xd1h\x94\xd5c\xbc\xed\xb6\xdb\xa6\xed\xa7P(\x84\xc3\x87\x0fchh\x08\xe1p\x98\xa9\x7fr\x97=\xe1\xfd\xef\x7f?.\xbd\xf4Rttt\xa0\xb1\xb1\x91%\xed(U\x19e\x8c(\x19 \x9f\xcf\x87\xee\xeen\x8c\x8d\x8d!\x14\x0a\xb1\xf7&W\xc2\x5c\xe1r\xb9\xd0\xde\xde\x8eK.\xb9\x04\xd5\xd5\xd5\xc5\x95\x9c%~\xd1Z\xca\x9f\xf5t\x09\xafr\x1d\x96\x95\x95\xb1\x90\x0b\x9a\xbf\xab\xae\xbaj\xc6:y\xc0\xc9\xba\xa0\x0f?\xfc0S\x92\xf5z\xfd\xa9\x8d\xa3J\x05\xa1?\x08\xf1\xf8\x11\xe4E\x81\xb9C#\x91\xc8\x9fTh\xb5l\xfd\x9e\xa2@@FY\xeeY\x98M5\xa63'\x10\x08L\xebU\xae\x0c\x85 \xc1CIv\xe6J\xfe\xe9RN\xaf\xb5\xdb\xed\xac\xf4J1\x98L&TTT0\xe5\x87~\x9fF\xa3a\x15\x1d\xe4kE\xadV\xa3\xb1\xb1\x11\x91H\x84\x95\xb6\xa1\xef\xb2f\xcd\x1a$\x93I\x1c:th\xd6\xd0\x0e\xf9\x19B\xef-\x0f\xe1QzB\x8a\x89\x143\x8d7u\xaf\xaa\xac\xacD}}=\xda\xdb\xdb\xa7\x9d\x1b&\x93\x09eeeH$\x12\x18\x1c\x1cd\x8aYMM\x0d\xd6\xacY\x03\x95J\x85\xba\xba:H\x92\xc4H]:\x9df\xe4O\x14E\xb8\x5c.VP\xbb\xab\xab\x8b\x11D\x97\xcb\x85\xf5\xeb\xd7C\x14E\xd6q\x8a\xcahi\xb5Z\xd4\xd7\xd7chh\x08\x13\x13\x13\xc8\xe5r\xb0Z\xadhii\x81\xc9d\xc2\xe0\xd0 \xfaz\xfb\x18\xc1s\xb9\x5c(++\x83\xd3\xe9Dyy94\x1a\x0d+\xd7F\xbd\xee\xa9\xd4N*\x95\x82^\xafG\xc7\x86\x0eT\xb9\xaa\x98\xfdkhh`\xe1N\xe1p\x18\x82 \xa0\xbc\xbc\x1ck\xd7\xaee\xe3B\xed\x01\x05A\xc0\xaaU\xabP^^\x8e\xa3G\x8f\x22\x1a\x8d\x22\x14\x0a\x9d\xf4\x04\x9cn\xd1F2V\x1a\x8d\x06n\xb7{\xc6x\xab\x99\xfa\x1b\x92\x8cK\xb7\x8c`0\x88\xc9\xc9I\xe6\xdaknnF{{;/0{\x0a\x86@\x92$x\xbd^\x8c\x8d\x8d\xe1\xd8\xb1c\x18\x18\x18`\x01\xb4>\x9f\x0fSSS\x88\xc5bl\xf1Q\xf0\xb2\x92@\xd0|vtt\xe0\xe6\x9bo\xc6\xea\xd5\xab\xd1\xd2\xd2\x02\x97\xcb\x05\xab\xd5zJ\xae_\xe5:\xa0x\xbfx<\x0e\x9f\xcf\x87\x81\x81\x01\x16B@EG)\xf1\x85bH)\x03\x8cjJR'\x0a\xb5Z\x8d\x9e\x9e\x1el\xdc\xb8\x91\xb5\x86\xd3h40\x99Lls\xcc\xd9 -\xa0\xb27\xd3<\xc9\x7f&E\x9dZ\xbey\xbd^\xb8\x5c.vp\x91\xbb\x96b\xffN\x9c8\x81P(\x84P(TP\xaeG\xfe\xbe\xb5\xb5\xb5\xe8\xe8\xe8@ss3\xea\xea\xeaX\xd2\x8e\xbc\xf3\x87\xf2\x10\x97\x7f&*(-/\xf2]\xcc}=\x97\x84\x0e\x0a\xdc\xa6\xc3\x9a\x5c9\xcbI\x89_*\x17\xc0\xd3\xfd\x0e\xa4\x8a\xe9\xf5z\xf6oR\xa3g*\x1e=\xd3\xef?\xe5q2\xa4N\xc6\x01jN\xfe.\xaa\x22!wK\x9f\xcew'\x8c\x8d\x8d\xb1KeEE\xc5\xac1\xe6r\xaf\x83\xfc;\x16ss+\x09\xe0|\xc7C\x1e\xca!I\x12\xaa\xab\xabYu\x8cb\xef\xa3\xd3\xe9`\xb7\xdba0\x18\xa6%\x90\x14K\xc8\xb0Z\xad\xd8\xb0a\x03+\x1bF\xcf\xd7\xe9tLY\x93\x87m\xd1z\x90\x8f\x11\x85\xd8P\x22\x0d\xbd\xbe\xa6\xa6\xa6\xe0s\x9e\xca\xfcP\xfc\x7f*\x95BYY\x19\xea\xea\xeaf\xf4\x80\x99\xcdf\x16\xa7Hjnuuu\xc1\xf3\xeb\xeb\xeba\xb5Z\x99+\xfe\xe8\xd1\xa3H\xa7\xd3hmmEkk+K\xaa#\x95\xb0\xa9\xa9\x09\xabV\xadb\x99\xcd---\xd3~\xb7V\xab\xc5\xea\xd5\xabQSS\x83L&Sp\xae556\xc1l2#\x10\x08\xc0d2\xa1\xaa\xaa\xaa \xf9\x0e\x00\x0c\x06\x03K\x96\x02N\x16hw8\x1c\x88\xc7\xe3\xb0X,'\xd5n\xd9w\xd0\xeb\xf5X\xb5j\x15\xacV+&&&\xa0\xd5j\xd1\xd0\xd0\xc0~gee%6n\xdc\x08\xaf\xd7\x0b\x8b\xc5\x82\xda\xdaZh\xb5Zd2\x19\x9c8q\x82er\x8b\x0b\x91\xea\xae\xcc\xe6\x9bO:7\x19t*2,\x08\x02\xdcn7#$\xc7\x8e\x1d\xc3\xf8\xf88+5B\x81\xeb\x92$\xcd\xe8v\x5c\x8c\x83\xf3l\xba\x9e\x95\xf5\x9f\x8cF#\x8b\x9d\x8b\xc7\xe30\x99L\xcc\xfd\x97H$X\xaf\xd8\xbe\xbe>\x04\x02\x01&gS-\xb9P(\xc4\xdc\xbc\x941ZL\xf5\x93+m\xe7\x9f\x7f>\xee\xbc\xf3N444\xa0\xa1\xa1\x01\xe5\xe5\xe5\x8cP\x9d\xae\x11$y\xdah4\xb2\x1b^[[\x1b\xba\xba\xbaX&\x9a\x5c\xa2\x97\xc7\xa8\xd1\xcd\x8d\x94\x86L&\xc3n\xcd\xc7\x8e\x1dCSS\x13jkkY]@r\x19\xcf\xc3\xaf\xb7h\x0a`1C@.\xf1D\x22\x81@ \x00\xaf\xd7\x8b\xff\xd7\xde\x99G\xc7]\x97\xfb\xff=\xfb\xbeg\xb6\xccdk\xd2\xa4{KiYZ\x0ae\x97\x02r\xa5\x02z\x11\x11\x5c\xa8\x82\x0a\xcaQ\xef\xfdq\xc5\x8b\xc7\x85\xe3\x82\x22\x02\xea\x11\xf1\x82\xe0\x02\x17/(\xdb\xbd*{i\x11*]\xd26M\x97\xecK\x93\xc9Lf\xdf\x7f\x7f\xc0\xf3\xf1;\x93I\x9a\xb4\x93t\x92<\xafs\xe64\xcd2\x99\xcc\xf7\xf3\xfd|\xde\x9f\xe7\xf3<\xefg\xfd\xfa\xf5cDR4\x1aE[[\x1b\xfa\xfa\xfaD\xee\x1f\x89?\xba\x86R\xe1N\xe6\xd1&\x93I\xf4\xe2\x96\x1e5O\xf4\x9a\xc6\xfb\xbe\xf9\x18e\x9bO\xa2\x94\x9e\xc7\xe9t\xa2\xbb\xbb\x1b\xc0{\x89\xeb3~=\xb3y@\x9e\x07d\x80R&\x87\x1c2\xe42YdSi@\xa5\xfeg\x95\xf0qN\xd1\xc9d\x12\xa3\xa3\xa3\xc2\xb8\xda\xe7\xf3\xa1\xaa\xaa\x0a}}}\xe3\xce\xfb\xc5E\x08\xf4\xb9RF\xca'*\x00\x8b\x85\x86\xd3\xe9D,\x16\xc3\x91#G\xa0V\xaba\xb5ZEZ\x07U\xe3Ses0\x18,x\x9d4g\x93\xe0\x90\xcb\xe5\xa2\x1bT\xb1\x08!\xf4z=\xccf\xb38\xa1kiiA\xfb\x81v\xc4\xe2\xff\x8c\x9a\x9a\xcdf\x18\x0c\x86\x02oB\xeabB\xa7\x0d':6\xb4Z-jjj\x84\x10+\x85\xcb\xe5\x12\x913\xaa\xbc.\xae\xb0\x95\xc9d\xa2\x98\xc9`4\x88\xe0\x82\xddn\x87\x5c.\x87\xd3\xe9\xc4\xa9\xa7\x9e\x8aX,\x06\x95J%r\xc8\x8b\xafk)aM\xaf\xad\xb8\xc8\xb5\xba\xba\xba\xa0\x96\xe1X\x91z\xb9\x5c.*\xee'\xdaL\x93\xa3Cq~\xbc\x5c.\x87\xdf\xef\x87\xd7\xeb-\xb8\xfe\xd5\xd5\xd5\xd0\xe9t\x22\xe0\xa2,\xc7\xe2}\x22\x17\x96r\x13h\x80P\xdfYz\xc8\xe5rtww\x8b\x81^UU\x85T*\x05\x85B\x01\x9b\xcdV\xf6\xc8`q\xa9\xf6T\x05\xe0t\x08E\xa9\xb0\xa6|,\xda\x0d%\x12\x09$\x12\x09\x1c=zT\x88\xc2@ \xc4!\xe5\xec\xd0\xcf\x90Ap:\x9d\x16\xc7\x85R\xe1Wl\x1a\x9c\xc9d\xb0n\xdd:\x5cv\xd9eX\xb0`\x01\x1a\x1b\x1bE\x05R\xb9\x8c\xb9\xe9\x18B\xa3\xd1\xc0b\xb1\xc0\xe7\xf3\xa1\xb6\xb6\x16---x\xe3\x8d7Dd\x8f\xc6\x09]#\xda\x11K\x13\x99)\x12\x98\xc9dp\xe8\xd0!\x1c:t\x08~\xbf\x1fN\xa7S\xb4<\xab\xd4h2\xbd\xff\xe4\xfbw\xf4\xe8Q1\xe1\x16\x8f\xcfC\x87\x0ea\xfb\xf6\xed\x08\x04\x02\xc2\xf7\x8f\xc4\xb1t\x822\x9b\xcdX\xb3f\x0d\xdcn\xb7H\xa9(\xe7{p\xa2\xe3\x9d\xc5_\xe5\xb3l\xd92\x1c>|X\x1c+\xcd\xe8\x86X\xad\x00\xbcFa\x04-3\x00\xe9L\x10\xb9l\x0ei\x87\x06:\x8b\xfe}u\xa0\x9c\xb2\xa5\x10u$\xda\xb3gO\xc1\xe7\xcf<\xf3L477#\x18\x0c\x8e\xdb\xdb\xb6T\xbb\xcb\xe2B\x17:^\xa4 \x07\x15~\xe4r9q\xb4>U\x9f<\x8dF\x03\x83\xc1\x80\x85\x0b\x17\xc2h4\xc2d2\xc1b\xb1`\xfb\xf6\xed\xe28Z\xab\xd5\x8a#j\xe9\xdaa\xb3\xd9\xb0j\xd5\xaa)\x1b\xe4\xd3\x111u&\xa9\xaf\xaf\x7f/H\x13\x8f\x89\xb5\xd2\xe9t\xc2\xe5r\x89\x9c\xb5|>/,\xde\xa4]\x9cJ5\x05\x98\xec\xdf\xeev\xbbE\xd7\x94\xf1\xae\xb3\xc3\xe1@KK\x0b\x0e\x1e<(\xe6\xce\x89\xda\x0f\xca \x83\xddn\x1f\xf3\x9a\xacVkIk\x96\x89^\xf3Tr\x5cO\xf4\xeb\xc5\xe3n\xa2kW\xfc\x7f*F\x02\xcal\x04]\x5c\xb41\x99\xc4pir1\x0d^24\x8eD\x220\x99L\xa2\xa4\x99\xbe\xa6R\xa9`4\x1a\x0b\x0c \xcb-\xb4*\x19\xba\x91\xc8u]zdC\xc7\xb8\x94\xd7Bm\xc4\xa4Q>\x12H\xd2hQ\xf1\xce\x86\x8eW\xaf\xb8\xe2\x0a\xac[\xb7\x0e\x8d\x8d\x8d\xa2}\x98\xd4\xf3\xaf\x5c\x0b8M\xc6\x06\x83A\x94\xc0755a\xef\xde\xbd\xc2\xe3\x89Z\xdb\x19\x8dF!\x0a\xe9\xef&s[\xaap\xa4\xe3\xef\xc3\x87\x0f\xa3\xae\xae\x0e~\xbf_\xb4\x16\x9cr\x14p\x86\xa0\xe2\x8dD\x22\x81P(\x84\x81\x81\x01\xacZ\xb5j\xcc\x0d\x9eN\xa7\xf1\xf2\xcb/chh\x08###\x22\xf2K\xc7\xc7\xd2\xe3\xa2u\xeb\xd6\x89]\xa2\xd9l.HR?Q\x91Wj\x93\xc4\x82n\xeeE\x12\xd7\xae]+:e\xbc\xf5\xd6[\x93\x9e\xdb\xcbB\x8d\x19\xb8e-\xbd \xa4GF\xd0\xb7#\x8bx\x22\x01\xcb\xcaf\x98\xab\xdf\x8f\x92\xc8e\x80\xe4\xf8q\xb2\x0b'\xd9`\xd1}e\xb7\xdb\xe1\xf5z\xa1V\xab'\xb4^1\x18\x0c0\x1a\x8d\x18\x1c\x1c\x14\xe3\x9e\x12\xf8\x09\xca\xd1\xa2H\xd4\xc0\xc0\x80\xf8ZUU\xd5q\xcdA4\xe7\x1a\x0c\x86\x82\x96k\x14\xed\xa3\xaf\xc9d2a\x15&\x15\x80\x14\xf9\x9a*\x1e\x8f\xa7\xc0\xc6\xc4f\xb3\x89\x82\x22\x99L\x06\xbd^\x0f\xab\xd5\x0a\xbb\xdd\x8e\xfe\xfe~\xe8t:8\x9dND\x22\x91\x82\xf9\xedx\x03+\x1e\x8f\x07\x0e\x87\xe3\x98\xf3\x8bB\xa1\xc0\x82\x05\x0b\x84\xc5\x11\xcfG\xc7\xb8\x07\xa6S\xa0LdGPj'\x90\xcb\xe5D\xb5\x93B\xa1\x10\xc9\xfc4\xf0\xa5\x09\xeat|\x5c*\x17\xe3d\x89\xb2\x99BZ>/\x15qd\x15@\xd1=iO`\x8a\x1eJm]J\x09?\xfa\xbc\xcdf\xc3\x87>\xf4!,^\xbc\x18\x0b\x16,(0|\xa6\x1dd\xb9o.2R\xb5\xd9l\xf0z\xbd\xa8\xa9\xa9\xc1\xe2\xc5\x8b\xf1\xf2\xcb/\x8b\x1cQJ\x98\xd5\xeb\xf5\x22e@\xab\xd5\x22\x99LB\xa5R\x89\xf4\x00\xaa:;p\xe0\x00\x9a\x9b\x9b\xd1\xdb\xdb+\xfa\x15Wb{8\xba\x06\xc9d\x12\x91H\x04CCCH$\x12X\xb3fM\x81\xb0\x92\xc9d\xd8\xbf\x7f?\xda\xdb\xdb\x11\x08\x04D\x82xq\xbf_\x00\xb0Z\xadX\xb5j\x15\x9cNgEF\xff\x98\xd9!\x00\xd7\xacY#\x16W\xdaLN5o\xf3\xb87\x07y\x14\x1c\xed\xcae\xef\x07\x19\xb2\xef\x15\x1fJ\xbf6U\xc3a\x12\x80/\xbf\xfc\xb28\xed8\xed\xb4\xd3\x0a\x1c(J\xfd\x1dF\xa3\x11\x8f\xff\xf6ql\xdf\xb6\x1d+V\xac@ss3\x1a\x1a\x1aD\xb4\xab\xb8\x15&\xbdw\x03\x03\x03\xe25R$k\xaa\x11@i\xe4Q\xfa\x9e\xfa|>\x04\x02\x01q2F\x22\xd3h4\x0a[\x1b\x83\xc1P\xb6\xb1Q\xaa;\x13\x00\xb4\xb4\xb4 \x99L\x8a<\xbbR\x1e\x80S\x9d7hS?U\xfd\xc1\xe2\xef$\x09\xc0\xa9F\xd3\xc8\xe8\x90\x84\x0a\x09\x1c\x8aZ\x91\xe8!\xf1\x22\xcdq:\x9e\xc5l\xb6-\x5c\xd2\x1b^\xba\xd0\x17w\x06\xa1\xef\xa1(\x90T\xec\x15G\xfa\x8a\xf3Q\xa8\xfd\x8fV\xab\xc5\xda\xb5kq\xce9\xe7\xa0\xa1\xa1\x01\xb5\xb5\xb5\xf0\xf9|\xb0\xd9l\xa2sD\xa9\xe3\x8frE\x01U*\x15t:\x9d\xc8\x05\x5c\xb0`\x01\xf6\xef\xdf\x8f\xa1\xa1!\xf1\x1a\xa9j\x9c*\xces\xb9\x9c8\xfaU\xab\xd5\x05\xbd(\xfb\xfa\xfap\xf0\xe0A\xd4\xd4\xd4\xc0\xe7\xf3\x15\xb4\xac\xab$\x11H\x1b\x1a\xca\xe1\xff\x22\xf6\xb4\xee\xc1\xeb\xaf\xbf\x8e\x05\x0b\x16\xe0G?\xfa\xd1\x98(`\xf1<\x1b\x08\x04\xc4\xef^\xb0`AAq\xcdT#\x80\xc5\xe2\x91\x0a\xf2\xe8$\x05x/wo\xcd\x9a5\xd8\xb9s'\xacV+\x1a\x1b\x1b\xcb\xb2\x0e\x01\xff\xec\xe0\x05@x3\x02\xef\xe5\xfd\x9ds\xce9\x00PP\x10R<&\xa6{\xdc\xb2\xf8\xab \x01x\xac\x9b\x92\xbaM\xd0\xc7\x94\x04O\xc7\x95\xc59j\xf3I\xdd\x97\xaa\xb4,\xf5\xb5R\x1f\x97\x12\xe1\xa5\xaa\xd6H|\xacX\xb1\x02\x1b6l@SS\x13jkk\xe1\xf5zE\xc9\xbaN\xa7\x13\xc7\x87\xd3\xf9\xdeS. u\x07\xa9\xa9\xa9Acc\xa3HrN&\x93B\x80\xd2$-\xf5\xb2\xa3\x9e\xa5\x1a\x8dF\x08\xc3\xdd\xbbw\xa3\xb9\xb9\x19\xfd\xfd\xfd\x22\x8aYi\xb9\x80T\xedL&\xcdj\xb5\x1a+W\xae\x1c\xb3h\xbe\xf3\xce;x\xfb\xed\xb7E\xcf_\xa9\xf1\xb3\xf4\xda\xba\x5c.\xb4\xb4\xb4\x08\xf1\xae\xd7\xebO\xb8Z{*\x0b\x043w\xe6\x1f\xb3\xd9\x8c\xfa\xfaz\x1c9r\x042\x99\x0c\x7f\xf8\xc3\x1f\xb0y\xf3\xe6I\xfd,\xdd\x9fO<\xf1\x04>\xfc\xe1\x0f\x9f\xf0\xdc=\x9e\x00:\x9e\x0d\x88F\xa3\xc1\x8b/\xbe(\xd6\x1e\xb7\xdb-\x92\xe6\xc7\x13\x11r\xb9\x1c\xfd\xfd\xfd\xe8\xe9\xed\x11Q\xc3\xb3\xce:K8ZH+\xe4\xa5~\xb8\xc3\xc3\xc3\x05\x82h\xd1\xa2E\xa2\xe5\x1a\xcd{\xe4\x91;\xd9\x08`1\xc5\x95\xa5\xc0{\xa7\x00\xeb\xd6\xad+{\xb1\xa4\xd4\xdaG\xa5R\x8d\xe9\xb7~\xac*j\xde,\xceq\x01(\xbd\xd0\xe3%G\x92\xc5\x04E\xfeb\xb1\x98\xc8c\xa3\xe8\x9f4\xf27\xdf\x0c\xa1K\xd9s\x90`\x9b(\x97b\xa2\xf7\xa9\xf8\x98D\xadV\xa3\xa1\xa1\x01\xe7\x9cs\x0e\x1a\x1b\x1b\xe1t:\xe1\xf3\xf9\xe0r\xb9`\xb7\xdb\xa1\xd7\xebE\x83\xed\x99h\xdbE\xbbh\x83\xc1\x00\xb7\xdb]\x10\x05\xa4\x8a6\x8a\x02R\xc4O.\x97\x17t\xa6\xa0\xb6p\xf4\xbd###\xd8\xbf\x7f\xbf\xa8\x086\x9b\xcd\x22\x87\xb1\x12D \x8dq\x8a\xfeuwwc\xed\xda\xb5b\xf1\xa0\xeb\x18\x8b\xc5\xb0s\xe7ND\x22\x911\x95\xbf\xc5cb\xf1\xe2\xc5\xa8\xad\xadEUU\x15,\x16\xcb\xb4\x19a3s\x1b\xda|\x5cv\xd9e\xb8\xef\xbe\xfb\xa0T*\xf1\xc6\x1bo\x1cS\xf0\xd3\xa6\xf2\xe7?\xff9\xbe\xf4\xa5/!\x9f\xcfc\xfd\xfa\xf5\xa2\xe2\xf4D^\xcfD\xfd\xc6\xa7\xb2.\xf4\xf4\xf4`\xfb\xf6\xed\xe2\xff\xeb\xd7\xaf/\xa8\xf8,57(\x95J\xec\xda\xb5K\xa4]\x00\xc0\xd2\xa5K\x0b\xc4Y\xa9\xea\xe0\x9e\x9e\x9e\xf7\x0c\xb4\xdf\x7f}\xabV\xad\x12\x96id\xca\xadR\xa9\x84?\xdbd\x04\xf0T\xafa9Q\xa9T0\x99L\x18\x1e\x1e\x86V\xab-Y=<\xde\x5cW\xaa\xb9\x03o\x1e\xe7\xa8\x00\x9c\xe8\xe6\xa4.!T\xb0@\xc9\xec\xa9TJ,\xf2\xc5\x1d\x0d\xe6\xd3\xce\xa1\xd8\x5c\xb3\xd4\xc7\xe3\x09;i\x84Oz\xf3\xd1\xee\xcd\xe5r\xa1\xb1\xb1\x11+V\xac@CC\x03\xecv;\x9cN'\xdcn\xb78\xee\xd5\xe9t\xc2\x98r&#<\xd4#\xd8b\xb1\xa0\xba\xba\x1a\x0d\x0d\x0dhhh\xc0\xbb\xef\xbe+\xf2\xfb\xd2\xe94t:\x9d8\xf2\x966\xe8V\xa9T\xe2\x98\x98\xc4\xe1\xfe\xfd\xfb\xb1h\xd1\x22\xf8\xfd~8\x1c\x0e\x98\xcd\xe6\x8a\xc9\x05\xa4<\xcdp8\xfc^{\x1e\xa5\x12---cr}\xde}\xf7]\x1c:t\x08}}}\xa2\xf2\x97\x22\xe4\xd2\xcd\x82\xd1h\xc4\x9a5k\xe0\xf5zQUUU\xf6\xdc?\xde\xcd\xcf\x1fh\xc3y\xc9%\x97\xe0\xbe\xfb\xeeC6\x9b\xc5\xf0\xf00^y\xe5\x15\x9c}\xf6\xd9\x13.\xf4\x1d\x1d\x1d\xb8\xe5\x96[\xc4\xbdy\xd7]w\xe1\x81\x07\x1e(\xcb\x9c(\x9d\xcf\x8a=\xe6\x8e5O\xd1\x91\xf43\xcf<#\x04\x97Z\xad\xc6y\xe7\x9dW \xe4J=\x8fL&\xc3[o\xbd%:c\xd8\xedv\xd1\xe3\x976\x94\xc5\xf7\x99\x5c.Goo\xaf\x10\x80J\xa5\x12\xd5\xd5\xd50\x9b\xcd\xe8\xe8\xe8\x80B\xa1\x10\xc5\x19\xc1`\xf0\x98bn\xaa\x86\xdf\xd3\x91\xab\xadT*\xd1\xdc\xdc\x8c\xe1\xe1aaf<\x99y\x82\xe7\x8ay*\x00\x8b/<\xe5\xb2\xa5\xd3i\x11\x05\x0c\x87\xc3\xa2B\xb5T\xb1\xc2|c\xb2;\x5c\xe9Q\x83t\xd2\x96\xe6\x0b\xaa\xd5j\xd4\xd7\xd7\xa3\xa9\xa9I\xf4\x0f\xa4\xbe\xcbv\xbb\x1dV\xabU\x1c\x15R\x0b\x9b\x89&\xc2\xe9^t\xc8\xcb\xca\xe3\xf1\xc0\xef\xf7c\xc9\x92%8p\xe0\x80\xb0\x06J\xa7\xd3\x88\xc5b\xd0j\xb5\x05\x93\x0fM\xe6\xd9lV\xfcK\x15\xc1\xed\xed\xed\x22\xa7\x91\xda\xc3\x9d\xec\x5c\xc0\xe2\xe8\xdf\xe0\xe0 \x9a\x9a\x9a\xc6x]\xa5R)\xbc\xf3\xce;\xa2Y{<\x1eG\x22\x91(\xc8\xfb\xa3\xeb\xb4|\xf9r\xd4\xd5\xd5\xc1\xe9t\xc2b\xb1@\xab\xd5\x96=\xfa'mIW\xca\xfb\x8c\x99;\x9b\xd0|>\x8f\xe5\xcb\x97\xc3\xeb\xf5\xa2\xaf\xaf\x0f\xb1X\x0cO?\xfd\xf4\x84\x02P.\x97\xa3\xa9\xa9\x09_\xf8\xc2\x17p\xef\xbd\xf7\x02\x00\x1e|\xf0A|\xeaS\x9f\xc2\xca\x95+\xcb2\x1eK\x1d3N6\xbf\xec\xd0\xa1C\xf8\xe5/\x7f)\x22j\xa7\x9f~:\x96/_\x8e\xc1\xc1A1\xef\x15\xcf\x0b\xf9|\x1e\x81@\x00;w\xee\x14\x9f[\xbat)<\x1e\x8f(H,NI!\xa1\xd6\xd3\xd3#^oMM\x0d\xdcn\xb7\xe88\x22\x97\xcb\xe1v\xbb\x91\xcdf'\xf4Y,\xd5\xa5\xe7d\xe2\xf1x\xe0\xf1x\xf8&a\x018y!C\xc2$\x91H\x88\x9eyT\xf9H\xbet\xd2\x82\x06fr\x93\xb4T\xa8)\x14\x0a\xd1\x9e\xa6\xaa\xaa\x0a---\xc2\x10\x93|\xa3l6\x9b\xf8\xbf\xc9d\x12\xa2\x8f\xec\x0fNv\x22-\x19;\x9b\xcdfTWW\xc3\xe7\xf3a\xe1\xc2\x85x\xe7\x9dw\x0a\x8e\x81\xa5cJj\x05Cm\x8f\xe8H&\x16\x8b\xa1\xbd\xbd\x1dMMM\xf0\xfb\xfd\x222v\xb2\xa3\x80\xb4\x09\xa2c\xddH$\x823\xce8\xa3\xe0~\x91\xc9dhmmE__\x1fz{{\x11\x0e\x87E\xf4\x5c\xbaQ\xa2\x8a\xf8\x0d\x1b6\xc0\xe1p\x88#\xfcJ\xf6>df\xc7\xfcRSS\x83\xb3\xcf>\x1b\xbf\xfb\xdd\xef\x00\x00/\xbc\xf0\x02\xee\xb8\xe3\x0e\x98\xcd\xe6\x09{\x80\xdfv\xdbmx\xec\xb1\xc7022\x82l6\x8b\x0f\x7f\xf8\xc38p\xe0@A\xa1\xd6\x89\xdc;\xe3E\xc2&\xaa\x08V(\x14\xb8\xe3\x8e;DG\xa1l6\x8b/\x7f\xf9\xcb\x18\x1d\x1d-\x88\xb2\x95\xca1\x1c\x18\x18\xc0\xe1\xc3\x87\xc5\xd7\xd7\xae]+r\x01\xa5U\xc0\xd2S\x9aT*\x85\xce\xceN\xf1<>\x9f\x0fn\xb7[XY\xa9\xd5j\x98\xcdf\xe1i:\x91\x88\xe5\xfb\x98\xa9x\x01(5\xb4\xa5\x85J\x0ay\xfeQ'\x0aJd\xa7\x9c\xbfb\x8cF#\xf4z\xbdh\xc6,\xbd\xc1\xe8f\x9f1_\xaacL\x94\xd3\xf5\x9c\xc5\xff\xd2b\xafV\xaba\xb1XD\x95'\xb5\xca!\xaf>\x12vz\xbd\x1eF\xa3\x11\x16\x8bE\x1c\xef\xd2\xd7(\xff\xa4\x12\x84\x9ft\xa2#\xbfG\xb7\xdb-\x8c\xa1)\x0aH\xa6\xd7\xe4AE\x16\x15\xf4\xbeP$\x90\x0a$\xb4Z-:;;\xd1\xd5\xd5\x85\xbe\xbe>\xd4\xd6\xd6\xc2j\xb5\x8a\xca\xd8\x93\xf17\xd315\xf9\xfeuuua\xf1\xe2\xc5\xa2I;]\x8bl6\x8b\xed\xdb\xb7\xa3\xb3\xb3S$\x93K;\xb7H\xef\x81s\xcf=\x17UUU\xa2\xf2w:\xa2\x7f\xcc\xfc\xe4\x8a+\xae\xc0\x13O<\x01\x00\xd8\xbd{7\xb6n\xdd\x8a\x8b.\xbah\x5c\x11\x96\xcf\xe7Q__\x8fo}\xeb[\xb8\xe9\xa6\x9b\xa0\xd5jq\xe4\xc8\x11|\xf0\x83\x1f\xc4\xb3\xcf>{B\x9bC\x12n\xa5N\x96(\xa7\x8e\x9c$\xa4si2\x99\xc47\xbf\xf9MaO\x92\xcb\xe5\xf0\x95\xaf|\x05Z\xad\x16\x89DB<\x7f\xb1\xd8\xcaf\xb3\xd0\xe9tB\x00+\x14\x0a\xe8t:\x9cw\xdeyH&\x93\xef}\xbfB>&\x07\x90\xd6\xc1\xbd{\xf7\x8a\xcf577\x0b\xf3\xfaK/\xbd\x14\x89d\x02&\xa3\x09\xd1h\x14\x0a\x85\x02\xd9lv\xdc\xf9h.\x09\xc0\xf9\x96\xd3?g\x05`\xb1MI>\x9f\x177\x98\xd4z\x82nX\xda\x9d\xd1\xf1/U\xfe\x16[\x95\xb8\x5c.\xac\x5c\xb9\x12\xd5\xd5\xd5\x22\x99]\x9a\xa0+\xad\x08\x9e\xe8\xa6\x99\xe9\x08\xdct\xdc$RGu\x12\xc1\xd4\x06\x8d\x1eT\x8dE\xff\xeat:!\x02\xe9c\xfa:y\xe6I\x05S\xa5\xa1P(\xa0\xd7\xeba\xb3\xd9\xe0\xf3\xf9\xe0\xf3\xf9\xd0\xd2\xd2\x82m\xdb\xb6\x89\xd7Lm|T*\x95h)(\xad\x10V\xa9T\x05\x1b\x8b={\xf6\x08c\xe8\xaa\xaa*\xe8t:\xd1\xc7r\xa6\xa1\x08\x1e\xf9\xfe\x85B!\x9cw\xdeyc*\x7f[[[\xd1\xd9\xd9\x89\xbe\xbe>D\xa3Q\x912Q\x5c\x1ce4\x1a\xb1b\xc5\x0ax\xbd^\xd1\xaaO\xa3\xd1L\xeb}q\xacj?fv,\xc4\x93\x19#\x1f\xfd\xe8G\xf1\xa5/}I\xb4g\xdc\xb2e\x0b:::D\xd5}\xa9y1\x93\xc9\xe03\x9f\xf9\x0c\xb6n\xdd\x8a\x87\x1f~\x18j\xb5\x1a\xcf=\xf7\x1cn\xb8\xe1\x06\xfc\xeaW\xbf\x9a\xb25\x8cT`\x8d'\x1ah\xdd\xc9\xe7\xf3H&\x93\x05\x7f[$\x12AGG\x87\xc8\x0d>\xf7\xdcs\xb1a\xc3\x06qZ \x15\x80\xd2M\xb7L&CGG\x07\xb6o\xdf.\xe6\x94k\xae\xb9\x06N\xa7S\xf4\x11V\xc8\x15\xc2\xa7P\x1a\x08\x19\x1a\x1aBww\xb7\xd8\x88m\xda\xb4I\xbc\x1e\x9a\x8b)7\xf0X\xf3\xf7\xa2\xfd=\x00\x00 \x00IDAT\xf0\x5c\x12J\x5c\xf81\xc7\x22\x80\xd2\x1085D\xa6\xddQ\xf1\xe0%\xdb\x8b\xe2\xfe\xa5\xf4=K\x96,\xc1Yg\x9d\x85\xfa\xfazQ\x98@\xc7\x93\xf31\x0c^\x9coE\xb9&\xb4\xd3%\x11#\x8d\xe6\xd1C*\x14\xe9{\xe89*\xf9&$qj4\x1a\xe1r\xb9\xd0\xd0\xd0\x80\xce\xceN\x1c|\x18g\x9ey\xe6\x98\xdc\xa3d2\x89\xdd\xbbw\xe3\xc0\x81\x03\x88F\xa3B\x00\x16wq\x01\x80SN9\x05>\x9f\x0f\x1e\x8f\x07V\xabU\xb4\xeac\x98\x13]\x88I$~\xe3\x1b\xdf\xc0\x96-[\xa0\xd1h\xd0\xd9\xd9\x89\xff\xfe\xef\xff\xc6\x95W^9\xae\x88\xa4{\xf3\xfe\xfb\xef\xc7\xae]\xbb\xf0\xf6\xdbo\x03\x00\x1e~\xf8a\xc8\xe5r\xfc\xe2\x17\xbf\x98\xb2\x08\x9c(\x02H\x7f\x8f^\xafG<\x1e\x1f\x93\x9f\xeap8\xf0\xf3\x9f\xff\x1c\xb7\xddv\x1b\xb4Z->\xfb\xd9\xcf\x8a\xcaT\xe9\xf3K\xdf\x97\x5c.\x07\x9b\xcd\x86;\xef\xbc\xb3`\xa3\xff\xe3\x1f\xff\x18\x7f\xfb\xdb\xdf\x0aOgd\x85\x01\x11\xb5Z\x8d\xd7^{M<\xaf\x5c.\xc7\xe5\x97_>\xee|\xa7\xd1hD$r\xbc\xf5u\xael:\xb8(d\x8e\x08\xc0R\x17\xd1n\xb7\x0b\x13g\xe9\xf7Q\x7fZi\xd4\xaf8\xfaWWW\x87\xf3\xcf?\x1f\x8b\x16-B}}=<\x1e\x0f\xf4z\xbd\x88fHo\x84\xc9V~\xcd\x85\x1bF*\x02\xa5\x0f\x12\x83R\xab\x96b\xaf<\xcaw\x99m\x93\x085.\xb7Z\xad\xf0x<\xa8\xaf\xaf\x87\xdf\xefG0\x18\x14\xd5\xb3\x14\xfd\x94&]K{\x03\x03\x10f\xc9\x0a\x85\x02\xbbv\xed\x12\xc50\xe4\x96?\xd3Q@\xca\xfd\x0b\x87\xc3\x18\x1a\x1aB.\x97\xc3\xb2e\xcb\x0a\x22\xe42\x99\x0c\x87\x0f\x1f\xc6\x8e\x1d;\x10\x0c\x06122\x22<3\xa9\xdf&a4\x1a\xb1t\xe9R\x11\xfd#\xdf?6Ee&\x8a\xf8\xb5\xb7\xb7\xe3\xd0\xa1C8\xf7\xdcs'\xec\xeeC\xc7\xa97\xddt\x13\xbe\xfa\xd5\xafbtt\x14j\xb5\x1a[\xb6l\xc1\x95W^9\xe1\x18\xa3\xcd\xe7\xd3O?\x8d\x0d\x1b6\xe0\xd0\xa1CP\xab\xd5x\xe8\xa1\x87p\xf0\xe0A<\xfe\xf8\xe3\xf0z\xbd\x93\x9a\xcb\xa5\x1b\xa4\x89\xc4\x03m\xfe\x8a\x85i.\x97\x83\xc1`\xc0\xb6m\xdb\xd0\xd6\xd6\x86D\x22!\xee%:U\xa2y\xc0j\xb3bxx\x18.\x97\x0b\xaf\xbe\xfa*v\xef\xde-:\x12\xdd~\xfb\xed\xc2\xfa\xa4\xb8\x08O\xda\xf7V\xa3\xd1\xe0/\x7f\xf9\x8b0\xd5^\xb7n\x9d(N+e\x17c2\x99D.\xa2\xc1`\x80\x5c.\x17=\x84I 2LEF\x00\xa5\x13\x0cE(\x8aof:\x9e\x8cD\x22\x05v/4\xc0\x95J%\xce>\xfbl466\x0a\xdb\x0e\xab\xd5Z\x90\xcb4\xdfv\x0d\xc5\x13\xccx\xbbA\xe9\xce\xb5\x94Y\xeal\x14\x024&\xccf\xb3\xe8\x0e\xb2h\xd1\x22\x1c\x9f\x18\x1fmmmH\xa7\xd3\xa2\x18\x86\xf2\xff\xc8\x08\x9a\xc6\x1e\x1d}\xd3\x04\x1a\x8f\xc7\xd1\xde\xde\x8e\xc6\xc6Faz\xad\xd7\xeb\xcbR\x998\x19\xa4\xbe\x7f###\x88\xc5b8\xe5\x94S\xc6\x5c\xdf\x9e\x9e\x1e\xbc\xf9\xe6\x9b\x08\x85B\x08\x85B\xc2$]\x9a\xfbJ\x1ekg\x9f}6\x1c\x0e\x87\xb8_\xa6\xbb\xeb\x874RY\x09\x05X\xcc\xd4\xb0X,x\xec\xb1\xc7\x84\xe0{\xe0\x81\x07p\xf7\xddwOx\x1cK\xd7\xfb\xd2K/\xc5\x05\x17\x5c\x80\xff\xfb\xbf\xff\x03\x00\xdc{\xef\xbd\xb8\xfe\xfa\xeb\xb1l\xd9\xb2\x09E[>\x9f\x87\xcf\xe7\xc3\xce\x9d;q\xd9e\x97\xe1\xe5\x97_\x86\x5c.\xc7\xd0\xd0\x10>\xfd\xe9O\xe3\x87?\xfc!\x1e~\xf8a\xac]\xbb\xb6 \x80P<\x8e\xa5\x11\xb6\xf1\x8e\x80K\xad\x17t2@\x1e\xa0\xa5\x02\x14\x14\x01\xa4\x8a\x5c\xbb\xdd\x8e\xfb\xef\xbf\x1f;v\xec\x10\xdf\xf3\xaf\xff\xfa\xafp\xbb\xddc\xde'\xe9\x06<\x9f\xcf\xc3b\xb1\xe0\xbf\xfe\xeb\xbf\x0a:|\x5c{\xed\xb5\x22-\xa5\xd4f\xd7b\xb1\x88cs\xa7\xd3YpoI\xd3w\xe6\x0a\xbcy<\xc9\x01\x96\x99\xbe\xd8T\xf9H\x85\x1b\xc5a\xffE\x8b\x16\xc1l6\xc3j\xb5\x8aH\x86\xb4R\x93\x1f\xf3\xefHO\x1a\x05\xa4N%\xcb\x96-\x13\x13<\xd9\x0aIm\x84h\xb2\x97\x16\xc5\x90(R\xab\xd5\xa2\x22\xb8\xb7\xb7\x17\xc3\xc3\xc3\xa2\x0b\xcdtOH\x94\xea\x90H$D\xdb\xb7\xea\xeajX\xad\xd61\x11\xb5m\xdb\xb6\xa1\xb7\xb7W\x1c\xfd\xc6b1Q8\x22\x15\x93k\xd6\xac\x11\xb9\x7f\x16\x8b\x05:\x9dnZ+\x9b+\xa5\xea\x9e9\xfe\x0d\x88\xdf\xef\xc7\xc6\x8d\x1b\x91N\xa7\xa1R\xa9\xf0\xbd\xef}\xef\x98Q5\xba\x17\xd5j5\xfe\xe3?\xfeC\x88,*\x88\xa0\xfb\xefX\x9bV\xadV\x8b\x97^z\x09\x9f\xfb\xdc\xe7\x0a*n\x0f\x1c8\x80\xd3N;\x0d\xcb\x97/\xc7\x1f\xff\xf8G\xf4\xf4\xf4\x8c\xc9\x87S*\x95\xa2\x8bOq\x0el\xf1\xeb,\xfe\xdd\xe4*`\xb3\xd9D\xbe\x1ed\xe3\xdf\xa72\x99\x0c;w\xee\xc4-\xb7\xdc\x22\xee\xa7\xea\xeaj\x5c~\xf9\xe5\x13\xfe\x9d\xb4\xd9\xe3LD\xffJEP\x98\xd9\xb1\xa1\xca\xe7\xf3\xf8\xc1\x0f~P\x10Q{\xf0\xc1\x07E\xca\xc4\xb1\xae\xff\xd9g\x9f\x8dO\x7f\xfa\xd3\xc2\x7fs\xef\xde\xbd\xf8\xc4'>!*\xf1\x8fu/g\xb3Y\xfc\xf4\xa7?\xc53\xcf<\x83u\xeb\xd6\x89\xa8\xb8N\xa7\xc3\xee\xdd\xbb\xb1y\xf3f477\xe3\xd2K/\xc5-\xb7\xdc\x82{\xef\xbd\x17\xbf\xfd\xedo\xf1\xdcs\xcf\xe1\xa5\x97^\xc2\xc8\xc8\xc81\xffF\xe9\xd8\xac\xad\xad\xc5\xc6\x8d\x1bq\xd6Yg\xc1\xe1p\xfcs3\x8d\xd2\xfd\x84\x95J%\xc2\xe1\xb0\xc8\x8d\xa4\xf7\xe8\x96[n\x81\xc9d\x9a\x94H\xfe\xdb\xdf\xfe&6\x96\x00p\xc3\x0d7\x1c\xb3\xe0E\xa1P`\xd1\xa2EX\xb6l\x19\x00\x14\xa4\xa6\xd0I\xc7l\xe5D{73\x15.\x00'\xb3\x18H\x8f\x8dJ-\xb6dUB\xbbK\x1e$\x0cM\xaa\xd4\x7f\xd2\xe5r\xa1\xb6\xb6\x16\x0b\x16,\x80\xc9d\x12\xd5\xe4\xe4+Y||D\x132E\x12\xc9^f\xef\xde\xbd\xe8\xed\xedEww\xb7\xc8\xb1\x9b\xce(`q\xee\xdf\xe1\xc3\x87q\xca)\xa7\x88\x02\x1d\xe9&\xe9\xed\xb7\xdfF[[\x1b\x82\xc1`A\xee\x9f\xf4\xbe\xc9\xe7\xf3\x22\xfa\xe7\xf5za\xb5ZO\xda\xa6\x89\xf3\x01g\xd7B\x9cN\xa7\xb1z\xf5j,\x5c\xb8\x10\x89D\x02J\xa5\x12\xdf\xf9\xcew&5\x8f\xd3\xe9\xcd\xfd\xf7\xdf\x8fe\xcb\x96!\x95JA\xa9T\xe2\xf1\xc7\x1f\xc7]w\xdd5)\x11I\x82\xea\xe2\x8b/\xc6_\xff\xfaW<\xf5\xd4Sp\xbb\xdd\x88\xc7\xe3P*\x95\xc2\xd4\xfd\xa5\x97^\xc2O\x7f\xfaS|\xf1\x8b_\xc4\xb5\xd7^\x8b\xcd\x9b7c\xcb\x96-hkk\x9b\xd0\xec\xb9\x18\xaa\x8a\xa7\x1c;\x00%\xc5\x1f\x09\xacd2\x89K.\xb9\x04###\xc2\xcc\xf9\xa6\x9bn\xc2\xca\x95+\x85\xdf\xe8D\x1c=z\x14/\xbf\xfc\xb2\xf8\xffE\x17]\x84\x85\x0b\x17Njc&\xcd\xdd\xa5>\xec\xf4\xda\xf4z\xfd\xac\x1dw<7\xccq\x01H\x15\xbd\xc7\xf2\xe5\xa3\x5c\xacR-\xa4\xa4\xed\xccJ5\xd6f\xe6\xb7\x08\x94\xe6\x02\xd6\xd5\xd5\xa1\xae\xaeN\x1c=I\x8bB\xa4\x91*z\xd01\xb0\xd4S\xf1\x9dw\xdeAOO\x0f\x06\x06\x06D\x7f\xdd\xe9\x8a\x02f\xb3Yd2\x19D\x22\x11\x11\xd9;\xed\xb4\xd3\x0a\xda\xaa\x01@WW\x17\xb6n\xdd*,b\x8a}\xff\x08\x9dNWP\xf9KQ\xf3\x99\xcc\x95e\xe17;!\x91v\xd7]w\x89\xb1\xd9\xd9\xd9\x89\xfb\xee\xbboR?O\xc6\xeb/\xbd\xf4R\x81\x0d\xd3\x9dw\xde\x89G\x1f}tR\xe2LZ\xa8u\xf9\xe5\x97\xa3\xbf\xbf\x1f\xcf?\xff\xf8\xa0\x98\x7f\xa4\xc7\xcb\xd2\x00\x85\xf4\xf7=\xf8\xe0\x83\xc2\xc7\xb4\xa1\xa1\x01\x9f\xf9\xccg\x8e\xeb:iuZ,jY\x84E\x8b\xdf\xb3D\x9b\x8dE \xc5\xc6\xd8\xcc\x1c\x15\x80\x93\xbd\xc0\xc5\xe2\xb08\x12\xc8\xdee\xccD\x22P\xab\xd5\xc2\xe1p\xa0\xa6\xa6\x06MMM\xa8\xae\xae\x16\xbez$\x04\xa9ZV\xba\x18\xa8\xd5j\xf1\xf3tL\x9aN\xa7\xd1\xda\xda\x8a\xae\xae.\x0c\x0e\x0e\x22\x1c\x0e\x1f3\x99\xfdx\x05 \xf5\xbc\x0e\x04\x02\x88F\xa3X\xbe|\xb9xm\xe4C\xb6\x7f\xff~\x1c8p\x00\x83\x83\x83\x08\x04\x02\xc2\xa7Lj\x94N\x0b\xcb\x19g\x9c\x01\xaf\xd7+r\xfff:\xfa\xc7\x85 \xb3_\x04\x9aL&l\xd9\xb2E\x88\xb0\xd6\xd6V<\xf5\xd4SSZ\xac/\xbc\xf0B<\xf2\xc8#B@\x01\xc0\xd5W_\x8d\x1f\xfd\xe8G\xc2~i2\xaf\x85\xbaw\xe4r98\x1c\x0e\x5c{\xed\xb5\xb8\xe7\x9e{\xf0\xe7?\xff\x19\x7f\xfd\xeb_\xf1\xdak\xaf\xe1\xb5\xd7^\xc33\xcf<\x83\xe6\xe6f\xa4\xd3\xe9I\x09\xc0\xe2\xc0\xc4x\xe2D&\x93\xe1\x8e;\xee\xc0}\xf7\xdd'N\xa9\xf4z=^\x7f\xfdux\xbd\xde1\xebSq\x852}|\xcf=\xf7\x08G\x81t:\x8d[o\xbd\xf5\xb87L2\xc8\xe0v\xbb\xd1\xd2\xd2\x02\x8b\xc52'\xc6\xdcd\x8d\xc7\x99Y&\x00'\xbb\xf8\x14W\xfe\x16G\x0a\xa7#\x02\xc3\xcc\x0d(\x11\x9ar\x01\xfd~?\x96/_.\xc4H,\x16\x13\xc7D\xa5\xa2\x80d\x8cM\x1fS\xc1\xc5\xe1\xc3\x87\xd1\xd5\xd5\x85\x91\x91\x91\x92\xfd\xab\xcb\x11\xfdK\xa5R\x18\x1d\x1d\xc5\xe0\xe0\xa08\xc6.\xe6\x9dw\xde\xc1\xf0\xf00\x06\x06\x06\x90L&\x0b\xbc2\xa5G_\x8d\x8d\x8dhnn\x86\xd3\xe9\x14\x95\xbf3\x19\xfdc\xe6\x0e\x9f\xfd\xecg\xe1\xf7\xfbE\xf5\xea\xf5\xd7_?\xa5{ \x97\xcb\xe1c\x1f\xfb\x18\xee\xb9\xe7\x1e\xc4\xe3qqzs\xdbm\xb7\xe1\x9b\xdf\xfc\xe6\x94\xac\x95J\xd9\xbe\x00\x80\xc9d\x82\xc7\xe3A]]\x1djkkE\x04p\xa2\xe78\x96\x07\xaa\xd4P:\x91H\xe0\xdf\xfe\xed\xdf\xb0u\xebV\xf17%\x12\x09\xec\xdf\xbf\x1fUUU\xc8d2c\x8c\xf8\xa5\xcfA\xddG\x8e\x1c9\x82{\xef\xbdW\xe40644\xe0\x92K.\x19\xf75\xcc\xb7\x0d\x07\xb5\x80e\xe6\xa0\x00\x9cj\xa8\xb7\x94\xad\x09G\xff\x98cA\x15\xc1v\xbb\x1d\xd5\xd5\xd5\x22\x12H\x96\x11\xa9T\x0a\xf1x\xbc\xa0\x070\x8d+*\x02!k!\xa5R\x89\xd1\xd1Q!\x00)\x0a(=J>Q\xa4\x95\xbf\xa1P\x08}}}\xd8\xb0a\xc3\x98{\xa1\xbd\xbd\x1d\x07\x0f\x1eDWW\x17\x82\xc1\xa0\xa8 \x94F\xff\xe8\xde\xa0j\xc6\xaa\xaa*q$\xc6>\x99\xcc\xf1\x8cM\x00\xf8\xc9O~\x22<5\xe5r9.\xb9\xe4\x12\xe1\x957\x99\x8d\x7f*\x95\xc2\xad\xb7\xde\x8a\x9f\xfe\xf4\xa7b\xbc*\x14\x0a|\xfd\xeb_\xc7\xd5W_\x8d\xde\xde\xde\xb2Dv\xa4y\xe1\x13\xdd\x9fr\xb9\x1cN\xa7SD\x09\xc7\xab@\xd5h4hmm\xc5\x96-[\xb0o\xdf>\xf1\xdcUUUhkk\x83\xd7\xeb\x1d\xb3NI\xc5\xa5\xf4y\xe5r9n\xb8\xe1\x06\x91\x8f\x0c\x00\x9f\xff\xfc\xe7\x11\x8f\xc7y\xa0\x15m\x18\x8aE!3\x07\x04`\xa9\x0b9\xd5s\x7f\xce\x13`&\xb3\xe0h4\x1aX,\x16x\xbd^\xd4\xd6\xd6b\xd1\xa2E\xc2\x0c\x9a,%\xa4\xc7\xc0R\xe3b\xaa2\xa7#aZ\x04\xba\xba\xba\xd0\xd9\xd9\x89@ \x80X,V\x96#N:\x9a\xa6\xe8_ww7\xbc^/\xbc^\xef\x98\xe7omm\x15\xbe\x84\xd1hTD\xff\xa4\x0b\x1d\xb5\x8c\xab\xaf\xaf\x87\xd7\xeb\x15\xb9\x7f\x95d\x974\xd5\xfb\x9d9\xb9Q\x99t:\x8d\x0f~\xf0\x83\xf8\xe8G?\x8aT*\x05\x85B\x81W_}\x15?\xfb\xd9\xcf&\xbd\xa9\xa0\x88\xdc\xe7>\xf79<\xf5\xd4Sb\xa3\xa6R\xa9\xf0\x87?\xfc\x01\xeb\xd7\xaf\xc7\xd0\xd0\xd0\x09\x8fQi\xca\xc4D\xbd\x80\x95J%\xea\xeb\xeb\xb1r\xe5J,]\xba\x146\x9bM|\x9d\x0c\xe35\x1a\x0d\x1ex\xe0\x01\xfc\xbf\xff\xf7\xff\x10\x0a\x85D\x8b\xb6\xfa\xfaz\xfc\xe3\x1f\xff\xc0\xc2\x85\x0b\x0b|\x0a\xa5\x05\x8a\xd2\x8fi\x1c?\xf4\xd0Cx\xe9\xa5\x97\xc4\xef\xd9\xb4i\x13\x1a\x1b\x1bgu\xe5n\xb9\x84^\xa9{\x9d\xd7\xfa9&\x00y\x82gfj\xd1\xa2\x8a\xe0\xaa\xaa*\xf8|>\xf8\xfd~444 \x95J\x15\xe4\x06\xa5R)1aK;\x04\xd0q\x17=\x12\x89\x04\xf6\xed\xdb'*\x82\xc3\xe1pY*\x82\xa9\xb0#\x16\x8baxx\x18\xbd\xbd\xbd8\xff\xfc\xf3\x0bz;\x03@OO\x0fv\xef\xde\x8d\xee\xeen\xf1\xbb\xa5\xa6\xcfR\xdf\xbfSN9\xa5 \xf7O\xab\xd5VD\xf4O\xfa^M\xf6\xbe\xe7h\xff\xc9\x87\xf2\xf4\xee\xbd\xf7^\xb8\xddnqDw\xe7\x9dw\xe2\xc8\x91#S\xda\x98\x01\xc0\xbf\xfc\xcb\xbf`\xcf\x9e=\xa2u\x1c\xf0\x9e\x0dJUUUY\xd6\x83\xc9\xf4\x02\xa6\xfb\xba\xb9\xb9\x19MMM\xa2\xea\x99ZF\xbe\xf2\xca+X\xb0`\x01\x9e|\xf2I1\x0e\xa9\x08\x85\x22\x7f\xc5\xbfS\x1a\x01\x94\x1e\xfd\xa6\xd3iD\xa3Q\xfc\xfb\xbf\xff\xbb\xf8\xba\xd3\xe9\xc4u\xd7]\x87\x5c.7'r\xf7\xca\xad\x07\x8a\xfdL\x999,\x00y\x92g\xca\x0d\xb5E2\x1a\x8dp\xbb\xdd\xa8\xaf\xafGCC\x03\xf4z=\xb2\xd9,\x92\xc9$\x92\xc9$r\xb9\x5c\x81Y4\x89@\x95JU\x109S\xa9ThkkCWW\x17\xfa\xfa\xfaDE\xf0\x89\x1c\x03\x93\xf8K&\x93\x08\x85B\xe8\xea\xeaBSS\x13\xecv\xfb\x98\x1c\xc5]\xbbv\xa1\xbd\xbd\x1d\xa3\xa3\xa3\x08\x87\xc3\x22\xff\xaf\xb8\xd2\xb1\xb1\xb1QTC\xda\xedv\xe1\x13V\xe9\x13~\xf1\xff)\xcf\xb7\x9c\xb9\x96\xcc\xf1\xa3T*QUU%z\xfa\xcad2\x0c\x0c\x0c\xe0\xfa\xeb\xaf/\xb0V\x9a\xec5onnF \x10\xc0\x86\x0d\x1b\xb0j\xd5*\xfc\xecg?C2\x99,\xcbZ0\x99\xcdN\xa9~\xc1\xf9|\x1e;w\xee\xc4\xe5\x97_\x8es\xce9\x07\xa3\xa3\xa3\x05\x91\xbc\x8f}\xeccx\xe4\x91GJ\xaeY\xb4i,6l\xa7t\x92\xdbo\xbf]\xf4\x1c\xcf\xe5r\xf8\xcew\xbe\x03\xaf\xd7\x0b\xbd^?\xa6\xc77\xc3\x81\xa2y#\x00\x19f: \xd1\xa6\xd7\xebEEpcc#\x5c.\x970L\x96V\xf3f\xb3\xd91\x91\x03\xa91\xb4L&C2\x99\xc4\x9e={\xd0\xd9\xd9\x89\xc1\xc1AD\x22\x91\x82<\xc2\xe3\x15\x80\x14\xfd\x0b\x87\xc38\xe5\x94S\xc6$\xba\x0f\x0c\x0c\xe0\xad\xb7\xde\xc2\xe0\xe0\xa0(B\xa1\xdf+\xcd\xfdS*\x95X\xb5j\x15jjj\xe0t:a0\x18\x0a|\x0dgj\x92&\x9fO\xfa\xdcd+1\xa5\xcf#=Zc*\x87\x8f}\xecc\xb8\xf4\xd2K\x85\x98y\xe5\x95Wp\xc3\x0d7L\xaa\xc3\x87\xf4\xba\xd3&\xeb\x85\x17^\xc0\xd6\xad[\x91\xcb\xe5\xa0\xd1h\xca\x22\x0a\xc6\x1b\xebTAL) 4\xbe\xe2\xf18\x1ey\xe4\x11\x5cq\xc5\x158\xf5\xd4S\xf1\xa7?\xfd\x09j\xb5Z\xe4\xd7.[\xb6\x0cw\xddu\x17\xae\xbb\xee:\xc4b1Q\x8d\x5c,\x00\x8b#\x80\xd4g\xfc\xdb\xdf\xfe6\xfa\xfa\xfa\xa0P(\x90N\xa7q\xc3\x0d7\xe0\x93\x9f\xfc$\x1a\x164\xe0\xb4\xd3N\x83V\xab\xe5\x81\xc5T\xe6\xc6\x8f\xdf\x02f\xd6\xee^\xde\xf7\xf5\xb3X,p\xbb\xdd\xa8\xa9\xa9\xc1\xa2E\x8b\xd0\xdb\xdb+\xaa\xf6\x92\xc9$\xf4z\xbd\x10-R?@Z\xa8(\x7f)\x93\xc9\xe0\xc8\x91#8|\xf80jkk\xe1r\xb9`2\x99\xc4\xf7L\x05\x8a8R\xf4\x8f*\x7fkjj\x0a\x16,\x99L\x86\xdd\xbbw\xa3\xa3\xa3\x03###\xe2\xf8\x97\x8e\xb1\xa5\xa2\xcb\xeb\xf5b\xc5\x8a\x15p\xb9\x5c\xb0Z\xad0\x18\x0c\xd3\xde\xf6\xedD\x16\xec\xf1\x16\xeab\x01\xc9T\x16\x7f\xfa\xd3\x9f\xe0\xf3\xf9088\x08\x85B\x81G\x1f}\x14\x8d\x8d\x8d\xf8\xc67\xbe1\xe5\x13\x9d\x135.\xa6\xfb\xa08\xe7N\xea\xfbIb/\x97\xcbA\xa9T\xc2`0\xc0b\xb1\xe0\x8d7\xde\xc0\xfd\xf7\xdf\x8f\xe7\x9f\x7f\x1e\x81@@\x8c5\x95J%\x8a4\xbe\xfc\xe5/\xe3\x8c3\xce\x80N\xa7\x13]H\xc6\x9bkJmZ\x1e{\xec1l\xdb\xb6M\xbc\xd63\xce8\x03\x0f<\xf0\x002\x99\x0cjkjy0\x1dC\xcc3'y\x0d\x9d\xc9\x8b\xcf\x83\x80)'\xd2(\xa0\xddn\x87\xdf\xefGcc#\xdcn\xb7\x88\x04\xe4r9Q\xd5+\xed\x17L\x22\x90\x1e\x9434::\x8a\xf6\xf6vtuuahh\xe8\xb8*\x82i\x9cSnP(\x14\xc2\xd0\xd0\x10V\xaf^=\xe6\xf5\xf7\xf7\xf7\xe3\xad\xb7\xdeB__\x1f\x82\xc1 \xb2\xd9\xac(^\xa1\xc8#5\xbe_\xb7n\x1d\x5c.\x17\x9cNg\xc5W\xfeR5i\xb1\x10\xa4\xbcF\x8e\xfcU.\xc9d\x12;v\xec\x10\xd7K&\x93\xe1[\xdf\xfa\x16\x9ex\xe2\x89\x19\xa9\xda\xa4\xe7\xdf\xb6m\x1b\xda\xda\xda\x10\x0a\x85\x10\x89D\x90H$\x84):U\xda\xc6\xe3qD\x22\x11\x8c\x8c\x8c\xe0\x9dw\xde\xc1SO=\x85\xaf}\xedk\xb0\xd9lX\xbf~=~\xf3\x9b\xdf`tt\xb4\xe0\xf9\xb5Z-n\xbe\xf9f\xf4\xf7\xf7\xe3\xa2\x8b.\x82B\xa1\x10=\xeaK\x8dY\xfa\xbf\xf4s*\x95\x0a\x7f\xfe\xf3\x9f\xf1\xa3\x1f\xfdH|\xde\xedv\xe3\xf1\xc7\x1f\x17Ef\x0cS\xe9\xcc\xd8(\xa5\x89\x84\xcdc\x99\xb2\xee`\xde\xaf\x08\xb6Z\xad\xf0z\xbd\xf0\xfb\xfdX\xb2d\x09zzz\xc4\x91\x0c\x1d\xf7\x90\x8b>E\x14\xb2\xd9\xac\x88\x06\x90\xe5E.\x97C{{;\x16-Z\x84\x9a\x9a\x1a\xb8\xddn\x98\xcd\xe61\x1d\x00hL\xd3sI\x05\x0dE\xee\x92\xc9$\xc2\xe10\x06\x06\x06\xa0\xd5j\xd1\xd4\xd44&\x82\xb2o\xdf>\xb4\xb5\xb5!\x12\x89 \x16\x8b\x89~\xc4\xd2$\xe9|>\x0f\xbf\xdf\x8f\xa5K\x97\x8a\xe8\x9f^\xaf\xaf\x88\xca\xdf\xf1\xaa0\xd5j5\x22\x91\xc8\x18\xa1G\x02\x90\xc49o\x0a+\x0f\x8dF\x03\x97\xcb\x85g\x9f}\x16\x17]t\x91\xe8\xdet\xf5\xd5W\xe3\xc9'\x9f\xc4\x87>\xf4!\xa4\xd3\xe9i\xebJ\x91\xcdf\xa1T*q\xcd5\xd7\xa0\xb3\xb3\x13\x00\xe0\xf1xPUU%r^GGGE\x8e\xef\xe8\xe8(\x8e\x1e=:\xe6o {\x1a\x8a\x10\xaeX\xb1\x02\x9b6m\xc2\xed\xb7\xdf\x0e\x87\xc3\x81\xe1\xe1a\xb1.QZ\xc2x\x1b\x13\x12\x80\xb9\x5c\x0ef\xb3\x19\x8f=\xf6\x18>\xfe\xf1\x8f\x8b{0\x95J\xe1\xe1\x87\x1fF}}=\x0f\xa0In\xde\x999.\x00yrgfb\x22!_@\x87\xc3\x01\x9f\xcf\x87\x86\x86\x06\xd4\xd6\xd6\xa2\xa3\xa3\x03\x0a\x85B\x08\xbcd2Y\xd0-\x83\x8eu(\xe7\x87\x04a(\x14B[[\x1b\xfc~?jjj`\xb5Z\xa1\xd5jE\x94\x90\xc6\xb64*X,\x00\xd3\xe94\xe2\xf18\x82\xc1 \x8e\x1c9\x82\xab\xaf\xbez\xcc\xc47<<\x8c\xad[\xb7bdd\x04\xc3\xc3\xc3\xa2\x0d]q\xee_>\x9f\xc7\xc6\x8d\x1b\xe1p8\xe0r\xb9`6\x9b\xc5\xeb\xa94\xb2\xd9,\xd4j5\xc2\xe1\xb0\xe8\x11K\x11L\x8a.\xa5\xd3\xe9\x13\xca\xaddf\x86\x0b/\xbc\x10w\xdf}7\xbe\xfa\xd5\xafB\xa3\xd1 \x99L\xe2\xaa\xab\xae\xc2\xa3\x8f>\x8a\x8f|\xe4#\xc8f\xb3\xd32\x06i\xdd\x18\x18\x18\x80\xd9lF4\x1aE\x7f\x7f?\xfa\xfb\xfb\xc7_\xc8\xde\xcf\xe5\xa5\xfb2\x99L\x8a\xcfo\xd9\xb2\x05\x9f\xf8\xc4'\xb0p\xe1B\xd8\xedv\xf1;hSW,\xf2&\x12,\x1a\x8d\x06\xbf\xf9\xcdo\xf0\xdd\xef~W|.\x95J\xe1\xf6\xdbo\xc7\x07>\xf0\x01.z<\x0em\xc0\xef\xd9\x1c\x13\x80\x5c\xe2\xcd\xcc$r\xb9\x1cj\xb5Z\xf8\x02RAHOO\x8f8*\xa2c^\xda\xf1\x17\xb7s\xa2\xa4u\x1a\xbf\xfb\xf6\xedCSS\x13:;;\x85\xd92\xe5\xdbI\xab\x00\x8b'/:\xbaM$\x12\x08\x06\x83\xe8\xee\xee\x86\xdb\xed\x86\xcf\xe7C.\x97+Xp:::p\xe8\xd0!\x04\x02\x01\xc4\xe3\xf11]?\xe8\xf9\x1a\x1a\x1aPWW\x87\xea\xea\xea\x93\xd2\xf3w\xbc\xfb\xbb\x18\x85B\x01\x93\xc9\x84\x91\x91\x11h4\x1ad2\x99\x82\xe35\xe0\xbdN-\xd4\xadE\x1a\x05\xe4\x05\xa0\xf2\xc8f\xb3\xf8\xcaW\xbe\x82X,\x86\xff\xfc\xcf\xff\x14\xd7\xe8\xba\xeb\xae\x83J\xa5\xc2\xe6\xcd\x9b\xa7\xe5\xf7\xaaT*Q\x80\xa5T*\x8f\xb9\x8e\xd0}H\xe3\xd2\xeb\xf5\xe2\x9ak\xae\xc1\x8d7\xde\x88%K\x96\x881{\xac\xce R\x93\xe9b\xb1\x22\x93\xc9`6\x9b\xf1\xf4\xd3O\xe3\xee\xbb\xef.\xd8\xe8}\xf1\x8b_\xc4E\x17]\xc4\x91\xad2\xcc!\xcc\x1c\x10\x80\x0c3\xd3\x02\x90\xfav\x92/`CC\x03\xf6\xed\xdb\x87\xbe\xbe>(\x95J\x11\x05\x94\xb6\x80#\xd1\xa2\xd1h\x84A4M\xe2\xa9T\x0a{\xf6\xec\x81\xcf\xe7\x83\xd3\xe9\x84\xd1h\x84Z\xad.\x10_$\x06\xa5\x93>\xf5!\x8eD\x22\x18\x1a\x1aB__\x1f\xae\xb8\xe2\x8a\x82D\xf6|>\x8fh4\x8a\xbf\xfc\xe5/\xa2\xf2\x97\x16<:V\x96.z\xcb\x97/GMM\x0d\xaa\xaa\xaaD\xf4o\xa6s\xe8\xc6\xeb\xa4 \xfd\x1aE[\x13\x89\x84\xe8\xb2\x22\xed\x05+\x93\xc9\xd0\xdd\xdd\x8dP($:\x9dX,\x16\x16\x80\x15\x0a\xf9c~\xe3\x1b\xdf\x80\x5c.\xc7\x9dw\xde)6P\x1f\xfe\xf0\x87\xf1\xc3\x1f\xfe\x10\xb7\xddv\xdb\x98\xd6\x9e\xe5\xc0h4\x22\x91H\xa0\xad\xad\x0dmmm8t\xe8\x10\xc2\xe10\xa2\xd1(\x86\x87\x87\xd1\xd9\xd9\x09\x9b\xcd\x86\xc5\x8b\x17\xc3\xe5ra\xc1\x82\x05\xa8\xaf\xaf\xc7\xc8\xc8\x08\x14\x0a\x05\x96.]\x0a\xa3\xd1\x88d2)\xa2\x83\xa5\xe6\x8d\x89\xc6x\xf1\xe7\x9f\x7f\xfey\xdc}\xf7\xdd\x22\xbf/\x93\xc9\xe0\x9e{\xee\xc1\xcd7\xdf\xcc\xe3\xf78`#\xe8y&\x00\xf9b3\xd36\x90\xdf\xaf\xd4\xb5X,\xa8\xae\xaeFCC\x03\x1a\x1b\x1bq\xf4\xe8Q\xd1\x98=\x93\xc9@\xaf\xd7\x8b\x89\x87\xf2\x00)\x82(\xb5\x83\x91\xc9d\xe8\xe9\xe9Akk+l6\x1b\x8cF#T*\x15\x9cN\xa78>\x92&\xc4S\x04\x22\x99L\x8a~\xbf]]]\xf0\xfb\xfd\xa8\xab\xab\x1b\x13M\xd8\xb9s'\xda\xda\xda\x84=\x0cY\xd6\x14\xe7\xc5\xb9\xddn\xac\x5c\xb9rL\x0e\xd4\xc9\x16\x80\xd2\x1d\x0c\xa3\xd1\x88\xcd\x9b7\xc3\xeb\xf5\x8eY$v\xef\xde\x8dW^y\x05\x07\x0e\x1c\xc0\xc8\xc8H\x81\xaf\x99\xb4\xf27\x97\xcb\xe1\x82\x0b.\x80\xddn\x87\xdb\xed\x86\xc5b\xa9\xa8\xe8_q\x15\x9f\xf4c\xb5Z-\x8cv\xe5r9\x0c\x06C\x81\xa7\xa1B\xa1\xc0\xe0\xe0 ^~\xf9e\xa4R)\xc4b1\xd4\xd5\xd5\x89\xc8\xeaLu6\x99/\xd0F&\x9f\xcfC\xa7\xd3\xc1h4\xc2h4\x8a\xb6\x89\x93\x8d\x04R\x84|\xe3\xc6\x8d\xd8\xb5k\x176m\xda\x84\xfd\xfb\xf7C\xab\xd5\x22\x9dN\xe3\xdak\xaf\xc5#\x8f<\x82\x9f\xfc\xe4'hjj\x12\xc2\xbf\xdc\x22\x89\xee\xdf1'J8~1A\xa6\xd2V\xab\x15o\xbf\xfd6n\xbd\xf5V\xbc\xf6\xdak\x05\x91\xeds\xce9GX\xbd\xb0\xf8cX\x00\x1ec\x91\xe0I\x9c\x99i\xe4r9\xb4Z\xad\xc8\x05\xf4\xfb\xfdX\xb4h\x11^}\xf5U$\x93I1iS^ y\x04\xd2\xcfR\x84M\xa3\xd1\xc0h4\xc2\xe7\xf3\xc1\xe3\xf1\xe0\x82\x0b.\xc0\x1bo\xbc\x81w\xdf}\x17\x89D\x02\xdd\xdd\xdd0\x18\x0cP(\x14H&\x93\x88F\xa3\xd0h48\xf3\xcc3\xb1|\xf9rq\x9c)]$R\xa9\x14v\xef\xde\x8d\xa3G\x8fb``@T\xfe\x16\x1f\xff\xe6\xf3y\xb8\xddn477\xa3\xba\xba\x1av\xbb\x1d:\x9dND\x1cO6\xd2\xfe\xc3\xc5]=\xe8\x08\x8e\x84\x9e4:H\xef\xb7\xc3\xe1@SS\x13\x94J%\xb6m\xdb\x86\xde\xde^,^\xbc\x18\x1e\x8fG\x1c\xad\xf3\xdcQ\x1e\xa1Dc0\x91H \x9b\xcd\xc2b\xb1\xc0\xe3\xf1\xa0\xba\xba\x1a2\x99\x0cz\xbd~JG\xee\xf4\xbd\x0b\x16,\xc0\xbb\xef\xbe\x8b\x9bn\xba\x09\xbf\xfe\xf5\xaf\xc5xx\xfe\xf9\xe7\xb1l\xd92\xdc|\xf3\xcd\xf8\xc1\x0f~ \x04h9\x8f\xf5e\xf2\x7f\x0a\xc0rT\x93\xe6r9\xe1\x12p\xe3\x8d7\xe2\xb7\xbf\xfd-\xe2\xf1\xb8x\x0fS\xa9\x14\xbe\xf0\x85/\xe0\xee\xbb\xef\x86V\xab-(\xe8b\x8e/0T\xfc13\xc7\x04`qd\x80afJ\x00R;\xa8\xaa\xaa*\xd4\xd4\xd4\xa0\xbe\xbe\x1emmm\x18\x1c\x1cD&\x93A\x22\x91\x00\xf0^Q\x02\xe5\x0dIs\x7f\x8cF\xa3\x10\x91\x16\x8b\x05MMMX\xb6l\x19\xd6\xacY\x03\x00\x18\x1c\x1cD2\x99DWW\x17\xd2\xe94\xccf3|>\x1f\x5c.\xd7\xb8\x13[6\x9b\xc5\x1f\xfe\xf0\x07\xbc\xfa\xea\xab8|\xf8\xb0\xb0\xa7!3\xdb\xe2\xe2\x8f\xb5k\xd7\xc2\xeb\xf5\xc2\xe5r\xc1b\xb1\x9c\x94\xca\xdf\xf1\x88\xc7\xe3\xa2x\xa6\xb8C\x8aF\xa3\x11\x06\xd0\x14\x0d\xccf\xb3\xd0\xe9t\xd0\xeb\xf50\x99L\xf0z\xbdhhh\xc0\xb9\xe7\x9e\x8b\xef}\xef{\xd8\xb3g\x0f\x8cF#\x02\x81\x00\x82\xc1 R\xa9\xd4\x94:\xaf0\x13\xcf\xbf===\xd8\xbbw/\x12\x89\x04t:\x1dZZZD\xd5<\x09\x9f\xe3\x19[r\xb9\x1c\x0f?\xfc06o\xde\x8cO}\xeaS\x18\x1c\x1c\x14\xe3\xff\x87?\xfc!~\xfc\xe3\x1f\xe3\x17\xbf\xf8\x05\xae\xbc\xf2JX,\x96\xf2\x09@\xc8D\xb1\xd0\x89\xe4\x95+\x95J(\x95J\xc4\xe3q\xbc\xf0\xc2\x0b\xf8\xf9\xcf\x7f.>O\xb8\x5c.\xfc\xfa\xd7\xbf\xc6\x05\x17\x5c \xc6$oN\xca?FY+\xb0\x00d\x98\xb2 \xcd\x05\xf4z\xbd\xa8\xaf\xafG}}\xbd\xa8\x08\xa6\x02\x8fT*U\x90\x0cO\x22\x8c\xaay\xa5\xf9m\xa9TJ\x1cu\x91\xd0\x93\xf6\xf5\xa5\x9f\x97V\x12RD,\x99L\xe2\xcd7\xdf\xc4\xae]\xbb088\x88H$\x82`0X\xb2\xf8\x03\x00\x9cN'\x16/^\x0c\xaf\xd7[q\xd1\xbf\xe2\x05Pz\x0cN\xef\x81\xb4\xad\x16u\x8c A\xa8\xd3\xe9\xa0\xd5jQ]]\x8d\xc6\xc6F\xa4\xd3iQ\xe5)\xad\x96f\xca\x0f\xb5s\xeb\xee\xee\x86\xcf\xe7\x83\xcdf\x83\xc1`8\xee\xe7#\x8b\x9fG\xa8\x91U\x00\x00\x1daIDAT\xcb/\xbf\x1c\xbbv\xed\xc2\xd7\xbe\xf65\xfc\xeaW\xbf\x12\xddwr\xb9\x1cn\xbc\xf1F<\xf7\xdcs\xf8\xfd\xef\x7f/\xa2\xee\xe5\x18\x7f\xc5\xde\x7f\x93]o\xa4'S\xad\xad\xadx\xec\xb1\xc7\xf0?\xff\xf3?\x18\x1a\x1a\x12v1d\x22}\xf3\xcd7\xe3\xfb\xdf\xff\xbe\xb8\xef\xa7\xab\xf3\xc9|\x15}\xac\x15* `2\x9dj\xbed\xa2.\xc3L\xf7\xa0~?\xbf\xcfl6\xc3\xe5r\x89c`\xb3\xd9,\xc4])\xcf=\x8aj%\x12\x09\xa4\xd3i\x84\xc3a\x0c\x0e\x0e\xe2\xc8\x91#hoo?\xe6\x22 5\x89\x96V\xc8\xbe\xf2\xca+x\xf6\xd9g\xd1\xde\xde\x8e\xfe\xfe~Q\xf8!\xb5O\xa1\x85I.\x97c\xc9\x92%\xa8\xab\xab\x83\xc7\xe3)\x88\xfeUZ\xe4\x81Z\xbeI\x0bSH\xe4Q!\x00\xe5VJ;\x81\x00\xefuA\x19\x1a\x1a\x82B\xa1\x18\x131\xa5k\xc2\x8f\xe3\x7f\xd0\xfbH\xe9\x0d\xdf\xf9\xcew\xf0\xc4\x13O\xa0\xab\xab\x0b\xb1XL\xe4\x9fR\xf4\xf9\xb8#\x08\xef\x1b5\xbb\x5c.<\xf4\xd0C\xd8\xbe};N?\xfdtQu\x0f\x00\x9f\xfd\xecg\x91\xcb\xe5\xcav\x0c,\x15\x80S][d2\x19^x\xe1\x05l\xdc\xb8\x11\x17^x!~\xf9\xcb_\x8aqH\xe3\xf3\xfc\xf3\xcf\xc7\x8e\x1d;p\xdf}\xf7\x89#_\x16\x7f\xe5\xd3\x08\x13\x8d7\xd6\x0a\xb38\x02X\xaep.\x87\xd8\x99rD\x01\xa5\xb9\x80\xb5\xb5\xb5hll\xc4;\xef\xbc\x03\x00\xa2\xe8\x22\x99L\x8a\xcaS\xa9\xb1s2\x99D*\x95B8\x1cF0\x18\xc4\xdbo\xbf\x0d\x97\xcb\x05\xb7\xdb=\xa9\xc5@.\x97c\xdf\xbe}\xd8\xb9s'^}\xf5U\xf4\xf7\xf7#\x18\x0c\x22\x12\x89\x08\xb3d\xa9\x08\xa5\xdfKy\x84.\x97\x0b6\x9b\xad\xa2*\x7fK\xdd\xef\x91HDD\x82\xa4\x22Pj\xa7Cy\x82$\x88\xbb\xba\xba`\xb5Z\xf1\xd2K/\xe1\xe2\x8b/\x86\xdb\xed\xe6\xfb\x7f\x9a\x04z<\x1e\xc7\xa3\x8f>\x8a'\x9f|\x12\x9d\x9d\x9d\x88\xc5bH$\x12H$\x12\x22\x1a^\x8e\xdfC\xd7}\xf5\xea\xd5x\xf3\xcd7\xf1\xfc\xf3\xcf\xe3\xe3\x1f\xff8\xd6\xacY\x833\xcf<\xb3\xac\xd7\x956Jt\x0fOE<<\xf1\xc4\x13\xb8\xea\xaa\xabJ\xf6\xa8\xf6\xfb\xfdx\xec\xb1\xc7\xb0a\xc3\x06\xf1\xdc\x5c\x90\x84\xb2\x8d\x0f\x16ys\x5c\x00N$\x0a\xa7\xb2\xcb\xe4\x1b\x8e)\xc7\xa4\xa3P(`6\x9bEw\x90%K\x96`\xdf\xbe}\x22\xc7,\x99L\x8a\x9d?\x1dO\xa5R)\xc8d2\xe8t:D\x22\x11a\x1f\x93L&\xf1\xe8\xa3\x8f\xe2\xd4SO\xc5\x8a\x15+\xa0\xd5ja6\x9b\x0b\xc6j6\x9bE(\x14B__\x1f\xda\xda\xda\xb0o\xdf>\xb4\xb6\xb6\x22\x12\x89`xx\x18\xa1P\x08\xb1XlL\xcf_\x22\x97\xcba\xfd\xfa\xf5p\xbb\xddp:\x9d\xa2\xebG\xa5\x0a@z\x9fI\xc8J\xefy\x8a\xa6\xd2\x91\x1fE^3\x99\x0cl6\x1bzzz\x10\x89D\x10\x8dF\xb1z\xf5j\xb4\xb4\xb4\xc0h4\x0a!\xce\x9c\xf8\xbc{\xe8\xd0!\xfc\xf1\x8f\x7f\xc43\xcf<\x83\xfe\xfe~\x91r maX\xceD|\xba\xe7R\xa9\x14>\xf0\x81\x0f`pp\x10\xdd\xdd\xdd\xd0j\xb5e\x1fs\xd2H\xfbT\xb8\xf8\xe2\x8b\xb1d\xc9\x12\xb4\xb6\xb6\xbe\xb7\x00*\x95X\xb2d\x096n\xdc\x88\x8f|\xe4#X\xb3f\x0d\x1f\xf7\xce\x80\x1e`\xe6\xa8\x00\x94\xde\x98\x94\x0fT\x5c\xe1\xc8032\xb8\x95Jh4\x1a8\x1c\x0e\xf8|>\xf8|>,_\xbe\x1c\xaf\xbf\xfe\xbaXD\xa8\xe2T\xaf\xd7\x8b\xf1I\xc6\xc5\xe9tZ\x88\xbfp8\x0c\x97\xcb\x85@ \x807\xdf|\x13~\xbf\x1f\x1e\x8fGT\x11SqIGG\x07\xfa\xfb\xfb\x0b\x04_8\x1c\x16\xc7\xceT\xf8!\xb5\xe6\xa0{\xc2d2a\xf5\xea\xd5p\xbb\xddp8\x1c\xc2\x1c\xb9\xd2\xc4\xdfxG\x8e\xd2\xaf\x93\xc8\xc8d2B\xd4\xc5b1h4\x1a\x04\x02\x01\xa4\xd3i\x04\x02\x01$\x93Itww\xc3\xe1p\xa0\xaa\xaa\x0a\xb5\xb5\xb5\xd0\xe9t\xc2\xf2\xc6h4B\xa7\xd3\x9d\x94\x85\xa7\x9c\xe2\x88\x9e\xebx\xaf\xe5d_\x7f2\x99D(\x14\xc2\xc1\x83\x07\xf1\xc6\x1bo`\xc7\x8e\x1dhooG(\x14\x12Q-i\xc7\x95\xe9\x18[j\xb5Z<\xb7\xdf\xef\x9f\x96MG\xa9\x8e1\x93y\x8fL&\x13\xd6\xad[\x87\xbe\xbe>|\xees\x9f\x13\xf3\x82Z\xadF$\x12A&\x93)\xbb`e\xfey}H\x0b\xb0G\xf0\x1c\x15\x80\xd2\x5c&\xbaY\xa5\xf9K\x1c\xddcf2:\xa5P(`4\x1a\x0b*\x82\xf7\xee\xdd\x8b\xd1\xd1QQ\xdc\xa1R\xa9D\xa7\x0a\xeap\x00\xbc\x17\xd1\xa3j]\x83\xc1\x80\x9e\x9e\x1etww\xc3\xe9t\xa2\xbd\xbd]\xfc\x0e\xb2\x8c\x19\x1c\x1cD.\x97C<\x1eG\x22\x91@4\x1aE4\x1a\x15Gm\xf1x\x5c\x88?\xa9\xef\x1fAG\xbf\x1e\x8f\x07V\xabU\x1cMWb\xee\x9f\xf4\xbe\x9e\xa8\xa7*E\x03\xb3\xd9,4\x1a\x8d\xb0\xcbI\xa7\xd3\xd0\xe9t\x18\x1a\x1a\x12\x1dN\xc8\x9f.\x16\x8b\x8d1\xd0\x9e\xca\x22_\x1cY\xad\xa4H\xc7tZ_P46\x12\x89 \x10\x08```\x00CCC\x22\xed\x80~7\xe5eR\x9e\xe6t\xbd\x96\xe9B*`\x8f\xe7\xfd\xbc\xf7\xde{\xf1\xb3\x9f\xfd\x0c\xb1X\x0c[\xb7n\x15\xc7\xe2F\xa3\x91\xd7\xa7i\x9e7\x8aSm\x989\x22\x00\xa5\x8b\xc2\xf0\xf00Z[[\x91\xc9d\x90N\xa7\xd1\xd1\xd1\x81\xd1\xd1Q\x84\xc3a$\x12\x09\x11\x0d\x91\xee\x06fb\xe2`\xe6\xe1\x00\x7f?\x0ah\xb7\xdb\xe1\xf5zQSS\x83\xc6\xc6F\xbc\xf5\xd6[\xa2Wm\x22\x91\x80\xc1`\x10BE\xea\x0bFQl\x12\x88z\xbd\x1e\xa1PHT\x19\x93P\xa4\xde\xb7\x99LF\x88<\xe9d\x97H$D\xe5\xb14\xfaG\xd8l6\xb4\xb4\xb4\xc0\xe7\xf3\x89\xca_\x8dFS1\xc7\xa1R\x93\xeaP(\x84}\xfb\xf6A\xa1P \x18\x0c\x8a\xc2\x96p8,\x22\xa7T|@\xa2'\x97\xcb!\x1a\x8d\x0a[\x18\x00\xc2k-\x14\x0a\x89B\x97t:\x0d\x83\xc1\x80\xae\xae\xaec\xce\x09\xb3\xcdG\xecD\xc5\xd6\xb1~>\x9b\xcd\x22\x95J!\x93\xc9 \x12\x89 \x16\x8batt\xb4\xc0V\x87\x22\x80\xd2\x0d\xfal\x9bs\xc7+\x02\x99lT\x89\xa2\xf6\xf4^p$jz\xaf\x15\xcdu}}}hmm\xc5\xc8\xc8\x08\x06\x06\x060<<\x8c\x91\x91\x91\x82\xd3\x96\xe2\x8d1\xfd\xcb}\xc2+\x5c\x00J/\x18%\xcf\xd3b\x11\x0e\x87\x0b\x92\xe0\xc73\xc0\x95F\x10\x19\xa6\x1c(\x14\x0a\x11\x05\xf4x<\xa8\xaf\xaf\xc7\xc2\x85\x0bq\xe0\xc0\x011Fe2\x99\xc8\x01\xa4\x1e\xb6T!IQ\x12\x95J\x05\x99L\x86H$\x02\x8dF\x83d2\x89`0\x88|>\x0f\xbd^/\x8e8\xa59\xaft\xdcKQ\xb0x<.\x04e\xf1D\xb6h\xd1\x22,X\xb0\x00.\x97\x0bV\xabU\x1c\x81V\xd2\xbd@\x133\xe5:\xaaT*\x84\xc3a\x0c\x0d\x0d\xa1\xbf\xbf_\xe4\xf4\xd1\x91x\xf1u\x00 r,\xa5\x0b\x84B\xa1@(\x14\x12\xdf\x17\x08\x04Dk\xbc\xa9F\x99\xa6k\x81\x98-\x0b\x0fy3R\x11\x13\xe5]J\xe7U\xe9b:[\xf3-O$\x02Hs\x02\xa5'\xb0\xb0\x98\xfey\x83\xdc\x15\x82\xc1 b\xb1\x18B\xa1\x10zzz\x10\x8dF1::\x8aX,&r\x89KE\xf0\xf9\xfa\xcc\x82\x08 M2\xc3\xc3\xc3\xd8\xb7o\x1f\x86\x87\x87E\x97\x04Z\x1c\xa4\x11\x02i\x04\x90&'\xee\x02\xc0\x94\x1b\x95J\x05\x8dF\x03\x9b\xcd\x06\xbf\xdf\x8f\xfa\xfaz\xd4\xd5\xd5a\xf7\xee\xddb\x13B\xd1\xbbX,V\x10!\xa1\xc8\x1e\x99\xe5*\x95J$\x93I\xa8T*\xf1 \xe3c\xe9B\x22\xb5\x96\xa1^\xc1\xd9lv\x8c\xfd\x0c\x15\x9c\xac]\xbb\x16n\xb7\x1bUUU\xd0\xeb\xf5\xa2%Z\xa5-\xba\x00\x10\x0e\x87\xd1\xd6\xd6&&n\x9a\xc4\xa3\xd1\xa8\x10\xbc\xc5\x02\xf0X\xd1\xbcb\x11CsA%L\xfc\xb3i\xf1\xa1\xb1%\x1dk\xd2\xb6\x9c\xd2\x08\xdal\xed\xc4p\x22>\x80\xe3\x8d5f\xfa\x85z \x10\xc0\x9e={\xd0\xdd\xdd-\xf4\xc0\xf0\xf0\xb0\x08\x08\xd1\xc6\xb1x\xbc\xd2ub\x11X\xc1\x02\x90\x8eyd2\x19FGG\x11\x0c\x06E\xd4\x83\x16\x06\xca\x85\x92\x1e\xb5\x95\xf2\x0ed\x01\xc8\x94;*\xa2R\xa9`4\x1a\xe1p8P[[\x8b\x95+Wb\xef\xde\xbdb\xa1$\x1b\x93\xe2\xb1'\x97\xcbE\xb50\xf5\x1a&\x93[\xaa\x10\xd6\xe9t\xc8d2\x05\x9b\x17\xb2\x92\xa1EY:\xfe\xa5c=\x9f\xcfc\xc9\x92%\xa8\xaf\xaf\x87\xcb\xe5\x82\xd9l\x86N\xa7\xab\xc8\x8d\x10\xbdn\xca1\xa3\xa3\xf1H$\x22\x8e\xb8\x8b\x0b[&\xbb8\x17\xe7\x0d\xce\xf5]\xfft\xe7\xdeI7\xd7\xf4\xb1TXOV\x8cW\xe2\xfb&\xed\x05|\x22\x22\xb6\xf8~\xe7ugz6$t\x8d\xfa\xfa\xfa\xd0\xdf\xdf\x8fd2)R\xc2h\xfe\x95\xce\x1d\xc5\x91\x7fi\xee S\xa1\x02\x90\x16K\x85B\x81H$\x82P($\xaa\x1e\xa59O\xc5\xea\x9e&)\xba\xa19A\x94\x99\x96\x81\xaeTB\xa7\xd3\x89\x5c@\x9f\xcf\x87U\xabV\xe1\xf5\xd7_\x1f\xd3\xcd\xa2xA\x90V\x0c\xd3\x18\xd6j\xb5H\xa5R\xa2\xa0A\xa5R\x15$\xd9\xd3\xc4GB\x90\xa2\x80\xd2\x85\x99\xee\x97\xf5\xeb\xd7\xc3\xe9t\x8a\xca_i\xff\xdcJ\x8b\xba(\x95JD\xa3Q\xd1U\x85v\xee\xe3\x1d\xdd0'O(\x8d'\xa4\xa9\x05\xdal\x8e*\x8d\x97\x03x\xbc\x82\x99\x05\xe0\xf4\x5c'\x9a\xc7\xe4r9\x22\x91HA\x0a\x98t\xde(\xbe~\xc5u\x01\xe5\xec%\xcdL\x83\x00\xa4\xe31\xbd^\x8fL&\x83\x91\x91\x91\x82\x04\xd0\xc9F\x05t:\x9d\xc8}\xe2\x1b\x92)\xe7\xf8\xa4\xee \x1e\x8f\x07uuu\xc2\x0f,\x18\x0cNj\xc1\x91\xf6\xec\xcd\xe7\xf30\x18\x0cB\x10R\xa7\x03\xfa\x7f\x22\x91\x80Z\xad\x169qT\x85Y\xdc\x0c\xfd\xcc3\xcfDuu5\xdcn\xb7\xe8\xfaA\xf9\x86\x95\x04\xfd}\x1a\x8d\x06\xf9|^\xe4?J\xff~\xa6\xb2\x04\xe0xc\xf9D\xfa\xffV\x8a\xb0(\xb7@\xa19\x82)\xffuR\xa9T\xd0\xeb\xf5H$\x12\x18\x1e\x1e\x16\xf3E\xa9`\xd0\xb1\xc6,U\xb03\x15$\x00I\xfc\xe9t:\x98\xcdf\x11\x15)\x95\x13u,\xccf\xb3h/\xc5\x17\x9a)'\x0a\x85\x02:\x9d\x0eV\xab\x15~\xbf\x1f\xb5\xb5\xb58\xe3\x8c3\xf0\xdcs\xcf\x8d\x9b\x08^*\xd2@\xc9\xe3\xd4\xf3T:V\xa9\xe2W\xa7\xd3\x09\xc3gz\x14\x1f\x8f\xeat:,_\xbe\x1c\x1e\x8f\x07UUU0\x1a\x8d\x15\x99\xfbG\x93\xb8F\xa3\x81\xd5j\x85V\xab\x15\xc7\xe2'\x9a\x83\xc5\xcc\xdcbLcY\xab\xd5Vt\x87\x99\xa9\xfc-'*\x109\x028\xbd\xd7I\xadVC\xa7\xd3\xc1b\xb1@\xa5R\x89\x22\xb0RQ\xbf\xf1\xae\x8b\x5c./\xf0\x06\xadDk\xacy+\x00)<\xabV\xaba2\x99\xe0\xf3\xf9D\x94`*\x0b\x02\x0d\x88\xaa\xaa*\x98L&\xa1\xf8yQa\xca\x05mT,\x16\x0b<\x1e\x0f\x9a\x9a\x9a\x10\x8b\xc5\xf0\x8f\x7f\xfc\x03}}}\xc7\x8c\xa4H\xad4\xc80Zz\xc4!\x15v\xe4\xf5G\xd1\xbfb\xdb\x17\x00X\xbat)\xea\xeb\xeb\x85\xef\x9f4\xfa]i\xc2\x99L\x99\xa9HE\xfa~0\x95\x8f4\xb7\xcal6\x8b\xcd\xc6l=Z\x1b/8 \xb5\x0d\x99\xaa\x00daQ\xfe\xeb#\x97\xcb\x85\x03\x834Mf2\xe3\x95H\xa5R\xb0Z\xad\x228\xc4\xd7\xa9\x82\x04 \xf5d\xd4\xe9t\x22\xc1~\xfd\xfa\xf5\x22G\xaa\xb8\x92\x87vo\xd2\xfc*\x95J\x05\xadV\x0b\x93\xc9\x04\xaf\xd7\x0b\x87\xc3!\x12\xe1\xf9\xec\x9f)\xe7\x8e\x94z\x04\xdb\xedv,X\xb0\x00\x00p\xd5UWa\xc7\x8e\x1d\xc2\x8bJ\x9a\xabJ\x95\x94\xa9TJT\xaee\xb3Ya\x19\xa3\xd5j\xa1\xd1h\xc4\xf8\xa6/\x04`:\x9d\xc6e\x97]\x86\xd3O?\x1d\xa3\xa3\xa3\xa20N\x1a\x18\xa2\xebI\xa2P\xa3\xd1\xc0`0\xc0n\xb7\x8f\x19\xb3\x9c\x1eVA\x02\x90&\x18\xab\xd5*\x12\xc5\xddn7\x12\x89\x84\x88\x10\x94*\xb9\x97VB\xaa\xd5j\x18\x0c\x06\x98L&q<\xc1\xa1^f:&&:\x8a\xa0vkf\xb3\x19\xe1p\x18\xb1XlL\xa1\x07\xf0\x9e\x17\xe0\xc8\xc8\x88hS\x16\x08\x04\xa0\xd7\xebQ[[+\xbe/\x9dNC\xaf\xd7\x17\xb4{\x93\x8e\x7fi\xe5/\x80\x82\xca_\x8a\xfeU\xea\xa4F\xef\x99\xd1h\x04\x00h\xb5Z8\x9dN\xd1\xd5G:\x91K==\xf9\xde\x9dY\x8a#`\xd2\xe3PZP\xb5Zm\xc1\x11\xf0l\xcd\x03,\xfe\x9b\x8b\xff\xfe\xc9\xfeM\x1c\x01\x9c>\xc8D\x9f\x8e\x80u:\x1d\x5c.\x17\x12\x89\x84h\xb7Y\xea:HuA\xf1\x98\xa5\x9ck\xa6\x82\x04 -\x10\x00`\xb1X\xa0\xd7\xeb\x0b\x8e\x87\x8e\xe58O\x17\x9bz\xb1\xd2\x83U>3]\x82\x86\x04\x17%\x18\xdbl\xb6\x92-\xda\xf2\xf9<\x22\x91\x08\x06\x07\x07\x11\x89Dp\xf0\xe0A\xa8\xd5j,^\xbcX\x08\xc8@ \x04$\xfd,\x89\xc0\xe2\xa3\xdf\x5c.\x87e\xcb\x96\xa1\xa9\xa9\x09n\xb7[\xecj+}!\xa6|\x5c\x12\x0e\x16\x8b\xa5\xe0\xfe\xe6hJ\xe5\x08\xa2R\xd11\x8a\xe2\xd2f[\xadV\xcf\xfaH\x0a\xb9L\x9c\xe8s\x14\x7f\xccc\xb7\xbc\x22\x10\x80\xf07\x95\xea\x82\xf1\xdeo\xe9\xc6E:fU*\xd5\xac\xdf\xb0\xccI\x01(\x15\x81$\xe4t:\xdd\xa4\x0bA\xa4G\x15\xec\x03\xc8\xcc\x04\x14\xb5V(\x14\xc8f\xb3\xd0\xeb\xf5\x05bFZ\xf0A\xad\xca\xe8hw\xf9\xf2\xe5\xb8\xfe\xfa\xeb\x11\x0e\x87q\xe4\xc8\x118\x1c\x0e\x1cW\xc5\x95\xbfk\xd7\xae\x85\xd7\xeb\x85\xd3\xe9\x84\xc1`\x98\x15\xb9\x7f\x0cS\x89\xd0=u\x22-\x08K9S0\xcc\xbc\xba\x8f\xf8-`\xe6;\x94\xaf\x07@\x18\xe3\xe6\xf3y$\x12\x09D\x22\x11\x0c\x0f\x0fC\xa3\xd1\xa0\xa5\xa5\xa5`\xb1\x88\xc7\xe3hkkC$\x12A,\x16C4\x1a\x15\xfe\x7f\xd2\xe8_>\x9fGuu5V\xacX\x01\xa7\xd39k*\x7f\x19\xa6R!\xcf\xccr\x1e\x01\xf3\xbd\xc8\xb0\x00d\x98y\xb8\x98\x90\xcd\x00\x1d\x07e\xb3Y\xe1\xff\xd7\xd5\xd5\x85\xb3\xce:k\xcc\xcfuvvb``\x00\xc1`\x10\x91H\xa4\xe0\xf8Wj\x7f\x94\xcf\xe7q\xe1\x85\x17\xc2n\xb7\xc3\xe9t\xce\x9a\xca_\x86\xa9\xd8\x85\xeb}\xe1&\xad\xd6\x97\x1e\xe5N\x16\x8e\x002,\x00\x19f\x1eS\xdc\xc1\x82L\x9cC\xa1\x10zzz`4\x1a\xd1\xd4\xd44\xe6\xb8\xa9\xad\xad\x0d}}}\x08\x87\xc3\xa2\x8b\x08\x89?\x22\x97\xcba\xe1\xc2\x85\xa8\xa9\xa9\x81\xd7\xeb\x85\xcdf\x83N\xa7\xab\xd8\xb6o\x0c3[6mr\xb9\x1c\xf1x\x5c\x08\xc0P($\x22\xf9\x93\x15\x91l\x04\xcd\xccg8\x07\x90a$bM*\xfe\x06\x07\x07\xd1\xd5\xd5\x85\xeb\xae\xbb\x0e\xb9\x5c\xae`\xb1\xe8\xed\xedEkk+\xba\xba\xbaD\xf4Oj\xfd\x22=\x8eZ\xb3f\x8d\xc8\xfd3\x99L\xa2\xcb\x0d\xc30'\xb6i\x8bF\xa3\xd8\xb3g\x0f\xd2\xe94\x06\x06\x06\xa6l\x0c\xcdG\xc0\x0c\x0b@\x86\x99\xc7\x90`\x93\x8a\xbf\xfe\xfe~\x1c\xe8gs\xb9\x1c\xd2\xe94\x12\x89\x04FGGq\xf4\xe8Q\x1c\x9f\xc7\xf0\xf00\x06\x07\x07100\x80\x03\x07\x0e\xe0\xc0\x81\x03\xe8\xec\xec\x14\x85&\xc1`P\x08?\x12\x7f\xd2\xc2\x0f\x85B\x81L&\x83\x8d\x1b7b\xc9\x92%\xa8\xa9\xa9\x81\xc3\xe1\x80\xc1`8\xa6Pe\x18\xe6\xf8\xe6\x84\xf16qS\xb9\xdf\x5c.\x17\x16/^\x0c\xadV\x0b\x8f\xd7\xc3o,\xc3\x02\x90a*\x09\x8a\xe6e\xb3YD\xa3Q\x0c\x0d\x0d!\x9dN\x17\xe4\x00\x92\xa9\xeb\xd0\xd0\x10\xe2\xf18\x86\x87\x87\x11\x0e\x87E\xa4 \x9f\xcfC\xa9T\xa2\xbf\xbf\x1f\x81@\x00n\xb7\x1b\xfd\xfd\xfd\x88\xc7\xe3\x08\x85B\x88\xc7\xe3\xe8\xed\xedE$\x12A\x7f\x7f?\xa2\xd1(b\xb1\x18\xc2\xe1\xb0\xb0\x9e\x91\xb6{\xa3\xe7\x94\xc9d\xc8f\xb3X\xbat)\xce>\xfbl\xf8\xfd~x\xbd^\x98L&\xf6\xfdc\x98\x93(\x10'\xb5\x00*\x95X\xb8p!\xbfa\x0c\x0b@\x86\xa9\xe4\xc9\x9c\xaam\xa9\x02W&\x93\xc1h4\xe2\xe8\xd1\xa3\xe8\xee\xee\xc6\x8b/\xbe\x88\xa3G\x8fb``\x00\x89DB\xe4\x0a\xd2sd\xb3Y\xb8\x5c.(\x95J\x8c\x8c\x8c \x91H@\xa1P\x08\x91\x17\x89D\x90\xcdf\x91J\xa5\x84\xc7 \x09?i\xab7\x12\x7fd\xf8\x5cUU\x85\x8b.\xba\x08\x0b\x17.Dmm\xad\xc8\xfd\xd3h4,\xfe\x18\xa6\x82\x05\xa0t#\xc70,\x00\x19\xa6\xc2\x90\x8a\xae\x5c.\x87@ \x80\xbf\xfd\xedop:\x9d\xc8\xe5r\xe8\xec\xecDww7\xf6\xee\xdd\x8b`0\x88t:\x8dT*U\x10\xa9\xcbd2\x00\x80\xde\xde^$\x12\x09qTL\xad\xe0\xf2\xf9\xe8\xf5zTUUA.\x97\xc3l6#\x1c\x0e\x0b\xc3\xe7l6[\xb0\x00\x94\x8a\x00\xd01\xb1\xb4\xb8C\xa1P\x88|\x22\x8dF#\xa2{Z\xadV|\xacR\xa9\xa0\xd1h\x0a*|9\xc2\xc00'\x17\xa3\xd1\x08\x83\xc1\x80\xba\xba:ttt\x08\xafO\x99\x9c\xab\x80\x19\x86\x05 3\xa7v\xfcj\xb5\x1af\xb3\x19\x1a\x8d\x06V\xabUt\xfc\x18\xaf\x95S\xa9*`\xea\xe0AB\x8e\xa2\x80$\x04\xe9\xb8Y\xadV\x8bJC\x85B\xc1G\xbd\x0cS\x81P\xe4_Z\x11,\xcb\xf3}\xca0,\x00\x999\x83L&\x83Z\xad\x16\x22M\xa7\xd3\x89N\x1d\xc5\x22o2\xcf%E*\xf0H\x0c\x96:6f\x18\xa6\xf2\xe6\x851~\x9f\xb2\xf7\x1f\x0c\xc3\xb0\x00d\xe6\xcedO\x11\xba\xe2N\x1f\xe5x\xeeR\xe2\x90a\x98\xcaG\x9a\x8f+\x93\xc9 \x97q~.\xc3\xb0\x00d\xe6\xfc\x84\xcf0\x0c\xc30\xcc\x14\xd7Q~\x0b\x18\x86a\x98\xd9\x0cG\xee\x19\x86\x05 \xc30\x0c3\x8f\x05 \xe7\xee2\x0c\x0b@\x86a\x18f\x1e\x8bA\x86aX\x002\x0c\xc30\xf3D\xf4\xb1\x08d\x18\x16\x80\x0c\xc30\xcc<\x13\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb8\xf8\xe3\x1c@\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe62\xd2N \x1c\x01d\x18\x16\x80\x0c\xc30\xcc<\x11\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb1\x18dA\xc80,\x00\x19\x86a\x98\xb9\xbe\x90qkH\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe60\xc560\x1c\x11d\x18\x16\x80\x0c\xc30\xcc<\x15\x83\x0c\xc3\xb0\x00d\x18\x86aX\xf41\x0c\xc3\x02\x90a\x18\x86a\x01\xc80,\x00\x19\x86a\x18\x86a\x18\x16\x80\x0c\xc30\x0cS\x99P\xe4\x8f#\x80\x0c\xc3\x02\x90a\x18\x86a\x18\x86a\x01\xc80\x0c\xc3\xcce8\x02\xc80,\x00\x19\x86a\x98\xf9\xb6\x90\xc9\xe5\xc8\xe7\xf3\xfcF0\x0c\x0b@\x86a\x18\x86a\x18\x86\x05 \xc30\x0c3'\xe1#`\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe6*2\x99\x8c#\x80\x0c\xc3\x02\x90a\x18\x86\x99\x8f\x22\x90a\x18\x16\x80\x0c\xc30\xcc<\x16\x83,\x08\x19\x86\x05 \xc30\x0c3\x0fD\x1f\xa1P(\xf8\x0da\x18\x16\x80\x0c\xc30\xcc\xbcZ\xd4\xe4\xbc\xac1\x0c\x0b@\x86a\x18f\xce#\x8d\x00\xf2\xf1/\xc3\xb0\x00d\x18\x86a\xe6\x99\x00\xe4\x08 \xc3\xb0\x00d\x18\x86aX\x002\x0c\xc3\x02\x90a\x18\x86\x99{\x0a\xb0\xb4\x18d\x18\x86\x05 \xc30\x0c3g\xf5\x1f\xe7\x002\x0c\x0b@\x86a\x18f~\x09@.\x02a\x18\x16\x80\x0c\xc30\xcc\xfc\x15\x80\x9c\x03\xc80,\x00\x19\x86a\x98y,\x06\x19\x86a\x01\xc80\x0c\xc3\xcc\x87EM\xc6\xcb\x1a\xc3\xb0\x00d\x18\x86a\xe6<\x05Q?^\xd5\x18\x86\x05 \xc30\x0c3\xbf\x04 G\x00\x19\x86\x05 \xc30\x0c3\x1f\x162\xb9\x1c\xf9|\x9e\xdf\x08\x86a\x01\xc80\x0c\xc3\xccGdr.\x02a\x18\x16\x80\x0c\xc30\xcc\xdc\x16|2Y\x81\xf5\x8bB\xae\xe07\x85aX\x002\x0c\xc30\xf3A\x042\x0c\xc3\x02\x90a\x18\x86\x99\xaf\x8b\x1a\x1bA3\x0c\x0b@\x86a\x18f~\xc1\xd1@\x86a\x01\xc80\x0c\xc3\xcc#\xf2\xf9<\x0b@\x86a\x01\xc80\x0c\xc3\xcc7X\x002\x0c\x0b@\x86a\x18\x86\x05 \xc30%\xf8\xff\x82\xe5f\x8a\x9e\x05\x9f\x09\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x13\xdf\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x04\xc8iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \xef\xa3\xbf Made on a Mac! exclusive for Smashing Magazine\x0a \x0a \x0a \x0a \x0a icons\x0a flavour\x0a smashing magazine\x0a addicted to coffee\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski \x0a \x0a \x0a \x0a \x0a\x0a\xee\x04Q\xd2\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0e\xadIDATx\xdab\xfc\xff\xff?\x036\xc0\x02c\xdc\xf5r\xff\xcf\xc4\xc2\xc2\xf0\xef\xcf\x1f\x06\xe5m;\x19\x19A:@\x82\xec\x82\x02\x0c\xcc\x1c\x1c\x0c\x7f\x7f\xfc`\xf8\xf9\xfe\x03\x03\x13H\xf5\xaf\xd7\xaf\x19\xfe\xbe\x7f\xc7\x10~\xec$\x98\x06\xf1\xc1\x12 \xed\x7f\xbe\xff`X\xac(\x0b\xa6A|F\x98\xe5W\x0d\xf5\xe0vh\x9f\xbf\xc4\x08\x10@\x8cx]u\xdb\xcd\xe9?3;\x07X`\xf7\xd7\x9f\x9f\xd2\xf7\xed\xe5g\x0aK\xcc\x9c\xcf\xc6\xcb\xc3\xc0!\x22\x0c\xc6\x1e\x82\xdc| \x05\x8c'\xb45\xfe\x08\x0b\x0b1'=\x7f\xc9P\xaf\xa6\xcc \xff\xf9\x0b\x83\xee\x95\x07\x07\x199\x84\xa4\x94.\xa8I\xdde\xe7\x03+d\xf8\xf9\xe9\x13\x83\xfa\xf1\xd3\x8cL\xdf\xdf>\xbd7\xf9\xf9\xdb\xc7\xdf\xde\xbec\x00a\x83[\xcf\xe6\x83\x14\x00\x04\x10\x86\xab@v\x06?\xbc\x19j\xc4\xc2\xc0\xbd\xf5\x17\xe3\x87\x97\x0eNn\xed\x0d\xd5\xa7a\xf2p\x0d`\x17\xb3\xb130\xb3\xb320\xb2\x001\x13\x13\xc3\xff\x7f\xff\x18\xfe\xff\xf9\xcd\xf0\xf7\xe7o\x86\xd5_~\xbd/?\xb0W\x08\xaca\xb5\xa3\xf3Wc\x1ev.\x16.N\x06\x16..\x86\xc2[w\x18X\x7f\xffa\xf8\xcd\xca\xc2\xd0\xaf\xa6\xc2\xf0\xe7\xdb7 \xfe\xce\x90\xc3%\xb6\x09\x1c\x1e'\x1e0 \x92\x18@\x1a`\x18\x08@\xc1\xae\xc7\xc6/\xba\x94]P\xe26\x1b\xafP7\x90\xaf\x86\xac\x06 \x80p\xc6\x1d.\xc0\x82.\x90j\xeb\xfc<\x85\xfd\xbf\xc8w`\xaa)\xfc\xc3y\xe7\xfc\x81\xad\xda\xc8\xf2p\x1b\x8c\x1c}.\xafb\xff\xa9\xc3\xc8\xcc\xcc\xc0\xc4\x04Nf\x0c\xff@\xc1\xfa\xf7/\xc3l3{\xc7\xce\xa6\x9a\x03p\x1b\x22\x92\xb3g\xac\xe2\xf8\xa5\xc3\xcc\xc6\xc6\xc0\x04\x8c\x0b&\xa0&\xb0\x06\xa0\xe2\x7f\xbf~2\xa4\x9e>\xb4\x1fd8\xdc\x86\xdb\xee\xce\xffY@\x91\x06\x8c\x07fv\xa0\x06`\xc4AR2(\xd2~2\xfc\x05\xc6\xc1\xbe\xaf\xbf>%\xef\xdb\xc3\x0f\xb6\x81\x91\x89\x99\x81\x11\x18IL\xac\xac\x0c\x13\xdf\x7fbx\xf4\xfe=X\xc3d;+\x86\x9f\x8f\x1e3\xfcc\xfd\xcd`\xcf\xfe\x87\x0f\xee\xa4\x9f/^0\xfc\x05F\xda\x7f`\xa4\xe5qs1D\xbdy\x03I\x89W\xaf2\xfc\xfe\xfa\x8d\xe1\x17(\xa6\x81\x11\x07\x02`\xdf\x81\xc3\xf8\xdf\x7fpDE^\xba\x06\xf2-\x18\xcf\xb9\xff\x00\x92\x9e\xfe\xc1\xe3\x09b\xc3\xdf\x7f\xff\xfe3\xff\xf9\xc3\xf8\xf7\xf7o\x86E\xca\x0a`\xa7\x81\xfd\x00\xe4\xff\x01\xfa\xe1?0\xb6M\x1f\xbf;\xfd\x1df\x83\xc5\xd3\x0f\xec\x7fAY\x15(\x09J3 g\x801\x90\x0d\x12\x03\xc9\x01\x93\x85\x19JpD\xb00C26\x083C,c\x00Y\x061\x1f\x9cn\xfeC3\x0b(\x19\xfc\x03\xe3\xbf`~\x0e\xaf\xd4\x96\x1d\xab\x17\xfa\xa2X\x90h\xed\xfc\xbc\x9a\x97I\x02d\x183\x1b\xc8``F\x02&w\x06I)\x86\xe2\xc3G\xc1\xaeg\xff\xf7\x87\xa1\xc3\xc8\x80\xa1\xf0\xd2U\xa0\x05\xff\x19\xb89\xd8\x18\x9a\xe5\xe5 \x86\x03\xcb\xb0\xff\x7f~\x01K\x97?`Kf\x98\xd8\xf8\xf4\xb4\xd4o\x85[p\xc2\xc2\xec\xb7\x00+\x0b\x0b3\x0b$\x0f\x811\xb0xb\x06\xd2\xac\xc2B\x0cI'\xce\x80}\x00\x03\xc6@\x8339\xd9\x18\xfe\x02\x0d\x06%\xf0\x7f\xc0b\x0b\x94\xb0A\x18\x94N\xbd>\xfd;~\xef\xe2\x09+x$\xbf\xfe\xf8\xe9\x17\x0f;\x1b\xcb\x7fP\xd0\xb0B\x0c\xfe\x0f\xb4\x80\x01H\xefz\xf8\x88\xe1\xf3\xd7\xaf\x90\x88\x86\xc6\xf2\xfe\x8b\x97\x18\xd2\xb4\xd5Q,\xf8\x0b\xb7\xe0/\xc3\xf3'o\x1f\xc0\xf3&\xb8Bx\xf5\xf9\x18(\xd2 \x11\xf6\x17R\xb6\x00]\xd2u\xf7\x1e\xc3\xbcWo\xc0\xdenw\xb6gX\xae\xa5\x06f\x83\xcb\xa5\xcb\xd7!\xae\x06\x05\x11(.@\xfa\x80\xfa?\xfe\xfd\xfb\x1b\x94\x101\x22\x19\x98\xdb\x8e\x03s\x9b\x05<\x92\x99Y\xc04<\xf7\xa1E2\xacD\x04E\xee\xbf\xbf\x90H\xfe\xf2\xfb\xcf\x1f\x87g\x1f\x94\x80Y\xf91\xd6d\x0a\xb4D\xa5Q\x90\xeb\x98\x17\x0f\x87(<\x992\xe3I\xa6\x7f\x11\xc9\x14X\xa0\xec\x07\x1a\xec\x847\x1f\xa0YVQ-\xc0U\x10\xc0\xc3.\x8e-\xa3}\xfa\xfb\xef\x8f\xf3\xb3\x0f\xe7\x81\x1cw\xa0\xc1\xef\xb1\x99A\xf3\xa2\x02 \x00\xf1\xd5\x16\x12E\x14\x86\xcf\x5cv*Mm\x8b\x14\xcb$B\xb4\xa2\x88\x08+27\x0d\x02\xa9\xb7.F\x10H/\xf5\x10=\x15\x85\x14b\xa4 \x06\x11\x05\x15Q\x16\xdd\x08_J\x8aX%\xdd\x84\x902\x10\x1f\xacvki\xcd\xdb\xd6n\xa4\xdb\xaa{\x9bK\xff\xb9M\xcej\xa6\x0f\xd2\xc0\xcf\xfc3\xe7\x9c\xff?s\xce\x7f\xbe\xef\x9b9O \xa29\xbe\xe4\xe9\x1a+\xabk\x0b\x9d\xaf:\xeeV\x18\xd1e\x1blFJ\x8e\x88\x94\x9f:R{u\x14{\x99\x10\xc2\xa1\xbc|g\xe3\x9d\xebGf\xbdD\xb5\xf5\x97R\x1e59\xbb\x9f\xa4\xaay\x02\xb9$R=\xa4\x8a\x10\xaf |\xd7\x903\x86F\xde\xe4\xaf}\xf0\xf0\xe6\xd5\x133Jp\xa6\xaa\xa6d\xd5kW\xd3N\x1bJ\xe7\x88*\xb0\x13\xcc\xe2\xd3*\xe2\xa4\xc2\x0e\xe6\xc1\xf8\x82\x9e.\xd7\xf3\xf5\xd3&\xa8\xac\xaeY]\xd0\xde\xd6\xb9]\x91\xd2p\xed\x93\xe0\xec\x1c\x104\x158\xad!\x86\xa4\x0cQ\x99_\x1e\x9f7)\x89e\x93\x17\xb9\x5c\x1dfp|z\xb1xP0\xf8) $l\xe4n\xf1I\x1b\x83s\x18\xd3\xa8\xc4\xd6aY3e\x82\xb2\x03\x15\xcf\xf6\xceGv38\x86\x08\x8c\xac\x0c\xb6M\x845\x8d\xb6\xd1>\x92\x99d\x97\xcfsh\xca\x04\xfb\x7f\x0c805\x9a\xcbB\x06\xcaD\x02\x09\x0c]yp\xec\x0b\xc4\xe4?\xa4\xc4\xc6\xedP\x84t\xac\xa4-ez\xea\xdc\xf9=\xc7\xc8\xa6\xd2\x0d%3\x12\xe9\xac\xaa|}h\x0c\x10\x13\x89\x02\xaaYS\x80\xae\xb8=(\x80\xb7\x0d\x08\xe7bI1\x12\x07\x07\x91\x00\xbe(SL\xc21\x16z?\x95Y\xbe\xe0\xc3g\xdfQ\xb2\x81\xb8L8j\xb2\xa5\xaa+\xda\x8a\x90\x84\xdfK\xe8l\xcfG\x14\x14e\xe2\xe7fe\x22\x1bh4\xbe4|\x1c\x8e\xb1[A\xe9\x96/p\xbb\xddK\xb5T\x98\x94M\xa2D\x13\x07\xb3Q\xb2Q\xfb\xbe\xa2\xfc\xcc,\xd4\x05\xaa\x90L \x12!%zze.J\x04\x03\x16\xa2!d\x03\x96\x13\xd7\x14K\x02\xff@\x7f\x5c\xcd\xb1\x93\xf54\xc0&\xb2\x19\xb6b\xc0\xfcv\xceh\x8c\x0b\xe2\xfd}\x84\x875\x16\xd4L\x04\xe4#&\x12B\xf2&\x0f\x99\x18\x0f\x8e\xc14\x0f;Q\xe8\x82\xc7\xcb1\xda\xbcZ\xfc\x01S\x1b\x91C\x87\xd8X\xf0\x03\xaa\x1eKN\xd0B;$\x9dR\xb0[\xbdti\xb0\x7f\xdcQL\xe5\x0a\xf8\x0d\x81 %\x1a.#\xc9\x9d\x06sE\xe2\xc3\xc9\x09\xee\x855C%=\xb8>\xd6(\x1d\xb6\x8e\x8e!\xfe\xbe(\xe0G'\x1dETO\xc3U\xef\xf5Q]\xa4\xd11\xe4=\xf4\xbd\xf6+\xfa~\x12T\x00{u\x02\x1f\x17\x8aLhI\xac\xc6\xf93\xe5d\xbe\x07\xc6\x04>\xa6\xa2\x0b\xab\x0b\xfc\xac\xaa\xaa\xb1e`8\x0f\xff~&\xc3\xf5\xbeQ]\xff\x92\xa6\xeb2^\x12L\xe0\x1cw\x04\x1c\x8c\x83\x9e\xf9\x87\xc0\xf8\x98\x91=\xc7$\x08\xfe\x96\x07\xb7\x9cd\xac\x02J\x87B\xf7\xf9\xd2\xe8L-hj\xc2R-f\xd5\xe0w\xb8M\xa5}\xf1\x98\xc7\xa1\x88\x1fB9\xa6\x85kX\xaa\xb6w\xcb\xed\xa5\x164\x15\xff\x82\xa6\x13\xecE8\x1a\xac\x1a\x1e\xdb\x06\x13\xf5\xfe\x93p ICsv\xc6\xe1\xc5\xb2d\x9b\x09\x1f\xdc\x18\x19\xef\xbf\x1d\x8en\x86\xe0\xdffL\xfa\x90\xc4\x0e\xb7\xe6\xd6\xec\x8c\x8d\xe9\x12(\xb0)\x18\xed\xe9h\xf4{\xed\xc8\xf8e\x08\x5c7kU\x01\xc1\x96\xe0\xdf!\xb0\x0c\xf8\xcd.G\x92\xbc\x09\xd0v\x05D\x0f\x19\x86\xee\xd1\xe3\xd1f5\x12\xee\x86v\x5c\xf3A\x88\x13\xfd/\xb2\xe5\xb7\x00\xddZklTE\x14>\xf7\xde\xdd\xeen\xdb\xdd\xd2\xad\x14[\xa8\xf2\x08\x10\xd3F\xa2\x16$\x0a\xe1\x19\x1b@B\x9a\xa0\xc1\x1f\xe0#\x1a4&\x9a\xa0`CQ\x8b)/\x8d\x1a\xe3\x8b?&*\x88\xc44)Jh\x03>\xd0\x00\xc6\x07\x06\xaa\xad\xb0\x81\x96\xb6\x92\x02\x05J\xbbw\xbblw\xf7\xcexf\xee\xdc{g\xb7]\x9e\x02\x89\x9b\x9d\xdc\xb9\x8f\xdc=\xdf\xcc9s\xbe\xef\xcc\xde\xf0\x1f\xb8\xa5\xb4\xe8j>+V\xd7,\xea>\xd73\xebBX/\xe9\xed\xd3\x8b\xe2\x89\xb87\xacG\x82\xec^\xc0\x9f\xdb\x93\xe5\xce\x8a\x0d\xcb\xf3\x9f\xca\x0f\xf8\xff)\xbc-\xb8\xf7\x9d\xf55_\xdf\xb2\x19x\xadv\xe3\xe8\xe6P\xdb\xba\x96c\xad\x0f\xac\xf4A\xf0^\x8d\xe6\xf8U\xe4f\x8a\xf4b\x90\xc2\xd2\x0aM \xf6i\x04\x97\x88?\x08\xf4\xbf\x15\x85\x9e\xd2\xf1\xe3~.\x9b8\xb6\xfa\x8d5U\xed7\x0c\xc0\xea\x9aue\xfb\x0f6}\xbc@?_:\xd7M\x03\x01M\xd1\xcc`QE\x88+V\xf4\xc8\x81\x94R\x7f\x90N\xa4he\xcb\x01\x81\x08\x05cO\x1c\xc2\xbbr\x0bZ\xa6\x95Ozn}Mu\xf3\x7f\x02\x80\xf1\xd4\xc6\x1f\xf7o^\xa1\xc5\x8b\x1ft+~\xdb`Uq\x0c\xe7_\xc5Q\xa0\xbc\xa4\x04\x90>\x05\x94H\x00\xac\x14K% \xc4<\x1e\x88\x1b\xfa\xdbFV\xd7\xbc\x99\xd3\x9e\xb5*aW\x0d\x80\xb1\xf7=\xfb~i\x5c\xa2\x9f\x9f47\x0b\xf2\xb8\xe1\xaai\xb0\xa2J\xa3\xce\x8e\x9a\xa4\x0d\xb9\xe5\x16(\x0b\x84H\xf6v\xfd\x84\xd8\x86\xb3\xd4\x03rJ\x17)\x9e%\xcc\xef\x12\xa4\xef\xcb\x9c\x82\xa6\x87\xa6O\x9d7T\x8d%#\x00\xa6i\xeaw\xff\xb0\xfd+?\x1d\xedC\xcb\x99\xf4`#n\x1b.\xfavi\x84\x1b\xaf\xa6\xceJ\x9a;\x0dr\x1b\x22\x04\xb0M1\x88]\x8a\xb3\x81`?\x8a\x08\x1f\x0dC{e\xc5\xec%r\x8d8#\x80\xaa\xd7k\xa7\xd7\xef\xde\xfb\xf9\xce\x00\xbd\x93\xcb&I\xde\xa4\xa8\x90AyT\x91\xc0(\xa2\xa0\xe9\xcc\x80\xf8:\xa3-\xb8\x8d\x9coAR1\xd6L\xf0\xe201\xe8\xc2>\xe8\xa8\xac\x98\xb5l\xe3\xda5\xfb2\x02@\xb7q\xd55|\xdf\xf4\x89\xeb\xe2D?\x06\xa9m8\xd4d:\x92\x1d\x07\x04f\x96\x8c\x82\xca;JL\xc3\x0dS\xe3\x99\x0d\x9fd\x14\x88\x12\xbbp\xaa\x1b\x86\xf1T\xdc\x1bZ<\x7f\xce$t\xa7\xe4\x90y\xe0h[\xe7\x9b\xcb V\xcc\x8dgn#\xdc\x81\x1b\xaf\x09 \xaa\xcb\x99\x05$z\x0f\x8f\x19\x03E\xa5e\xb0\xf5\xf7\x83\x0e\xe1\xc3\xe7w\x5c\x08C\x17R\xa6b\x9f\x17vt\x9f\x03\x0d\x99?\x88\xba\x09\xfb,\x9b2\x19\xee\x8bF\x91Z\xc5\x81\x0e\x9a1\xb4\x8fI\x0dF\xf9\x14\xc2g\xd4\xafj\xdaR\x88\x15\x1dj\xeb\xd8\x80WW\x0e\x09\xe0H\xe8\xd8\xe4'I\xdcG\x19\xa9W\x09w\x09*\x5cD\xa5\x9a\xe9\x03*\x8a(\xd6g\xd7\x85+\x94\x93$\x8c\x981\x1d6\xec\xfdI\xc4\x00\xe5`\x0e\xe8\x11P\xf4~QU2@\xa1f\x8d\xf7\xe5Y3`\xec\xd93\x9c\x03*\x9c^\x1bNY\xd1\xa6\xd0D\x94\xb6\x1c\xd7\xba\x87&\xb3\xb7\x85\x8eO\xcd\x98\x89O\xb4\x1e\xd7\xfc\x05\x1eWR\xd3$\xff6E\x99j\xf1G\xcd){\xb1\x99 \xbc\xaf\xc1\xc8H\x04\xd6\xcf\x9c\x06/4~\x8b\x0b\x08MYR\xad \xc6\xd5\x00\xde_P\x01\x81\xb6VA\xc9\xb9\x7f\xdbF\x13\x19D\x8a[\x99n\xe47\x0c\xd7\x89\xb3\xddZF\x00\xd1\xfeH\xefE\xbfJ|.\xa2\xc9\x00\x14\x95\xbd\xdc,\xa9Y\x05|\xca\xfb\xaa\xe8\xb3\xd1Bi\xfb\xcd.\x18\xa0\x90j\xbcS\x16\xe3\xc7\xda\xfa\x9d\xb0\xe1\xae\xf1|\xf6l\x03\xa5Q\xb7\x00\x90!\x00D\x93\x06a6^\xaa\x06u\xb8%nD\xe4\x8a<\xa5r\xc5F\xce\x9eB\x86\xe1\xcbu\xf4\xe3g\x9aZ\xa0\xd3 \xf6}\xd6lA\x97\xdaZ1\x07\x0a\xb2}\xceR\x89\xf7\xa3\x08l\xf9_G\xa1\x1dc\x84\xa4I\x17\x13\x90\xf5\xac$\xa0\xf1\xbb5\xc2m\xfb\xe2r\x9a\xe9\xa5W\x87\xf9V-\xcc\xf1\x14\xa6\xb8\x91\x22\xfc\x1f\xfb\xdc\xff\xc52\x9a=a\x02\xb8\x83A\xbe\x221\x09\xca\x82\x9b\xeduE[\x9a\xc1\x08\xeb\xfc\x9d\xee\xe1\x85\xe0\xc5e\x15D`\x82\xd8\xf8K\xf6\xf4@4\x14J\xf1wb\xeb\xe6\xd4et\x87\xce\xd5\xcd&T7\xef^\x89&\xdbT=\xcc\xf7\xf8\xa2\x1c\xcf\x08\x1b\x84\xa2\xa4\x89\xc0\xb4\xf3kHd4-\x91\xd1\xb4\xd9\xb5\xee\x0b\xe3?C\xe3_\xb9\x22=\xc0\x1eD\x10\x1d\xa7\x0dR\xb5<\xe0+1\x97OG\xef)82\x0aa\x86S\x1e\xe0\xd7K%l\xc3\xe9`*\xb1\xb9\x8f\x0b\xd6\x8dh\xd3GW\xcdF\x11D!\x1e>]\x9b\x9f]>?\xdb3<#\x99S\x1d2\xa7\x88\x9a\xfc\xa5\xc8\x9c\xb3(P\xdb\xd7\xd3\xc9\x5ccd\x80\xc9x\xcc\x8e\xf0\x04\x1a\xdf}]z\x80\xedP\xe0\xe1\xc3Uy\xbe\xbb\x1f\xc9\xf5\xde~EtZ\x19\x82N\xd3\xcb\xd3\xe9\xbaH\xec\xf4\xa6\xde\xe8\x9f\xf8\xd4\xf3\xe95\x88\xebVd\x08\x84m\xca.e\xa3\xf2b\x9eo\xdcc\xb9\x9e\x22\xcd\xe4\x1a\xd7,h0\xe9\xd1mz\xec\xd4{}\x17[\xf1\xd2\x166\xe3hx\xe2\xa6\x88z\x04T\xc2\xea\xc3\xd8\xe6O\xf1\xb8F\xdd\xefu\x07F\xbbT\xefHM\xf5\xe6k\x9a;\xa8)|\x17\xba\x87\xd0Do\x92$N\x1aF\xac=Ib\xbf\xc6\x12\xe1\xdf\x06\x92'\xf1V\x03\xb6:ks\xea\xa6hb\x1cU\xf6\xd7\x86\x1cl\xd9\x0c\x83\xf8\xab\x83\x07[\x96h.\xd1T)\xcf\x10\xd1\x92\xa2\xc5Ec\x85NV\x8fa\x1b\xebL\xb0\xf4g\xaa\xcf\xfco\xcb*\xff\x02G\xbd\x8aL\x08S\xf3a\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xbf\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xa9\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10^IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfe\x07\xeb\xb8q\xe9\xb0\x5c\xdf6\x06\x86\xf5\x14\x83\x82\x8e\xcb\xae\xdb'W\xde\xde\xbau\xab\x15\x0b''\x97q\xed*\x06\xc6\xbcH3\x86wW\x97\x9e:\xf8\x7f\x05\x03\x83\x0c\x837\xb2\xa5L\xe6\xe6\xe6\xb1\xc6\xc6\xc6\xb7@|\x0c\xcbMLL\xb2\x81T\x0d@\x00\xa1H\xcc\x9b7\xef?+++\xc3\x97/_\x18~\xfc\xfa\xafY\x98\x9fu\x03\xdd\xb5`\x0dS\xa6L\xf9\xf3D \xc1\x5c_\x89\x87A_\x9e\x81AQ\x94\x81a\xfb\xf1\xc7\x0c\xff?^\x9f\x1c\xec\xe7f\x85\xe1=NNN\xe6\x9a\x10\x1e\x06C\xe9\x8f\x0ci\x13\x1f0\xf4ne`\xf8\xc5*\xcb\xc0\xcb'\xb8\x88\x91\x91Qy\xc7\xfd\xa5\xffMm\x8c\xfe\x83h\xb8\x93f\xcc\x9c}\x8d\x9b\x87\xff\x83\x8aIP>\xeb\xd7k\xc6\x0f\xef\xdf\x9e~\xe2\xdc\x05\x86KgN\xfd}\xf9\xf2e\x8c\xbe\xbe~\xfe\xa2E\x8b\x82\xb0\x0692\x06\x86\x82+(\x0a\x80\xf4e\x948\xc2\x11\x1bB@\xac\x02\xc4\xb6\x0a\x0a\x0a=@Z\x0c \x80p\xc6\x1d.\xc0\x02c\xf4O\x99\xa3.\xcc\xc7v\xe3\xcf\x9f?\x0c\x9f>}b(((`\xc4\xa6\x01l\xc3\x8c\x99\xb3\x0e\xbd\xe3\xb4\xea\xff\xc6\xaa\xf0HO\x91\x87AO\x8e\x81\xe1\xca\xb15gBBB\x18\xb1\xda\xc0/(\xce\xf2\x9d[\xfd\x91\xad2+\x83\x9a$\x03\x83\x00\x17\x03C\xc2>I\x93/_\x16\xfcIHH`\xc1\x88\x87\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\xdd\xf2\xcc\x17\x8d\xb5eLN\xbb\x96\x9c`\xd05\xb0`\xf01d`P\x93``\xb8\xff\xfc7\xc3\xf6++\x1f\x14\xc5\xd4\xc0\x92\xceK\xb0\x1f\xb6o\xdf\xfe\xbf\xeb\x98\x91\x89\xbd\x818\x83\x020YhJ10p\xff\xb9'\xb7{\xfb\xc6\x07\xd7\x7f\x9e\xd8\xaak\xad*\x09\xd2\xb0\xa9\xe9\xe4T\xb0\x0d\x9e\x9e\x9e\x8cO\x9f\xce\xf9/\xc4\xa5\x97\xa9\xaddt\xf6\xf1\x8d\x03\x13\x9f~\xfad\xf9\xe2\xed{\x86\xd9\xad\xabX\x83\x83\x837\x7f\xfc\xf8\xf1\xee\xee\xdd\xbbsP\xe2\xe1\xc2C\x06Vmi\x06vV\x16\x86/ \xbe\xbf\x9f\x1f0\xa132<}\xfa\x94\xff\xcc\x993\x9f\xe0\x9e\x86\x01\x03y\x86\xdf0\xc5 \xb0q\xd3&F`2`\x90\x96\x02\xa6J\xe4P\xc2\x07@\x9a\x80I\x94\xc1\xcf\xdf\xef?JL\x03\x83\x0c]-'4-\xf1\x83\x5c\x07\xc4\xaf@\x82\x00\x01\x843-\xd9U>fdgad\xe0\xe1ba\x13\x13`\xff+/\xc1\xf9\xaf*\x90\xe3\x1f\x03\x89\x00\xc3\x829s\xe7]\x14\x14\xe0\xd7cf\xe3=\xfe\xe2\xbb\xe0\xa2\xfb\xdfU\xce\x8a\x09\xb22H3]3\xfe\xf7\xf9A\xdc\xb7\xaf\x9f,\xff\xfd\xfbw)--M\x9f$\x0b\xbaz'\x18IK\x88\x9e}\xf2O?\xe8\xe5_\xf9G\xc2|\xac\x0c\x92B\xec\x0c\x12\x02\x8c@\xcc\xc0 \xc2\x03\xf4?\x10\xf3p00\xec\x06\x06q`h\xf2\x01\xd3\xb9\x107\x03\xc3\xc7\xaf\xbf\x19\xb2\xfb.2<\xf8(\xc4\xa0\xa6\xaa\xf5\xc8\x5c\xf0[&\xe3\x9fg\xe7A\x8e\x84\xc6\x09P\x15\x030\xbd2\xb0\x03\xf1[ ~\x0dt\xfc?\xb8\x05,\xc0T\xae\xa6mrVL\x80\x19\xecZP\x06\xfa\xf2\xe3\x0f\xc3\xe2\xadW\x19\x16\x1e\xf8\xc1\xa0\xaci\xce\xa0\xa2b\xc2\xa0\x0b\x14\xe7\x07\x1a%\xca\xa3\x7f\xf6\xf5\xfb\x0b`\x83\x81E\xf5\xab\x03O\xd7q!\xbb\xfc\xdf\x0fF\x86\xe2\xe2\xe2jd\x0b\xfe~\x7f\xbc\x7f\xa2\xb2\x9akn\xcf\xaa\xfb\x0c;N\xbeb\x90T\xb1\x00\x96\x98\xfa\x0cA\xc0\xfaD\x04\xe8#an\x88\xaf@Au\xf3\xcc\xf6\x89?~\xfc\xf8\x0b\xcaz\xe79\xb7\xb1]8\xcb\xc0\xb0\xa2z\x0f\xdc\x82\x88V\x17\x867o\x98}Q\x22y\xd1\xe2\xa5\xc7\xb98\xd9-\x1e\xff\xd5\x0d\xfa\xc1\xa1\xfeH\x00h\xa0\x08/\xd0`\xa0\x81\xa0x\x10\x04\xba\xf1\xef\xf7\x17\x0e\xa7O\x1e\xefy\xf4\xe4\x09\x83\x99\x89\xc9O+++.{{\xfb\x06\x19\x7f\xb6\x92\x7f\x1c\xbf8af\xb1\x9dT8\xb5p\xe1\xc2\x00\xac\xc9\xb4\x7f\xea\x22]\xc6\xdf\xef.\xb1\xb3\xb33\x800\x0b\xa4\x90`\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab5\xa6\xad2\x0c\xbf\x9cCO\xdbs\xdaA{zg=]\xd0-Fp8\xe7\x80\xf0c\xa8\x01o\x08\xd1\xc4\xf1c1!\xb8\xc4\x82n\x89+\xc9\xe2t&\xc61\x89a\x8a\xcb\xf8\xb3\xb0\x18/Y2\xc4\xc4\xe1d:\xc7\x0c\xa0eNS\xb5:1A\xb2r\x0e\xc85PF/\xd0\xbb\xef\xd7\xd6\xe2\x22\xd9\xc5t_r\xd2\xe6$\xe7}\xde\xf7\xfd\xde\xefy\x9e\xef\x8e\x03\xdc\x90N\x1f\x7fcJ\xf6\x5c\xc7\xbc2\xab\x15\x10\x16\xdd\xbd\xe9|7\xabdv\x11JFe\x83H$\x02\xa1P\x08B\xe1HO\x8bc\x7f\xfd\xff\x06x\xf7x\x97\xc5\xc4s\x7f\xa9\xd5j\xf0\xc56t\x8e\xfb-}1\xb9qa#\xb7\xc8+W\xff\xa8Y]\x9e\xddK\xc0\xf0\x5c\x14466N\xdd\x16@GG\x07\xa3\xd7\xebCr\xb5\xe9L\xef\xd5\x92V\xa3F\x0e\x84QMZ\x05X4)F%\x14\xf1\xdb\xf7\xe7\x0e\xf9\xfd\xbe\xa7\xbd\xfe\x98\xa2i\xcf\xee\xd0-{\x114aA9\xab\xf9\xf1\xe2\xf4\x03\xad\x82\x81\x01\x13\xaf\x00}^.XQ\xa3\xf4\xc8C\x1a.\xc5E\xfa\xea'Z\x9d\xdf\x0e\x16P\xd4|\xe0\xdf\xdf\xdf\xb0\x82\xb6\xf6\xf7\xacf}\xbe\xf4s\xb4\xae\xd4\xa0e\xe3\xba<\x19Xy\x1a\xf4\xea\x14\xc9\x91\xe0\x84]\xbd\xfe(<\x7fd\x18|\xb9\x9b(\xfb\xd6\xcb?,x\x97uM/\xecY\xb8i\x05\x0c\x15?N\xf8\xc6\xca\xab\xe2F\xa2^\xd8\x12\x925\x9f\x16\x19\x85\x0c\xe0\xd4\xd7\x1ex\xff\xfc<\xd0\xba\x0a\xb8_\xc8\x8d\xc7b\xc3\x10\x0d\xaf\x9c\xc0\xcf\x9f\xc5a \xd3\x88\x96\x04x|H\xdbf0\xf1@\x06\x80\xa6\xa9\x87\x91\xc4\xe0n3\x93\xec5\xc9\x9c\x04\xce\xc3\x01\x9d\xbb\x16\x81\xa3\xdd\x1e\x18p/\xc2\xb6\xed\xe5`\xceO\xbd\x8f_\x8b\x03Jg5\xf1KmmmgJ\x9f\xbc\xafFi\xc8\x81\xe8*\xc0\xc4\xf0R\x10\xdf\xdf\x8b b\x12\x80\x04'K\xd0\xa1\xa5\xdc\x90\x0a\xa0\xc6gd\xdc\x07\x8ec\x97!\xaa\xad\x80\xf2\xb2-`B-\xc8cS\x14\x9e@g\x82\x00\x09\x87\xc3q\xa0\xac\xfe\x9e\x1aJ\x11\x860\xb1\x04\x0c@\xc1CJ\x16Eh\x04A\x0c\xe9\x0ar\x07\x10\xa4n\xa36%\x8f\xb94\xc0\xe1\x0f~\x87\xfe\x11\x06tB\x15\x14[q\x08\x18\xc8\xb4\x8d$\xe1\xf1$\xc8\x19\xb9P\x5c\x5c\xdc\x12\xca\x09\xc0\xeb\xf5\xefd\xfa~\xf8\xe3\x16\xd0\x96\xc8\x88\xc2\x99\x92\x00\x9cJ\xb5\x8f\xa6\xe9:-\x1b\xa3\xaeN\x87\xe3G\xbb\xc7`d\x86\x85\x92\xa2\xbb\x92\xedJN\x90*\x05@\x82\xe7+c\xd4(\x9e\x07\xd4\x07\xfb\xc0\xc0@\x9fQ\x17+\x9f]^;\x16\xdf\x5c\xf9\x12\x12Q \x94-\xcf\x9c\x83\x9e\x9e\x9e(\xaf3\xfct\xe0T\xa8\x99\xdf\xfc(l\xb5\xa6\x14\x8c\xa8\x9a!\x9d9\x99(\x02\xe8r\x9e;=99Qh\xb7\xdb\xe9\xea\xea\xea\xce\xc2\x06\xc5K\xc5\xb65\x17sEtCb\x92\x9d\xee:\xf8\xc9\xb6\xcc\x1c\xb3*\x8dz~n&\xb8\xff1\xf5\xa1?)hU+\xd6\x02f\xaa\xc0\xdf_]Cg\xbd\xdeE\xf3\x83;J\xa1\xb9\xb9\xb9\xa1\xbf\xbf\xffHCA\xc3\x8eKe\xdf\x95f\x08n\x95Y\x99\xfc\xcaw\x928\x8b\xeb\xa8\xa2\xbd\xf3#[\xbe<4N\xeeSr\xb5\xb1Sc.\xea3\x9a-\x0bLt\x8e_\x9c\xf5\xd4,y\x17\xf7\xfa|>(\xdc\xbc\x05\xde|\xebmx\xf9E;\xbcz\xf0\x95\x06\x97\xcbu\xa1\xaa\xaa\xea5\x8b\xc5\xb2=\x10\x08,\x8d\x8d\x8d}\xe8v\xbb?\xc5\xd8\xd1\xff\x90]\xfb\x17@S\xa3\xc7>\x933t-\xd1c\x02\x86\xfb\x93\xbc\xdb \x0f\xf5:\x9d\xce]\xb5\xb5u!-\xaf\xcd9\xd9\xd5\x05\xa2$B0\x10\xdc\x87\x0a\xd6y[zpi\x14(\xdc\xd4\x1c\xb4)JN\x01\x11*\x07\xae\xe3\x9d\xca\xca\x9daA\x10d\xc4wI\x92\x04\x98y\x13\x82\x9c\xc8\xaa\xe0T\xee\xac\x94\x04\x9b\xd5J@DQ\x8a\x07\x83\x81g\x10\xe4\xf3[\x16\x9c\x9b\xad\xc1\xa1AA\x12\xa5\xa47\xb5\xd9\x04\x8a\xe3\xd8^\xd4\xe5G\xb2\x06@V \x18\xac\x90D\xf1\x17r\xf3\x10\x04\x1bp\x1cw\x11A\x9e\xca\x1a\x00\xb6d\x05A*Eib\x924\x1b\xf7\x05X\x8e=\x9b5\x804\xc82\xf6_\x98\x10%R\x08\xd8\xac\xb6\xa2u7y\x9dk\xd1zK\x96\xbe.\xfd\xf3(\x08\xdb\xa4\xa9Ze\xb6\x98\xf9\xe9\xa9\xe9!\xfc?\x85\xb1}w\xdc\xb6\xfc-@wV\x1e\x1bE\x19\xc5\xdf\xce\xec\xce\xec\xce\xcc\xeel)\xa5\xdb\x16H\x01)\x14I\xb8\x84?\x00mbPK+\x876\xf5V\xee\xa8\x1c\xfdCb4\x9a@D4\x1c\x16\x94\x16B \x08\xe1P9S\x82\x5c!\x04J\xc2!\xc8e\xb1\xa5\x14\xcae\xd9\x1e\x94\xbd\xba;\xdbmw\xd7\xf7\xe6\xc0\x82\xd5\x80\x8a6\xee\xe6\xe5\x9b\xec\xec\xec\xfe~\xf3\xbd\xf7}\xbf\xdf\x9bG\xfe\x07\x8f\xfa\xf5\xd0\x04\xe6/Y\xdfE\xb2\x84sXS4\x8fa\x99n\xf1X4\x19\xe21W4\x16\xaf\xc5\xed\xa7\x1e\xf7\xa0\x1b\xd1\xb8i\xbb\xc9\x22\xedy\xbf`J\xfd\x7fN`n\xe1wR\x8a=\xb4\xd0\xc6\xb3\xd38\x8e\xb3\x904\xa00\xf2\x0d\x01\xab\xcb \xe9?\xb2'\xc6H\xf2\x8c\x02\xcf\xb7\xa0\x22Xew8?\x98\xf0\xd6\xeb\xc1\x7f\x8d@QQQ\x92M\x10O\x88\x82\xad'*2\xdc\xa5\x85S\x9efq\xd7\xe1\x9b\xbd\xf6q\x1c\x1f\x13x\x06l\x18\xa2\xd5\x0c\x92\x8d\xd5\xc2\xca\xaa&\x9a\x04\x14\xcfF\x99\xda\xaa\xd2l\xa5\xe9\xce\x98\xd6\x96\xe6\xa1D\x0c\xff\xab:\x16g\x86O\x9a\xf8f\xdd#%P\xbc|\xc52I\x14fI\x92\x041\xb3\x5cr\xa6\xa1g\xf1\xedH'/g!\xc0\x8c\x0e\xd8\x0cDB\x03nRe\x8e\x80\xf6[\xc4\x108M\xa8\xd11\x8d\x8d\x8d\xf5\xce\x8b\x15\x17f*\xa1\xd0x\x9a\x15%\x1c)~\xed\xd5\x97g=\x12\x02K\xbf\xfc\xaa\xc4a\x97\xc6\x11\xf8&\x93k\xce9\xff\xc0=\xda\xddf\x11 \x0b\x22\x02w\x08,\x90\x1e \x80\xb8\xd1\x81\xc4k\xed\x0b\x1b\x82\xe5,\x1ah\x0a\xca\xb2HK\x0c\xca\xaa}\xb0tk5<\xdbO\xc9q\x89M\xf3\xa8u\x8a\x9b_\xc9\x84\x09\x13^\xf8G\x09,^\x5c8C\x92\x84b\x02\x1f\xe7\x93\x8b+[\x87\xaf\xb3\x0b\x16\xb0\x0bf\x95\x00\x81&\xa0\x0e\x9b6\xf2fM\xebQ\x10\x19\x22@\xfa\x13\xcb\x02\x0e\x9e\xbd\x03\x17\xaa\xbdp\xbe\xea\x0e\x94]\x0f\x83\x9c\xd2\x1f\xba$:\xe1\xf9\xd4\xc3\x13\xb9h\xfd\xccP(\x04\xa1\x90\xf2\xc6\xf4\xe9\xefn\xfa;\x04\xeeq\x0ef33\x83\xb4\x09\xa9\xe0\x90\xad\xf7\xee\x1e\x92\x88\xea\xd7\xa4\x02'\x15,\xb6\x01K\xc1#`\x0b\xab\x8d\x01\x94\xd3?\xe1\x9d.\xbb\xdc\x08\x1b\xf7W\x83'\xe2\x00\xde\x91\x06\x9d\x93\x9e\x80'Gh3E\xb3\x12`z\xeevF\xdc3\xa9&b\xb1\xe8\xc7E\xfb\xe1\x9bY\xcf\x81\xda\xce$\xed\x8f*zRZZ\xda\xf8\x84\x84\x84^\xb2,;\x08\x97\xcf\xe7\xf3{<\x9e+555%K\x96,Y\xab\xf7\x88Z\x7fG\x00\xc1g\x12xZaRS\xbb6J\x82I\x05L\xf9\xcd\xebiA\xa3M\x1f\xe3\xfa*\xb4\xf0\xdb\xabp\xe0l\x00\xa2\xc0C\xabI\x80\x84\xae\xa3`\xb0S\xd3\xb3T\x0f4SV\x83\xbc%\xa51\x1e\xd4\x9a\xd3xm\xe6\xa6\x05\xf9LA\xf6\xb6.\xe8-V\xf7\xe9\xd3';q\xb0\x85e\xef3\xcc\xc9\x90D\x06\xaeST\xc9\x18:r\xe4\xc8y\x95\x95\x95\xfb\x10\xe34\xbbR~,\xa7\xce\xfd\xcb<2\x16\xb8\x0a\xed,((\x18O\x8fW\xf2\xf2\xf2\x8a]\xd9\xec\x08F\x8e\xda\x09\xc7\xfe\x05\xa7\xdb%\xf0\xdc\x87C\xb4\xcd\xd3\xc7\x06j\xf7E\x8fn\xdf\xbe}f\xbb\xfb\xc0\x96-[V\xa2\xd9x[}Z\x141\x97\xec\xab\xeaZ\xdc,\xf6\xf7\x92}r\xe8\xf5`\xac@\x94&m\x8b\xdb8o\x90\xa3\xcf=\x0d7R\xce\xfcxb\xa1\x99e\xfa\xd1\x12jbL\xd1\xe3\xc7\x8e\xcfG\x99\xf9\x05:\xa2\xf8\x80\x01\x03\xc6dff\xbec\x1d\xe1\x1f\x16\xb7\xb5\xfci\xcb\xc8\xa4X\x94\xf0Q\xc7\xc9\x8a\x8a\x8a\x95\xe8\x9cv\xfd\xe1N\xbcb\xf5F\x97\xcd\x12\xfd\x81eL\xddUo\xceXO\xb5\x9a;\xed\xf2\xdb\xb3p'fc\xc6\xda/\xe8\xe0\xd5ML'\xc1\xa9\x04\xd1\xdd\x96\x9f\xcd\x0e\x05\x9b\xa6y\xbd\xden\x8a\xa2\x80\xcf\x1f\x80\xdd\x07\x0eB\xdf\x8c\xde\xf0J~\x1e464x\xf0.~v\xe8\xd0\xa15UUUrVV\xd6\x94\x94\x94\x94\xa7\x98\xc7\x9a2Z\xc5\x90\x14\xe3\x9bU2L3\xaf\x98\x83BS\xec\xb2t\xc9\xedv\x1f)--]\x83\x1f\xd7\xb4k\xfb\xee\x7f-Z\xb5\xdfa\x8d\x5c]\x04\xb1\xc8T\xac\x0d\x96f\x85\xb4\x90a\x07\x8d \x92F\x13\x82~\x93\xf4\x10E$\x12\x89\xe2\x12x\x10e\xc9\xb0\xa9S&;\xcf\x97]\x80\xcd[w@E\xe5E\xb0\x98bp\xbb\xae\x1emK\x90:\xac\x1f\x91?\xd55\xba\xa4\x07\xaf\xc3 G\xd8\xa4\x87Bm\xf6\xbf\xa4Fw\x9c\x04\xd3\xads\x9b\xd2Z\xc3\xbe\xdcx\xb4e,\x8bj\x14\xe1\xba\x10x\x12F\x03\x86\x1b\xbfv\x13\x09\xee\xc25\xfc\xfb\xfc\xfc\xfc\x9a\xb6\xd7\xe7\xe4\xe4\xbe8z\xf4\xe8eee\xe7S\x15%lBqH\x04qf\xfc\xe0\xf3z\xa9..\xe3\xd7\xa6cj\x1d\xe8\xb0~\x00\x0d!\x15\xeaKh\x0e\xdf\x93\x9dr?\xd9.\x03\xc7s\xd0\x8cD\xfc\xbf\x11\x09\xe0w&#\x91m\x1d\xd6\xd0 \x11j\x1fMA\x22\xf3\x90\x88\xd3 rwF<^J-\xf5\x118\x12Y\xd5\xa1\x1d\x19\x92\x99$\x08\xe2\xd7\xce\x04\x19d\xbb\x03\x89\xf0*\x11\xbf\xdf\x07\x1e\x8f\x0f5S\xb0\x96\xbc\x14=\x92A2\x81\x0ek)\x91\xc8X$\xb2\xd6\xe9\x94;a\x0d\x81Q#~\x1f\x12\xf1\xf9\xe2\xa1`\xb0\x8c\x1e\x1c\x22\x89\xa2\x0e\xed\x89\x91\xc83\x82(,\xc3\xcc\xea{\x97H\xb3\x9aZu>\x9fgTi\xe9\x91\x0b\x1d\xde\xd4#\x09Z\x93G\x8b\x82\xf8\xa9\x9c \xa7\xca\x0e\xf9\xe9\x0d\x1b6\x94?T\x0d<`\x9f\xe5A\x1e\xd8\xb0\xba\xf2\xb5\xb4\x19\x8d0\xce\xd1\x9f\x09z\x8fF\xd0\xfb5f\xab\xd5\xca\x87\xc3a/\x1e{0\xee\xe8A{A\xe0\x7f\xd1V\xf9\x15N\x0d\x19H\xb5\x03\xcb\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\xaa\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05_IDATx\xdab\xfc\xff\xff?\x036\xc0\xc4\x80\x03`H\x18\xe4\xf1\x81\x8d`\x81\x09\xd8\xf5\x08\xff\xff\xf4\xe87\x03\x9f\x1c+\x98\xcf\x88\xcb\x0e\xb8\x0e\xe32\xfe\xff\x7f\x7f@\x14]\x98\xf4\x89\x11 \x80\x18)s\x15\xccEp;\xecz\x84\x80.\xfa\x83\xa9\xe3\xfb\xbb\xbfpK\xadZ\x05!\xba@\x96\x83\xb0Q)\xef\x7f\xfd\x5c\xde\xff0>@\x00\xe1t\x15\xd1AE\x08\xb0\xa0\x0b8N\x16\x81X\xc9\x08F`\x86\x82\x84\x08\xc3\xbc\x90\xeb\x8c(n\x05a\x8b&\x01\x14\xf7\xc2\xb0]\xaf\x10\x5c\x0c\xc5\x86\xef\xaf\xff2\xb0\xf131\x98\xd5\x0a\xfcg\x04\xda\xf3\x1fj\x0b\xb2/Q<\xcd\xc8\xc8\xc8\x0e\xa4\xe4\x818\x18\x88O\x00\xf1] \xfe\x04\xc4\xdf\x80\xea~\x81\xd4\x00\x04\x10\xedCi\x805\xa4l\xd0\xc6\xf0 \x8a\xa7\x1d\xa7\x88\x82\x02\x93\x01\x1e\xa4P\xa9w\xb7\x7f\x81\x13\x1a\x86\x0d\x7f\x7f\xfccx\x7f\xeb\x17X\x01\x88\xfe\xf3\xf3?\xc3\xbe\xdc7\x8c8\x93\xc6\x9f\xef\xff\xe0&\xc1\x80}\x9f\xf0\x7f\x9c~\xf8\xf6\xfa/\x86?\xfe\x00m\xc5i\x83\xba\x96\x10\x8390Y\x80\x8d\xfc\xcf\x00O\x17\xc8\xb6\xa2h\xb8y\xed\x1d\x033\x07#\x03,\x1b\xc3r\x15\x03\x9e\xb4\xc4\x0d\xa4\xf4\x80\xd8\x04\x88\x8f\x02\xf1#P\x9a\x04\xaa\xf9\x0aS\x03\x10@$\xa7%\x9a\xa7\x8c\xe1c\x01r!\x89\x0f\x10\x8c\x03|\x06\x09k\xb2\x83\xca(\x86\xb77~a\xa4\x06\x82\x16\x80\x0c\x16Pb\x03&\xbb\xff\x0c\xa8:\x19Q\x13%\x14|\xb8\xf7\x1b\xab%,\xb8\x5c\xc7#\xc9\x02\xce\x05_\x9e\xfd\xc1\xaa\x11\xe63\x90\x1c\x88-\xa8\xc2\x06\x16CW\x8b\xd3\x82?\xdf\xff\x83]\x8f\x0b \x1b\xc4#\xc3\xc2\xf0\x1b-K\x92\x14\x07\x1az\x22\x0c\x0f\x9f\xbdg\xf8\xfe\xe6/V5\x1c\xc2\xcc\xe0\xe0\xfa\xf1\xf6\x1f\xfe\x9c\x89\x0cL*\xf9\xff3\xc2\xc3\x1c\x16\xda\xb0:\x89\x11\x1a\x0b\xff\xe1\xb1\xf1\xfb\xeb\x7f\xacAITN\x06\xf9\x82\x11\xd8f8\xdf\x8b=\xa5P\x94L)\x05\x00\x014Z\x16\x8dZ0j\x01\x05\x16\x80\xdaM\xc4T:dW8\xc2\x1a\xec\xe0\xf6\x96\xa2\xb8\x18\xc3\xd9\x03\x8f\xc9\xabp\x04\xd5\xd8 \x0d7p\x13\x05\xa9z\xf9\x0f-\x02\x19\x11U\x8f\x82\x84(\xa2%NL\x10\xf1+\xb12\xfc\x07vC\xfe\xfd\xfd\xcf\xf0\xef\x0f2\x06\x89A\xf1\xef\xff\x105@|\xee\xd0S\xe2\xfa\x090\xd7\x83\xba\x5c \xdf}z\xfc\x1b\xabF^\xa0<\xc8\xb9\x9f\x1e\xfd\x82\xd7jD[\x00\x02 \xd7)H\x8b0,)\xbd\xc5\x88\xcb\x11\xb0pw\x9c$\xfc\xdf\xc0F\x9a\xb4T\xf4\x17h\xc1\xbf_\xff\x08V\x99 \x8b\xfe\x82\x83\xed\x1fi\x16\x80\xc2\xfb\xef\xdf\x7fD%\xd9\xff\xd0\xf8!\xc9\x02\x90\xa6\x9bW\xdf\xc0\x83\x03_\x9a\xff\xfb\x07\x12\xd9$\xc5\xc1\xa9\xb6\x8f\xe0 \x88\x98\xa3\xf4\x1fT\xa9\xffx\xfb\x17k\x9e\x00\xc9\xfd\xfb\x8d;\x88\xf0\xe6\x035-\x11\x86\x07O\xdf\xc1\x13;#J\xe5\xcf\x88\xe0\x03\x89_\x9f\xfe\x11_\xe9\x83\x0cg\xe1fDjA\xfc\xc7h\xd31\xa0uB\xcf\xb4\x7fd$\xb9\xa8\x00Y\xc4\xcc\x0e\x0d\xe7\x9f\x0cD5\xc2\xc8*\x8b\x98X\x19\xc0\xe1\x8c\xcf \xa2{\xfcH\xdd#N %\x08\xc4\x86@\xac\x0f\xec\xd8/\x00\x8a\xf9\x01\xd9\xa0\xa4\xf5\x1e\x88\xdf\x82Z\x98P\x0c\xeaz\xff\x01:\xf6\x0f\xdd\x9b-\x00\x01\xda\xb5\x9a\x95\x06b \x9c\xa8U(\x05Q\x16\x11\x17\xa1\x1e\xfc\xa1\x0aB\xef\xe2\xcb\xf8\x0e>\x96\x8f\xe0A\x8f\xeaE\x0f\x1e<\xb4hA\xa1\x94j\x11\xb5\xdb\xce:\x93MLv\xb7\xd8M\xeaV\x94\x0d\x0c\xcd\x12\x9a\xe4K6\x99\xf9\xbe\xd9\xdc\x07\xf8\xf3\x1e\xb3\x00P\x00(\x00\x14\x00\xdc\x1cA^\xc5\x8c\x18\x5c<\xd7\xaf\x03\xa0\xe2\xd5\x16D\x00qt\xb2+\xbc\xd0\xe5\xe9\xbd3\x18gG\xa6V\xb2~\xb8\x8e\x9d\x80\xd4|gd\x04\x03\xfa\xed\xe4\xc0\x92bP\xe3\xa9m\xe8)\xa4\x17sVE\xfe\xc2\xb0\x1f\x15\xfdg\x05d\x0d \xd2\xb4JB^\x13\x83\x1b\x9a\x1a\xe7\xba\xaf\x10\x11qn\x86gZ\xf6\xe1*`\xa3\x04\x03\xe7:|\x0b5\x7f\xea\xde\xf53\x81\xb0\x02 \xe8N\xb5\xc4R\xbd\x1aA\xa4\xaeF\xa8\xd4\xa4\xa26\x15\xc4\x86\xb1\xffR\xfbs#p:\x17\xd6g \x0c\xa2\xe1\x05m]\xf5\xd8\xf5\xc5\xa3\xd5\xa0\xb4\x08\x15\x7fNL\xba\xf70H1$\xb2\xfa\x81\xcf\xae\xceZ\x99\xfa\xb5\x06@\x14L\xa9\x800t;\xc4\xe7\xc7\x1d\x9e\x04EL\x99\xb4_\xda\x12\xc8\xc8\xd6\xdcv`\x00\x0c\x84\xc4\x09V\x03\x8d\xbb:\x896\xaa\x06\xa2\xfb\xb992\xc1\xf0\xa4\x860\x0c\xc0J1\xf9~a\xa8o\x90|9\xc73\xf0\xf1\x02\xa9\x95T\x13\xae\xed\xaf0WvAJ\x82:\xd5\x10d\xdf\xd9\x89\xfd\xc0\xf6\x9e\xc7\x9aH\x9d\x8d\x5c\x17\xab\xfaK\xb2F~\x01\xc6\xbe\x00\x8dV;\x96\xdd\xd8\xf0\x97\xd9\xedM;\x9fkts\xc7\x93\x83v\x8c\x8b1\xcd\xf7Y,G\x9c&\xeb<\xe6\x13\xf4\xc5J\xcf\xfd\x1e\xe4\xeb\xc8\xe8w\xb6\xcc\xbf\xb2\xad\xe9L\xb6tJ\xcay\xa9\x94z\x98h70Q\xfe\xc6\xc5\x0fL\xcc\x89\x85v1\xcf\xb5\x0a\x89\x15\xe8\x87?\x12\xa8M%\x98\x1bu\x98\xa7Y\xacw@~\xda0#m\x11\xad\x82VF#\x1df\x0bmM.L\x17\xad\x89\xd6\xa3\xcb\x0b\xed\x1d\xed\x0d\xedU\xd6!aCi\xe2y\x94F\xf3/e\x95O\x08\xef\x09\x03\x8d\x1a\xcd\xa5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" -qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x06\x07\xa7(\x98\x00s\x00p\x00l\x00a\x00s\x00h\x00\x0a\x08\x94\x19\x07\x00s\x00p\x00l\x00a\x00s\x00h\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x0a\x0c\xad\x0f\x07\x00d\x00e\x00l\x00e\x00t\x00e\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x09\x0f8\xb4\xc7\x00k\x00e\x00y\x00_\x00E\x00.\x00p\x00n\x00g\x00\x0b\x0a*w\xe7\x00p\x00r\x00i\x00n\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x0c\x06\xeb\x91\xa7\x00z\x00o\x00o\x00m\x00_\x00o\x00u\x00t\x00.\x00p\x00n\x00g\x00\x09\x0fM\xb4\xc7\x00k\x00e\x00y\x00_\x00Z\x00.\x00p\x00n\x00g\x00\x0b\x05\x03\x9b'\x00z\x00o\x00o\x00m\x00_\x00i\x00n\x00.\x00p\x00n\x00g\x00\x09\x0fI\xb4\xc7\x00k\x00e\x00y\x00_\x00V\x00.\x00p\x00n\x00g\x00\x09\x0fE\xb4\xc7\x00k\x00e\x00y\x00_\x00R\x00.\x00p\x00n\x00g\x00\x09\x0fA\xb4\xc7\x00k\x00e\x00y\x00_\x00N\x00.\x00p\x00n\x00g\x00\x09\x03g\xbf\x9f\x00p\x00y\x00l\x00o\x00t\x00.\x00i\x00c\x00o\x00\x09\x0fJ\xb4\xc7\x00k\x00e\x00y\x00_\x00W\x00.\x00p\x00n\x00g\x00\x09\x0fF\xb4\xc7\x00k\x00e\x00y\x00_\x00S\x00.\x00p\x00n\x00g\x00\x0a\x033{\x87\x00z\x00o\x00o\x00m\x00_\x000\x00.\x00p\x00n\x00g\x00\x08\x00FX'\x00s\x00y\x00n\x00c\x00.\x00p\x00n\x00g\x00\x0a\x0c\xba\xf2|\x00i\x00n\x00d\x00e\x00x\x00.\x00h\x00t\x00m\x00l" -qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x14\x00\x00\x00\x05\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02&\x00\x00\x00\x00\x00\x01\x00\x02A\xd3\x00\x00\x02\x0c\x00\x00\x00\x00\x00\x01\x00\x02&&\x00\x00\x00z\x00\x00\x00\x00\x00\x01\x00\x00\xbc\xce\x00\x00\x01\xc4\x00\x00\x00\x00\x00\x01\x00\x01\xfc\xed\x00\x00\x01`\x00\x00\x00\x00\x00\x01\x00\x01\xb1\x5c\x00\x00\x01*\x00\x00\x00\x00\x00\x01\x00\x01\x85\xea\x00\x00\x01\x0e\x00\x00\x00\x00\x00\x01\x00\x01k\xcb\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x01I\xf1\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x01\x16\x10\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x01\x00\x01\x5cQ\x00\x00\x01\xac\x00\x00\x00\x00\x00\x01\x00\x01\xed\x13\x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x00\xad&\x00\x00\x00\xc4\x00\x00\x00\x00\x00\x01\x00\x019\x96\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x01\xdd-\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x01\x00\x02\x16D\x00\x00\x00J\x00\x00\x00\x00\x00\x01\x00\x00\x9e\x08\x00\x00\x00\xac\x00\x00\x00\x00\x00\x01\x00\x01)\xf3\x00\x00\x01|\x00\x00\x00\x00\x00\x01\x00\x01\xcd<\x00\x00\x01\xdc\x00\x00\x00\x00\x00\x01\x00\x02\x05\xaf\x00\x00\x01H\x00\x00\x00\x00\x00\x01\x00\x01\xa1\x99\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1a\x00\x00\x02<\x00\x00\x00\x00\x00\x01\x00\x02R\x81" +qt_resource_data = "\x00\x00\x9e\x04\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x02\x80\x00\x00\x01\x00\x08\x06\x00\x00\x00#\x95\xc7f\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x17\x12\x00\x00\x17\x12\x01g\x9f\xd2R\x00\x00\x00\x07tIME\x07\xdf\x07\x07\x090\x11\xbal\xcd}\x00\x00 \x00IDATx\xda\xec}w\x94\x5c\xe5y\xfe3w\xee\xf4\x99\x9d\xb2\xb3;\xdb\x9bV\xbb*+\xd1-\x0c\xa2\x83\xb0\x0d\x1cSbL\xe28v0v|lp\x0eq\x09\x04\x88c\x1cl\x9c\xe08\x80K\x82c\xe3\xb8!sL\x5c\xe4P\x02\x18\x04\x085TWm{/\xb3\xd3{\xbf\xf3\xfbC\xbf\xf7\xf3\x9d\xbb\xb3\xb3\xbb\xd2\xae\xb4\xda\xfd\x9est\xa4\x1d\xcd\xcc\xce|\xed}\xbe\xe7m\xaa|>\x9f\x07\x07\x07\x07\x07\x07\xc79\x8c|>\x8fT*\x85\xed\xdb\xb7#\x95J!\x97\xcb\xe1\xb6\xdbn\x83$I\x10\x04\x81\x0f\x10\x07\x87\x02|Wppppp\x9c\xf3P\xa9T\xc8\xe7\xf3\xc8\xe7\xf3P\xa9T\xc8\xe5r|P888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0\x0e-\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0X\xee\xe0* \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqppp\x02\xc8\xc1\xc1\xc1\xc1\xc1I \x07\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x9c\xfcqpp\x02\xc8\xc1\xc1\xc1\xc1\xc1\xc1\x09 \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x9c\x00rppppp,%P!h\x0e\x0e\x0eN\x009888888888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\xe3\x9c\x07w\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xc1\x01\x95J\xc5\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\xe5\x0aI\x92\xb8\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7J\x05W\xfe888\x01\xe4\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\xe0\x04\x90\x83\x83\x83\x83c\xb9A\xe9\xfe\x95$\x89\x0f\x0a\x07\x07'\x80\x1c\x1c\x1c\x1c\x1c\xcb\x9d\x00\xcaI\x1f\x8f\x07\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x10@\xe5\xcf<\x16\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\x02\xb9\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a \x7fr\xc5\x8f\x13@\x0e\x0eN\x0098888V\x08\x09\xe4\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15D\xfe\xe4\xa4\x8fg\x01spp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x08 A\xa5Rq\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95F\x029\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x05\x91?N\x00988\x01\xe4\xe0\xe0\xe0\xe0X!\x04\x90\xc7\x00rpp\x02\xc8\xc1\xc1\xc1\xc1\xb1\x02\x09 \x95\x82\xe1\x0a \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0cR\x9e+\x80\x1c\x1c\x9c\x00rpppp\xac(p\x05\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15F\xfc\xf2\x12'\x80\x1c\x1c\x9c\x00rpppp\xacH\x22\xc8\xc1\xc1\xc1\x09 \x07\x07\x07\x07\x07'\x80\x1c\x1c\x9c\x00\xf2!\xe0\xe0\xe0\xe0\xe0Xn\xe0\x9d@888\x01\xe4\xe0\xe0\xe0\xe0X\x81\xe0\x04\x90\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x86\x5c.\xc7\x07\x81\x83\x83\x13@\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eN\x0098888\x96-x+8\x0e\x0eN\x0098888V\x18x\x0c \x07\x07'\x80\x1c\x1c\x1c\x1c\x1c+\x0c\x5c\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83c\x85\x81+\x80\x1c\x1c\x9c\x00rpppp\xac \xf0:\x80\x1c\x1c\x9c\x00rpppp\xac@p\x02\xc8\xc1\xc1\x09 \x07\x07\x07\x07\xc7\x0a\x03\x8f\x01\xe4\xe0\xe0\x04\x90\x83\x83\x83\x83\x83\x13@\x0e\x0e\x0eN\x0098888\x963\xb8\x0b\x98\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x15\x04\x95J\x85L&\xc3\x07\x82\x83\x83\x13@\x0e\x0e\x0e\x0e\x8e\x95\x04\xde\x0b\x98\x83cf\x88\x8b\xf5\xc6$\xbds\x09~\xf1o\xb9\xf2\xbf\xf3\xf9<\xfb7\x07\xc7b\xedm\xf9:+\xb5\xc7\xe9y\xf4\xb7r\xbdrpp\xfc\x09\x14\xb3\xc8K\xd8,\xdf\xb3S~n\xe6\xf3y\xa8\xd5\xeai\xf6\xfc\x9c$\x80\xf9|\x1e\xd9l\x16*\x95\x0a\x92$A\x92\xa4i\xc6\x82c\xe1\x17\x92\xdc\xb0\x0a\x82P\xf0X\xb1\xe7pp\x9c*\xe9\xa3}M\xc6\xaa\xd4\xfe\xce\xe7\xf3\xc8\xe5r\x10\x04\x81\xad?A\x10\xa0V\xab\xa1R\xa9\x0a\x0e>\x0e\x8e\x85\x14\x1e\x94\x84j\xa9\x93>I\x92\x98ZI{\x8a\xdb\xcd\xe5\xb76%I\x82J\xa5*8\x17\xe9L\x14\x04\x81\xfd9\xe7\x08`6\x9bE6\x9b\x85$I\xc8d2H\xa5R\xecg2\x14\x1c\x0b\x07Z\xb8$IB:\x9d\x86$Il\x7fg\xb3Y\xf6\xff\xc5@\x97\xc1|>\x0f\xadV\x0b\xbd^\x0f\xbd^\x0f\xadV\x0b\x00\x9c\x04r,\x0a\x01\xa4\xbf\x97\xba\x0b8\x9b\xcd\x02\x002\x99\x0c\xb2\xd9,2\x99\x0c\xd2\xe94r\xb9\x5c\xc1e\x8bcy\x81\xe6W\xa3\xd1@\x14E\x98\xcdfh\xb5Zh\xb5\xda3J\xfc\xc5\x85\xfa2\xf4'\x1e\x8f#\x1c\x0e#\x1c\x0e#\x16\x8b!\x99L\x22\x97\xcbq\x02\xb8\xc0 \x05EN\xfch1i\xb5Zh4\x1ah4\x1ah\xb5Z\xf6\x98(\x8aP\xab\xd5\x10E\x11*\x95\x0a\x1a\x8d\xa6@1\xe4\xe0(\xa5R\xa4\xd3i\xa4\xd3i\xb6\xbf#\x91\x08\x92\xc9$\xbb\xe8\x15\x83Z\xadfF\xd8h4\xc2n\xb7\xa3\xbc\xbc\x1cv\xbb\x1d\x1a\x8d\x86\x0f,\xc7\xa2\x13\xac\x5c.\xb7\xa4/\x1a\x99L\x06\x89D\x02\x89D\x02\xa1P\x08\xe1p\x18\x89D\x02\x99L\x86\x13\xc0eL\x00\xb3\xd9,DQ\x84\xd5jEyy9\x9cN',\x16\x0b4\x1a\xcd\x19[\xaf\xe2B~\xa1X,\x86`0\x88\xee\xeenx\xbd^\xf6E\xe4j\x15\xc7\xc2\x19\xe4h4\xca\x16\x92\x5ce\x15E\x11\x06\x83\x01&\x93\x09v\xbb\x1dF\xa3\x11F\xa3\x11z\xbd\x1e:\x9d\x0ez\xbd\x9e\x91C\xadV\x0bA\x10\x18)\xe4\xf3\xc4QLU\xc9\xe5rH\xa7\xd3\x08\x06\x83\xe8\xeb\xeb\xc3\xe8\xe8(\xbbP\x90\xb2\x5c\x0c\xf1x\x1c{\xf7\xeeE,\x16Css3\xd6\xaf_\x8f\x96\x96\x16\x08\x82\x00\xbb\xdd^\xf2\xb5\x1c\x1c\xa7z9\x96\x9f\x93K\x95\x00\x92r\x9eN\xa7\x11\x8dF1>>\x8e\xae\xae.\xe6\xa5!\xdb\xc9\xb1\xfc\xce\xd2\xfd\xfb\xf7cxx\x18v\xbb\x1d\xabW\xaf\xc6\xfa\xf5\xeb!\x08\x02\x13e\xce\x19\x02H_(\x9b\xcd\x22\x16\x8badd\x04\x95\x95\x95\xb8\xfa\xea\xab\x0bT*\x8e\x85_Hr\xf7\x1b\x11\xc0L&\x83\x89\x89\x09\xc4\xe3qLNN\xe2\xd8\xb1c\xc8f\xb30\x1a\x8dp:\x9d\xb0\xdb\xed0\x18\x0c\xd0\xeb\xf50\x99L0\x9b\xcd0\x18\x0c\xd0\xe9t\xd0h4|\xce8f\xdc\xe3\xf1x\x1c\x13\x13\x13p8\x1c\xd8\xb4i\x13[+\xa5\x0e\xabl6\x8b\x1bo\xbc\x11\xdb\xb7o\xc7\x0f~\xf0\x03\x08\x82\x00\xadV\x0b\x9b\xcd\x06\x8b\xc5\xc2\x14i\x0e\x8e\x85Z\xab\xca\x8b\xf2RU\xd1\xc8\x0d\x98L&\x11\x0c\x06\x11\x0e\x87q\xe7\x9dw2e\x88\x0b'\xcbw\x8dn\xd9\xb2\x05G\x8e\x1c\xc1\xd3O?\x8d#G\x8e \x16\x8b\xa1\xac\xac\x0cF\xa3\x11:\x9d\xee\x8c\xb9\x81O\xfb\xe4\xa5\xa0\xc6t:\x8dx<\x8e\xa9\xa9)\xe8\xf5z\x98\xcdf\xee\xf6]dP\x1c\x95r\x9c+++\x0b~\x0e\x06\x83p\xbb\xdd\xd8\xbf\x7f?\xba\xba\xba \x08\x02\x0c\x06\x03l6\x1b\x1c\x0e\x07\xacV+\xca\xca\xca`6\x9ba4\x1a\xd9\xe2#U\x90\x83\x1fX\x14\xd7\xeb\xf3\xf9PYY\x09\x8b\xc52\xeb\xfe\xa6\xb8\xbf\xae\xae.<\xf7\xdcs\xe8\xe9\xe9\xc1\xaaU\xabP__\x8fh4\x0a\xbb\xdd\xce\xd60\x07\xc7b`\xa9\xc7\xd1e\xb3Y$\x93I\x84\xc3a\xf8\xfd~\x18\x0c\x06fS9\x96'T*\x15\xa6\xa6\xa6\xf0\x9b\xdf\xfc\x06===\x98\x9a\x9a\x82\xdb\xedF0\x18\x84\xcb\xe5bb\xce9A\x00\x05A@:\x9dF2\x99D,\x16C\x22\x91\x80N\xa7c_\x94\xe3\xcc,\xa8b\xb7`\xca$\xb3\xd9l\xb0\xd9lhooG>\x9fGgg'zzz\xb0o\xdf>\x08\x82\x80\xd6\xd6VTTT\xc0\xe9t\xc2\xe1p\xb0\x9b\x08\xbd7w\xd3\xadl\xe4r9d2\x19\xc4\xe3q\xc4b1\xa4\xd3\xe9Y\xf7w>\x9f\x87 \x08\x18\x1d\x1d\xc57\xbf\xf9M\x9c8q\x02\x1e\x8f\x07\x91H\x04\xf1x\x1c\x99L\xa6d\xf2\x08\x07\xc7\xa9^V\x94?/U!\x82\x12&\x13\x89\x04\xdb[\xb4\xaf\xf8y\xbb<\xd7\xa5J\xa5B8\x1c\xc6/~\xf1\x0b\xec\xdc\xb9\x13###,|.\x1e\x8f\xb3\x98\xd53\x85\x05q\x01\x93\x8b\x88\xbe\xc4\x5c\x82\xbb\xb9:\xb8p\xe4Oy[\x98\xa9\xd6\x1a=o\xe3\xc6\x8d\xd8\xb8q#6o\xde\x8c\xb7\xdez\x0b/\xbe\xf8\x22\xea\xea\xea\xd0\xda\xda\x0a\x97\xcb\x85\xaa\xaa*8\x9dN\xe6\x1e\xa6\x0cb\xee\x16^\xb9kL\x92$\xc4\xe3qD\xa3\xd1Y\x89\x1f\x91\xbf}\xfb\xf6\xe1\xa7?\xfd)\x0e\x1e<\x08\xaf\xd7\x8bL&\xc3\xfePl\x167t\x1c\x8bmx\x97\xaa\xad!\x17o&\x93A \x10(i7\x17\xab\xae\xee|\xf6\xdfB\xfe\xee\x95\xb4\xef\xe5\x84\xde\xeb\xf5\xe2\xfb\xdf\xff>\xde|\xf3M\xf4\xf7\xf7#\x16\x8bA\xadV\xb3\x90\xae3\x9d0\xbb \x04\x90\x0cD4\x1a\xc5\xd8\xd8\x18\xbc^/_\x00gx\x81\x9d\xca\xf3\x5c.\x17\xee\xb8\xe3\x0e\xdcz\xeb\xad\xf8\xe7\x7f\xfeg\xbc\xf2\xca+hkkCss3\x9a\x9b\x9bQUU\x05\x87\xc3\x01\xb3\xd9\xcc\xe6\x9a\x97\xedX\xb9F4\x99Lbjj\x0aUUU%\xd7X<\x1e\xc7[o\xbd\x85\x17^x\x01\x87\x0f\x1f\x86\xdf\xefG<\x1e\x9fv\x18\xf23\x80c\xb1\x94\x16\xc2Rv\x01+\xe3\xe7\xa7\xa6\xa6f=\xbb\x17\xf2\xbb\x9c\xa9\xb1Q\xce\x89\xbc4\xd4\xd9\xba\xcc\x9e\xa95\xa1R\xa9\x90L&\x11\x8dF\xd1\xdd\xdd\x8d\xd7_\x7f\x1dG\x8f\x1eEWW\x17b\xb1\x18KP\x22q\xe5L\x8b,\xe2B.\xa6\x5c.\x87H$R\x92\x00F\x22\x11\xe4\xf3yLNN2\xe6;\xd3\xfbQ\x8d\xa4RFi)\xdd\xeeN\xf5\xf3\x9c\xcakT*\x15R\xa9\x14\xaa\xaa\xaa\x10\x0e\x87a\xb5Z!I\x12\xca\xca\xca\x90\xcb\xe5`6\x9b\xa1R\xa9\xa0\xd5jg,\xd2+7\xc4_\xfb\xda\xd7\xb0{\xf7n\xfc\xe4'?A\x7f\x7f?.\xb8\xe0\x02\xb4\xb6\xb6\xa2\xbe\xbe\x1e.\x97\x0b6\x9b\x8d\x05\xebs\x12\xb8\xf2@\xfb;\x1e\x8f\xc3\xe7\xf3\x15}N.\x97\xc3\xc0\xc0\x00^y\xe5\x15\xec\xd9\xb3\x07'N\x9c\xc0\xc8\xc8\x08b\xb1\x18;p)\xcb\x8d\x07\xb8s\xccv\x86.\xc4\x85s\xa9\x87\x19\x90\x1b8\x1e\x8fc||\xbc\xe8\xf9\x1c\x8b\xc50<<\x0c\xb7\xdb\x8d\xde\xde\xde\x19\xc9\xd3\x5c\xec\x88\xdc\x15y*\x9f\xf5T^W,\xc9\xebTm\xe5b\xb8H\xe7\xf3YJ=\xaf\x98\xc7M\x10\x04D\xa3Q\x04\x02\x01\x8c\x8f\x8fcdd\x04\x83\x83\x83\x88F\xa3,\x0c\x86\x88\xdf\xd9h\xda .\xd4\x00\x92\x01\x88\xc7\xe3,FH\x8e\xd1\xd1Q\xf4\xf7\xf7\xa3\xbb\xbb\x1b\xf1x\x1c\x1e\x8f\xa7(\xdb\xa5\x85\x9fJ\xa5\x90N\xa7Y\x11c\xe5\x00+[K-$\xb9:]\xe26\xd7\x00\xce\xf9\xc8\xfa\xb4\xf9\xe4\xef-\xaf\x01HcAn[\x8b\xc5\x82\xf2\xf2r\x18\x8dF\x94\x95\x95\xa1\xac\xac\x0cv\xbb\x1dv\xbb\x1d:\x9d\x8e\x1d\xac\xf4\x1e\xd9l\x16\x9b6m\xc2\xea\xd5\xab\xf1\xf4\xd3O\xe3\xc5\x17_\xc4\xfb\xdf\xff~\x04\x83A\xa4\xd3id\xb3Y\xf6ZN\x02W.\xb2\xd9,\x82\xc1`\x01\xe9K\xa7\xd3\x98\x9a\x9a\xc2\xfe\xfd\xfb\xb1\x7f\xff~\xf4\xf6\xf6bhh\x08n\xb7\x1b\x89D\xa2\xc0\xad!\xdf\xb3<\x0c\x84C\xde\x05\x83J\x9f(\xcf\xceP(\x04\xb7\xdb\x8d@ P\xd0UF\xaf\xd7\xa3\xbd\xbd\xbd\x80`\xccG\x01<\x1d2\xb4P \xfb\x96\xc9d\x0ab\x00\x01\xc0\xef\xf7\xa3\xbf\xbf\x1f;v\xec@\x7f\x7f?\xc2\xe10\x82\xc1`A\xfd\xd6S\xb5u\x0bE\x8a\xe5cX\xec\xf7SR\x8b\xb2\xc5\x9d\xb2\x1d\x9arLf\xfa^D\x98\x8b\xa9\x8a\xa5~.5N\x0b\xd5v\xaf\xd8{\xe4r9$\x12\x09V?\xd5\xe7\xf31\x8e$O\xf6\xa0j\x0ag\xfaL\x5c\xd0\xfa\x0b\xc4\xce'&&\xd8\x97\x0f\x06\x83\xe8\xe9\xe9\xc1\xc1\x83\x071::\x0a\x9f\xcf\x87d2\x09\xaf\xd7[@\xee\x94\x93F\x93\x12\x8f\xc7\xd9\x82 \xb9\x5c.\x87'\x93\xc9\x05'p\x8bI\x0eO\xf5\x90\xa0X<\xe5\xe3t\xf8i4\x1a\xe8t:\x18\x0c\x06$\x12\x098\x1c\x0eH\x92\x04\xb3\xd9\x0c\x93\xc9\xc4J\xc0TVV\xc2f\xb3\xc1n\xb7\xa3\xa2\xa2\x02&\x93\x09\x06\x83\x01\x82 \x97\xcb!\x95J\xe1\xc6\x1bo\x84\xc5b\xc1\x0b/\xbcPP\xec7\x97\xcb\xc1\xe1p\xc0h4\xb2R\x05\x1c+\x03tI\xc8f\xb3\xf0z\xbdH&\x93\xd0\xe9tH&\x93\xe8\xec\xec\xc4\xee\xdd\xbb\xd1\xdb\xdb\x8b\x91\x91\x11LNN\xc2\xe7\xf3!\x12\x89\xb0RE\xb4v\xc9\xc0s\xf2\xc7\x89\x1f)$\xa2(B\x14Ex<\x1e\xbc\xfd\xf6\xdbx\xeb\xad\xb7\xf0\xf6\xdboc``\xa0 ^T\xbefr\xb9\x1cZ[[\xf1\xd6[o\xc1\xe9t\xce\xe8:+\xe5j\xccI9\xa8p\xf6[\x12\x92]\x8bF\xa3\xe8\xec\xec\x84 \x08\x18\x1f\x1fGoo/\x06\x06\x06\xd0\xdd\xdd\x8d@ \x80d2\x89D\x22\x01\xe0d\xf1\xe8\xb3I\x5c\xf3\xf9<#\xecr\xb5\x8bHL>\x9f\x87\xc9d\x9aF\xc0\x95s\xa1\xd5j\x0b\xc6\x9fBMJ\xa9k\xc5\xbc\x834\x1er\x01\xe9L\x9d1\xf4{\x8b\xfd>\xba\xacd\xb3YV\xec;\x95JM[\xcf\xf2\x8bM\xb1\xb8\xfe%O\x00I\xae\xcf\xe7\xf3\xac\x04L:\x9df$.\x9dN#\x16\x8b\xc1\xe7\xf3\xb1\xfaG\xca~\x87$\x8bR\xab\xa9L&\x03\x8dF\xc3\xda\xe3H\x92\x04A\x10\x90J\xa5\xd8k\xa8v\xddb\xa8t\xa7\xb3\x80\x16r\xf2h,\x09T`\x9bz\x0aR\xc9\x16Q\x14!I\x12L&\x13<\x1e\x0f4\x1a\x0d\xab+\xd4\xd3\xd3\x03\x87\xc3\x01\xbd^\x8f\xf2\xf2r\xd4\xd7\xd7\xc3j\xb5\xa2\xb6\xb6\x16N\xa7\x13\xa2(\xb2[h:\x9d\x86F\xa3\xc1\xc5\x17_\x8c\xbd{\xf7\x16\x14\x9c&\xc5P\xa7\xd3-\xf9\x0a\xfb\x1c\x0b{\xc0\xc9\x0f)\xba|%\x12\x09$\x93I\xa6\x12+[X)\x0fF\xa5r\xcf\x13\x8bV\x06\x94\xe7m,\x16\x83\xc7\xe3\xc1;\xef\xbc\x83_\xfd\xeaWx\xe9\xa5\x97\x0a\xd6\x09u6\x92\xaf!\xe5:\x9a\xcb\x99-\xef\xb1+\x7fN\x22\x91\xc0\x1f\xff\xf8G\xd4\xd4\xd4\xa0\xa3\xa3cV;\xb2\xd8\xe3\x92\xcb\xe5\x10\x8dF\xf1\xf2\xcb/#\x16\x8b!\x93\xc9\xa0\xaf\xaf\x0f\x1e\x8f\x07^\xaf\x97u\x06\xc9d2KfNi\xaf\x13\x91#\x11\x81:R%\x12\x09&\x14P\xe7)R\x04\xe9\x5c\xa1\xccW\xf9x(\xc3\x96H\x80\x90\x17\xa4\xa7\x0eC\xc4\x17\xe8w\x93\xa7\xecL\xf7\xd4-\xc5\x15\xe8<$\x1e\x93\xc9d\x0a\xfa>\xcb/\xc6g\x1a\x0bF\x00\xc9(\x00@\x7f\x7f?\xf6\xec\xd9\x03I\x92\xe0\xf3\xf9\xd0\xdf\xdf\x8f\xfe\xfe~LNN\x22\x1c\x0e#\x9b\xcd\xb2\xa0pb\xef\xc4\xe0\x05A`\x841\x97\xcb\xb1Vr\xc4\xa2\xcf\x04\xab_\xca\xea\x04\xa9n\xb4\x80\xa8`(\x19g\xb5Z\x8dX,\x06\x95J\xc5\x5c\xbd\x1a\x8d\x06\x16\x8b\x05\x81@\x00eee\x98\x9c\x9cD__\x1f\xecv;\x1c\x0e\x07\x0c\x06\x03V\xaf^\xcdnc\xe1p\x18\xa2(B\xaf\xd7\xa3\xb5\xb5\x15G\x8f\x1ee\xad\xfeh\xa1\x92;\x98\xc7r\xad\x1c\xd0\x9as\xbb\xdd8r\xe4\x08\x04A\xc0\xd4\xd4\x14\x06\x07\x071<<\x8c\xf1\xf1q\xf8|>D\xa3\xd1\x02\xd5X\x0ey\xb8\xc2R\xdfk\x1c\x0b\xb3fH-\x02\x80\xd7_\x7f\x1d\xdb\xb6m\xc3\xee\xdd\xbb\xb1k\xd7\xae\x82K-\x11\x05\xf9e\xa2\x14\xc6\xc6\xc6fue\x16S\xa0T*\x15N\x9c8\x81t:\x8d\xf1\xf1q477\xc3n\xb7\x9fUrL\xf5\x00\xdf|\xf3M\x18\x0c\x06\xa6\xa2\xc7b1\xa4R)\xa4R\xa9\xb3\x9681\x1b\x04A`\xaa\x1d\xa9\x80\x94\xd8\xa0\xd5j\x19\x09$\xe2M\x82\x05\x915\xa5\xfd \x92K]R\xe89\xd4\x8aR\x9e<#\x9fc\xb9r\xb6XY\xd3\xf3\x15|\xe8\xf7\xcb\x9b5\x14\x9b\xc7\xb3U\xfaG\x5c\x8cA\x10E\x91)D\xd4aB\xde\xda\x86\x9e\xa7V\xab\x11\x8f\xc7\xd9\xe4\x91\x9f\x0f\xe5\xe5\xe5\xf0\xf9|0\x9b\xcd\xa8\xa8\xa8\x00\x00x<\x1ev\x135\x9b\xcd\xe8\xec\xecd]\x1bh.m6\x1b\xdb\xcc\x1c+\x07t\xa9 %\x98\x0es\xba\x8c\xc8\xd7\xecL5*9\x96\xbf\xe2\x97\xcdf\x99\x8a\xf7\xf4\xd3O\xe3{\xdf\xfb\x1e\x06\x07\x07\x91J\xa5\xd8\xda\xa0\xb3C\xaenY\xadV\x5cs\xcd5hkkCCC\x03\xfbc\xb5Z\x0b\xde_\xa3\xd1\xa0\xa2\xa2\xa2\xc0\x16\xccu}%\x93I\xf6\xbaR\xc9\x86gb\x9c\x88\x10\xe4r9\xf4\xf5\xf5!\x99L\x22\x95J!\x99L2\xb5\x88D\x10\xb9z\xb6\x14m\x14\x8d)\x9d\x11\xe9t\x1a:\x9d\x0e\xd9l\x96\xf5\xa8'\x8f\x16u\xbc(f\xd3\x88\xf0\xd2\xbf3\x99\x0c\xb2\xd9,\xe3\x04t\xb1P\xd6\xbc\x95\x7f\x9e\xa5r\xd6(\x95k%9%\xe1\xe6L\xb9}\x17\x8d\x00\xcewP\x22\x91\x08\x93ri\xc1\xd3m\x87\xfc\xe4rW\x92\xfc\xef3Y,q\xa9\x93Be\xf6\x10-(\x22\xe3Z\xad\x96\xf5\x07\xa6q\xa6\x9f\xa9\x83\x8b\xc9d\x82\xcb\xe5B(\x14b\xad\xe2\xd4j5\x8cF#\xdb|\x07\x0f\x1edq\x86\xd4K\x98nv\x5c\x05\xe4\x06\x9f\x83C~&\x85\xc3a<\xf5\xd4Sx\xf4\xd1G\xd9Y\xa44\x86z\xbd\x1e\x95\x95\x95\xb8\xf3\xce;q\xc3\x0d7\xe0\xf2\xcb/\x9f\xd6\x1d\x86\xc8Q\xb15\xb6\x10Y\xc2\x14^s6\xce1y\x22\x9f \x08\x18\x1b\x1b\x83Z\xadf\x1e0y\xa8\xd4R\xdc\xefJ;$'\xaa\xe4\x0e&\xb2N\xa4\x8cD\x84L&3\xcdU+O\xf2 \xc50\x9b\xcd2AH9&\xa7\x9b\x88y6\xc6l\xa6X\xfe\xb3\xa1\x02\x8a\x8b\xb5\xf9U*\x15\xeb\x0c\x92L&\xd9d\xc9\xcbI\xe8t:\xa4\xd3\xe9\x82\xe0H\x8au(\x15GT,\x93h\xa5\xa9\x7f\xf2\x9b\xa0|\x1c\xe4A\xa7\x14\x17\x91\xc9d \x8a\x22\x0b\xdc\xcff\xb3\xcc}\xeb\xf5z\xa1\xd7\xeb\xd9\x1cP\x1b\xbfd2\x09\x8dF\x83p8\xcc$\xfd`0\x88}\xfb\xf6\x15\xf4\x10\xa6x\x0d\xde6n\xf9\x13f\xf2\x18\x8b\x5c.\xc7\x08!\x05\xd2\x92{\x97\xdc\xec\x14\x9bID\x906\xb3Z\xadF&\x93\x81Z\xad\xc6\xd0\xd0\x10\x8e\x1e=\xcaJ\xcb\x18\x8dFF\xfe\xb8+\x98+\x7f\x1c+\x0ft~h\xb5Z<\xf2\xc8#\xf8\xe1\x0f\x7f\x08\xb7\xdb\x0d\x00,\x06L\x92$\xacY\xb3\x06\x8f<\xf2\x086o\xde\x8c\x86\x86\x86\xa2kh\xb1\xcf\x10eAe\x95J\x05)/\xcd\xba\xc6\xe5\xee\xec\x85T\xff\xc8\xcbb\xb5ZY\xf8\x8d\xd2\xce\xcd&\x06\x9cm\x05\xb0X\xfb=\xa5r)\x0f\xfb\x22\xc5\x8b\xc2Fh.\x94\xe4\x8f\x04!\xb2M\xc5\xde\x93\x88\xe5\xb9$>(\xe7\xd4`0\xc0f\xb3\xc1h4N\xcb\x8a>\xa7\x08\xa0\xbc\xae\x8d\xdb\xedFUU\x15\xe2\xf18K@ \xd7$\x11\x0br\xf5\xc6b\xb1\x82x\x07\xb9\xaa\xa0\xcc>,\xa64\xae$\x05p\xa6\x1aF\xc5T\x18\xf9\xd8\x90\x22H\xb7(\x9dN\xc7\xe4v\x8b\xc5\x02\x95J\x85H$\x02\xbd^\xcfn\xc5\x92$\xb1\x22\xbe\xf2\xf2\x1d\x9d\x9d\x9dp:\x9d\xacw0\x91@\xee\x0a\xe6d\x90c\xe5\x81\x5c\x97\xd7]w\x1d\x06\x06\x06\x0a\x5cz\xe9t\x1a\x97]v\x19\xbe\xff\xfd\xef\xe3\xbc\xf3\xce\x9b\xb1T\xc6\x99\x5c\x83rwr>\x9fG^*}\xc9>r\xe4\x08\xc6\xc6\xc6\xb0a\xc3\x06\xd4\xd6\xd6.\xd89G\x99\xb1F\xa3\x11.\x97\x0b\xf5\xf5\xf5\x05\x04j&\x22\xb8\x14I\x8c2\x16Oi\x93\xc8\x0bEv%\x95J\xb1\xd2f\xa4\xd6\x91:H\xc9\x1f\x14\x06&O\x08*V6\xee\x5c%\x7f\x04\xb5Z\x0d\x97\xcb\x05\x8b\xc5rn\x13@\x22\x81\x82 \xa0\xa1\xa1\x81\x05\xd7\xca\xbf4e\xf1\xa4R)\xa6\x14\x92\x8f_\x1e\xf3\x07\x00z\xbd\x1e\xf5\xf5\xf5X\xb5j\x15+[B\x81\xe6\xf2E\xb7\x9cI\x87|<\xe8\xdf\xd9l\x16\xa1P\x88e[\xd2f\x19\x1d\x1dE4\x1aE8\x1cf\x8f\xd1\xa6Q\xb6\xdf\xa1C\x862\xb8r\xb9\x1cL&\x13{\x1e5)'\xd7\xb1^\xafg\x85J\x83\xc1 \x0e\x1f>\x8c\xea\xeajTWW\xc3b\xb1\xb0\x98A^\x16fe+A\x8be\xd89\x96\x16\xc8\xdd\x9bJ\xa5\xf0\x9d\xef|\x07\x0f>\xf8 \x04A`e\xbb\x00\xe0\xaa\xab\xae\xc2?\xfc\xc3?`\xcb\x96-,\xd6\xeel\x94[Q\xaeKe\xd8B)\x050\x99Lb||\x9c\x950\xab\xad\xad]P2 \x08\x02\xccf3jkkq\xe9\xa5\x97\xe2\xa7?\xfd)\xc6\xc7\xc7\x0bB\xa1\xe4b\xc7R =r\xd7\xab<\xb35\x93\xc9 \x99Lbxx\x18\x03\x03\x03\x18\x19\x19A$\x12a\x17\x82L&\xc3\x1a\x09P9\x18\x22\x85\xc0\xc9Lp*\x1cO|\x80\x94?9\xf9\xd3h4\x8c\x17\xd4\xd5\xd5\xa1\xbc\xbc\x9c%\xa5\x15k\x16q\xb6/\xc5\xca\x905\xb9\xdb\xdfn\xb7\xa3\xaa\xaa\x0av\xbb\x1dV\xab\x95y\xde\xcei\x17\xb0\xb2\xb1\xb1\xcdf\xc3\xc0\xc0\x00#\x17\x82 0\xb7/\xc9\xbdJ\xe5\xcf\xe5ra\xf3\xe6\xcdX\xb7n\x1d\x9cN'\x1b\x1cr7\xcao\x99+A\x01T\xc6\xaa\xd0x\x11y\xa6\x04\x9ah4\x0a\xb7\xdb\x8d`0\xc8Z\xcf\xf4\xf5\xf5\x15,D\xb9\x9cN\x8a \x118yk\x1aJ\xceQ\xab\xd5,k\x8e2\x81\xfb\xfa\xfa\xd0\xd5\xd5\x85\x9a\x9a\x1a8\x9dN\x94\x95\x95\xb1ZP\xdc\xf0\xaf\x1c\x92W\x8a\xf0qEpy\x93\xbf\xa3G\x8f\xe2\xe3\x1f\xff8\x0e\x1c8p\xd2\x98\x88\x22\xd2\xe94\xd6\xacY\x83'\x9f|\x127\xdcpCA\xf8\x892\xb9c1\x8cm1\xa3\xaf\xfcY^/\x0e\xc0\x8c\x0a\xa0\xbc$\x8d \x08\xec\x02\xbc\x90g\xbbZ\xadf\x09xZ\xad\x16eee\xa8\xac\xacd\xd51\x8ay\xc0\x96\xc2\xbeR\xc6\xda\x11YK\xa7\xd3X\xb7n\x1d&''\xd1\xdd\xdd\x8d\xfd\xfb\xf7\xa3\xbf\xbf\x9f}\x07\xaa\xe8\x91\xcdf\x99`@\xff&\xce@\xb1\x7fD\x80\xe5j\xa2\xcdf\xc3\xd5W_\x8d\x8e\x8e\x0eTVV\xc2j\xb5\xb2J#d\xb7\x96\xd2\x19\xa4\x8c\xcf\x97\xab~T-\xc5j\xb5\x16\x10\xc0sZ\x01\x94\x7fqb\xf8\xb4\x89\x08\xf2\xac\x1e\xba9\xc8\x0d\x8b\xddn\xc7\x07>\xf0\x01\xac[\xb7\x0e---p\xb9\x5c\x05\x04C\x9e\xf6\x7f\xb6\xea\xe7\x9c\xe9\x03\x97\xc6\x94\xfe-'p\x14\xcfG1\x13\xd1h\x14\xd1h\x14\xc1`\x10>\x9f\x0f\x13\x13\x138|\xf80:;;Y\x11m\xda\xb4\x82 \xb0\xc2\x9a\xc9d\xb2\xe0\xa6I%d\xe8\x00\x94\xbb\x81s\xb9\x1c\x0e\x1f>\xcc\xfa\x05;\x9dN\xe6\x0a\xe6*\xe0\xf2\xc5\xd9l\xe4\xceq\xf6A1\xc1/\xbf\xfc2\xfe\xec\xcf\xfe\x8c\x91\x22:\xe7\xef\xbf\xff~<\xf1\xc4\x13\x05\x86\xf8L\x9f\x07\xb3%&)\xeb\xc9\x96Z\xcbD\x16\x17\x83\x00\xd2\xb8Q\xd7\x0c\xadV\x0b\xab\xd5\xcaD\x11%i\x90\x17d_*kAN\x02\xc9\xae\x93\x08A\x84&\x9f\xcfctt\x94\xa9w\xe4\xe6\xa4\xf8P\xbd^\xcf:w\x10\x1f\x90\x8b\x14\xf4\xddM&\x13n\xbf\xfdv\xb4\xb7\xb7\xa3\xb9\xb9\x99\xb9M\xa9\xde\xadR%]*c\xa4\xbc\x84\xd0\xe5\x83l%%\x80P\x99\x9c39\xbf\x8b\x1aqK\x0bV\xde\xdaE\xb9`\xc8%,\x1f\xa4\xcd\x9b7c\xcd\x9a5X\xbf~=\x93x\x0d\x06\x03\x0b\x10V\xca\xe0\xcb\xd5\x18\xcd\x14`+\x1f/\xda(tsR\x12A\x9f\xcf\x07\x8f\xc7\x83\xa6\xa6&\x9cw\xdeyx\xe7\x9dw\xd0\xd7\xd7\xc7Z\xea\x91\xbb\x86\x12v\xe8\xbd\x89\x94S\x89\x04y\x0b>\xfa{jj\x0a===hllDuu5/\x0e\xcd\xc1\xb1\x8c\x89\x1f\x19\xd7\x1f\xfc\xe0\x07\xf8\xdc\xe7>W@\xec\x9a\x9b\x9b\xf1\xb3\x9f\xfd\x0c\x9b6mb\xc1\xfcg\x03\x92$\xa1\xaf\xaf\x0f555\x05IjJ\xdbS\xaa=Y\xb1\xf3V\x10\x04\xd6\x86m!]\x8bt\xd1&\x85\x94b\xb3g\x22\x0f\xf2\xdf\x7f\xb6\xec\x9e\xd2\xcd*\xff\x9b*|P\xd7)I\x92\x10\x0a\x85\xe0\xf5z\x0b\x08\x9a^\xaf\x07\x00\x18\x8d\xc6\x82\xf6v\xca,_\xb9\xbd\xdb\xb2e\x0bZZZ\xb0f\xcd\x1a455\xb1\xdfA\xea\xdfR\xb49J\x05P\xe9\x0a&[)o\xe8p&!.\xf6\xa1Q,iA\x1e\xd8)\x7f\x8e$I\xa8\xae\xae\xc6\xda\xb5k\xd1\xd8\xd8\x88\xba\xba:TUU\xc1b\xb1\xb0\xf6g\xf2\xc5\xb6\x12\xdaH\xcd\xa5\x06\x94\xfc@\xa3MDD\xb0\xba\xba\x1a\xc1`\x10\x1e\x8f\x07UUU\xa8\xae\xae\xc6\xae]\xbb\xb0w\xef^\x84B!6\x1f\xf4|Z\x94\xb4A\xe5E*S\xa9T\x81\x0b>\x16\x8b\xa1\xbf\xbf\x1f\xa3\xa3\xa3hjj\x82\xcb\xe5\x82\xc9d\xe2\xb1\x80+\x84\x10p\xac\x1c\x90\xd1\xfa\xd4\xa7>\x85\x1f\xff\xf8\xc7\xac\x96[.\x97\xc3=\xf7\xdc\x83g\x9ey\x86]H\xcf\x06\xf9#\x92v\xe4\xc8\x11<\xf0\xc0\x03\xa8\xab\xabCmm-V\xb5\xb4\xe0\xaf?\xf5)\x5c\x7f\xddu\x8c\xfc\xc9E\x07\x22-\xb3\xa9\x89\xe4)Y\xac\xb1%5H\xab\xd5\x16\xcdt]\xaa(\x16_Iud\xa9\x8e\xdf\xf8\xf88\xea\xea\xea055\x05\xa3\xd1XP9\x82TARe\x95\x8d\x1f\x08\xabW\xafFSS\x13\x9a\x9b\x9b\xd1\xd8\xd8\x88\xca\xcaJ\xa6\xfe)\xc3\xc1\xce\x15^\xa0$\x83g\x03\xe2\x99\xfe\xc2\xa4\x08*\xb3Wi\x00\xd6\xae]\x0b\xbb\xdd\x8e\xca\xcaJ\xd8l6\x98\xcd\xe6\x82\xfa8+\xf5\xe0-\x059\xd9\xd2j\xb5\x8c\x10f\xb3YX,\x16X\xadV\xd8l6\x94\x97\x97\xc3f\xb3A\xab\xd5\xa2\xbc\xbc\x1c\xff\xf7\x7f\xffW@\x02\xd3\xe94+\xd0I2}>\x9f/\x88\xd1\xa4\xc3\x8a\xc8\xe0\xe0\xe0 \xc6\xc7\xc7\xe1v\xbbY\xa2\x8e\x9c\xacs,_,\x85:[\x1cgf\x9eU*\x15\xee\xba\xeb.<\xff\xfc\xf3\xec\x02\x98L&\xf1\xcc3\xcf\xe0\xd3\x9f\xfe\xf4\x9f\x0c\xcaY*\x05E\xc6\xf4\x8f\x7f\xfc#\x00`tt\x14\xa3\xa3\xa3\xd8\xbd{7~\xfb\xbb\xdf\xa1\xa9\xa9\x09\x9f\xfb\xdc\xe7\xf0\xf9\xcf\x7f\x9e\x95\xb5\x9a\xcbE\x86\xce\xd23q\x9e\x9d\x8bY\xad\xcaq\xa1\xd86\xe0d(\x97\xcb\xe5b\x7f\x02\x81\x00\xb3\xe7\x0e\x87\x83%0R\xbc#\x91\xf1b\xed\xd2jjj\xe0r\xb9PUU\x05\x87\xc3\x01\x8b\xc5\x02\x83\xc1pN\xf3\x82\xa50\xcf\xe2\x99>H\xe80Q\xa6\xe2\x13(\xde\xcfl6\xc3d2\x9d\xb5&\xdd\xe7\xf2\xa2\x22\x05\x8e\xba\x80h4\x1a\xe8\xf5z\x18\x8dF\xe8\xf5z\xd6\xbe+\x9f\xcf\xe3\xa5\x97^b}\x99\x95-\x87\xe4\xe4\x9c$jy\xcb\x1aA\x10\x10\x0e\x87\xe1v\xbb\xe1\xf1x\x10\x0a\x85PQQ\xc1:\x85ppp\x9c\xdb\xa0K\xfa\xddw\xdf\xcd\xc8\x1f\xa9,o\xbf\xfd66o\xde\xccz\xbd.\x05\xec\xd8\xb1\xa3\xa0\xf6,p\xb2\xf2DOO\x0f\xee\xbd\xf7^\xdc\x7f\xff\xfdx\xf8\xe1\x87\xb1v\xedZ\xe8t\xbaY\x0b\x06\xcf\xd4\xb7\xb5\x94\x9a\xc3m\xd0I{a2\x99`\xb1X`\xb3\xd9\xd8\xdfv\xbb\x9d\xf5]\xa6\xb2dF\xa3\x91\xcd\x13\xcd\x87\xbc\xc4\x9bV\xab\x85\xc3\xe1`\xbc@\xa7\xd3\xb1\xe2\xe2\x1cK\x90\x00\xca'Q\xf98\xc5\xac\xc9\xff\xc8AA\xa2D\xfcVB\x92\xc7b\x93A\xea\xd8!\xef\x0dL\x99\xd8\xf1x\x1c\xaf\xbe\xfa*#\x7f\xca\xc0^j\xe5C\x89 D\x02\xe57\xfe\xa3G\x8f\xe2\xfc\xf3\xcfG0\x18D\x22\x91@YY\x19\x0b\x9c\xe6X\xfe\x04a>\x8fs\x9c{g\xc8_\xfe\xe5_b\xeb\xd6\xad,h\xbf\xac\xac\x0c\xef\xbe\xfb.\xd6\xad[\xc7\x12\xfdf[\x1f\x8b}\x86Sm\xd9\xef~\xf7\xbbx\xf1\xc5\x17\xd1\xdf\xdf\x8f\xde\xde^\x1c9r\x84\x91A\xbd^\x8fl6\x8b\xaf~\xf5\xab0\x9b\xcd\xb8\xe9\xa6\x9bp\xfb\xed\xb7\xb3\xb8\xbbb%D\x94\xa5\xc9\x94\xe7\x1a\xfdL\xf1\xd4\xfc\xe2\xfb\xa7\xf9 \x976u9\xb1\xdb\xed\xa8\xad\xad\x85\xc1``%\xc6\xa8\x12\x88<\xa9\xb1Xm[\x12/\xc8\x8eq^\xb0\x04\x09\xa0r\x03\x15K`\xa0\x85A\x05\xa1\x95\xc5\x9c\xc9}H$\x83O\xf2\xc2\x80\xb2\x8d\xe8\xdf\xf9|\x1e\xf1x\x1c\xe1p\x18\x93\x93\x938t\xe8\x10\x80\xc2\x12\x09\xf2lk\x22{\xf1x\xbc\xe0\xff\x04A\xc0\xe4\xe4$B\xa1\x10\x82\xc1 \xe2\xf18#\xf9\x9c\x00.op\x17\xf0\xf2\xc7'>\xf1\x09l\xdd\xba\x95\x15\xe9\xd5\xe9t8t\xe8\x10\x1a\x1b\x1bg\x8d\xb7\x92\xb7\x06;S\xeb\xb1\xaa\xaa\x0a\x97]v\x19\xae\xb9\xe6\x1aD\x22\x11\x04\x83A\x04\x02\x01\xfc\xe2\x17\xbf\xc0\x81\x03\x07\xd8%8\x1a\x8d\xe2W\xbf\xfa\x15^{\xed5<\xf0\xc0\x03\xf8\xd2\x97\xbe\xc4\xea\x9d\xceDdU*\x15|>\x1f\x92\xc9$\x02\x81\x00\x9a\x9b\x9ba\xb1X\xe0\xf1x\xf0\xf5\xaf\x7f\x1dO=\xf5\xd4\x92RC\x97\x0a\x11$\x8f\x13\x95\xbb\xa1\xf8Q\xaf\xd7\x8bL&\x03\xb7\xdb\xcdb3\x8b\xad\x17y\x1b9\xce\x07\x16x~\x16\xe3\xc6(\x872\xd1CN\x0e\x8b\xc9\xebt\xa8\xcc\xd4\xfd\x83\xe3\xf4H\xa0V\xab\x85^\xafGEE\x05\x1a\x1b\x1b\xb1j\xd5*\xb4\xb7\xb7\xa3\xbc\xbc\xbc\xb06\xd6\xff\x9f\x17\xbau\xa9\xd5j\xe4r\xb9\xa2\x1d?(\xf3.\x12\x890\x02\xc8\x15\xa0\xe5\x0f>\xc7\xcb{^\xbf\xfb\xdd\xef\xe2\xa7?\xfd);\xc7\x8dF#zzzPSS3\xa7\xcb\xb9 \x08x\xf5\xd5W\xb1k\xd7\xae3\xb6^\xa8\xb2D\x22\x91\x80Z\xad\x86\xd3\xe9\xc4\x1dw\xdc\x81\xfd\xfb\xf7\xe3\xbd\xf7\xde\xc3\xc6\x8d\x1bY\xfc\x1fp\xb2\xa8\xfd\x97\xbf\xfcel\xd9\xb2\xa5\xa4\x1d#\xbb\xb4o\xdf>\x1c?~\x1c}}}\x18\x1e\x1e\xc6\x81\x03\x07\xd0\xd6\xd6\x86\xef}\xef{x\xf2\xc9'9\xf9S\xcc?\x09=\x82 \xa0\xae\xae\x0e\xabV\xadB[[\x1b\xf3\xf0%\x12\x09X,\x16&P\xc8\xcb\xc5\x15\xe3\x16\xe4\xd5\xe2\xbc`\x89\x12\xc0\xb9lt\xb9\xebW9\x91\x9c\xf8-.\xa8\xf8\xa4\xd9lf$\xb0\xb9\xb9\x19\xf5\xf5\xf5Lu\xa59\xd0\xeb\xf5H&\x93\xac\x04\x0c\x91\xbfb\xca\xad\xc7\xe3A8\x1cF4\x1ae\xdd]8\x96\x0ff\xda\x8fs\xc9R\xe78w@1\xc0\xaf\xbf\xfe:\xee\xbb\xef>f\xbc\x8dF#v\xef\xde\x8d\xda\xda\xda\x92nN\xf9Zx\xea\xa9\xa7\xb0e\xcb\x16\x5cv\xd9ep\xbb\xddg\xa5t\x09)J\xd9l\x16\x17]t\x11\xf6\xed\xdb\x87\xe7\x9e{\x0e\xeb\xd7\xafg\xdfW\x14E\xbc\xfa\xea\xabhoo\xc7\xdbo\xbf\x0d\x00\x05$Q\x0e*\xb1%I\x12|~\x1fn\xbc\xf1F\x04\x83A\xe4\xf3y|\xe5+_\xc1\xce\x9d;\xf9\x22\x92\xd9y9\x014\x99L\xa8\xa9\xa9\x81\xd1hD}}=DQdmH\x95\xf6\xbf\xd8:\x91W\x0b\xe1g\xce\x12$\x80r\xf5h.\x13T\xcc\xa8PM;>\xc1\x8b\x07J\x10\xb1Z\xadp:\x9d\xa8\xad\xadEcccAp-\xb9J,\x16\x0b\x8cF#s\xf5\xd0\x86Vf\x7fMLL \x1e\x8f#\x91H\xb02\x0b|\x0e\x97\x8f\x224\x9f\xb9\xe4\xf3~n\x9f\x0d]]]\xb8\xf9\xe6\x9b\x99\xfbN\x92$\xfc\xcf\xff\xfc\x0f\xd6\xacY3\xeb\xdcR\xec\xf0\x87?\xfca\xfc\xed\xdf\xfe-T*\x15t:\x1d.\xbd\xf4R$\x12\x89E\xbf\xd8\x17\xf3(E\xa3\xd1\x82f\x04W\x5cq\x05\xbe\xf1\x8do\xe0\x8b_\xfc\x22\x8b\x0b\x14E\x11\xc3\xc3\xc3\xd8\xb2e\x0b\xfe\xe5_\xfe\x05\x1a\x8dfZ\xfc\x9f\xfc;\xaa\xd5j\xd8]\xe1/\x0f\x00\x00 \x00IDAT\xac6<\xf5\xd4S\xec2\x9cN\xa7q\xd3M7!\x1c\x0e\x17\xd4\xbe]\xa9{\x83\x92<\x88\xd4\x95\x97\x97\xa3\xa6\xa6\x06\xf9|\x1e\xc1`\xb0\xa0\x86\x9f\x92\x03\xcc\xa4\xc0r,a\x02(\xdf \xf2\x94\xf6\x996\xd1L\x87\x01/$\xbc\xf8j\x8e(\x8a\xd0\xeb\xf5(//\x87\xcb\xe5Buu5\x1c\x0e\x07{\xdcl6\x17\xa4\xe6\xd3\xebf:\xc0\xd2\xe94\xc2\xe10\xe2\xf18S\x009\x11X>\xeb\x85\xf6%\xc7\xf2\xc7G>\xf2\x11\x96\x91\x99H$\xf0\xd4SO\xe1\xc6\x1bo,j\x98\x8b\x91\xad\xcd\x9b7\xe3\xf7\xbf\xff=#\x94\xa9T\x0a\x7f\xfd\xd7\x7f\x0d\x83\xc1\xb0\xe8g\x82\xf2\xfd%I\xc2\xe0\xe0 \xdez\xeb-\xec\xdd\xbb\x97\xd53\x15\x04\x01W]u\x15~\xf2\x93\x9f\xa0\xbd\xbd\x9dy9R\xa9\x14\xfe\xfe\xef\xff\x1e\x0f=\xf4\x10\x8b{\x9ciO\xa4\xd3i\xdcu\xd7]\xb8\xef\xbe\xfb\x98\xda\x15\x08\x04p\xe5\x95W2b\xa9\xfc,+)n\x96\xba\xc6\xd0w\x0e\x85B\x08\x85B\xa8\xaa\xaaBCC\x03\xcb\xfe-v\xc9,\x96@\xca\xed\xc99B\x00\xe5\x13z*\x0b\xbeTaN\x8e\x85\xbb\x9dQ\x86\xb0\xddnGEE\x05\xacV+\xabF/\x8a\x22l6\x1bk\xe7C\xedvH\xceW\xcek<\x1e\x87\xcf\xe7\x9b\xd6\xf6o\xb6\xf5!/\x05\xa4|Ly\xa0\xf3\x03\xe0\xd4\xf7a\xb11\x9ei\xbc\x8b\xa9(3\xdd\xcc9\x96\xcf:\x01\x80\xc7\x1e{\x0c\x9d\x9d\x9d\xcc\x13\xf3\xc9O~\x12\xf7\xddw\x1f\x0b\xd0/\xf5Z\xaf\xd7\x8b\xcb.\xbb\x0c;w\xeed\xcf\xb5Z\xadx\xeb\xad\xb7\xf0\x8f\xff\xf8\x8f\x05\xdd\x84\xce\x04\xf9\x93?\x9e\xc9dX\xa9*\xf9z\x06\x80\x17^x\x01\x0f?\xfcpA\xd7\x8fo|\xe3\x1b\xf8\xf2\x97\xbf\xccb\x9fg\xda\x0f\x99L\x06O>\xf9$\xb6l\xd9\x82\x5c.\x07\x8dF\x83C\x87\x0e\xe1[\xdf\xfa\x16\x0b\x99\x01N\xba\x8e\xfb\xfa\xfap\xe8\xd0!\xd6\xf9b%\xd8\x18\xea\xf3\xabR\xa9PSS\x83\xb6\xb66\x96\x8c8\x1fn\xc0\x15\xc0s\x8c\x00\x16c\xf1\x9c\xc9/-UG\x1e\x0fh\xb3\xd9PQQ\x81|>\xcfZ(\xe9\xf5z\x16#Cs\xa9\x8c\x13\x94oPR\xff(\xad\x9f68\x15\xa5\xa6^\x91\xd4\x09F\xfe\x98\xfcg\x0a\xe4\xa6\xee$\xd45F\xdeA\x86\xa3\xf8!)o\xcc>\xdb\x18\xd3\xf8R_n\xf98+\xc30\x94\x8a\xfdL$\x91\xe3\xdcD&\x93A__\x1f\x1e~\xf8a\xf6X{{;\xfe\xf3?\xff\x93\xd5b+u\x96D\xa3Q\x5c}\xf5\xd5\xe8\xec\xecd\xc5\xe8\xdb\xda\xdap\xf4\xe8Q\x5cq\xc5\x15\xec\x1c9\x955M\xeb9\x9dN\x97\xdc\xfbs\x89S\x0d\x06\x83\xcc;A\xeei\xbf\xdf\x8f\xaf\x7f\xfd\xeb\xf8\xcdo~\x03\x00\xac+\xc7\x13O<\x81G\x1ey\x04v\xbb\xbd\xe8\xa5\x96\x08_:\x9d\xc6\xb6m\xdbPSS\x83L&\x03A\x10\xf0\xe8\xa3\x8f\xa2\xab\xab\x8b}\xa6@ \x80\xae\xae.\x0c\x0f\x0f#\x14\x0a\xad(;#o\x137\xd7\x02\xdc\xa5\xe6\x90c\xe1pFR\x96x\xb9\x88\xa5M\x02\x0d\x06\x03,\x16\x0b\xccf3\xacV+\xacV+\xeb\xb3\x98N\xa7\xe1\xf3\xf9\x10\x8b\xc5\xd8\x0d\xac\xd8A\x9b\xcb\xe5\x90L&\x19\xf9\x937\xf3&2A\x07@\xa9,\xf0b\x87\x87<\xfbK\xde\xa1\x84g\xdc\x15\xee1\x1ag\x22x\xf2\xdaZ\xb3\x1d\xac\xf22L\x1a\x8d\x06:\x9d\x8e\x198\xf9\xf3h.\xe4e~\xf2\xf9\xfcY\xeb\xfd\xca\xb10\xe4O\xab\xd5\xe2\xe6\x9bofk@\x92$|\xf7\xbb\xdf-I\xfc\x08\xa9T\x0a\x1f\xf8\xc0\x07p\xec\xd81\xd62\xed\xa2\x8b.\xc2\xae]\xbb\xd8\x1e\x9do'\x0d\xba@\x16\x8b7>U\x15\x10\x00\xa2\xd1\xe84%\x89>\xe3\xad\xb7\xde\x8a\x83\x07\x0f\xe2\xfc\xf3\xcfg$\xf6\xbf\xff\xfb\xbf\x99\x0b[\xa9\xdc\xd1\xfe\xa21\xfa\xe1\x0f\x7f\x88\x9bn\xba\x09\x92$!\x1e\x8f\xe3\xcb_\xfe2~\xfd\xeb_\xb3\xf2Y\xa4bq{\xc89\xc1\x8a\x22\x80\x1cK\x9b\x00\xeat:\x98L&\x98\xcdf\x88\xa2\x08\xbb\xdd^P\x0f\x8bj8\xe5r9v\x80)\x89C.\x97C,\x16c7u\xf9\xe3\x92$\xb1\xe4\x10\xca\xa2\xa3\xdb|\xa9\x9b\x9d\xbc96e\x22\xd2\xe7\xa4 m\xder\xeeO*\x09\x91py2\x0e\x11\xee\x99\x0e\x5c*\x08\x9e\xc9d\x0a\xaa\xf7\x93\x0a,\x8f\xff$\xe2\xa7\xd7\xeb\x0b\x94`y\xed\xb3\xd9\xdc4\xdc\x03\xb0\xf4\xd6\x8eF\xa3\xc1\x13O<\x81\x13'N@\xab\xd5\x22\x9dN\xe3\x9e{\xee\xc1\xf5\xd7__r\x8f\xd1\xff\xdds\xcf=\xd8\xb1c\x073\xec\x97\x5cr\x09\xf6\xec\xd9\xc3\x8a\xfd\x9e\x0ab\xb1\x18\x02\x81\x00\x8e\x1c9\x82\x1d;v`\xdf\xbe}\xe8\xea\xea\xc2\xc7?\xfeq<\xfa\xe8\xa3\xa7\xdcy#\x93\xc9L\xfb>\xf2\xb3j\xfd\xfa\xf5\xe8\xea\xeaB{{;[\xaf[\xb7nE}}=\xae\xbc\xf2\xca\x0276\x9d\x83\xf4\xf3\x87>\xf4!\xdcz\xeb\xad\xf8\xedo\x7f\x0b\xb5Z\x8dm\xdb\xb6\xe1\xd5W_\xc5\x87>\xf4!\xd6\xee\x8c\x8aK\xaf4;\xc3\xc3GV \x01\xe4\xedq\x96>\x04A`\xad\xe2L&\x13$IbE^EQD(\x14B.\x97c\x07\x98\xbc\xf2\xbfrn\xe5\xaa\x13\x1d\x90*\x95\x0a\xc9d\x12\xd1h\x14\xe1p\x18^\xaf\x17>\x9f\x0f\xa1P\x08\xc9drNY\x85\x00\xa0\xd3\xe9X\xb2\x0a\xf5\x89\xd6\xe9t+~\x8d\xc9\xdd\xbe\xd1h\x14\x81@\x00~\xbf\x1f\x13\x13\x13\xac3K)\xd0\xeb\xf2\xf9<\xccf3\xea\xea\xeaP[[\xcb\x02\xb8\xa9\xd9\xba\x9c\xf4'\x93I\xb6\x0e(K4\x95J1e\x96\xe3\xdc2\xce\xb1X\x0c\x8f?\xfe8#\x7f\x8d\x8d\x8d\xf8\xe1\x0f\x7fX\xb202\x91\xfe\xa7\x9f~\x1a?\xff\xf9\xcfY\xb0\x7fKK\x0b^{\xed5\xe4\xf3y\x18\x0c\x869}\x06\xf9\x05b\xe7\xce\x9d\xd8\xb6m\x1b^{\xed5\xec\xdd\xbbw\xdas\x87\x87\x87g,!6\x177a)\x22B%c\xda\xda\xda\xf0\xea\xab\xaf\xe2\x86\x1bn`\x17\x96'\x9f|\x12k\xd7\xae\x85\xd5je\x04R\xe9\xc1\x08\x06\x83\xb8\xfb\xee\xbb\xf1\xca+\xaf0\x97\xf1_\xfd\xd5_\xc1\xe7\xf3!\x1a\x8d\xb2D\x11^\x22\x8bcE\x10@~\xdb_\xfa\x87\xbf\xbc\xca:\xb9=jjj`\xb7\xdb\x11\x0c\x06\x11\x8b\xc5\x98\xb2D\x07d\xb1\x04\x0e:\x10)\xbe\x86\x0eiR\x04\x03\x81\x00\x04A\x80V\xab\x85\xc1`\x80\xc3\xe1`\x8d\xc1\x8b)\x0c\xa4j\xa5\xd3itww#\x14\x0a\xe1\xbd\xf7\xdeCkk+#\x1f\xd4\xd2n\xa5\x13\xc0l6\x8bD\x22\x01\x9f\xcf\x87\xb1\xb11\x0c\x0f\x0f\xa3\xbe\xbe\x9e5M'\xb7y1\x8c\x8f\x8f\xa3\xb7\xb7\x17{\xf6\xecA<\x1e\x87V\xab\xc5\x95W^\x09\xa3\xd1\x08\xb3\xd9\x5c\xe0\x02T\xb6f\xcc\xe7\xf3\x88D\x22\x10E\x11f\xb3\x99\xb9\x9e\xe7c\x909\xce>\xbe\xf5\xado\xc1\xe7\xf3\xb1y}\xf6\xd9ggu\xeb\x8b\xa2\x88]\xbbv\xe1\x0b_\xf8\x02\xdb\x8b\x00\xb0g\xcf\x1e\x94\x95\x95\xcd\xf9w\xa7\xd3ih\xb5Z\xec\xda\xb5\x0b\xf7\xde{/\xba\xbb\xbb\x11\x89D\x0a.\xa7\x94l\x01\x00###L\xad>\x15\xcc\xd4\x83^N\x02\x01\xe0\xfa\xeb\xaf\xc73\xcf<\x83\xcf|\xe63,\xce\xef\xa1\x87\x1e\xc2\x8f\x7f\xfcc\xc4\xe3q\x88\xa2X\xe0\xc1\xc8\xe5r\xd8\xb9s'4\x1a\x0d>\xfb\xd9\xcf\xe2;\xdf\xf9\x0e\xb4Z-\xfc~?\xfe\xf5_\xff\x15\xb7\xdcr\x0b\xc6\xc7\xc7\x91\xcf\xe7\xe1\xf1xPYY\x09\xa3\xd1\xb8b\xce..\x04-s\x02\xa8\xec!;\xdbm\x8bci\x90\x07r\xb3R\xdd?\x8dF\x83\xf7\xbd\xef}\xe8\xea\xeab\xaa[(\x14\x82\xc3\xe1\xc0\xc8\xc8H\xc1\x5c\x17K\x04\x90\xf7\xfe\xa4\xf5\x90N\xa7\x91L&\xd1\xdd\xdd\x8d\x8f}\xecc\xf3\xfe\x9c\x17^x!\xa2\xd1(\xee\xb9\xe7\x1e\xa6J\x10y\x5c\xc9\xbd7i\xbc\xd3\xe94\xa2\xd1(\xbc^/\xba\xbb\xbbq\xf3\xcd7\xa3\xae\xaenN\xef\xb1f\xcd\x1a\xc4b1x\xbd^tvv\xc2\xe7\xf3\xe1\xf2\xcb/G8\x1cFee%\xfb=\xf2\xf9T\x1e\xec\x82 \x14\x0a\xf1,\xbds\x0c\x92$abb\x02?\xfe\xf1\x8f\xd9<_{\xed\xb5\xb8\xf4\xd2Kg5\xda\x99L\x06w\xddu\x17DQ\x84$IH&\x93\xd8\xbbw/l6\xdb\xbcT\xf9h4\x8a\xbf\xf8\x8b\xbf\xc0+\xaf\xbc2MA\xa6z\xa5uuu\xd8\xbcy3\x1a\x1b\x1bq\xe1\x85\x17\xb2\x8b\xdfL{\xa2\xd4\xef\x9ek\x11\xe1\x5c.\x87O\x7f\xfa\xd3x\xf1\xc5\x17\x99Kwrr\x12\xff\xfe\xef\xff\x8e\xaf|\xe5+\x88D\x22\x05\x1e\x91\x91\x91\x11\xd6&\xf3\xba\xeb\xae\xc3\xd6\xad[111\x01Q\x14\xf1\xf8\xe3\x8f\xe3\xa2\x8b.b\x97\xb0\x89\x89\x09\xf8|>\xb4\xb5\xb5a\xd5\xaaU\xcb\xde\x8b\xc1\xfb\x85\xaf\x10\x05P\xbe\x88\x95\xea\x1f\x9f\xec\xa5y+#\xb7\x04\x05\x5c\x9b\xcdf\xb4\xb5\xb5\xa1\xae\xae\x0e\xdd\xdd\xdd\x88\xc7\xe3\xd0\xeb\xf5\xd3\x0e\xdc\x992\xbc\x8b)r\x94!\xec\xf3\xf9\xf0\xf6\xdbo\xe3\x8a+\xae\x98\xf3\xe5\x80n\xd7\xdb\xb6m\x83\xdf\xefG4\x1a\x85\xc3\xe1@EE\x05\xca\xca\xcaX\xab\xba\x95z\xd1\xc8f\xb3H\xa5R\x08\x04\x02\x18\x1c\x1c\x84\xddnG]]\x1d+\xbfP\x8a\xf8'\x93I\xfc\xe4'?\xc1\xb3\xcf>\x8b\xde\xde^\x04\x02\x01\x5cs\xcd5\xacN\x9a\xbc\xdb\x8b8\x96>\xf9S\xa9Tx\xe1\x85\x17066\xc6\xce\x80\xcf\x7f\xfe\xf3sr\xdd>\xfe\xf8\xe3\x18\x1a\x1ab??\xf6\xd8c\xb8\xf8\xe2\x8bY\x9c\xf0l{Z\xa5R\xe1\xd9g\x9f\xc5\xa7?\xfdi\xe4r9\x18\x0c\x06\x16\xae\xd0\xda\xda\x8a\x1bn\xb8\x017\xddt\x13\xae\xbf\xfe\xfa9'\x18\xcd\xc5\xc6\x90\x87b\xb6\xe7\xaa\xd5j\xc4b1|\xeb[\xdf\xc2\xde\xbd{166\x06A\x10\xf0\xc6\x1bo\xe0\xc6\x1boD{{{\xc1\xfb\xd0\x05\x88\x92`>\xf3\x99\xcf\xe0k_\xfb\x1a\xb2\xd9,\xfc~?\x9e}\xf6Y|\xecc\x1fC*\x95\x82J\xa5bY\xd7\xabV\xad\xe2\x8b\x91\xe3\xac\xe1\x8c\x97\x81\xe1\xd9?K\xebfFA\xc9j\xb5\x1a\xe9t\x9ae\xc0\xe9t:\xe6\xa6\xb0Z\xad\x88\xc7\xe3\xecfL%E\x94\x07)\x95O(X`\xff_]\xa4\xf7\xdf\xb6m\x1b\xb2\xd9\xec\xb4l8\xb9{Q\x99iz\xf0\xe0A\xec\xd8\xb1\x03^\xaf\x17\xbd\xbd\xbd\x18\x1c\x1c\xc4\xf8\xf88\x02\x81\x00\x8bG[\x89\xa0\xb9\x88D\x22\xf0x<\xf0x<\xb8\xe5\x96[\x0aJ\xf7(\xdd\xb64']]]x\xe0\x81\x07\xf0o\xff\xf6o8~\xfc8\xc2\xe10\x0c\x06\x03\x9a\x9b\x9ba\xb5Za4\x1a\xa7\xc5z*/x\x84p8<\xef\x0b^\xb1\xf7\xe48s\xc8d2P\xa9Tx\xe4\x91G\xd8\xfa\xb8\xf0\xc2\x0bq\xfb\xed\xb7\x97\x9c\xcbl6\x8b\xf1\xf1q<\xfa\xe8\xa3\xec\xb1\xf7\xbd\xef}x\xf0\xc1\x07Y\xdch)P7\x90?\xff\xf3?\xc7\xddw\xdf\x8d\x5c.\x07\xadV\x8bD\x22\x81\xc6\xc6F<\xf7\xdcs\xd8\xb1c\x07\xbe\xff\xfd\xef\xe3\xa6\x9bnb1\xa6\xf2\x18\xd4\xd3!\x81\xa9T\x8a%\xab\xcd\x16\xa2\xa4\xd3\xe9\x90\xc9d\xf0\x85/|\x81\x85\xc7\xa4\xd3i\xfc\xf2\x97\xbfD2\x99d\x09V\xf2\xefE\xfb\xeb\xe2\x8b/\xc6\xa6M\x9b\xd8{\xed\xd8\xb1\x03~\xbf\xbf\xa0\xd6 \x9d\xa9\x00\x10\x89D\xd0\xdf\xdf\x0f\xaf\xd7\xcb\xc5\x12\x8e\xe5A\x00y\x0c\xd0\xd2W\x00Ia\xa3@\xfe\x8d\x1b7\x02\x00\x0c\x06\x03k\x17\xe7\xf5z\x91\xcdf\xa1\xd7\xeb\x0b\x5c\xbcJ\x90\x0b\x99\x0a\x7f\xca{\x0aS\xad\xc1\x81\x81\x01\xfc\xd3?\xfd\x13N\x9c81k\xdfgA\x10p\xe4\xc8\x11\xec\xde\xbd\x1b]]]\x88D\x22\xf0\xfb\xfd\xe8\xe9\xe9\xc1\xd8\xd8\x18|>\x1f\xe2\xf1x\xc1A\xbc\x92\xc8;eW\x87\xc3a\x0c\x0f\x0fc\xc3\x86\x0d\x05m\xfb\x8a\xcd\xb7\xdf\xef\xc7\xcf\x7f\xfes<\xf8\xe0\x83\xf8\xedo\x7f\x8b\xd1\xd1Qf\x10\x9b\x9a\x9a\xd0\xd4\xd4\x04\xbb\xdd\x0e\x93\xc9\xc4\xe6\xb2\xd8\xd8\xcac\xfd\x94*!\xc7\xd2_;:\x9d\x0e\xbf\xff\xfd\xef\x11\x0e\x87\xa1\xd3\xe9\x90\xcdf\xf1\xd5\xaf~uV\xf5L\x14E|\xf1\x8b_d\xff\xd6h4x\xec\xb1\xc7\xe6\xd4\xbf]\x92$\x18\x0c\x06\x9c\x7f\xfe\xf9\xd8\xbau+{~:\x9d\xc6\xc3\x0f?\x8c\x81\x81\x01|\xf4\xa3\x1fe\xa1\x07\xf2s`.=\xe2\xe7\x12s.I\x12FGG\xe7\x1c\x9f.I\x12:::p\xf5\xd5W\xb3\xdf\xf1\xde{\xef\xa1\xab\xabkZ\xc2\x9b\x1ceee\xf8\xc8G>\xc2>\xfb\xc0\xc0\x00\xc6\xc6\xc6\x8a\xc6\x1f\xe6\xf3y\x0c\x0c\x0c\xe0\xc4\x89\x13\xe8\xef\xefG:\x9d\xe6\x8b\x94\xe3\x8c\x80\x97\x81Y\xe1\x86\x80n\xd7\xa4&Q\x00\xb7$I\xa8\xa8\xa8@6\x9b\x85\xd9l\x86Z\xadF&\x93\x99V\xdbO~\x90\xe9\xf5z\x88\xa2\xc8\x12\x07\xa8\xcc\x8cV\xab\x85\xc5bAee%\x9a\x9b\x9b\xf1\xc6\x1bo`\xff\xfe\xfdhoo\xc7\xb5\xd7^\x8b\x86\x86\x06TUU\xc1h4\x22\x9dN#\x9dN#\x91H`hh\x08\xef\xbd\xf7\x1e\x8e\x1f?\x8e@ \x80x<\x8et:\x8d\xfe\xfe~\x8c\x8c\x8c\xa0\xa9\xa9\x09\x15\x15\x15\xac|\xcdJ\xaa\x0bH\xf3E\xf1{\xa9T\x0a\x1b6l(J\xce\xf3\xf9<\xbc^/\x8e\x1d;\x86\xe7\x9f\x7f\x1e{\xf6\xec\xc1\xd0\xd0\x10\x22\x91\x08S\x82DQ\xc4E\x17]\x84\xea\xeaj\xb8\x5c.F\x00\xe5FWi\xe4\x94\xe5,\xb8\x9awn\x80\xc2>\x1ey\xe4\x11\xb6\xaf\xadV+n\xb9\xe5\x16\x96\x94Q\x0cj\xb5\x1a\x13\x13\x13\xd8\xbau+\xf4z=\x92\xc9$n\xbb\xed\xb6Y\xcb\xc5\xc8\x09\xe4e\x97]\x86\xc3\x87\x0fC\x14Ed\xb3Y\xb4\xb4\xb4\xe0\x97\xbf\xfc%6m\xda\x84t:}Z1\xbdsq\xeb\x0e\x0e\x0e\xe2\xfe\xfb\xefGMM\x0d\xbe\xf9\xcdo\x96\xfc}*\x95\x8a\x85X\xdc}\xf7\xdd\xd8\xb1c\x07+L\xff\xcc3\xcf\xe0G?\xfa\xd1\x8c%]\xe2\xf18>\xfa\xd1\x8f\xe2\xf1\xc7\x1fG(\x14\x82$I\xf8\xfd\xef\x7f\x8f\x07\x1ex\x80e\xcc\xcb?w8\x1cF6\x9bE0\x18D2\x99\x5c\xb6u5\x8b\x85\x85\xf1K\xe32!\x80|\x22\xcf]\x02H\x99u\xf2\xfaW\xf9|\x1eF\xa3\x91\xd5\x0a\xa3\x8e\x11\xf2b\xce\xf4>\x1a\x8d\x06\x16\x8b\xa5\xa0e\x1c\xbd\x8fF\xa3AYY\x19\xaa\xab\xab\xd1\xdc\xdc\x8c\x9e\x9e\x1e\x1c\x8f\x91\x91\x11\x84B!LNN\x22\x99L\xb2\xcf499\x89\xbe\xbe>444\xc0\xe5r\xc1f\xb3\x15(\x8f+\xc1\x80Sfv(\x14\xc2\xd4\xd4\x14\xaa\xaa\xaa\xa6)'4?\x07\x0f\x1e\xc4K/\xbd\x84\x1d;v\xa0\xaf\xaf\x0f^\xaf\x17\x91H\xa4\xa0\xd9}[[\x1b:::P]]\x0d\x9b\xcd\x06\x83\xc1\xc0\xc63\x97\xcbM\xeb\xef]l\xbf\xf3\xf0\x8es\x03j\xb5\x1a\xfb\xf7\xef\xc7\xe1\xc3\x87\xa1\xd3\xe9\x90J\xa5\xf0\xf8\xe3\x8f\x9f4\x083\x5c\xa2(\x1c\xe3S\x9f\xfaTA\xf8\xc6\xcf~\xf63d2\x99\x92D\x8aH\xe5\xad\xb7\xde\x8a}\xfb\xf61bu\xfd\xf5\xd7\xe3\xb9\xe7\x9e\x83\xd3\xe9D6\x9b\x9dS\xd1\xe9\xd3\xd93^\xaf\x17\xf7\xde{/\xd2\xe94zzz\xf0\xd8c\x8f\x15t>Q\x92\xd5\xae\xae.LLL\x00\x00\xca\xcb\xcbq\xe7\x9dw\xe2\xd9g\x9f\x85V\xab\xc5\xe0\xe0 v\xef\xde\x8d\xcb/\xbf|\xc6\xd7\xd7\xd5\xd5\xe1\xd6[o\xc5\x7f\xfd\xd7\x7fA\x14E\xbc\xf3\xce;\x88D\x22\xd0j\xb5E\xdb/R\x9f\xe2\x85\xaa\x13\xc8\xcb\xb0q\xcc\x86\x05s\x01\x173\x06|\xf1-}\x15I\xde\xb5C\xa3\xd1\xa0\xad\xad\x8d\x110jh\x1e\x8f\xc7Y\xbc\x9d\xb2\x06\x96<\x19\xc0`0L\xeb\x19L\x06\xc7h4\xc2\xe1p\xa0\xbe\xbe\x1e\xad\xad\xad\xb0\xd9l\x98\x9a\x9a\xc2\xf0\xf00\x0e\x1e<\x887\xdex\x03\xbbw\xef\xc6\x9e={\xb0\x7f\xff~\xec\xdc\xb9\x13\x03\x03\x03\x08\x87\xc3\x88\xc5b\x05\xbf+\x9dN\xa3\xb7\xb7\x17\xa3\xa3\xa3p\xbb\xdd\x08\x85B\xb3\xb6\x89Z\x8e\xea\x1f\xd5\xfd\xf3z\xbd\xb8\xe2\x8a+\x8a\x1a\xde]\xbbv\xe1\xe7?\xff9\xdex\xe3\x0d\xf4\xf6\xf6\xc2\xe3\xf1 \x12\x89\xb09'R}\xfd\xf5\xd7\xc3\xe9t\xa2\xb2\xb2\x92\x95\x8eQ\xba\x92\x95Y\xfe3\xa9&\x1cK\x1f?\xfb\xd9\xcf\x00\x9c\x8c\x89\xd3\xe9t\xb8\xe3\x8e;X\x1b\xb3\xa2\x86B\x10p\xfc\xf8ql\xdf\xbe\x9d\xc5\xf3\xde{\xef\xbd0\x99L%\xe7\x9cb\xfc\xbe\xf4\xa5/\xe1\xa5\x97^b\xcf\xbd\xf2\xca+\xf1\xd2K/\xc1\xe9t\x96$\x9e\x0b)BX\xadV\xac_\xbf\x9e\xfd|\xe0\xc0\x01<\xff\xfc\xf3E_322\x82\xbe\xbe>6&\xa1P\x08w\xdcq\x07\x8b\x0bT\xab\xd5\xf8\x8f\xff\xf8\x0fx\xbd^$\x12\x09\xa6\xa4\xcb\xf7\xc1\xa4{\x12\xdf\xf8\xc67\x0a\xbe\xdfK/\xbd\xc4.\xd5r[\xa9\x8c\x87>\x1d\xc4\xe3qvy^\x0a\xe0g\xc2\x0a \x80\xf31\x12\xa5\x16\x87\xbc\x84\x0c\xc7\xe2\xaa\x7fD\x12R\xa9\x14\xfc~?\xee\xb8\xe3\x0eF\xd8\x08\xe1p\x98\xa9x\x94\xe1V\xac\x9f\xa3N\xa7\x83\xddng\xc9\x03r\x02(W\x01].\x17\x9a\x9b\x9b\xd1\xde\xde\xce\x8a\x10\x87\xc3a\xf8\xfd~x\xbd^\x8c\x8f\x8fcbb\x02\xb1X\x8c\xd5\x0fL&\x93\xec}(\xeehhh\x08\xc3\xc3\xc3\x18\x1d\x1den\x13e\xff\xda\xe5:oT\x1f1\x1c\x0ec||\x1c\xd5\xd5\xd5(//\x9f\xb6gz{{\xf1\xe2\x8b/\xe2\xe0\xc1\x83\x18\x19\x19\x81\xc7\xe3A\x22\x91(p\xdfK\x92\x84\x0b/\xbc\x10UUU\xa8\xaa\xaabsH\xf3]l\xaf\x16#\x86\x1c\xe7\x0e$I\xc2\x9e={\xd8\xcf\x1f\xfe\xf0\x87a\xb3\xd9fu\xe1n\xdd\xba\x95\xc5\xdc\x02\xc0\xfd\xf7\xdf_\xb2\xc7/%\x85l\xdf\xbe\x1d\xdf\xfe\xf6\xb7Y\x0cq[[\x1b\xb6o\xdf~F3\xc7\xa9\x06\xe9\x83\x0f>\x88\xd6\xd6Vv\xce\xfd\xeaW\xbf\xc2\xb1c\xc7\xa6\xadc\xea\x19L \xd2{\xf7\xddw\xb3\xe7MMM\xe1\xd7\xbf\xfe5\x0e\x1c8P\x90\xd0A\xc8\xa43\xa8\xa8\xa8\xc0\xa6M\x9b\xd8\xe5\xf9\xcd7\xdf\x84\xc1`(8\x7f\x95\x04\xf0t\xf7S\x7f\x7f?\xf6\xee\xdd\x8b\xce\xce\xce%aG\xb9\xabw\x05\x11@9\xa1S.>\xf9\x02\x98)\xb0w\xa6zc\x1c\x0bo\x04\x88H\x84B!\xc4\xe3q\xd4\xd7\xd7\x17(z\xc1`\x10\xe9t\x1a\xc1`\x10\xe1p\x98e\xff\xca\xfb\xce\x12\x0c\x06\x03*++\xa1\xd5j\xa7\xa9G\xd4\xc7\xd7h4\xa2\xbc\xbc\x1c\xf5\xf5\xf5hllDKK\x0b\xe2\xf18#\x81\xe1p\x18\xd1h\x94\x156\xa6vfT\x80\x9a\x0aVS\xfb\xb2c\xc7\x8eatt\x14\xe3\xe3\xe3\x88D\x22\xec0]\x09\xf3\x16\x8dFY\xe1\xe7\x0f~\xf0\x83\x05\xbdy\x01`ll\x0c\xbf\xfb\xdd\xef\xb0\x7f\xff~\xf8\xfd~6>\xcan-j\xb5\x1a\x17_|1jkk\x0b\xd4?\xb9\xd2[\xea`\xa7B\xd4\x1c\xe7\x0e\x8e\x1f?\xceH\x0f\x00\x5c}\xf5\xd5E\x09\xbf\x1c\x89D\x02\xaf\xbf\xfe:\xfb\xf9\xb6\xdbnCccc\xc9\xd7\xa8T*V\xe7O\xab\xd5\x22\x99L\xc2j\xb5\xe2\xddw\xdfea\x05\x8bE6f\xfa?\xa3\xd1\x88\x87\x1ez\x88\x95.\x12E\x11_\xfa\xd2\x97\x90\xcf\xe7\x91\xc9d\xe0\xf7\xfb155\x85X,V4\xeb\xfd\xe2\x8b/\x86^\xafg!\x18;v\xec@8\x1c\x9e\x96\xb8!\xb7a\x9f\xf8\xc4'\xd8\x99:66\x86\x91\x91\x11F\x9a\xc9s1W\x028[\xa2\x15\x95\x9d\x11E\x11\xd1h\x14\xd1htI]^g\xeb\xfd\xce\xb1\x0c\x14\xc0\xb9L\xba\xbc\x91|\xb1\xd7s\x17\xf2\xe2\x92\x08\xca\xfa\x0d\x87\xc3\x98\x9a\x9aBEE\x05\xaa\xab\xab\x0b6\x22\x95|\xa0\x96mD\xb0\x8a\x19\xfc\x8a\x8a\x0a\x18\x8dFV\x97\x8f\x12\x08\xe4\xf3-\x8a\x22\xca\xca\xcaPUU\x85\x86\x86\x06\xacZ\xb5\x0aF\xa3\x11\x99L\x86\xf5\x08\xa6^\xb6Tf\x81\x0eF\xeaTB\x17\x07\x9dN\x87\xc1\xc1A\x0c\x0d\x0da||\x9c\xb9b\x96\xb3\x0aH\xea\x1f\xc5\xfe\x0d\x0f\x0fc\xe3\xc6\x8d0\x99L\xd3\x9e\xb3\x7f\xff~\xf4\xf7\xf7#\x14\x0a1\x82\x9f\xcdf\xa7\x8dOGG\x07\x9a\x9a\x9a\x98\xfaG\x89<\xc5\x0c\xd9L\xfbu\xa6va\x1cK\x13\x87\x0f\x1fF0\x18\x84J\xa5\x82\xcdfc\x85\x9fK\xad\xbb\x9e\x9e\x1e\xd6\xef\x17\x00n\xbc\xf1\xc69\xc5\xdc>\xf1\xc4\x13\x18\x1f\x1fg\x04\xe9\x97\xbf\xfc%\x1c\x0e\xc7Y\xeb\xe0\x93\xc9\x9cT\xe5\x1e~\xf8a\xe6\xb6\x0d\x87\xc3\xb8\xef\xbe\xfb\xa0\xd1h\xb0w\xef^\xbc\xf7\xde{\xacs\x91\x92\xa0\x94\x97\x97\xe3\x82\x0b.`\x8f\x1dD\x22\x11f\x8c\xe4c\xe4\xf7\xfbq\xf8\xf0aLLL \x10\x080w\xba\xb2\x0b\x82N\xa7\xc3\xc6\x8d\x1bQ]]\x8d\x8a\x8a\x0a\x98L&F\xb2g2\xce\xc5B6\xf8E\xed\xdc\x00\xed\xa7W^y\x85\xcdeMMM\x01\xa1)\xb6\xe6T*\x15\xdez\xeb-\x00'c\xd9\xca\xcb\xcb\xf1\xa1\x0f}h\xd6\xdf511\x81\xef}\xef{\xec\xb1\xbb\xee\xba\x8b\xbdn\xb1\xd4\xbf\xb9\xf4\x16O\xa7\xd3\xe8\xe8\xe8\xc0u\xd7]\xc7\xe2\xf9\x9e\x7f\xfey\xbc\xfa\xea\xab\xac\x9da1\x12\xa6R\xa9\xa0\xd3\xe9p\xd1E\x17\xb1\xc7\xfa\xfb\xfb\xe1\xf7\xfbg\xfc<*\x95\x0a\x06\x83\x01UUUl\xff\x1d\x04\x83A\xdcv\xdbm,\xf1C\x8eP(\x84\xb1\xb11\x0c\x0e\x0e\x22\x1c\x0e#\x91H\xb0\xd8\x18\xb9\x92D\xf3q\xfe\xf9\xe7\xc3b\xb1\xc0f\xb317m\xa9\x182\xa5\x0aX[[\x8b\xe6\xe6f\xf4\xf6\xf6B\x14EV\x9a\x86\xe6]\x14E\xe6N\xa1\x9e\x9c\xf4\xb7(\x8a\x08\x87\xc3\xe8\xef\xefg\xad\xcc\x9cN'+G\xb3\x1c\x08\x09\x19\x85x<\x8e`0\x88\xc1\xc1A\x96Y(\x1f\xe7\xc9\xc9Itww#\x16\x8b\xb1\xb8\xa0T*UP\xb3\x91\x08\xde\xe5\x97_\x8e\x8a\x8a\x0a8\x9dN\x94\x95\x95\xb1q.\xa6\xd8\x92\xa23\x9b\xd2R,\xa4\x83c\xe9\xac!A\x10\x0a2y\x01`\xdd\xbau3v\xf1\xa0\xd7\xec\xdc\xb9\xb3\xe0\xf1\x8b.\xbah\xc6\xe2\xcf\xb4\xc6\x9ey\xe6\x19\x00'=:\xf5\xf5\xf5X\xb7n]\xc9.#\xb3|\xf8\xb90\x89y\xbfm*\x95\xc2e\x97]\x06\x8b\xc5\x82H$\x02\x00\xd8\xb9s'ZZZ\x98\x9d*v~\x85\xc3a\xdc\x7f\xff\xfd\xd0j\xb5\x88\xc7\xe3,\xfe\xae\x94\x22J\x9d\x8e(<\xc5\xe3\xf1\xa0\xbd\xbd\x9d\xb9h\xe5\xe5\xd3\x02\x81\x00\xd4j5\x12\x89\x04\xecv;\x9cN';\xc7i\x8c}>\x1f***\x10\x89DX8\xd5\xd8\xd8\xd8\xb4\xb9\xa40\xaa\x85\xdc\x97\xd4\x13Y\xa7\xd3!\x97\xcbadd\x04\x83\x83\x83\x00\x00\x8b\xc5\x82\xb6\xb66\xbe\xe18\x01\x9cy\xf1\x10\x01\xd4h4\xc8\xe5r0\x18\x0c,K\xd4\xedvcdd\x04\xfd\xfd\xfdp8\x1c,\xa1@\xd9\xe0\x9e\x03\xd3T\x1d\xfa\x9b\x5c\xa0\x99L\x86e\xd5\x86\xc3ax\xbd^\xf4\xf4\xf4\xc0n\xb7\xe3\xf6\xdboGUU\xd5\xb4\xb9\xc9d2\xd8\xb1c\x07v\xef\xde\x8d@ \xc0\x0a1\xa7R\xa9\x82Vp\xf4\xfb\xd7\xad[WP@X\x19\xffW\x0cD\xe4\xacV+\xaa\xab\xab\xd1\xd0\xd0\x80\xd5\xabWchh\x88\xa9}Db\xe5\xb1\x80\x14\x9fCa\x04\xf4\x1d\x0d\x06\x03:;;\xd1\xde\xde\x8e\x91\x91\x11\xe6\xd2\x9c\xcbg9W\xd4?r\xfb\x8c\x8c\x8c\xa0\xbe\xbe\x1eN\xa7\xb3\xe0`\x97$\x09\xfb\xf6\xedCww7+\xfbB\xea\x822\xcbp\xed\xda\xb5hjjBuu5\xecv;t:]\xc9\xd6Ss\xddo\xbc\x17\xf0\xd2?+\x0e\x1e<\xc8\xc8\x0f\xc5\xa5\xcd6g\x87\x0e\x1d\x02p2\xc1\xa0\xae\xaenN\x8a\xce\x1f\xfe\xf0\x07\xd6e\xe4o\xfe\xe6o\x0aT\xc7y\xe3\x14\xd6\xdf|\x5c\xc1\x9b7o\xc6\xb6m\xdb\x00\x00o\xbf\xfd6>\xf9\xc9O\xb2\xf2S3\xbd&\x1e\x8f\xb3R1\xb3\x91\xabL&\x83\xea\xeaj\x98\xcdf\x84\xc3a\xd6\xfb\xf7\xf2\xcb/G4\x1a-\x88\xe3U\xa9T\x18\x19\x19\xc1\xf0\xf00\xb2\xd9,\x9cN'6m\xda\xc4\xcef\x82\xd7\xebE\x7f\x7f?\xe2\xf18\xb3\x89>\x9fo\xda\xe7!\xaf\xcdlc_\xac\xc7{\xb1\xc7(\x16qdd\x04\xeb\xd6\xadCYY\x19\x02\x81\x00\xfb\x0cn\xb7\x9b\xd5YT\x8eY\xb19\xe1\xe7\xc5\x0a#\x80\xe4\x02\xa4\xd6?z\xbd\x1e\xa1P\x08\xa2(\xb2\x1e\x89\xc7\x8f\x1fg\xeeD\xca\x94\xa2 \xf5\xb3U>`)\x1e\xe8\xf2\x929D\xfa\xc8\xddKeU\xa2\xd1(\x82\xc1 <\x1e\x0fB\xa1\x10.\xbf\xfcr\x5ct\xd1EE\x8b\xf9\x0a\x82\x80\xbe\xbe>\xbc\xf3\xce;\x18\x1b\x1bC0\x18D4\x1ae\x99eJW\x82 \x08\xd8\xb0a\x03\xaa\xaa\xaa\xe0p8X\x06\xb0\xb2\x06\xa0r\x93\xd3!e4\x1aQQQ\x81\x86\x86\x06455\xa1\xae\xae\x0e\x03\x03\x03\x05\x87\x18)}r\x12(\xbfDP}\xb1L&\x83\xc3\x87\x0f\xa3\xa9\xa9\x09\xf5\xf5\xf5p8\x1c\xcc\xady.\xabR\xf2\xd8?\xaaQv\xfb\xed\xb7O\xcb\x8e\x9e\x9c\x9c\xc4\xd1\xa3G\xe1\xf1x\x98\xfb\xb7X\xe6\xaf(\x8a\xe8\xe8\xe8@MM\x0d*++QVV\x06\x9dN\xc7\x0e\xf0\xd3q\x17\x15\xdb\x97|\xaf.-\x02\xd8\xdf\xdf\xcf\xce\x0cy\xdb\xc7R\xaf9|\xf80\xbb\x84o\xdc\xb8\xb1\xe4kT*\x15\xc6\xc7\xc7Y\xf7\x1e\x00\xb8\xf9\xe6\x9b\xd9\xc5n\xde\x18\x0d\x03\xbf\xef\x06D\x01@\x91\xdfI\x0f]\xdb\x044\x9b\xe7E(T*\x15b\xb1\x18\xae\xb9\xe6\x1al\xdb\xb6\x0d:\x9d\x0e\xe3\xe3\xe3s\xda\x03\xf3\x11\x22\xb2\xd9,\xacVkA\xb9$\x9f\xcf\x07\x00\xac\x16k\xb1q\xa7\x18\xe7t:=\xed\x1c\xcdd2\x18\x19\x19\x99V[\xb7\x18\xf9\x94\xf2\x12\x90/>|\xf3\xdd\xbb\xb1X\x0c\x83\x83\x83\x88\xc7\xe3\xe8\xee\xeeFmm-s\xfb\x92::\x97\xb9^\x8c\xe4\x0f^?x\x09\x11@r\xdb\xc9'\x99\x8c~,\x16c\x1d\x1d\xa2\xd1(\xecv{A?Z\x8dF\x83\x13'N0\x22\x13\x8f\xc7QSS\xc32\xaf\xe6\xbb\x01\x97\x0b\x94\x07\x13);D\xfc\xa8TK,\x16+\x88\xa3\xd4h4hnn\xc6\x95W^9-XY\xbe\xd9\x8f\x1f?\x8em\xdb\xb6\xb1\xae\x11\x81@\xa0@IR\x06+\xd7\xd5\xd5a\xed\xda\xb5,\x8e\x8c\xdc\xbf\xc5H\xbf\x12\x94\xf9Ku\x01\xa90\xf4\xd8\xd8\xd84\xd7\xb5\xc1`(\xd8\xe0\x82 \xb0\x18R*\xe3@1<\xc3\xc3\xc3hhh`I)\xa2(\x9e\xb3\x8d\xd5\xe5\xb1\x7f\x91H\x04\xe3\xe3\xe3\xacc\x87RQ\xa7\xde\xca\xa4\xda\x16+\xfa\x0c\x00UUUX\xbf~=\x5c.\x17S\xffHe?\x9dCw9\x97\xdfYN\x04p``\x00Z\xad\x16\xe9t\x1a\x8d\x8d\x8dszMgg'\x8b\xdd\xa6Vj\xa5\xd6Kgg'K\xe42\x18\x0c(//?\xf5\x0f\x9e\xce\x01\x13Q@3\x03\x01\x14p\x92\xdc$\xb2\xa7\xe4\x06N\xa7\xd3\xd8\xb0aC\xc1c===hnn>\xed\xf5L\xaf\xa7s\xd3f\xb3\xb1\xc7\x02\x81@\xc1\xe5n&2\x94\xcb\xe5\x10\x8b\xc5X\x17\x95R\xf6`\xa6\xef'\xc3E\x87\xb0\x00\x00 \x00IDATe%\xe4uy\xa80s\xa2\xcf\xc0\xc0\x00\xb4:-\xaa\xab\xaaY\xd8\xc7\xc4\xc4\x04&''\xb1f\xcd\x1aX\xadVF\x5c\x93\xc9$k\x8fG\xb1\x88d\x8f\xb3\xd9,\x92\xc9$\xccf\xf3\xbc/\x80s\x1doyMZ\xb2gT\xab\x96B\x7ff\xb2\x9f\xc41\x96k\xac2\xc5\x89\xce\xf5\xb2%\x9e\xce\x22\x97\xdfJ\xe4\x7fK\x92\x84\xf2\xf2r\x8c\x8d\x8dM{>%#\x10,\x16\x0b3\xea:\x9d\x0e\xe5\xe5\xe50\x9b\xcdp:\x9d\x08\x87\xc3x\xf3\xcd7\x11\x08\x04P[[\x0b\xb3\xd9\xcc\x12\x00V2\x01\xa4\x80`\x22eJ\xa5\xc7\xe1p\xa0\xac\xac\x0c\xcd\xcd\xcd\xa8\xaa\xaaBmm-s\x9f*\x95#\x8a\x19\xe9\xed\xed\xc5\xb6m\xdbp\xe4\xc8\x11\x8c\x8c\x8c\xc0\xef\xf7#\x1a\x8d\xb2\xd2\x02\xf2\xa4\x0a*\x7f\xf0\xfe\xf7\xbf\x1f\x0d\x0d\x0d\xa8\xac\xac\x84\xd9l.(\xcf2\x97uE\xae\xdc\xf2\xf2r\xd4\xd5\xd5\xa1\xad\xad\x0dG\x8f\x1e\x85\xdf\xefg\xc9&\xa4l\xca\xe3\xcb\xe41\xa4\xa4&\x93[\xe6\xe8\xd1\xa3hhh@MM\x0dS\x8d\x89 \x9e\x8b\xf3M\xfb\xc5\xef\xf7#\x10\x08\xe0\xca+\xaf,\xd8\xdc*\x95\x0a^\xaf\x17\x9d\x9d\x9d\x98\x9a\x9ab\x074\x112\xf9\x9cK\x92\x84M\x9b6\xb1\xba\x7ff\xb3\xb9@\x95P\x92E\xa5\xda\x5c\xaa\xec\x07\xc5\xf0\xce\xe7f?\xdb\xfbr,<\x01\x9c\x9a\x9ab\x17\xaa\xe6\xe6\xe6\x92\xc6\x97\x1e\x0b\x87\xc3,\xde\xaf\xb6\xb6\xb6`\xee\x8a\xd9\x84\xd1\xd1QFz\xea\xeb\xeb\x0b.p\xf32\xf6*\x15TR\xfed\x0c\xa0<\xd9\xa9\xe0C\xaaNK\x05\x92$\x09f\xb3\x19f\xb3\x99\xd5\xcc\x1b\x1e\x1eF[[\x1b\x8bC\x9e\x8f=\xd4\xeb\xf5\xd3\xceK\xba\xa8;\x9d\xcei\x040\x91H`rr\xb2d\xdd\x5cj\xc7y*H\xa5R\xc8J\xd9\x821\x97'z\x09\x82\x80\x13'N\xa0\xab\xab\x0bZ\xad\x16\xa3#\xa30\x9b\xcd\x88F\xa3\xec\x1cN&\x93\xb8\xe4\x92K`4\x1a\xa7\x95\x95\x917\x05\xa0\xc7\xe3\x898\xca\xca\xca\x98m\x9a\xc9\xfd{\xaa\x9c\x83\x0a\xff\x93\xfd\xa0nV\xd9l\x16\xb5\xb5\xb5X\xbf~=\xcb\x19\xa0\xf5@\x9d\xa3B\xa1\x10\xf4z=.\xb8\xe0\x82\x82\x18\xd6\xb9\xac\x9bd2\xc9<\x5ct\xf1\x96$\x09\xb1X\x0c\x16\x8b\xa5\xe0}\x8a\xbd\x9f\xfc\xff\xe8\xbbP\x85\x0d\x9b\xcd6\xe3\xe5z\xa6\xcf\xa6t\xd1\x07\x02\x01\xf4\xf4\xf4@\x92$\xd4\xd4\xd4\xa0\xa1\xa1\x81\xbdvrr\x92\x09it\xe6\xe7\xf3y\x88\xa7K\xa2\xe4\x85\x9ciQi\xb5ZLNN\xa2\xa2\xa2\x02\xa9T\xaa\x80\x14P\xe0\xbe|Ah\xb5ZVC\xce\xe1p`\xcd\x9a5\xb8\xfd\xf6\xdbQYY\xc9\xcaY\xe4\xf3y\x84B!\x04\x83\xc1eY\xe6c\xae\xaa*\x15T\xb6\xdb\xed\x8c\x04\x91+\xddf\xb3\xc1d21uL9\xb7\xca\xf8\x0e\xbaA\xed\xde\xbd\x1b/\xbe\xf8\x22\xba\xbb\xbbY/\xdeh4\xca\x02\x9c)+\x8d\x16S.\x97\xc3\x86\x0d\x1bp\xc9%\x970\xf7/\x91\xf3\xf9\xb8[)\x09\xc8f\xb3\xb1X\xc0\xf3\xce;\x0f/\xbf\xfc2\x04A@*\x95b\xea\x94\xbc\x9c\x01\xb9\x82\xa9\x86\x17e\x03\xe7r9\x0c\x0e\x0ebpp\x10\xf5\xf5\xf5\xac\xbb\xc5\xe9(\x5cg\x1b\xe9t\x1a\x91H\x04>\x9f\x0f:\x9d\x0ek\xd7\xae\x9dv@P1l\x8f\xc7\xc3\x92?h\x8f\xc8\xc3\x04jjjX\xe1g\x87\xc31\xad\xed\xdbL\x87\x8d\xb2m\xa3\x92(\xca]\xf4\xb3\x19w\xba\xec\xc9K@q\xb7\xcd\xe2\x83\x92/.\xbc\xf0Bx\xbd^\x0c\x0f\x0f3\x028\xd3\xe5\xa8X\x92\x07)A\xa5\x14\x06\xb7\xdb\xcd.\xab.\x97\x0bf\xb3y\xda\xf3\xe7<\xe7\x1a\x0d \x08\x80\xa0\xa6E\xaf\xf8\xfb\xf4\xc6\x85\xca\xd4\xd4\xd4\xd4\xa0\xbb\xbb\x1b\xc0\xc9,\xddS\x09\x1bQ\xab\xd5\xa8\xa8\xa8\xc0\xd4\xd4T\x81}\xa2\xcb\xac\x5c\x09\x0d\x04\x02\x8c\xa0\x95\xb2e\x92$\xc1\xef\xf7\x17\x9d\x8b\xb9z\x10\x90GQ[\x90\xcf\xe7Y\xb9/\xba\xd4{<\x1ex<\x9e\x82\xe7\x06\x83A\x9c8q\x02k\xd7\xae\x85\xdb\xed\x9e\x95\x90\x0c\x0f\x0d#\x93>\x19\x92\x13\x08\x04\x10\x8dF\x17d\x8f\xabT*\x04\x83A\xb8\xddnv\x86\xc8\xc7\x98\xe2'\xa9\xbe%\xd9\xb8\x9e\x9e\x1e\x8c\x8e\x8e\x16(\xad\xe1p\x18v\xbb\x1d\xa2(\xa2\xb5\xb5\x15&\x93\x09\xbd\xbd\xbd\xe8\xe9\xe9\x81\xd1h\xc4\x05\x17\x5c\x80\xb2\xb22\xf6\x9d\x8e\x1f?\x8e\xc1\xc1A\xf6\xfc\x96\x96\x16\xf4\xf4\xf4```\x00\xd9l\x16:\x9d\x0e\x97^z)L&\x13\xb3\x95\x9d\x9d\x9d\x98\x98\x98\x80\xc1`@{{;\x5c.W\xc1\xda\x9f\x9c\x9c\xc4\x81\x03\x07X7\xa5\xf5\xeb\xd7#\x9b\xcd\xc2\xeb\xf52QDN`'''\x11\x89D \x8a\x22\xf3p\x112\x99\x0c:;;\xd9\xba\x9a\x9a\x9a\x82N\xa7\x83\xcb\xe5BWW\x17\xba\xbb\xbb\x91\xcf\xe71::\x8a\x0b/\xbc\x10f\xb3\xf9$\x97\x90W\x15\x9f\xaf\x14K\x03\x9f\xc9d\xe0v\xbb\x11\x0c\x06\x99\xf4O\x8bK\xe96\xa4/\x1b\x8f\xc7\x19Y$\x03m2\x99\x98q\x22bSYY\x89\x9a\x9a\x1a~\x82\xcfC!\x9cOl\xca\xeb\xaf\xbf\x8e\xed\xdb\xb7\xb3\x96jtS\xa0^\xbc\xf2\xac_\x9a?\x83\xc1\x80K/\xbd\x94\xd5\xa4\xa26l\xf3%\x80\xf4\x19\x92\xc9$\xa2\xd1(\x04A`\x89!\x1e\x8f\x07\x82 0\xb5\xd8h4N\xcb2%7\xb2N\xa7c\xaen\xbf\xdf\x8f\xee\xeenX\xadVh4\x1a\x84\xc3a\xd8l\xb6\x19\x13BJ\x8dU\xa9[9\xfb7\xf2,\xbeF\xe9bQ\x12ny(\x84\xf2&^p0#\x8f\x5c\xf6O1\x9d~\xbf\x1fCCC\xac\xa5\x94\xfc\xb9\xd1h\x14\x87\x0e\x1d\xc2\xe4\xe4$\x02\x81\x00\x8b\x87T\x96\xeb\xc9\xe7\xf3\xd8\xb8q#r\xb9\x1c\x22\x91\x08\x9bgRm\xe5\xdd\x08\xe4\xddz\xe8p\x09\x85B\x98\x9c\x9c,X\x17\xc5\xc2\x8f<\xf20\x8c%Q;\xe0A^#\x14\x84~X\xad\xd6\xe9$\xf4\x14\xd5\xe4\x5c.\x07\xbb\xdd\xce~\x8e\xc5b\xa7D\x00\xc9\x86)\xd73\xd9C\xa7\xd3\xc9\x88\x22e\x1d\xcf\xe5\xf3Ro\xf4\xf9|7\xaa\xd9(I\x12\xeb\x92\x14\x0e\x87Q^^\x0e\xadV\x8b\x9e\x9e\x1e\x0c\x0f\x0f\xcf\xd9;2<<\x8c\x91\x91\x919y\xde\xc6\xc7\xc7\xd9s\xb3\xd9,+F_\xcc^\x95R\xb8\x8a)p\xa4.[,\x16\x88\xa2\x88L&\x03\x87\xc3\x81\x96\x96\x16D\x22\x11\xf4\xf5\xf5\xb1\x8b\xb0\xd9d\xc6\xbb;\xdf\x85\xd7sR-t8\x1c\xec\xfc\xa2$\x1eI\x92011\x01\x8b\xc5\x02\x8f\xc7\x03\xb5Z\x8dP(\x84\xae\xae.ttt\xc0`0\xa0\xaf\xaf\x0f'N\x9c`cz\xf8\xf0atvv\x16x\xd3\xb2\xd9,v\xee\xdc\x89\xf6\xf6v\xf6\x9a\x89\x89\x09\xa8\xd5j\xa4R)\x1c8p\x00\xe7\x9dw\x1etz\x1db\xd1\x18&''\x0bzBG\xa3Q\xd6jQ\xadV\xa3\xaf\xaf\x0fn\xb7\x1b\xf5\xf5\xf5\x08\x87\xc38z\xf4h\x81\x08\xd2\xd9\xd9\x09\xb3\xd9\x8c\xc6\xc6F\xb8\x5c.\x0c\x0e\x0e\xb2\x5c\x0a\xaa\xa4\xf1\xce;\xef\xa0\xba\xba\x1a~\xbf\x9f\xed\xdf@ \x80\xbe\xbe>\xb4\xb4\xb4`jj\x0a\xe2{\xef\xbdw\xda\x12:\x19\x15\xb7\xdb] \x9b\x0b\x82\x80d29\xad\x03\x01\xb9\xecHB\xcdf\xb3L\xb1\x92+\x18\xf2N\x10\xa7BN\x973\x8a\xcdQ\xa9\x22\xbe\xf2\xc5\x9aH$\xd0\xdd\xdd\x8d\xd7^{\x0d]]]\xac\xf4\x8e\xcf\xe7C<\x1eg\xca\x1f\x91\x08e\xa0\xf1%\x97\x5c\xc2\xfaL\xba\xddnD\xa3\xd1\x02\xf7\xef|n\xfatK\x8dF\xa3H&\x93\x10E\x11\xb5\xb5\xb5\x98\x9c\x9cd\xe4\x93.\x15\xa4\x80\x12\x11$\x05\x93n\x83d\xb0\x8e\x1d;\x06\x9b\xcd\xc6n\x8c\x0e\x87\x03:\x9d\xee\xd4\xb3\x10K\xcd\xc3I\xe6\x87\x93\xb6?\xbf\xa0sKk?\x12\x89\xc0`0`\xcd\x9a5\xd3J:\xec\xdb\xb7\x0f\x03\x03\x03\xacc\x0b\x91?e1Y\xa7\xd3\x09\x8b\xc5\x82d2\x09\x9f\xcfW\x90}/\xff]\xc5\xe6-\x97\xcb!\x1e\x8f\xc3\xe3\xf1 \x91H\x14\xb8W(\x0bq\xae\xc9#\xd1h\x14\x1e\x8f\x07###\x88\xc5b\xacn\xe4B\xee\x83\xd39\xcf\x94\xef9/\x17 \xad\x85\x05\xfc\xfc\xf3\xfd\x0c\xb3\xc1\xe3\xf1\xb0\xf3\xa0T\xc1v\xb9\xaa%_GtY\xcbKy\xa8\x04\xd5\x8cdj\xcd\x9a5\xc8d2\xa8\xab\xabC2\x99\x9cV\x1ajN\xdfI\xa5\x82\x86B]\x00H\xb9?\x15\xa0\xcff\xb3\xa8\xac\xac<\xf9\x19\x16`\xdb\xc9\x09\xca\x5c\xd6\xa3<\xfb^\xfe\x98^\xaf\x9f\xf6\x7fd\xcb\xe4*\xde|\xe64\x9dN\xb3\xec\xe1\xb9\x90\x7f\xbd^\x0f\xab\xd5\x0a\xaf\xd7\x0bI\x92p\xe2\xc4\x09v\x8eR\xa1\xfeD\x22\xc1\xe2\x0a\xe5\x7f\x94\x97V\xf2\x10\xcc\xa7&/}\x7f:\xbf)1\xb1\x14\xf1\x9fK\x0b\xbfP(\x84d2\x09\xa7\xd3\x89\xd6\xd6V8\x1c\x0e\xc4b1\xb8\x5c.V\xcagdd\x04\x89D\x02CCC'\xd5\x5c\x9f\x1fz\xbd\x1eMMMhnn\x86\xd7\xeb\xc5\x91#G\x90H$\x98-\xa1\x84I\xb5Z\x8dL&\x03A\x10\xe0\xf3\xf90>>\x0e\xa3\xd1\x88\xbe\xbe>\x08\x82\x00\x83\xc1\xc0B\x8dr\xb9\x1c4\x1a\x0d\xca\xcb\xcbQQQ\x81\xf1\xf1q\xf8\xfd~\xec\xdb\xb7\x8fy\xc9\x8cF#\xf4z=\xc2\x91\x931\xd9\x9d\x9d\x9d\x90$\x09\xf1x\x9c\x952\xa3P\x1c\x9f\xcf\x87h4\xca\x08\x5c,\x16\xc3\xe8\xe8(\xc6\xc7\xc7Y\xc2lee%\x9cN'2\x99\x0c\xb3\xd7\xc7\x8f\x1f\xc7\xe8\xe8(\xa2\xd1(\xab\xefZQQ\xc1jwNMM1\xd2N\x89\xb6\x14\xe2\x15\x0a\x85 RP\xef)\x1dx*\x15rR\x0e\xb9l\x0eSSS\xac\xce\xd3L\x86DyP\x90A\xa6\xbaB\xe1p\x18Z\xad\x16\x1e\x8f\x07\xef\xbd\xf7\x1e\x1c\x0e\x07\x93T\xe9\xbd\xb8\xbbhn\xd2?\xfd-w\xdd\x05\x83Atuu\xe1\xd0\xa1Cl\xe1P\xcd\xc5X,\xc6b\xfe\xe4\x0a\x92R\xb5\xda\xbcy3n\xb8\xe1\x06\xac^\xbd\x1a\x8d\x8d\x8dp:\x9d\xd0\xeb\xf5,\x93t\x0eV\xb2P9P\x01\xd9\xcc\xc9\x1b\xae\xdb\xed\x86\xd3\xe9\x84(\x8a\x98\x9a\x9a\xc2\xc8\xc8\x08ssS\xbc#\x1d\xa2D\x00I)\xa6D\x0f\x8aY\x09\x04\x020\x9b\xcd\xac\xc4\x0c}Nf\xb4\xf2s$\x0bE\x94\xbd\x85\x9c\xa7bcF\xb13\xd1h\x94\xd5c\xbc\xed\xb6\xdb\xa6\xed\xa7P(\x84\xc3\x87\x0fchh\x08\xe1p\x98\xa9\x7fr\x97=\xe1\xfd\xef\x7f?.\xbd\xf4Rttt\xa0\xb1\xb1\x91%\xed(U\x19e\x8c(\x19 \x9f\xcf\x87\xee\xeen\x8c\x8d\x8d!\x14\x0a\xb1\xf7&W\xc2\x5c\xe1r\xb9\xd0\xde\xde\x8eK.\xb9\x04\xd5\xd5\xd5\xc5\x95\x9c%~\xd1Z\xca\x9f\xf5t\x09\xafr\x1d\x96\x95\x95\xb1\x90\x0b\x9a\xbf\xab\xae\xbaj\xc6:y\xc0\xc9\xba\xa0\x0f?\xfc0S\x92\xf5z\xfd\xa9\x8d\xa3J\x05\xa1?\x08\xf1\xf8\x11\xe4E\x81\xb9C#\x91\xc8\x9fTh\xb5l\xfd\x9e\xa2@@FY\xeeY\x98M5\xa63'\x10\x08L\xebU\xae\x0c\x85 \xc1CIv\xe6J\xfe\xe9RN\xaf\xb5\xdb\xed\xac\xf4J1\x98L&TTT0\xe5\x87~\x9fF\xa3a\x15\x1d\xe4kE\xadV\xa3\xb1\xb1\x11\x91H\x84\x95\xb6\xa1\xef\xb2f\xcd\x1a$\x93I\x1c:th\xd6\xd0\x0e\xf9\x19B\xef-\x0f\xe1QzB\x8a\x89\x143\x8d7u\xaf\xaa\xac\xacD}}=\xda\xdb\xdb\xa7\x9d\x1b&\x93\x09eeeH$\x12\x18\x1c\x1cd\x8aYMM\x0d\xd6\xacY\x03\x95J\x85\xba\xba:H\x92\xc4H]:\x9df\xe4O\x14E\xb8\x5c.VP\xbb\xab\xab\x8b\x11D\x97\xcb\x85\xf5\xeb\xd7C\x14E\xd6q\x8a\xcahi\xb5Z\xd4\xd7\xd7chh\x08\x13\x13\x13\xc8\xe5r\xb0Z\xadhii\x81\xc9d\xc2\xe0\xd0 \xfaz\xfb\x18\xc1s\xb9\x5c(++\x83\xd3\xe9Dyy94\x1a\x0d+\xd7F\xbd\xee\xa9\xd4N*\x95\x82^\xafG\xc7\x86\x0eT\xb9\xaa\x98\xfdkhh`\xe1N\xe1p\x18\x82 \xa0\xbc\xbc\x1ck\xd7\xaee\xe3B\xed\x01\x05A\xc0\xaaU\xabP^^\x8e\xa3G\x8f\x22\x1a\x8d\x22\x14\x0a\x9d\xf4\x04\x9cn\xd1F2V\x1a\x8d\x06n\xb7{\xc6x\xab\x99\xfa\x1b\x92\x8cK\xb7\x8c`0\x88\xc9\xc9I\xe6\xdaknnF{{;/0{\x0a\x86@\x92$x\xbd^\x8c\x8d\x8d\xe1\xd8\xb1c\x18\x18\x18`\x01\xb4>\x9f\x0fSSS\x88\xc5bl\xf1Q\xf0\xb2\x92@\xd0|vtt\xe0\xe6\x9bo\xc6\xea\xd5\xab\xd1\xd2\xd2\x02\x97\xcb\x05\xab\xd5zJ\xae_\xe5:\xa0x\xbfx<\x0e\x9f\xcf\x87\x81\x81\x01\x16B@EG)\xf1\x85bH)\x03\x8cjJR'\x0a\xb5Z\x8d\x9e\x9e\x1el\xdc\xb8\x91\xb5\x86\xd3h40\x99Lls\xcc\xd9 -\xa0\xb27\xd3<\xc9\x7f&E\x9dZ\xbey\xbd^\xb8\x5c.vp\x91\xbb\x96b\xffN\x9c8\x81P(\x84P(TP\xaeG\xfe\xbe\xb5\xb5\xb5\xe8\xe8\xe8@ss3\xea\xea\xeaX\xd2\x8e\xbc\xf3\x87\xf2\x10\x97\x7f&*(-/\xf2]\xcc}=\x97\x84\x0e\x0a\xdc\xa6\xc3\x9a\x5c9\xcbI\x89_*\x17\xc0\xd3\xfd\x0e\xa4\x8a\xe9\xf5z\xf6oR\xa3g*\x1e=\xd3\xef?\xe5q2\xa4N\xc6\x01jN\xfe.\xaa\x22!wK\x9f\xcew'\x8c\x8d\x8d\xb1KeEE\xc5\xac1\xe6r\xaf\x83\xfc;\x16ss+\x09\xe0|\xc7C\x1e\xca!I\x12\xaa\xab\xabYu\x8cb\xef\xa3\xd3\xe9`\xb7\xdba0\x18\xa6%\x90\x14K\xc8\xb0Z\xad\xd8\xb0a\x03+\x1bF\xcf\xd7\xe9tLY\x93\x87m\xd1z\x90\x8f\x11\x85\xd8P\x22\x0d\xbd\xbe\xa6\xa6\xa6\xe0s\x9e\xca\xfcP\xfc\x7f*\x95BYY\x19\xea\xea\xeaf\xf4\x80\x99\xcdf\x16\xa7Hjnuuu\xc1\xf3\xeb\xeb\xeba\xb5Z\x99+\xfe\xe8\xd1\xa3H\xa7\xd3hmmEkk+K\xaa#\x95\xb0\xa9\xa9\x09\xabV\xadb\x99\xcd---\xd3~\xb7V\xab\xc5\xea\xd5\xabQSS\x83L&Sp\xae556\xc1l2#\x10\x08\xc0d2\xa1\xaa\xaa\xaa \xf9\x0e\x00\x0c\x06\x03K\x96\x02N\x16hw8\x1c\x88\xc7\xe3\xb0X,'\xd5n\xd9w\xd0\xeb\xf5X\xb5j\x15\xacV+&&&\xa0\xd5j\xd1\xd0\xd0\xc0~gee%6n\xdc\x08\xaf\xd7\x0b\x8b\xc5\x82\xda\xdaZh\xb5Zd2\x19\x9c8q\x82er\x8b\x0b\x91\xea\xae\xcc\xe6\x9bO:7\x19t*2,\x08\x02\xdcn7#$\xc7\x8e\x1d\xc3\xf8\xf88+5B\x81\xeb\x92$\xcd\xe8v\x5c\x8c\x83\xf3l\xba\x9e\x95\xf5\x9f\x8cF#\x8b\x9d\x8b\xc7\xe30\x99L\xcc\xfd\x97H$X\xaf\xd8\xbe\xbe>\x04\x02\x01&gS-\xb9P(\xc4\xdc\xbc\x941ZL\xf5\x93+m\xe7\x9f\x7f>\xee\xbc\xf3N444\xa0\xa1\xa1\x01\xe5\xe5\xe5\x8cP\x9d\xae\x11$y\xdah4\xb2\x1b^[[\x1b\xba\xba\xbaX&\x9a\x5c\xa2\x97\xc7\xa8\xd1\xcd\x8d\x94\x86L&\xc3n\xcd\xc7\x8e\x1dCSS\x13jkkY]@r\x19\xcf\xc3\xaf\xb7h\x0a`1C@.\xf1D\x22\x81@ \x00\xaf\xd7\x8b\xff\xd7\xde\x99G\xc7]\x97\xfb\xff=\xfb\xbeg\xb6\xccdk\xd2\xa4{KiYZ\x0ae\x97\x02r\xa5\x02z\x11\x11\x5c\xa8\x82\x0a\xcaQ\xef\xfdq\xc5\x8b\xc7\x85\xe3\x82\x22\x02\xea\x11\xf1\x82\xe0\x02\x17/(\xdb\xbd*{i\x11*]\xd26M\x97\xecK\x93\xc9Lf\xdf\x7f\x7f\xc0\xf3\xf1;\x93I\x9a\xb4\x93t\x92<\xafs\xe64\xcd2\x99\xcc\xf7\xf3\xfd|\xde\x9f\xe7\xf3<\xefg\xfd\xfa\xf5cDR4\x1aE[[\x1b\xfa\xfa\xfaD\xee\x1f\x89?\xba\x86R\xe1N\xe6\xd1&\x93I\xf4\xe2\x96\x1e5O\xf4\x9a\xc6\xfb\xbe\xf9\x18e\x9bO\xa2\x94\x9e\xc7\xe9t\xa2\xbb\xbb\x1b\xc0{\x89\xeb3~=\xb3y@\x9e\x07d\x80R&\x87\x1c2\xe42YdSi@\xa5\xfeg\x95\xf0qN\xd1\xc9d\x12\xa3\xa3\xa3\xc2\xb8\xda\xe7\xf3\xa1\xaa\xaa\x0a}}}\xe3\xce\xfb\xc5E\x08\xf4\xb9RF\xca'*\x00\x8b\x85\x86\xd3\xe9D,\x16\xc3\x91#G\xa0V\xaba\xb5ZEZ\x07U\xe3Ses0\x18,x\x9d4g\x93\xe0\x90\xcb\xe5\xa2\x1bT\xb1\x08!\xf4z=\xccf\xb38\xa1kiiA\xfb\x81v\xc4\xe2\xff\x8c\x9a\x9a\xcdf\x18\x0c\x86\x02oB\xeabB\xa7\x0d':6\xb4Z-jjj\x84\x10+\x85\xcb\xe5\x12\x913\xaa\xbc.\xae\xb0\x95\xc9d\xa2\x98\xc9`4\x88\xe0\x82\xddn\x87\x5c.\x87\xd3\xe9\xc4\xa9\xa7\x9e\x8aX,\x06\x95J%r\xc8\x8b\xafk)aM\xaf\xad\xb8\xc8\xb5\xba\xba\xba\xa0\x96\xe1X\x91z\xb9\x5c.*\xee'\xdaL\x93\xa3Cq~\xbc\x5c.\x87\xdf\xef\x87\xd7\xeb-\xb8\xfe\xd5\xd5\xd5\xd0\xe9t\x22\xe0\xa2,\xc7\xe2}\x22\x17\x96r\x13h\x80P\xdfYz\xc8\xe5rtww\x8b\x81^UU\x85T*\x05\x85B\x01\x9b\xcdV\xf6\xc8`q\xa9\xf6T\x05\xe0t\x08E\xa9\xb0\xa6|,\xda\x0d%\x12\x09$\x12\x09\x1c=zT\x88\xc2@ \xc4!\xe5\xec\xd0\xcf\x90Ap:\x9d\x16\xc7\x85R\xe1Wl\x1a\x9c\xc9d\xb0n\xdd:\x5cv\xd9eX\xb0`\x01\x1a\x1b\x1bE\x05R\xb9\x8c\xb9\xe9\x18B\xa3\xd1\xc0b\xb1\xc0\xe7\xf3\xa1\xb6\xb6\x16---x\xe3\x8d7Dd\x8f\xc6\x09]#\xda\x11K\x13\x99)\x12\x98\xc9dp\xe8\xd0!\x1c:t\x08~\xbf\x1fN\xa7S\xb4<\xab\xd4h2\xbd\xff\xe4\xfbw\xf4\xe8Q1\xe1\x16\x8f\xcfC\x87\x0ea\xfb\xf6\xed\x08\x04\x02\xc2\xf7\x8f\xc4\xb1t\x822\x9b\xcdX\xb3f\x0d\xdcn\xb7H\xa9(\xe7{p\xa2\xe3\x9d\xc5_\xe5\xb3l\xd92\x1c>|X\x1c+\xcd\xe8\x86X\xad\x00\xbcFa\x04-3\x00\xe9L\x10\xb9l\x0ei\x87\x06:\x8b\xfe}u\xa0\x9c\xb2\xa5\x10u$\xda\xb3gO\xc1\xe7\xcf<\xf3L477#\x18\x0c\x8e\xdb\xdb\xb6T\xbb\xcb\xe2B\x17:^\xa4 \x07\x15~\xe4r9q\xb4>U\x9f<\x8dF\x03\x83\xc1\x80\x85\x0b\x17\xc2h4\xc2d2\xc1b\xb1`\xfb\xf6\xed\xe28Z\xab\xd5\x8a#j\xe9\xdaa\xb3\xd9\xb0j\xd5\xaa)\x1b\xe4\xd3\x111u&\xa9\xaf\xaf\x7f/H\x13\x8f\x89\xb5\xd2\xe9t\xc2\xe5r\x89\x9c\xb5|>/,\xde\xa4]\x9cJ5\x05\x98\xec\xdf\xeev\xbbE\xd7\x94\xf1\xae\xb3\xc3\xe1@KK\x0b\x0e\x1e<(\xe6\xce\x89\xda\x0f\xca \x83\xddn\x1f\xf3\x9a\xacVkIk\x96\x89^\xf3Tr\x5cO\xf4\xeb\xc5\xe3n\xa2kW\xfc\x7f*F\x02\xcal\x04]\x5c\xb41\x99\xc4pir1\x0d^24\x8eD\x220\x99L\xa2\xa4\x99\xbe\xa6R\xa9`4\x1a\x0b\x0c \xcb-\xb4*\x19\xba\x91\xc8u]zdC\xc7\xb8\x94\xd7Bm\xc4\xa4Q>\x12H\xd2hQ\xf1\xce\x86\x8eW\xaf\xb8\xe2\x0a\xac[\xb7\x0e\x8d\x8d\x8d\xa2}\x98\xd4\xf3\xaf\x5c\x0b8M\xc6\x06\x83A\x94\xc0755a\xef\xde\xbd\xc2\xe3\x89Z\xdb\x19\x8dF!\x0a\xe9\xef&s[\xaap\xa4\xe3\xef\xc3\x87\x0f\xa3\xae\xae\x0e~\xbf_\xb4\x16\x9cr\x14p\x86\xa0\xe2\x8dD\x22\x81P(\x84\x81\x81\x01\xacZ\xb5j\xcc\x0d\x9eN\xa7\xf1\xf2\xcb/chh\x08###\x22\xf2K\xc7\xc7\xd2\xe3\xa2u\xeb\xd6\x89]\xa2\xd9l.HR?Q\x91Wj\x93\xc4\x82n\xeeE\x12\xd7\xae]+:e\xbc\xf5\xd6[\x93\x9e\xdb\xcbB\x8d\x19\xb8e-\xbd \xa4GF\xd0\xb7#\x8bx\x22\x01\xcb\xcaf\x98\xab\xdf\x8f\x92\xc8e\x80\xe4\xf8q\xb2\x0b'\xd9`\xd1}e\xb7\xdb\xe1\xf5z\xa1V\xab'\xb4^1\x18\x0c0\x1a\x8d\x18\x1c\x1c\x14\xe3\x9e\x12\xf8\x09\xca\xd1\xa2H\xd4\xc0\xc0\x80\xf8ZUU\xd5q\xcdA4\xe7\x1a\x0c\x86\x82\x96k\x14\xed\xa3\xaf\xc9d2a\x15&\x15\x80\x14\xf9\x9a*\x1e\x8f\xa7\xc0\xc6\xc4f\xb3\x89\x82\x22\x99L\x06\xbd^\x0f\xab\xd5\x0a\xbb\xdd\x8e\xfe\xfe~\xe8t:8\x9dND\x22\x91\x82\xf9\xedx\x03+\x1e\x8f\x07\x0e\x87\xe3\x98\xf3\x8bB\xa1\xc0\x82\x05\x0b\x84\xc5\x11\xcfG\xc7\xb8\x07\xa6S\xa0LdGPj'\x90\xcb\xe5D\xb5\x93B\xa1\x10\xc9\xfc4\xf0\xa5\x09\xeat|\x5c*\x17\xe3d\x89\xb2\x99BZ>/\x15qd\x15@\xd1=iO`\x8a\x1eJm]J\x09?\xfa\xbc\xcdf\xc3\x87>\xf4!,^\xbc\x18\x0b\x16,(0|\xa6\x1dd\xb9o.2R\xb5\xd9l\xf0z\xbd\xa8\xa9\xa9\xc1\xe2\xc5\x8b\xf1\xf2\xcb/\x8b\x1cQJ\x98\xd5\xeb\xf5\x22e@\xab\xd5\x22\x99LB\xa5R\x89\xf4\x00\xaa:;p\xe0\x00\x9a\x9b\x9b\xd1\xdb\xdb+\xfa\x15Wb{8\xba\x06\xc9d\x12\x91H\x04CCCH$\x12X\xb3fM\x81\xb0\x92\xc9d\xd8\xbf\x7f?\xda\xdb\xdb\x11\x08\x04D\x82xq\xbf_\x00\xb0Z\xadX\xb5j\x15\x9cNgEF\xff\x98\xd9!\x00\xd7\xacY#\x16W\xdaLN5o\xf3\xb87\x07y\x14\x1c\xed\xcae\xef\x07\x19\xb2\xef\x15\x1fJ\xbf6U\xc3a\x12\x80/\xbf\xfc\xb28\xed8\xed\xb4\xd3\x0a\x1c(J\xfd\x1dF\xa3\x11\x8f\xff\xf6ql\xdf\xb6\x1d+V\xac@ss3\x1a\x1a\x1aD\xb4\xab\xb8\x15&\xbdw\x03\x03\x03\xe25R$k\xaa\x11@i\xe4Q\xfa\x9e\xfa|>\x04\x02\x01q2F\x22\xd3h4\x0a[\x1b\x83\xc1P\xb6\xb1Q\xaa;\x13\x00\xb4\xb4\xb4 \x99L\x8a<\xbbR\x1e\x80S\x9d7hS?U\xfd\xc1\xe2\xef$\x09\xc0\xa9F\xd3\xc8\xe8\x90\x84\x0a\x09\x1c\x8aZ\x91\xe8!\xf1\x22\xcdq:\x9e\xc5l\xb6-\x5c\xd2\x1b^\xba\xd0\x17w\x06\xa1\xef\xa1(\x90T\xec\x15G\xfa\x8a\xf3Q\xa8\xfd\x8fV\xab\xc5\xda\xb5kq\xce9\xe7\xa0\xa1\xa1\x01\xb5\xb5\xb5\xf0\xf9|\xb0\xd9l\xa2sD\xa9\xe3\x8frE\x01U*\x15t:\x9d\xc8\x05\x5c\xb0`\x01\xf6\xef\xdf\x8f\xa1\xa1!\xf1\x1a\xa9j\x9c*\xces\xb9\x9c8\xfaU\xab\xd5\x05\xbd(\xfb\xfa\xfap\xf0\xe0A\xd4\xd4\xd4\xc0\xe7\xf3\x15\xb4\xac\xab$\x11H\x1b\x1a\xca\xe1\xff\x22\xf6\xb4\xee\xc1\xeb\xaf\xbf\x8e\x05\x0b\x16\xe0G?\xfa\xd1\x98(`\xf1<\x1b\x08\x04\xc4\xef^\xb0`AAq\xcdT#\x80\xc5\xe2\x91\x0a\xf2\xe8$\x05x/wo\xcd\x9a5\xd8\xb9s'\xacV+\x1a\x1b\x1b\xcb\xb2\x0e\x01\xff\xec\xe0\x05@x3\x02\xef\xe5\xfd\x9ds\xce9\x00PP\x10R<&\xa6{\xdc\xb2\xf8\xab \x01x\xac\x9b\x92\xbaM\xd0\xc7\x94\x04O\xc7\x95\xc59j\xf3I\xdd\x97\xaa\xb4,\xf5\xb5R\x1f\x97\x12\xe1\xa5\xaa\xd6H|\xacX\xb1\x02\x1b6l@SS\x13jkk\xe1\xf5zE\xc9\xbaN\xa7\x13\xc7\x87\xd3\xf9\xdeS. u\x07\xa9\xa9\xa9Acc\xa3HrN&\x93B\x80\xd2$-\xf5\xb2\xa3\x9e\xa5\x1a\x8dF\x08\xc3\xdd\xbbw\xa3\xb9\xb9\x19\xfd\xfd\xfd\x22\x8aYi\xb9\x80T\xedL&\xcdj\xb5\x1a+W\xae\x1c\xb3h\xbe\xf3\xce;x\xfb\xed\xb7E\xcf_\xa9\xf1\xb3\xf4\xda\xba\x5c.\xb4\xb4\xb4\x08\xf1\xae\xd7\xebO\xb8Z{*\x0b\x043w\xe6\x1f\xb3\xd9\x8c\xfa\xfaz\x1c9r\x042\x99\x0c\x7f\xf8\xc3\x1f\xb0y\xf3\xe6I\xfd,\xdd\x9fO<\xf1\x04>\xfc\xe1\x0f\x9f\xf0\xdc=\x9e\x00:\x9e\x0d\x88F\xa3\xc1\x8b/\xbe(\xd6\x1e\xb7\xdb-\x92\xe6\xc7\x13\x11r\xb9\x1c\xfd\xfd\xfd\xe8\xe9\xed\x11Q\xc3\xb3\xce:K8ZH+\xe4\xa5~\xb8\xc3\xc3\xc3\x05\x82h\xd1\xa2E\xa2\xe5\x1a\xcd{\xe4\x91;\xd9\x08`1\xc5\x95\xa5\xc0{\xa7\x00\xeb\xd6\xad+{\xb1\xa4\xd4\xdaG\xa5R\x8d\xe9\xb7~\xac*j\xde,\xceq\x01(\xbd\xd0\xe3%G\x92\xc5\x04E\xfeb\xb1\x98\xc8c\xa3\xe8\x9f4\xf27\xdf\x0c\xa1K\xd9s\x90`\x9b(\x97b\xa2\xf7\xa9\xf8\x98D\xadV\xa3\xa1\xa1\x01\xe7\x9cs\x0e\x1a\x1b\x1b\xe1t:\xe1\xf3\xf9\xe0r\xb9`\xb7\xdb\xa1\xd7\xebE\x83\xed\x99h\xdbE\xbbh\x83\xc1\x00\xb7\xdb]\x10\x05\xa4\x8a6\x8a\x02R\xc4O.\x97\x17t\xa6\xa0\xb6p\xf4\xbd###\xd8\xbf\x7f\xbf\xa8\x086\x9b\xcd\x22\x87\xb1\x12D \x8dq\x8a\xfeuwwc\xed\xda\xb5b\xf1\xa0\xeb\x18\x8b\xc5\xb0s\xe7ND\x22\x911\x95\xbf\xc5cb\xf1\xe2\xc5\xa8\xad\xadEUU\x15,\x16\xcb\xb4\x19a3s\x1b\xda|\x5cv\xd9e\xb8\xef\xbe\xfb\xa0T*\xf1\xc6\x1bo\x1cS\xf0\xd3\xa6\xf2\xe7?\xff9\xbe\xf4\xa5/!\x9f\xcfc\xfd\xfa\xf5\xa2\xe2\xf4D^\xcfD\xfd\xc6\xa7\xb2.\xf4\xf4\xf4`\xfb\xf6\xed\xe2\xff\xeb\xd7\xaf/\xa8\xf8,57(\x95J\xec\xda\xb5K\xa4]\x00\xc0\xd2\xa5K\x0b\xc4Y\xa9\xea\xe0\x9e\x9e\x9e\xf7\x0c\xb4\xdf\x7f}\xabV\xad\x12\x96id\xca\xadR\xa9\x84?\xdbd\x04\xf0T\xafa9Q\xa9T0\x99L\x18\x1e\x1e\x86V\xab-Y=<\xde\x5cW\xaa\xb9\x03o\x1e\xe7\xa8\x00\x9c\xe8\xe6\xa4.!T\xb0@\xc9\xec\xa9TJ,\xf2\xc5\x1d\x0d\xe6\xd3\xce\xa1\xd8\x5c\xb3\xd4\xc7\xe3\x09;i\x84Oz\xf3\xd1\xee\xcd\xe5r\xa1\xb1\xb1\x11+V\xac@CC\x03\xecv;\x9cN'\xdcn\xb78\xee\xd5\xe9t\xc2\x98r&#<\xd4#\xd8b\xb1\xa0\xba\xba\x1a\x0d\x0d\x0dhhh\xc0\xbb\xef\xbe+\xf2\xfb\xd2\xe94t:\x9d8\xf2\x966\xe8V\xa9T\xe2\x98\x98\xc4\xe1\xfe\xfd\xfb\xb1h\xd1\x22\xf8\xfd~8\x1c\x0e\x98\xcd\xe6\x8a\xc9\x05\xa4<\xcdp8\xfc^{\x1e\xa5\x12---cr}\xde}\xf7]\x1c:t\x08}}}\xa2\xf2\x97\x22\xe4\xd2\xcd\x82\xd1h\xc4\x9a5k\xe0\xf5zQUUU\xf6\xdc?\xde\xcd\xcf\x1fh\xc3y\xc9%\x97\xe0\xbe\xfb\xeeC6\x9b\xc5\xf0\xf00^y\xe5\x15\x9c}\xf6\xd9\x13.\xf4\x1d\x1d\x1d\xb8\xe5\x96[\xc4\xbdy\xd7]w\xe1\x81\x07\x1e(\xcb\x9c(\x9d\xcf\x8a=\xe6\x8e5O\xd1\x91\xf43\xcf<#\x04\x97Z\xad\xc6y\xe7\x9dW \xe4J=\x8fL&\xc3[o\xbd%:c\xd8\xedv\xd1\xe3\x976\x94\xc5\xf7\x99\x5c.Goo\xaf\x10\x80J\xa5\x12\xd5\xd5\xd50\x9b\xcd\xe8\xe8\xe8\x80B\xa1\x10\xc5\x19\xc1`\xf0\x98bn\xaa\x86\xdf\xd3\x91\xab\xadT*\xd1\xdc\xdc\x8c\xe1\xe1aaf<\x99y\x82\xe7\x8ay*\x00\x8b/<\xe5\xb2\xa5\xd3i\x11\x05\x0c\x87\xc3\xa2B\xb5T\xb1\xc2|c\xb2;\x5c\xe9Q\x83t\xd2\x96\xe6\x0b\xaa\xd5j\xd4\xd7\xd7\xa3\xa9\xa9I\xf4\x0f\xa4\xbe\xcbv\xbb\x1dV\xabU\x1c\x15R\x0b\x9b\x89&\xc2\xe9^t\xc8\xcb\xca\xe3\xf1\xc0\xef\xf7c\xc9\x92%8p\xe0\x80\xb0\x06J\xa7\xd3\x88\xc5b\xd0j\xb5\x05\x93\x0fM\xe6\xd9lV\xfcK\x15\xc1\xed\xed\xed\x22\xa7\x91\xda\xc3\x9d\xec\x5c\xc0\xe2\xe8\xdf\xe0\xe0 \x9a\x9a\x9a\xc6x]\xa5R)\xbc\xf3\xce;\xa2Y{<\x1eG\x22\x91(\xc8\xfb\xa3\xeb\xb4|\xf9r\xd4\xd5\xd5\xc1\xe9t\xc2b\xb1@\xab\xd5\x96=\xfa'mIW\xca\xfb\x8c\x99;\x9b\xd0|>\x8f\xe5\xcb\x97\xc3\xeb\xf5\xa2\xaf\xaf\x0f\xb1X\x0cO?\xfd\xf4\x84\x02P.\x97\xa3\xa9\xa9\x09_\xf8\xc2\x17p\xef\xbd\xf7\x02\x00\x1e|\xf0A|\xeaS\x9f\xc2\xca\x95+\xcb2\x1eK\x1d3N6\xbf\xec\xd0\xa1C\xf8\xe5/\x7f)\x22j\xa7\x9f~:\x96/_\x8e\xc1\xc1A1\xef\x15\xcf\x0b\xf9|\x1e\x81@\x00;w\xee\x14\x9f[\xbat)<\x1e\x8f(H,NI!\xa1\xd6\xd3\xd3#^oMM\x0d\xdcn\xb7\xe88\x22\x97\xcb\xe1v\xbb\x91\xcdf'\xf4Y,\xd5\xa5\xe7d\xe2\xf1x\xe0\xf1x\xf8&a\x018y!C\xc2$\x91H\x88\x9eyT\xf9H\xbet\xd2\x82\x06fr\x93\xb4T\xa8)\x14\x0a\xd1\x9e\xa6\xaa\xaa\x0a---\xc2\x10\x93|\xa3l6\x9b\xf8\xbf\xc9d\x12\xa2\x8f\xec\x0fNv\x22-\x19;\x9b\xcdfTWW\xc3\xe7\xf3a\xe1\xc2\x85x\xe7\x9dw\x0a\x8e\x81\xa5cJj\x05Cm\x8f\xe8H&\x16\x8b\xa1\xbd\xbd\x1dMMM\xf0\xfb\xfd\x222v\xb2\xa3\x80\xb4\x09\xa2c\xddH$\x823\xce8\xa3\xe0~\x91\xc9dhmmE__\x1fz{{\x11\x0e\x87E\xf4\x5c\xbaQ\xa2\x8a\xf8\x0d\x1b6\xc0\xe1p\x88#\xfcJ\xf6>df\xc7\xfcRSS\x83\xb3\xcf>\x1b\xbf\xfb\xdd\xef\x00\x00/\xbc\xf0\x02\xee\xb8\xe3\x0e\x98\xcd\xe6\x09{\x80\xdfv\xdbmx\xec\xb1\xc7022\x82l6\x8b\x0f\x7f\xf8\xc38p\xe0@A\xa1\xd6\x89\xdc;\xe3E\xc2&\xaa\x08V(\x14\xb8\xe3\x8e;DG\xa1l6\x8b/\x7f\xf9\xcb\x18\x1d\x1d-\x88\xb2\x95\xca1\x1c\x18\x18\xc0\xe1\xc3\x87\xc5\xd7\xd7\xae]+r\x01\xa5U\xc0\xd2S\x9aT*\x85\xce\xceN\xf1<>\x9f\x0fn\xb7[XY\xa9\xd5j\x98\xcdf\xe1i:\x91\x88\xe5\xfb\x98\xa9x\x01(5\xb4\xa5\x85J\x0ay\xfeQ'\x0aJd\xa7\x9c\xbfb\x8cF#\xf4z\xbdh\xc6,\xbd\xc1\xe8f\x9f1_\xaacL\x94\xd3\xf5\x9c\xc5\xff\xd2b\xafV\xaba\xb1XD\x95'\xb5\xca!\xaf>\x12vz\xbd\x1eF\xa3\x11\x16\x8bE\x1c\xef\xd2\xd7(\xff\xa4\x12\x84\x9ft\xa2#\xbfG\xb7\xdb-\x8c\xa1)\x0aH\xa6\xd7\xe4AE\x16\x15\xf4\xbeP$\x90\x0a$\xb4Z-:;;\xd1\xd5\xd5\x85\xbe\xbe>\xd4\xd6\xd6\xc2j\xb5\x8a\xca\xd8\x93\xf17\xd315\xf9\xfeuuua\xf1\xe2\xc5\xa2I;]\x8bl6\x8b\xed\xdb\xb7\xa3\xb3\xb3S$\x93K;\xb7H\xef\x81s\xcf=\x17UUU\xa2\xf2w:\xa2\x7f\xcc\xfc\xe4\x8a+\xae\xc0\x13O<\x01\x00\xd8\xbd{7\xb6n\xdd\x8a\x8b.\xbah\x5c\x11\x96\xcf\xe7Q__\x8fo}\xeb[\xb8\xe9\xa6\x9b\xa0\xd5jq\xe4\xc8\x11|\xf0\x83\x1f\xc4\xb3\xcf>{B\x9bC\x12n\xa5N\x96(\xa7\x8e\x9c$\xa4si2\x99\xc47\xbf\xf9MaO\x92\xcb\xe5\xf0\x95\xaf|\x05Z\xad\x16\x89DB<\x7f\xb1\xd8\xcaf\xb3\xd0\xe9tB\x00+\x14\x0a\xe8t:\x9cw\xdeyH&\x93\xef}\xbfB>&\x07\x90\xd6\xc1\xbd{\xf7\x8a\xcf577\x0b\xf3\xfaK/\xbd\x14\x89d\x02&\xa3\x09\xd1h\x14\x0a\x85\x02\xd9lv\xdc\xf9h.\x09\xc0\xf9\x96\xd3?g\x05`\xb1MI>\x9f\x177\x98\xd4z\x82nX\xda\x9d\xd1\xf1/U\xfe\x16[\x95\xb8\x5c.\xac\x5c\xb9\x12\xd5\xd5\xd5\x22\x99]\x9a\xa0+\xad\x08\x9e\xe8\xa6\x99\xe9\x08\xdct\xdc$RGu\x12\xc1\xd4\x06\x8d\x1eT\x8dE\xff\xeat:!\x02\xe9c\xfa:y\xe6I\x05S\xa5\xa1P(\xa0\xd7\xeba\xb3\xd9\xe0\xf3\xf9\xe0\xf3\xf9\xd0\xd2\xd2\x82m\xdb\xb6\x89\xd7Lm|T*\x95h)(\xad\x10V\xa9T\x05\x1b\x8b={\xf6\x08c\xe8\xaa\xaa*\xe8t:\xd1\xc7r\xa6\xa1\x08\x1e\xf9\xfe\x85B!\x9cw\xdeyc*\x7f[[[\xd1\xd9\xd9\x89\xbe\xbe>D\xa3Q\x912Q\x5c\x1ce4\x1a\xb1b\xc5\x0ax\xbd^\xd1\xaaO\xa3\xd1L\xeb}q\xacj?fv,\xc4\x93\x19#\x1f\xfd\xe8G\xf1\xa5/}I\xb4g\xdc\xb2e\x0b:::D\xd5}\xa9y1\x93\xc9\xe03\x9f\xf9\x0c\xb6n\xdd\x8a\x87\x1f~\x18j\xb5\x1a\xcf=\xf7\x1cn\xb8\xe1\x06\xfc\xeaW\xbf\x9a\xb25\x8cT`\x8d'\x1ah\xdd\xc9\xe7\xf3H&\x93\x05\x7f[$\x12AGG\x87\xc8\x0d>\xf7\xdcs\xb1a\xc3\x06qZ \x15\x80\xd2M\xb7L&CGG\x07\xb6o\xdf.\xe6\x94k\xae\xb9\x06N\xa7S\xf4\x11V\xc8\x15\xc2\xa7P\x1a\x08\x19\x1a\x1aBww\xb7\xd8\x88m\xda\xb4I\xbc\x1e\x9a\x8b)7\xf0X\xf3\xf7\xa2\xfd=\x00\x00 \x00IDAT\xf0\x5c\x12J\x5c\xf81\xc7\x22\x80\xd2\x1085D\xa6\xddQ\xf1\xe0%\xdb\x8b\xe2\xfe\xa5\xf4=K\x96,\xc1Yg\x9d\x85\xfa\xfazQ\x98@\xc7\x93\xf31\x0c^\x9coE\xb9&\xb4\xd3%\x11#\x8d\xe6\xd1C*\x14\xe9{\xe89*\xf9&$qj4\x1a\xe1r\xb9\xd0\xd0\xd0\x80\xce\xceN\x1c|\x18g\x9ey\xe6\x98\xdc\xa3d2\x89\xdd\xbbw\xe3\xc0\x81\x03\x88F\xa3B\x00\x16wq\x01\x80SN9\x05>\x9f\x0f\x1e\x8f\x07V\xabU\xb4\xeac\x98\x13]\x88I$~\xe3\x1b\xdf\xc0\x96-[\xa0\xd1h\xd0\xd9\xd9\x89\xff\xfe\xef\xff\xc6\x95W^9\xae\x88\xa4{\xf3\xfe\xfb\xef\xc7\xae]\xbb\xf0\xf6\xdbo\x03\x00\x1e~\xf8a\xc8\xe5r\xfc\xe2\x17\xbf\x98\xb2\x08\x9c(\x02H\x7f\x8f^\xafG<\x1e\x1f\x93\x9f\xeap8\xf0\xf3\x9f\xff\x1c\xb7\xddv\x1b\xb4Z->\xfb\xd9\xcf\x8a\xcaT\xe9\xf3K\xdf\x97\x5c.\x07\x9b\xcd\x86;\xef\xbc\xb3`\xa3\xff\xe3\x1f\xff\x18\x7f\xfb\xdb\xdf\x0aOgd\x85\x01\x11\xb5Z\x8d\xd7^{M<\xaf\x5c.\xc7\xe5\x97_>\xee|\xa7\xd1hD$r\xbc\xf5u\xael:\xb8(d\x8e\x08\xc0R\x17\xd1n\xb7\x0b\x13g\xe9\xf7Q\x7fZi\xd4\xaf8\xfaWWW\x87\xf3\xcf?\x1f\x8b\x16-B}}=<\x1e\x0f\xf4z\xbd\x88fHo\x84\xc9V~\xcd\x85\x1bF*\x02\xa5\x0f\x12\x83R\xab\x96b\xaf<\xcaw\x99m\x93\x085.\xb7Z\xad\xf0x<\xa8\xaf\xaf\x87\xdf\xefG0\x18\x14\xd5\xb3\x14\xfd\x94&]K{\x03\x03\x10f\xc9\x0a\x85\x02\xbbv\xed\x12\xc50\xe4\x96?\xd3Q@\xca\xfd\x0b\x87\xc3\x18\x1a\x1aB.\x97\xc3\xb2e\xcb\x0a\x22\xe42\x99\x0c\x87\x0f\x1f\xc6\x8e\x1d;\x10\x0c\x06122\x22<3\xa9\xdf&a4\x1a\xb1t\xe9R\x11\xfd#\xdf?6Ee&\x8a\xf8\xb5\xb7\xb7\xe3\xd0\xa1C8\xf7\xdcs'\xec\xeeC\xc7\xa97\xddt\x13\xbe\xfa\xd5\xafbtt\x14j\xb5\x1a[\xb6l\xc1\x95W^9\xe1\x18\xa3\xcd\xe7\xd3O?\x8d\x0d\x1b6\xe0\xd0\xa1CP\xab\xd5x\xe8\xa1\x87p\xf0\xe0A<\xfe\xf8\xe3\xf0z\xbd\x93\x9a\xcb\xa5\x1b\xa4\x89\xc4\x03m\xfe\x8a\x85i.\x97\x83\xc1`\xc0\xb6m\xdb\xd0\xd6\xd6\x86D\x22!\xee%:U\xa2y\xc0j\xb3bxx\x18.\x97\x0b\xaf\xbe\xfa*v\xef\xde-:\x12\xdd~\xfb\xed\xc2\xfa\xa4\xb8\x08O\xda\xf7V\xa3\xd1\xe0/\x7f\xf9\x8b0\xd5^\xb7n\x9d(N+e\x17c2\x99D.\xa2\xc1`\x80\x5c.\x17=\x84I 2LEF\x00\xa5\x13\x0cE(\x8aof:\x9e\x8cD\x22\x05v/4\xc0\x95J%\xce>\xfbl466\x0a\xdb\x0e\xab\xd5Z\x90\xcb4\xdfv\x0d\xc5\x13\xccx\xbbA\xe9\xce\xb5\x94Y\xeal\x14\x024&\xccf\xb3\xe8\x0e\xb2h\xd1\x22\x1c\x9f\x18\x1fmmmH\xa7\xd3\xa2\x18\x86\xf2\xff\xc8\x08\x9a\xc6\x1e\x1d}\xd3\x04\x1a\x8f\xc7\xd1\xde\xde\x8e\xc6\xc6Faz\xad\xd7\xeb\xcbR\x998\x19\xa4\xbe\x7f###\x88\xc5b8\xe5\x94S\xc6\x5c\xdf\x9e\x9e\x1e\xbc\xf9\xe6\x9b\x08\x85B\x08\x85B\xc2$]\x9a\xfbJ\x1ekg\x9f}6\x1c\x0e\x87\xb8_\xa6\xbb\xeb\x874RY\x09\x05X\xcc\xd4\xb0X,x\xec\xb1\xc7\x84\xe0{\xe0\x81\x07p\xf7\xddwOx\x1cK\xd7\xfb\xd2K/\xc5\x05\x17\x5c\x80\xff\xfb\xbf\xff\x03\x00\xdc{\xef\xbd\xb8\xfe\xfa\xeb\xb1l\xd9\xb2\x09E[>\x9f\x87\xcf\xe7\xc3\xce\x9d;q\xd9e\x97\xe1\xe5\x97_\x86\x5c.\xc7\xd0\xd0\x10>\xfd\xe9O\xe3\x87?\xfc!\x1e~\xf8a\xac]\xbb\xb6 \x80P<\x8e\xa5\x11\xb6\xf1\x8e\x80K\xad\x17t2@\x1e\xa0\xa5\x02\x14\x14\x01\xa4\x8a\x5c\xbb\xdd\x8e\xfb\xef\xbf\x1f;v\xec\x10\xdf\xf3\xaf\xff\xfa\xafp\xbb\xddc\xde'\xe9\x06<\x9f\xcf\xc3b\xb1\xe0\xbf\xfe\xeb\xbf\x0a:|\x5c{\xed\xb5\x22-\xa5\xd4f\xd7b\xb1\x88cs\xa7\xd3YpoI\xd3w\xe6\x0a\xbcy<\xc9\x01\x96\x99\xbe\xd8T\xf9H\x85\x1b\xc5a\xffE\x8b\x16\xc1l6\xc3j\xb5\x8aH\x86\xb4R\x93\x1f\xf3\xefHO\x1a\x05\xa4N%\xcb\x96-\x13\x13<\xd9\x0aIm\x84h\xb2\x97\x16\xc5\x90(R\xab\xd5\xa2\x22\xb8\xb7\xb7\x17\xc3\xc3\xc3\xa2\x0b\xcdtOH\x94\xea\x90H$D\xdb\xb7\xea\xeajX\xad\xd61\x11\xb5m\xdb\xb6\xa1\xb7\xb7W\x1c\xfd\xc6b1Q8\x22\x15\x93k\xd6\xac\x11\xb9\x7f\x16\x8b\x05:\x9dnZ+\x9b+\xa5\xea\x9e9\xfe\x0d\x88\xdf\xef\xc7\xc6\x8d\x1b\x91N\xa7\xa1R\xa9\xf0\xbd\xef}\xef\x98Q5\xba\x17\xd5j5\xfe\xe3?\xfeC\x88,*\x88\xa0\xfb\xefX\x9bV\xadV\x8b\x97^z\x09\x9f\xfb\xdc\xe7\x0a*n\x0f\x1c8\x80\xd3N;\x0d\xcb\x97/\xc7\x1f\xff\xf8G\xf4\xf4\xf4\x8c\xc9\x87S*\x95\xa2\x8bOq\x0el\xf1\xeb,\xfe\xdd\xe4*`\xb3\xd9D\xbe\x1ed\xe3\xdf\xa72\x99\x0c;w\xee\xc4-\xb7\xdc\x22\xee\xa7\xea\xeaj\x5c~\xf9\xe5\x13\xfe\x9d\xb4\xd9\xe3LD\xffJEP\x98\xd9\xb1\xa1\xca\xe7\xf3\xf8\xc1\x0f~P\x10Q{\xf0\xc1\x07E\xca\xc4\xb1\xae\xff\xd9g\x9f\x8dO\x7f\xfa\xd3\xc2\x7fs\xef\xde\xbd\xf8\xc4'>!*\xf1\x8fu/g\xb3Y\xfc\xf4\xa7?\xc53\xcf<\x83u\xeb\xd6\x89\xa8\xb8N\xa7\xc3\xee\xdd\xbb\xb1y\xf3f477\xe3\xd2K/\xc5-\xb7\xdc\x82{\xef\xbd\x17\xbf\xfd\xedo\xf1\xdcs\xcf\xe1\xa5\x97^\xc2\xc8\xc8\xc81\xffF\xe9\xd8\xac\xad\xad\xc5\xc6\x8d\x1bq\xd6Yg\xc1\xe1p\xfcs3\x8d\xd2\xfd\x84\x95J%\xc2\xe1\xb0\xc8\x8d\xa4\xf7\xe8\x96[n\x81\xc9d\x9a\x94H\xfe\xdb\xdf\xfe&6\x96\x00p\xc3\x0d7\x1c\xb3\xe0E\xa1P`\xd1\xa2EX\xb6l\x19\x00\x14\xa4\xa6\xd0I\xc7l\xe5D{73\x15.\x00'\xb3\x18H\x8f\x8dJ-\xb6dUB\xbbK\x1e$\x0cM\xaa\xd4\x7f\xd2\xe5r\xa1\xb6\xb6\x16\x0b\x16,\x80\xc9d\x12\xd5\xe4\xe4+Y||D\x132E\x12\xc9^f\xef\xde\xbd\xe8\xed\xedEww\xb7\xc8\xb1\x9b\xce(`q\xee\xdf\xe1\xc3\x87q\xca)\xa7\x88\x02\x1d\xe9&\xe9\xed\xb7\xdfF[[\x1b\x82\xc1`A\xee\x9f\xf4\xbe\xc9\xe7\xf3\x22\xfa\xe7\xf5za\xb5ZO\xda\xa6\x89\xf3\x01g\xd7B\x9cN\xa7\xb1z\xf5j,\x5c\xb8\x10\x89D\x02J\xa5\x12\xdf\xf9\xcew&5\x8f\xd3\xe9\xcd\xfd\xf7\xdf\x8fe\xcb\x96!\x95JA\xa9T\xe2\xf1\xc7\x1f\xc7]w\xdd5)\x11I\x82\xea\xe2\x8b/\xc6_\xff\xfaW<\xf5\xd4Sp\xbb\xdd\x88\xc7\xe3P*\x95\xc2\xd4\xfd\xa5\x97^\xc2O\x7f\xfaS|\xf1\x8b_\xc4\xb5\xd7^\x8b\xcd\x9b7c\xcb\x96-hkk\x9b\xd0\xec\xb9\x18\xaa\x8a\xa7\x1c;\x00%\xc5\x1f\x09\xacd2\x89K.\xb9\x04###\xc2\xcc\xf9\xa6\x9bn\xc2\xca\x95+\x85\xdf\xe8D\x1c=z\x14/\xbf\xfc\xb2\xf8\xffE\x17]\x84\x85\x0b\x17Njc&\xcd\xdd\xa5>\xec\xf4\xda\xf4z\xfd\xac\x1dw<7\xccq\x01H\x15\xbd\xc7\xf2\xe5\xa3\x5c\xacR-\xa4\xa4\xed\xccJ5\xd6f\xe6\xb7\x08\x94\xe6\x02\xd6\xd5\xd5\xa1\xae\xaeN\x1c=I\x8bB\xa4\x91*z\xd01\xb0\xd4S\xf1\x9dw\xdeAOO\x0f\x06\x06\x06D\x7f\xdd\xe9\x8a\x02f\xb3Yd2\x19D\x22\x11\x11\xd9;\xed\xb4\xd3\x0a\xda\xaa\x01@WW\x17\xb6n\xdd*,b\x8a}\xff\x08\x9dNWP\xf9KQ\xf3\x99\xcc\x95e\xe17;!\x91v\xd7]w\x89\xb1\xd9\xd9\xd9\x89\xfb\xee\xbboR?O\xc6\xeb/\xbd\xf4R\x81\x0d\xd3\x9dw\xde\x89G\x1f}tR\xe2LZ\xa8u\xf9\xe5\x97\xa3\xbf\xbf\x1f\xcf?\xff\xf8\xa0\x98\x7f\xa4\xc7\xcb\xd2\x00\x85\xf4\xf7=\xf8\xe0\x83\xc2\xc7\xb4\xa1\xa1\x01\x9f\xf9\xccg\x8e\xeb:iuZ,jY\x84E\x8b\xdf\xb3D\x9b\x8dE \xc5\xc6\xd8\xcc\x1c\x15\x80\x93\xbd\xc0\xc5\xe2\xb08\x12\xc8\xdee\xccD\x22P\xab\xd5\xc2\xe1p\xa0\xa6\xa6\x06MMM\xa8\xae\xae\x16\xbez$\x04\xa9ZV\xba\x18\xa8\xd5j\xf1\xf3tL\x9aN\xa7\xd1\xda\xda\x8a\xae\xae.\x0c\x0e\x0e\x22\x1c\x0e\x1f3\x99\xfdx\x05 \xf5\xbc\x0e\x04\x02\x88F\xa3X\xbe|\xb9xm\xe4C\xb6\x7f\xff~\x1c8p\x00\x83\x83\x83\x08\x04\x02\xc2\xa7Lj\x94N\x0b\xcb\x19g\x9c\x01\xaf\xd7+r\xfff:\xfa\xc7\x85 \xb3_\x04\x9aL&l\xd9\xb2E\x88\xb0\xd6\xd6V<\xf5\xd4SSZ\xac/\xbc\xf0B<\xf2\xc8#B@\x01\xc0\xd5W_\x8d\x1f\xfd\xe8G\xc2~i2\xaf\x85\xbaw\xe4r98\x1c\x0e\x5c{\xed\xb5\xb8\xe7\x9e{\xf0\xe7?\xff\x19\x7f\xfd\xeb_\xf1\xdak\xaf\xe1\xb5\xd7^\xc33\xcf<\x83\xe6\xe6f\xa4\xd3\xe9I\x09\xc0\xe2\xc0\xc4x\xe2D&\x93\xe1\x8e;\xee\xc0}\xf7\xdd'N\xa9\xf4z=^\x7f\xfdux\xbd\xde1\xebSq\x852}|\xcf=\xf7\x08G\x81t:\x8d[o\xbd\xf5\xb87L2\xc8\xe0v\xbb\xd1\xd2\xd2\x02\x8b\xc52'\xc6\xdcd\x8d\xc7\x99Y&\x00'\xbb\xf8\x14W\xfe\x16G\x0a\xa7#\x02\xc3\xcc\x0d(\x11\x9ar\x01\xfd~?\x96/_.\xc4H,\x16\x13\xc7D\xa5\xa2\x80d\x8cM\x1fS\xc1\xc5\xe1\xc3\x87\xd1\xd5\xd5\x85\x91\x91\x91\x92\xfd\xab\xcb\x11\xfdK\xa5R\x18\x1d\x1d\xc5\xe0\xe0\xa08\xc6.\xe6\x9dw\xde\xc1\xf0\xf00\x06\x06\x06\x90L&\x0b\xbc2\xa5G_\x8d\x8d\x8dhnn\x86\xd3\xe9\x14\x95\xbf3\x19\xfdc\xe6\x0e\x9f\xfd\xecg\xe1\xf7\xfbE\xf5\xea\xf5\xd7_?\xa5{ \x97\xcb\xe1c\x1f\xfb\x18\xee\xb9\xe7\x1e\xc4\xe3qqzs\xdbm\xb7\xe1\x9b\xdf\xfc\xe6\x94\xac\x95J\xd9\xbe\x00\x80\xc9d\x82\xc7\xe3A]]\x1djkkE\x04p\xa2\xe78\x96\x07\xaa\xd4P:\x91H\xe0\xdf\xfe\xed\xdf\xb0u\xebV\xf17%\x12\x09\xec\xdf\xbf\x1fUUU\xc8d2c\x8c\xf8\xa5\xcfA\xddG\x8e\x1c9\x82{\xef\xbdW\xe40644\xe0\x92K.\x19\xf75\xcc\xb7\x0d\x07\xb5\x80e\xe6\xa0\x00\x9cj\xa8\xb7\x94\xad\x09G\xff\x98cA\x15\xc1v\xbb\x1d\xd5\xd5\xd5\x22\x12H\x96\x11\xa9T\x0a\xf1x\xbc\xa0\x070\x8d+*\x02!k!\xa5R\x89\xd1\xd1Q!\x00)\x0a(=J>Q\xa4\x95\xbf\xa1P\x08}}}\xd8\xb0a\xc3\x98{\xa1\xbd\xbd\x1d\x07\x0f\x1eDWW\x17\x82\xc1\xa0\xa8 \x94F\xff\xe8\xde\xa0j\xc6\xaa\xaa*q$\xc6>\x99\xcc\xf1\x8cM\x00\xf8\xc9O~\x22<5\xe5r9.\xb9\xe4\x12\xe1\x957\x99\x8d\x7f*\x95\xc2\xad\xb7\xde\x8a\x9f\xfe\xf4\xa7b\xbc*\x14\x0a|\xfd\xeb_\xc7\xd5W_\x8d\xde\xde\xde\xb2Dv\xa4y\xe1\x13\xdd\x9fr\xb9\x1cN\xa7SD\x09\xc7\xab@\xd5h4hmm\xc5\x96-[\xb0o\xdf>\xf1\xdcUUUhkk\x83\xd7\xeb\x1d\xb3NI\xc5\xa5\xf4y\xe5r9n\xb8\xe1\x06\x91\x8f\x0c\x00\x9f\xff\xfc\xe7\x11\x8f\xc7y\xa0\x15m\x18\x8aE!3\x07\x04`\xa9\x0b9\xd5s\x7f\xce\x13`&\xb3\xe0h4\x1aX,\x16x\xbd^\xd4\xd6\xd6b\xd1\xa2E\xc2\x0c\x9a,%\xa4\xc7\xc0R\xe3b\xaa2\xa7#aZ\x04\xba\xba\xba\xd0\xd9\xd9\x89@ \x80X,V\x96#N:\x9a\xa6\xe8_ww7\xbc^/\xbc^\xef\x98\xe7omm\x15\xbe\x84\xd1hTD\xff\xa4\x0b\x1d\xb5\x8c\xab\xaf\xaf\x87\xd7\xeb\x15\xb9\x7f\x95d\x974\xd5\xfb\x9d9\xb9Q\x99t:\x8d\x0f~\xf0\x83\xf8\xe8G?\x8aT*\x05\x85B\x81W_}\x15?\xfb\xd9\xcf&\xbd\xa9\xa0\x88\xdc\xe7>\xf79<\xf5\xd4Sb\xa3\xa6R\xa9\xf0\x87?\xfc\x01\xeb\xd7\xaf\xc7\xd0\xd0\xd0\x09\x8fQi\xca\xc4D\xbd\x80\x95J%\xea\xeb\xeb\xb1r\xe5J,]\xba\x146\x9bM|\x9d\x0c\xe35\x1a\x0d\x1ex\xe0\x01\xfc\xbf\xff\xf7\xff\x10\x0a\x85D\x8b\xb6\xfa\xfaz\xfc\xe3\x1f\xff\xc0\xc2\x85\x0b\x0b|\x0a\xa5\x05\x8a\xd2\x8fi\x1c?\xf4\xd0Cx\xe9\xa5\x97\xc4\xef\xd9\xb4i\x13\x1a\x1b\x1bgu\xe5n\xb9\x84^\xa9{\x9d\xd7\xfa9&\x00y\x82gfj\xd1\xa2\x8a\xe0\xaa\xaa*\xf8|>\xf8\xfd~444 \x95J\x15\xe4\x06\xa5R)1aK;\x04\xd0q\x17=\x12\x89\x04\xf6\xed\xdb'*\x82\xc3\xe1pY*\x82\xa9\xb0#\x16\x8baxx\x18\xbd\xbd\xbd8\xff\xfc\xf3\x0bz;\x03@OO\x0fv\xef\xde\x8d\xee\xeen\xf1\xbb\xa5\xa6\xcfR\xdf\xbfSN9\xa5 \xf7O\xab\xd5VD\xf4O\xfa^M\xf6\xbe\xe7h\xff\xc9\x87\xf2\xf4\xee\xbd\xf7^\xb8\xddnqDw\xe7\x9dw\xe2\xc8\x91#S\xda\x98\x01\xc0\xbf\xfc\xcb\xbf`\xcf\x9e=\xa2u\x1c\xf0\x9e\x0dJUUUY\xd6\x83\xc9\xf4\x02\xa6\xfb\xba\xb9\xb9\x19MMM\xa2\xea\x99ZF\xbe\xf2\xca+X\xb0`\x01\x9e|\xf2I1\x0e\xa9\x08\x85\x22\x7f\xc5\xbfS\x1a\x01\x94\x1e\xfd\xa6\xd3iD\xa3Q\xfc\xfb\xbf\xff\xbb\xf8\xba\xd3\xe9\xc4u\xd7]\x87\x5c.7'r\xf7\xca\xad\x07\x8a\xfdL\x999,\x00y\x92g\xca\x0d\xb5E2\x1a\x8dp\xbb\xdd\xa8\xaf\xafGCC\x03\xf4z=\xb2\xd9,\x92\xc9$\x92\xc9$r\xb9\x5c\x81Y4\x89@\x95JU\x109S\xa9ThkkCWW\x17\xfa\xfa\xfaDE\xf0\x89\x1c\x03\x93\xf8K&\x93\x08\x85B\xe8\xea\xeaBSS\x13\xecv\xfb\x98\x1c\xc5]\xbbv\xa1\xbd\xbd\x1d\xa3\xa3\xa3\x08\x87\xc3\x22\xff\xaf\xb8\xd2\xb1\xb1\xb1QTC\xda\xedv\xe1\x13V\xe9\x13~\xf1\xff)\xcf\xb7\x9c\xb9\x96\xcc\xf1\xa3T*QUU%z\xfa\xcad2\x0c\x0c\x0c\xe0\xfa\xeb\xaf/\xb0V\x9a\xec5onnF \x10\xc0\x86\x0d\x1b\xb0j\xd5*\xfc\xecg?C2\x99,\xcbZ0\x99\xcdN\xa9~\xc1\xf9|\x1e;w\xee\xc4\xe5\x97_\x8es\xce9\x07\xa3\xa3\xa3\x05\x91\xbc\x8f}\xeccx\xe4\x91GJ\xaeY\xb4i,6l\xa7t\x92\xdbo\xbf]\xf4\x1c\xcf\xe5r\xf8\xcew\xbe\x03\xaf\xd7\x0b\xbd^?\xa6\xc77\xc3\x81\xa2y#\x00\x19f: \xd1\xa6\xd7\xebEEpcc#\x5c.\x970L\x96V\xf3f\xb3\xd91\x91\x03\xa91\xb4L&C2\x99\xc4\x9e={\xd0\xd9\xd9\x89\xc1\xc1AD\x22\x91\x82<\xc2\xe3\x15\x80\x14\xfd\x0b\x87\xc38\xe5\x94S\xc6$\xba\x0f\x0c\x0c\xe0\xad\xb7\xde\xc2\xe0\xe0\xa0(B\xa1\xdf+\xcd\xfdS*\x95X\xb5j\x15jjj\xe0t:a0\x18\x0a|\x0dgj\x92&\x9fO\xfa\xdcd+1\xa5\xcf#=Zc*\x87\x8f}\xecc\xb8\xf4\xd2K\x85\x98y\xe5\x95Wp\xc3\x0d7L\xaa\xc3\x87\xf4\xba\xd3&\xeb\x85\x17^\xc0\xd6\xad[\x91\xcb\xe5\xa0\xd1h\xca\x22\x0a\xc6\x1b\xebTAL) 4\xbe\xe2\xf18\x1ey\xe4\x11\x5cq\xc5\x158\xf5\xd4S\xf1\xa7?\xfd\x09j\xb5Z\xe4\xd7.[\xb6\x0cw\xddu\x17\xae\xbb\xee:\xc4b1Q\x8d\x5c,\x00\x8b#\x80\xd4g\xfc\xdb\xdf\xfe6\xfa\xfa\xfa\xa0P(\x90N\xa7q\xc3\x0d7\xe0\x93\x9f\xfc$\x1a\x164\xe0\xb4\xd3N\x83V\xab\xe5\x81\xc5T\xe6\xc6\x8f\xdf\x02f\xd6\xee^\xde\xf7\xf5\xb3X,p\xbb\xdd\xa8\xa9\xa9\xc1\xa2E\x8b\xd0\xdb\xdb+\xaa\xf6\x92\xc9$\xf4z\xbd\x10-R?@Z\xa8(\x7f)\x93\xc9\xe0\xc8\x91#8|\xf80jkk\xe1r\xb9`2\x99\xc4\xf7L\x05\x8a8R\xf4\x8f*\x7fkjj\x0a\x16,\x99L\x86\xdd\xbbw\xa3\xa3\xa3\x03###\xe2\xf8\x97\x8e\xb1\xa5\xa2\xcb\xeb\xf5b\xc5\x8a\x15p\xb9\x5c\xb0Z\xad0\x18\x0c\xd3\xde\xf6\xedD\x16\xec\xf1\x16\xeab\x01\xc9T\x16\x7f\xfa\xd3\x9f\xe0\xf3\xf9088\x08\x85B\x81G\x1f}\x14\x8d\x8d\x8d\xf8\xc67\xbe1\xe5\x13\x9d\x135.\xa6\xfb\xa08\xe7N\xea\xfbIb/\x97\xcbA\xa9T\xc2`0\xc0b\xb1\xe0\x8d7\xde\xc0\xfd\xf7\xdf\x8f\xe7\x9f\x7f\x1e\x81@@\x8c5\x95J%\x8a4\xbe\xfc\xe5/\xe3\x8c3\xce\x80N\xa7\x13]H\xc6\x9bkJmZ\x1e{\xec1l\xdb\xb6M\xbc\xd63\xce8\x03\x0f<\xf0\x002\x99\x0cjkjy0\x1dC\xcc3'y\x0d\x9d\xc9\x8b\xcf\x83\x80)'\xd2(\xa0\xddn\x87\xdf\xefGcc#\xdcn\xb7\x88\x04\xe4r9Q\xd5+\xed\x17L\x22\x90\x1e\x9434::\x8a\xf6\xf6vtuuahh\xe8\xb8*\x82i\x9cSnP(\x14\xc2\xd0\xd0\x10V\xaf^=\xe6\xf5\xf7\xf7\xf7\xe3\xad\xb7\xdeB__\x1f\x82\xc1 \xb2\xd9\xac(^\xa1\xc8#5\xbe_\xb7n\x1d\x5c.\x17\x9cNg\xc5W\xfeR5i\xb1\x10\xa4\xbcF\x8e\xfcU.\xc9d\x12;v\xec\x10\xd7K&\x93\xe1[\xdf\xfa\x16\x9ex\xe2\x89\x19\xa9\xda\xa4\xe7\xdf\xb6m\x1b\xda\xda\xda\x10\x0a\x85\x10\x89D\x90H$\x84):U\xda\xc6\xe3qD\x22\x11\x8c\x8c\x8c\xe0\x9dw\xde\xc1SO=\x85\xaf}\xedk\xb0\xd9lX\xbf~=~\xf3\x9b\xdf`tt\xb4\xe0\xf9\xb5Z-n\xbe\xf9f\xf4\xf7\xf7\xe3\xa2\x8b.\x82B\xa1\x10=\xeaK\x8dY\xfa\xbf\xf4s*\x95\x0a\x7f\xfe\xf3\x9f\xf1\xa3\x1f\xfdH|\xde\xedv\xe3\xf1\xc7\x1f\x17Ef\x0cS\xe9\xcc\xd8(\xa5\x89\x84\xcdc\x99\xb2\xee`\xde\xaf\x08\xb6Z\xad\xf0z\xbd\xf0\xfb\xfdX\xb2d\x09zzz\xc4\x91\x0c\x1d\xf7\x90\x8b>E\x14\xb2\xd9\xac\x88\x06\x90\xe5E.\x97C{{;\x16-Z\x84\x9a\x9a\x1a\xb8\xddn\x98\xcd\xe61\x1d\x00hL\xd3sI\x05\x0dE\xee\x92\xc9$\xc2\xe10\x06\x06\x06\xa0\xd5j\xd1\xd4\xd44&\x82\xb2o\xdf>\xb4\xb5\xb5!\x12\x89 \x16\x8b\x89~\xc4\xd2$\xe9|>\x0f\xbf\xdf\x8f\xa5K\x97\x8a\xe8\x9f^\xaf\xaf\x88\xca\xdf\xf1\xaa0\xd5j5\x22\x91\xc8\x18\xa1G\x02\x90\xc49o\x0a+\x0f\x8dF\x03\x97\xcb\x85g\x9f}\x16\x17]t\x91\xe8\xdet\xf5\xd5W\xe3\xc9'\x9f\xc4\x87>\xf4!\xa4\xd3\xe9i\xebJ\x91\xcdf\xa1T*q\xcd5\xd7\xa0\xb3\xb3\x13\x00\xe0\xf1xPUU%r^GGGE\x8e\xef\xe8\xe8(\x8e\x1e=:\xe6o {\x1a\x8a\x10\xaeX\xb1\x02\x9b6m\xc2\xed\xb7\xdf\x0e\x87\xc3\x81\xe1\xe1a\xb1.QZ\xc2x\x1b\x13\x12\x80\xb9\x5c\x0ef\xb3\x19\x8f=\xf6\x18>\xfe\xf1\x8f\x8b{0\x95J\xe1\xe1\x87\x1fF}}=\x0f\xa0In\xde\x999.\x00yrgfb\x22!_@\x87\xc3\x01\x9f\xcf\x87\x86\x86\x06\xd4\xd6\xd6\xa2\xa3\xa3\x03\x0a\x85B\x08\xbcd2Y\xd0-\x83\x8eu(\xe7\x87\x04a(\x14B[[\x1b\xfc~?jjj`\xb5Z\xa1\xd5jE\x94\x90\xc6\xb64*X,\x00\xd3\xe94\xe2\xf18\x82\xc1 \x8e\x1c9\x82\xab\xaf\xbez\xcc\xc47<<\x8c\xad[\xb7bdd\x04\xc3\xc3\xc3\xa2\x0d]q\xee_>\x9f\xc7\xc6\x8d\x1b\xe1p8\xe0r\xb9`6\x9b\xc5\xeb\xa94\xb2\xd9,\xd4j5\xc2\xe1\xb0\xe8\x11K\x11L\x8a.\xa5\xd3\xe9\x13\xca\xaddf\x86\x0b/\xbc\x10w\xdf}7\xbe\xfa\xd5\xafB\xa3\xd1 \x99L\xe2\xaa\xab\xae\xc2\xa3\x8f>\x8a\x8f|\xe4#\xc8f\xb3\xd32\x06i\xdd\x18\x18\x18\x80\xd9lF4\x1aE\x7f\x7f?\xfa\xfb\xfb\xc7_\xc8\xde\xcf\xe5\xa5\xfb2\x99L\x8a\xcfo\xd9\xb2\x05\x9f\xf8\xc4'\xb0p\xe1B\xd8\xedv\xf1;hSW,\xf2&\x12,\x1a\x8d\x06\xbf\xf9\xcdo\xf0\xdd\xef~W|.\x95J\xe1\xf6\xdbo\xc7\x07>\xf0\x01.z<\x0em\xc0\xef\xd9\x1c\x13\x80\x5c\xe2\xcd\xcc$r\xb9\x1cj\xb5Z\xf8\x02RAHOO\x8f8*\xa2c^\xda\xf1\x17\xb7s\xa2\xa4u\x1a\xbf\xfb\xf6\xedCSS\x13:;;\x85\xd92\xe5\xdbI\xab\x00\x8b'/:\xbaM$\x12\x08\x06\x83\xe8\xee\xee\x86\xdb\xed\x86\xcf\xe7C.\x97+Xp:::p\xe8\xd0!\x04\x02\x01\xc4\xe3\xf11]?\xe8\xf9\x1a\x1a\x1aPWW\x87\xea\xea\xea\x93\xd2\xf3w\xbc\xfb\xbb\x18\x85B\x01\x93\xc9\x84\x91\x91\x11h4\x1ad2\x99\x82\xe35\xe0\xbdN-\xd4\xadE\x1a\x05\xe4\x05\xa0\xf2\xc8f\xb3\xf8\xcaW\xbe\x82X,\x86\xff\xfc\xcf\xff\x14\xd7\xe8\xba\xeb\xae\x83J\xa5\xc2\xe6\xcd\x9b\xa7\xe5\xf7\xaaT*Q\x80\xa5T*\x8f\xb9\x8e\xd0}H\xe3\xd2\xeb\xf5\xe2\x9ak\xae\xc1\x8d7\xde\x88%K\x96\x881{\xac\xce R\x93\xe9b\xb1\x22\x93\xc9`6\x9b\xf1\xf4\xd3O\xe3\xee\xbb\xef.\xd8\xe8}\xf1\x8b_\xc4E\x17]\xc4\x91\xad2\xcc!\xcc\x1c\x10\x80\x0c3\xd3\x02\x90\xfav\x92/`CC\x03\xf6\xed\xdb\x87\xbe\xbe>(\x95J\x11\x05\x94\xb6\x80#\xd1\xa2\xd1h\x84A4M\xe2\xa9T\x0a{\xf6\xec\x81\xcf\xe7\x83\xd3\xe9\x84\xd1h\x84Z\xad.\x10_$\x06\xa5\x93>\xf5!\x8eD\x22\x18\x1a\x1aB__\x1f\xae\xb8\xe2\x8a\x82D\xf6|>\x8fh4\x8a\xbf\xfc\xe5/\xa2\xf2\x97\x16<:V\x96.z\xcb\x97/GMM\x0d\xaa\xaa\xaaD\xf4o\xa6s\xe8\xc6\xeb\xa4 \xfd\x1aE[\x13\x89\x84\xe8\xb2\x22\xed\x05+\x93\xc9\xd0\xdd\xdd\x8dP($:\x9dX,\x16\x16\x80\x15\x0a\xf9c~\xe3\x1b\xdf\x80\x5c.\xc7\x9dw\xde)6P\x1f\xfe\xf0\x87\xf1\xc3\x1f\xfe\x10\xb7\xddv\xdb\x98\xd6\x9e\xe5\xc0h4\x22\x91H\xa0\xad\xad\x0dmmm8t\xe8\x10\xc2\xe10\xa2\xd1(\x86\x87\x87\xd1\xd9\xd9\x09\x9b\xcd\x86\xc5\x8b\x17\xc3\xe5ra\xc1\x82\x05\xa8\xaf\xaf\xc7\xc8\xc8\x08\x14\x0a\x05\x96.]\x0a\xa3\xd1\x88d2)\xa2\x83\xa5\xe6\x8d\x89\xc6x\xf1\xe7\x9f\x7f\xfey\xdc}\xf7\xdd\x22\xbf/\x93\xc9\xe0\x9e{\xee\xc1\xcd7\xdf\xcc\xe3\xf78`#\xe8y&\x00\xf9b3\xd36\x90\xdf\xaf\xd4\xb5X,\xa8\xae\xaeFCC\x03\x1a\x1b\x1bq\xf4\xe8Q\xd1\x98=\x93\xc9@\xaf\xd7\x8b\x89\x87\xf2\x00)\x82(\xb5\x83\x91\xc9d\xe8\xe9\xe9Akk+l6\x1b\x8cF#T*\x15\x9cN\xa78>\x92&\xc4S\x04\x22\x99L\x8a~\xbf]]]\xf0\xfb\xfd\xa8\xab\xab\x1b\x13M\xd8\xb9s'\xda\xda\xda\x84=\x0cY\xd6\x14\xe7\xc5\xb9\xddn\xac\x5c\xb9rL\x0e\xd4\xc9\x16\x80\xd2\x1d\x0c\xa3\xd1\x88\xcd\x9b7\xc3\xeb\xf5\x8eY$v\xef\xde\x8dW^y\x05\x07\x0e\x1c\xc0\xc8\xc8H\x81\xaf\x99\xb4\xf27\x97\xcb\xe1\x82\x0b.\x80\xddn\x87\xdb\xed\x86\xc5b\xa9\xa8\xe8_q\x15\x9f\xf4c\xb5Z-\x8cv\xe5r9\x0c\x06C\x81\xa7\xa1B\xa1\xc0\xe0\xe0 ^~\xf9e\xa4R)\xc4b1\xd4\xd5\xd5\x89\xc8\xeaLu6\x99/\xd0F&\x9f\xcfC\xa7\xd3\xc1h4\xc2h4\x8a\xb6\x89\x93\x8d\x04R\x84|\xe3\xc6\x8d\xd8\xb5k\x176m\xda\x84\xfd\xfb\xf7C\xab\xd5\x22\x9dN\xe3\xdak\xaf\xc5#\x8f<\x82\x9f\xfc\xe4'hjj\x12\xc2\xbf\xdc\x22\x89\xee\xdf1'J8~1A\xa6\xd2V\xab\x15o\xbf\xfd6n\xbd\xf5V\xbc\xf6\xdak\x05\x91\xeds\xce9GX\xbd\xb0\xf8cX\x00\x1ec\x91\xe0I\x9c\x99i\xe4r9\xb4Z\xad\xc8\x05\xf4\xfb\xfdX\xb4h\x11^}\xf5U$\x93I1iS^ y\x04\xd2\xcfR\x84M\xa3\xd1\xc0h4\xc2\xe7\xf3\xc1\xe3\xf1\xe0\x82\x0b.\xc0\x1bo\xbc\x81w\xdf}\x17\x89D\x02\xdd\xdd\xdd0\x18\x0cP(\x14H&\x93\x88F\xa3\xd0h48\xf3\xcc3\xb1|\xf9rq\x9c)]$R\xa9\x14v\xef\xde\x8d\xa3G\x8fb``@T\xfe\x16\x1f\xff\xe6\xf3y\xb8\xddn477\xa3\xba\xba\x1av\xbb\x1d:\x9dND\x1cO6\xd2\xfe\xc3\xc5]=\xe8\x08\x8e\x84\x9e4:H\xef\xb7\xc3\xe1@SS\x13\x94J%\xb6m\xdb\x86\xde\xde^,^\xbc\x18\x1e\x8fG\x1c\xad\xf3\xdcQ\x1e\xa1Dc0\x91H \x9b\xcd\xc2b\xb1\xc0\xe3\xf1\xa0\xba\xba\x1a2\x99\x0cz\xbd~JG\xee\xf4\xbd\x0b\x16,\xc0\xbb\xef\xbe\x8b\x9bn\xba\x09\xbf\xfe\xf5\xaf\xc5xx\xfe\xf9\xe7\xb1l\xd92\xdc|\xf3\xcd\xf8\xc1\x0f~ \x04h9\x8f\xf5e\xf2\x7f\x0a\xc0rT\x93\xe6r9\xe1\x12p\xe3\x8d7\xe2\xb7\xbf\xfd-\xe2\xf1\xb8x\x0fS\xa9\x14\xbe\xf0\x85/\xe0\xee\xbb\xef\x86V\xab-(\xe8b\x8e/0T\xfc13\xc7\x04`qd\x80afJ\x00R;\xa8\xaa\xaa*\xd4\xd4\xd4\xa0\xbe\xbe\x1emmm\x18\x1c\x1cD&\x93A\x22\x91\x00\xf0^Q\x02\xe5\x0dIs\x7f\x8cF\xa3\x10\x91\x16\x8b\x05MMMX\xb6l\x19\xd6\xacY\x03\x00\x18\x1c\x1cD2\x99DWW\x17\xd2\xe94\xccf3|>\x1f\x5c.\xd7\xb8\x13[6\x9b\xc5\x1f\xfe\xf0\x07\xbc\xfa\xea\xab8|\xf8\xb0\xb0\xa7!3\xdb\xe2\xe2\x8f\xb5k\xd7\xc2\xeb\xf5\xc2\xe5r\xc1b\xb1\x9c\x94\xca\xdf\xf1\x88\xc7\xe3\xa2x\xa6\xb8C\x8aF\xa3\x11\x06\xd0\x14\x0d\xccf\xb3\xd0\xe9t\xd0\xeb\xf50\x99L\xf0z\xbdhhh\xc0\xb9\xe7\x9e\x8b\xef}\xef{\xd8\xb3g\x0f\x8cF#\x02\x81\x00\x82\xc1 R\xa9\xd4\x94:\xaf0\x13\xcf\xbf===\xd8\xbbw/\x12\x89\x04t:\x1dZZZD\xd5<\x09\x9f\xe3\x19[r\xb9\x1c\x0f?\xfc06o\xde\x8cO}\xeaS\x18\x1c\x1c\x14\xe3\xff\x87?\xfc!~\xfc\xe3\x1f\xe3\x17\xbf\xf8\x05\xae\xbc\xf2JX,\x96\xf2\x09@\xc8D\xb1\xd0\x89\xe4\x95+\x95J(\x95J\xc4\xe3q\xbc\xf0\xc2\x0b\xf8\xf9\xcf\x7f.>O\xb8\x5c.\xfc\xfa\xd7\xbf\xc6\x05\x17\x5c \xc6$oN\xca?FY+\xb0\x00d\x98\xb2 \xcd\x05\xf4z\xbd\xa8\xaf\xafG}}\xbd\xa8\x08\xa6\x02\x8fT*U\x90\x0cO\x22\x8c\xaay\xa5\xf9m\xa9TJ\x1cu\x91\xd0\x93\xf6\xf5\xa5\x9f\x97V\x12RD,\x99L\xe2\xcd7\xdf\xc4\xae]\xbb088\x88H$\x82`0X\xb2\xf8\x03\x00\x9cN'\x16/^\x0c\xaf\xd7[q\xd1\xbf\xe2\x05Pz\x0cN\xef\x81\xb4\xad\x16u\x8c A\xa8\xd3\xe9\xa0\xd5jQ]]\x8d\xc6\xc6F\xa4\xd3iQ\xe5)\xad\x96f\xca\x0f\xb5s\xeb\xee\xee\x86\xcf\xe7\x83\xcdf\x83\xc1`8\xee\xe7#\x8b\x9fG\xa8\x91U\x00\x00\x1daIDAT\xcb/\xbf\x1c\xbbv\xed\xc2\xd7\xbe\xf65\xfc\xeaW\xbf\x12\xddwr\xb9\x1cn\xbc\xf1F<\xf7\xdcs\xf8\xfd\xef\x7f/\xa2\xee\xe5\x18\x7f\xc5\xde\x7f\x93]o\xa4'S\xad\xad\xadx\xec\xb1\xc7\xf0?\xff\xf3?\x18\x1a\x1a\x12v1d\x22}\xf3\xcd7\xe3\xfb\xdf\xff\xbe\xb8\xef\xa7\xab\xf3\xc9|\x15}\xac\x15* `2\x9dj\xbed\xa2.\xc3L\xf7\xa0~?\xbf\xcfl6\xc3\xe5r\x89c`\xb3\xd9,\xc4])\xcf=\x8aj%\x12\x09\xa4\xd3i\x84\xc3a\x0c\x0e\x0e\xe2\xc8\x91#hoo?\xe6\x22 5\x89\x96V\xc8\xbe\xf2\xca+x\xf6\xd9g\xd1\xde\xde\x8e\xfe\xfe~Q\xf8!\xb5O\xa1\x85I.\x97c\xc9\x92%\xa8\xab\xab\x83\xc7\xe3)\x88\xfeUZ\xe4\x81Z\xbeI\x0bSH\xe4Q!\x00\xe5VJ;\x81\x00\xefuA\x19\x1a\x1a\x82B\xa1\x18\x131\xa5k\xc2\x8f\xe3\x7f\xd0\xfbH\xe9\x0d\xdf\xf9\xcew\xf0\xc4\x13O\xa0\xab\xab\x0b\xb1XL\xe4\x9fR\xf4\xf9\xb8#\x08\xef\x1b5\xbb\x5c.<\xf4\xd0C\xd8\xbe};N?\xfdtQu\x0f\x00\x9f\xfd\xecg\x91\xcb\xe5\xcav\x0c,\x15\x80S][d2\x19^x\xe1\x05l\xdc\xb8\x11\x17^x!~\xf9\xcb_\x8aqH\xe3\xf3\xfc\xf3\xcf\xc7\x8e\x1d;p\xdf}\xf7\x89#_\x16\x7f\xe5\xd3\x08\x13\x8d7\xd6\x0a\xb38\x02X\xaep.\x87\xd8\x99rD\x01\xa5\xb9\x80\xb5\xb5\xb5hll\xc4;\xef\xbc\x03\x00\xa2\xe8\x22\x99L\x8a\xcaS\xa9\xb1s2\x99D*\x95B8\x1cF0\x18\xc4\xdbo\xbf\x0d\x97\xcb\x05\xb7\xdb=\xa9\xc5@.\x97c\xdf\xbe}\xd8\xb9s'^}\xf5U\xf4\xf7\xf7#\x18\x0c\x22\x12\x89\x08\xb3d\xa9\x08\xa5\xdfKy\x84.\x97\x0b6\x9b\xad\xa2*\x7fK\xdd\xef\x91HDD\x82\xa4\x22Pj\xa7Cy\x82$\x88\xbb\xba\xba`\xb5Z\xf1\xd2K/\xe1\xe2\x8b/\x86\xdb\xed\xe6\xfb\x7f\x9a\x04z<\x1e\xc7\xa3\x8f>\x8a'\x9f|\x12\x9d\x9d\x9d\x88\xc5bH$\x12H$\x12\x22\x1a^\x8e\xdfC\xd7}\xf5\xea\xd5x\xf3\xcd7\xf1\xfc\xf3\xcf\xe3\xe3\x1f\xff8\xd6\xacY\x833\xcf<\xb3\xac\xd7\x956Jt\x0fOE<<\xf1\xc4\x13\xb8\xea\xaa\xabJ\xf6\xa8\xf6\xfb\xfdx\xec\xb1\xc7\xb0a\xc3\x06\xf1\xdc\x5c\x90\x84\xb2\x8d\x0f\x16ys\x5c\x00N$\x0a\xa7\xb2\xcb\xe4\x1b\x8e)\xc7\xa4\xa3P(`6\x9bEw\x90%K\x96`\xdf\xbe}\x22\xc7,\x99L\x8a\x9d?\x1dO\xa5R)\xc8d2\xe8t:D\x22\x11a\x1f\x93L&\xf1\xe8\xa3\x8f\xe2\xd4SO\xc5\x8a\x15+\xa0\xd5ja6\x9b\x0b\xc6j6\x9bE(\x14B__\x1f\xda\xda\xda\xb0o\xdf>\xb4\xb6\xb6\x22\x12\x89`xx\x18\xa1P\x08\xb1XlL\xcf_\x22\x97\xcba\xfd\xfa\xf5p\xbb\xddp:\x9d\xa2\xebG\xa5\x0a@z\x9fI\xc8J\xefy\x8a\xa6\xd2\x91\x1fE^3\x99\x0cl6\x1bzzz\x10\x89D\x10\x8dF\xb1z\xf5j\xb4\xb4\xb4\xc0h4\x0a!\xce\x9c\xf8\xbc{\xe8\xd0!\xfc\xf1\x8f\x7f\xc43\xcf<\x83\xfe\xfe~\x91r maX\xceD|\xba\xe7R\xa9\x14>\xf0\x81\x0f`pp\x10\xdd\xdd\xdd\xd0j\xb5e\x1fs\xd2H\xfbT\xb8\xf8\xe2\x8b\xb1d\xc9\x12\xb4\xb6\xb6\xbe\xb7\x00*\x95X\xb2d\x096n\xdc\x88\x8f|\xe4#X\xb3f\x0d\x1f\xf7\xce\x80\x1e`\xe6\xa8\x00\x94\xde\x98\x94\x0fT\x5c\xe1\xc8032\xb8\x95Jh4\x1a8\x1c\x0e\xf8|>\xf8|>,_\xbe\x1c\xaf\xbf\xfe\xbaXD\xa8\xe2T\xaf\xd7\x8b\xf1I\xc6\xc5\xe9tZ\x88\xbfp8\x0c\x97\xcb\x85@ \x807\xdf|\x13~\xbf\x1f\x1e\x8fGT\x11SqIGG\x07\xfa\xfb\xfb\x0b\x04_8\x1c\x16\xc7\xceT\xf8!\xb5\xe6\xa0{\xc2d2a\xf5\xea\xd5p\xbb\xddp8\x1c\xc2\x1c\xb9\xd2\xc4\xdfxG\x8e\xd2\xaf\x93\xc8\xc8d2B\xd4\xc5b1h4\x1a\x04\x02\x01\xa4\xd3i\x04\x02\x01$\x93Itww\xc3\xe1p\xa0\xaa\xaa\x0a\xb5\xb5\xb5\xd0\xe9t\xc2\xf2\xc6h4B\xa7\xd3\x9d\x94\x85\xa7\x9c\xe2\x88\x9e\xebx\xaf\xe5d_\x7f2\x99D(\x14\xc2\xc1\x83\x07\xf1\xc6\x1bo`\xc7\x8e\x1dhooG(\x14\x12Q-i\xc7\x95\xe9\x18[j\xb5Z<\xb7\xdf\xef\x9f\x96MG\xa9\x8e1\x93y\x8fL&\x13\xd6\xad[\x87\xbe\xbe>|\xees\x9f\x13\xf3\x82Z\xadF$\x12A&\x93)\xbb`e\xfey}H\x0b\xb0G\xf0\x1c\x15\x80\xd2\x5c&\xbaY\xa5\xf9K\x1c\xddcf2:\xa5P(`4\x1a\x0b*\x82\xf7\xee\xdd\x8b\xd1\xd1QQ\xdc\xa1R\xa9D\xa7\x0a\xeap\x00\xbc\x17\xd1\xa3j]\x83\xc1\x80\x9e\x9e\x1etww\xc3\xe9t\xa2\xbd\xbd]\xfc\x0e\xb2\x8c\x19\x1c\x1cD.\x97C<\x1eG\x22\x91@4\x1aE4\x1a\x15Gm\xf1x\x5c\x88?\xa9\xef\x1fAG\xbf\x1e\x8f\x07V\xabU\x1cMWb\xee\x9f\xf4\xbe\x9e\xa8\xa7*E\x03\xb3\xd9,4\x1a\x8d\xb0\xcbI\xa7\xd3\xd0\xe9t\x18\x1a\x1a\x12\x1dN\xc8\x9f.\x16\x8b\x8d1\xd0\x9e\xca\x22_\x1cY\xad\xa4H\xc7tZ_P46\x12\x89 \x10\x08```\x00CCC\x22\xed\x80~7\xe5eR\x9e\xe6t\xbd\x96\xe9B*`\x8f\xe7\xfd\xbc\xf7\xde{\xf1\xb3\x9f\xfd\x0c\xb1X\x0c[\xb7n\x15\xc7\xe2F\xa3\x91\xd7\xa7i\x9e7\x8aSm\x989\x22\x00\xa5\x8b\xc2\xf0\xf00Z[[\x91\xc9d\x90N\xa7\xd1\xd1\xd1\x81\xd1\xd1Q\x84\xc3a$\x12\x09\x11\x0d\x91\xee\x06fb\xe2`\xe6\xe1\x00\x7f?\x0ah\xb7\xdb\xe1\xf5zQSS\x83\xc6\xc6F\xbc\xf5\xd6[\xa2Wm\x22\x91\x80\xc1`\x10BE\xea\x0bFQl\x12\x88z\xbd\x1e\xa1PHT\x19\x93P\xa4\xde\xb7\x99LF\x88<\xe9d\x97H$D\xe5\xb14\xfaG\xd8l6\xb4\xb4\xb4\xc0\xe7\xf3\x89\xca_\x8dFS1\xc7\xa1R\x93\xeaP(\x84}\xfb\xf6A\xa1P \x18\x0c\x8a\xc2\x96p8,\x22\xa7T|@\xa2'\x97\xcb!\x1a\x8d\x0a[\x18\x00\xc2k-\x14\x0a\x89B\x97t:\x0d\x83\xc1\x80\xae\xae\xaec\xce\x09\xb3\xcdG\xecD\xc5\xd6\xb1~>\x9b\xcd\x22\x95J!\x93\xc9 \x12\x89 \x16\x8batt\xb4\xc0V\x87\x22\x80\xd2\x0d\xfal\x9bs\xc7+\x02\x99lT\x89\xa2\xf6\xf4^p$jz\xaf\x15\xcdu}}}hmm\xc5\xc8\xc8\x08\x06\x06\x060<<\x8c\x91\x91\x91\x82\xd3\x96\xe2\x8d1\xfd\xcb}\xc2+\x5c\x00J/\x18%\xcf\xd3b\x11\x0e\x87\x0b\x92\xe0\xc73\xc0\x95F\x10\x19\xa6\x1c(\x14\x0a\x11\x05\xf4x<\xa8\xaf\xaf\xc7\xc2\x85\x0bq\xe0\xc0\x011Fe2\x99\xc8\x01\xa4\x1e\xb6T!IQ\x12\x95J\x05\x99L\x86H$\x02\x8dF\x83d2\x89`0\x88|>\x0f\xbd^/\x8e8\xa59\xaft\xdcKQ\xb0x<.\x04e\xf1D\xb6h\xd1\x22,X\xb0\x00.\x97\x0bV\xabU\x1c\x81V\xd2\xbd@\x133\xe5:\xaaT*\x84\xc3a\x0c\x0d\x0d\xa1\xbf\xbf_\xe4\xf4\xd1\x91x\xf1u\x00 r,\xa5\x0b\x84B\xa1@(\x14\x12\xdf\x17\x08\x04Dk\xbc\xa9F\x99\xa6k\x81\x98-\x0b\x0fy3R\x11\x13\xe5]J\xe7U\xe9b:[\xf3-O$\x02Hs\x02\xa5'\xb0\xb0\x98\xfey\x83\xdc\x15\x82\xc1 b\xb1\x18B\xa1\x10zzz\x10\x8dF1::\x8aX,&r\x89KE\xf0\xf9\xfa\xcc\x82\x08 M2\xc3\xc3\xc3\xd8\xb7o\x1f\x86\x87\x87E\x97\x04Z\x1c\xa4\x11\x02i\x04\x90&'\xee\x02\xc0\x94\x1b\x95J\x05\x8dF\x03\x9b\xcd\x06\xbf\xdf\x8f\xfa\xfaz\xd4\xd5\xd5a\xf7\xee\xddb\x13B\xd1\xbbX,V\x10!\xa1\xc8\x1e\x99\xe5*\x95J$\x93I\xa8T*\xf1 \xe3c\xe9B\x22\xb5\x96\xa1^\xc1\xd9lv\x8c\xfd\x0c\x15\x9c\xac]\xbb\x16n\xb7\x1bUUU\xd0\xeb\xf5\xa2%Z\xa5-\xba\x00\x10\x0e\x87\xd1\xd6\xd6&&n\x9a\xc4\xa3\xd1\xa8\x10\xbc\xc5\x02\xf0X\xd1\xbcb\x11CsA%L\xfc\xb3i\xf1\xa1\xb1%\x1dk\xd2\xb6\x9c\xd2\x08\xdal\xed\xc4p\x22>\x80\xe3\x8d5f\xfa\x85z \x10\xc0\x9e={\xd0\xdd\xdd-\xf4\xc0\xf0\xf0\xb0\x08\x08\xd1\xc6\xb1x\xbc\xd2ub\x11X\xc1\x02\x90\x8eyd2\x19FGG\x11\x0c\x06E\xd4\x83\x16\x06\xca\x85\x92\x1e\xb5\x95\xf2\x0ed\x01\xc8\x94;*\xa2R\xa9`4\x1a\xe1p8P[[\x8b\x95+Wb\xef\xde\xbdb\xa1$\x1b\x93\xe2\xb1'\x97\xcbE\xb50\xf5\x1a&\x93[\xaa\x10\xd6\xe9t\xc8d2\x05\x9b\x17\xb2\x92\xa1EY:\xfe\xa5c=\x9f\xcfc\xc9\x92%\xa8\xaf\xaf\x87\xcb\xe5\x82\xd9l\x86N\xa7\xab\xc8\x8d\x10\xbdn\xca1\xa3\xa3\xf1H$\x22\x8e\xb8\x8b\x0b[&\xbb8\x17\xe7\x0d\xce\xf5]\xfft\xe7\xdeI7\xd7\xf4\xb1TXOV\x8cW\xe2\xfb&\xed\x05|\x22\x22\xb6\xf8~\xe7ugz6$t\x8d\xfa\xfa\xfa\xd0\xdf\xdf\x8fd2)R\xc2h\xfe\x95\xce\x1d\xc5\x91\x7fi\xee S\xa1\x02\x90\x16K\x85B\x81H$\x82P($\xaa\x1e\xa59O\xc5\xea\x9e&)\xba\xa19A\x94\x99\x96\x81\xaeTB\xa7\xd3\x89\x5c@\x9f\xcf\x87U\xabV\xe1\xf5\xd7_\x1f\xd3\xcd\xa2xA\x90V\x0c\xd3\x18\xd6j\xb5H\xa5R\xa2\xa0A\xa5R\x15$\xd9\xd3\xc4GB\x90\xa2\x80\xd2\x85\x99\xee\x97\xf5\xeb\xd7\xc3\xe9t\x8a\xca_i\xff\xdcJ\x8b\xba(\x95JD\xa3Q\xd1U\x85v\xee\xe3\x1d\xdd0'O(\x8d'\xa4\xa9\x05\xdal\x8e*\x8d\x97\x03x\xbc\x82\x99\x05\xe0\xf4\x5c'\x9a\xc7\xe4r9\x22\x91HA\x0a\x98t\xde(\xbe~\xc5u\x01\xe5\xec%\xcdL\x83\x00\xa4\xe31\xbd^\x8fL&\x83\x91\x91\x91\x82\x04\xd0\xc9F\x05t:\x9d\xc8}\xe2\x1b\x92)\xe7\xf8\xa4\xee \x1e\x8f\x07uuu\xc2\x0f,\x18\x0cNj\xc1\x91\xf6\xec\xcd\xe7\xf30\x18\x0cB\x10R\xa7\x03\xfa\x7f\x22\x91\x80Z\xad\x169qT\x85Y\xdc\x0c\xfd\xcc3\xcfDuu5\xdcn\xb7\xe8\xfaA\xf9\x86\x95\x04\xfd}\x1a\x8d\x06\xf9|^\xe4?J\xff~\xa6\xb2\x04\xe0xc\xf9D\xfa\xffV\x8a\xb0(\xb7@\xa19\x82)\xffuR\xa9T\xd0\xeb\xf5H$\x12\x18\x1e\x1e\x16\xf3E\xa9`\xd0\xb1\xc6,U\xb03\x15$\x00I\xfc\xe9t:\x98\xcdf\x11\x15)\x95\x13u,\xccf\xb3h/\xc5\x17\x9a)'\x0a\x85\x02:\x9d\x0eV\xab\x15~\xbf\x1f\xb5\xb5\xb58\xe3\x8c3\xf0\xdcs\xcf\x8d\x9b\x08^*\xd2@\xc9\xe3\xd4\xf3T:V\xa9\xe2W\xa7\xd3\x09\xc3gz\x14\x1f\x8f\xeat:,_\xbe\x1c\x1e\x8f\x07UUU0\x1a\x8d\x15\x99\xfbG\x93\xb8F\xa3\x81\xd5j\x85V\xab\x15\xc7\xe2'\x9a\x83\xc5\xcc\xdcbLcY\xab\xd5Vt\x87\x99\xa9\xfc-'*\x109\x028\xbd\xd7I\xadVC\xa7\xd3\xc1b\xb1@\xa5R\x89\x22\xb0RQ\xbf\xf1\xae\x8b\x5c./\xf0\x06\xadDk\xacy+\x00)<\xabV\xaba2\x99\xe0\xf3\xf9D\x94`*\x0b\x02\x0d\x88\xaa\xaa*\x98L&\xa1\xf8yQa\xca\x05mT,\x16\x0b<\x1e\x0f\x9a\x9a\x9a\x10\x8b\xc5\xf0\x8f\x7f\xfc\x03}}}\xc7\x8c\xa4H\xad4\xc80Zz\xc4!\x15v\xe4\xf5G\xd1\xbfb\xdb\x17\x00X\xbat)\xea\xeb\xeb\x85\xef\x9f4\xfa]i\xc2\x99L\x99\xa9HE\xfa~0\x95\x8f4\xb7\xcal6\x8b\xcd\xc6l=Z\x1b/8 \xb5\x0d\x99\xaa\x00daQ\xfe\xeb#\x97\xcb\x85\x03\x834Mf2\xe3\x95H\xa5R\xb0Z\xad\x228\xc4\xd7\xa9\x82\x04 \xf5d\xd4\xe9t\x22\xc1~\xfd\xfa\xf5\x22G\xaa\xb8\x92\x87vo\xd2\xfc*\x95J\x05\xadV\x0b\x93\xc9\x04\xaf\xd7\x0b\x87\xc3!\x12\xe1\xf9\xec\x9f)\xe7\x8e\x94z\x04\xdb\xedv,X\xb0\x00\x00p\xd5UWa\xc7\x8e\x1d\xc2\x8bJ\x9a\xabJ\x95\x94\xa9TJT\xaee\xb3Ya\x19\xa3\xd5j\xa1\xd1h\xc4\xf8\xa6/\x04`:\x9d\xc6e\x97]\x86\xd3O?\x1d\xa3\xa3\xa3\xa20N\x1a\x18\xa2\xebI\xa2P\xa3\xd1\xc0`0\xc0n\xb7\x8f\x19\xb3\x9c\x1eVA\x02\x90&\x18\xab\xd5*\x12\xc5\xddn7\x12\x89\x84\x88\x10\x94*\xb9\x97VB\xaa\xd5j\x18\x0c\x06\x98L&q<\xc1\xa1^f:&&:\x8a\xa0vkf\xb3\x19\xe1p\x18\xb1XlL\xa1\x07\xf0\x9e\x17\xe0\xc8\xc8\x88hS\x16\x08\x04\xa0\xd7\xebQ[[+\xbe/\x9dNC\xaf\xd7\x17\xb4{\x93\x8e\x7fi\xe5/\x80\x82\xca_\x8a\xfeU\xea\xa4F\xef\x99\xd1h\x04\x00h\xb5Z8\x9dN\xd1\xd5G:\x91K==\xf9\xde\x9dY\x8a#`\xd2\xe3PZP\xb5Zm\xc1\x11\xf0l\xcd\x03,\xfe\x9b\x8b\xff\xfe\xc9\xfeM\x1c\x01\x9c>\xc8D\x9f\x8e\x80u:\x1d\x5c.\x17\x12\x89\x84h\xb7Y\xea:HuA\xf1\x98\xa5\x9ck\xa6\x82\x04 -\x10\x00`\xb1X\xa0\xd7\xeb\x0b\x8e\x87\x8e\xe58O\x17\x9bz\xb1\xd2\x83U>3]\x82\x86\x04\x17%\x18\xdbl\xb6\x92-\xda\xf2\xf9<\x22\x91\x08\x06\x07\x07\x11\x89Dp\xf0\xe0A\xa8\xd5j,^\xbcX\x08\xc8@ \x04$\xfd,\x89\xc0\xe2\xa3\xdf\x5c.\x87e\xcb\x96\xa1\xa9\xa9\x09n\xb7[\xecj+}!\xa6|\x5c\x12\x0e\x16\x8b\xa5\xe0\xfe\xe6hJ\xe5\x08\xa2R\xd11\x8a\xe2\xd2f[\xadV\xcf\xfaH\x0a\xb9L\x9c\xe8s\x14\x7f\xccc\xb7\xbc\x22\x10\x80\xf07\x95\xea\x82\xf1\xdeo\xe9\xc6E:fU*\xd5\xac\xdf\xb0\xccI\x01(\x15\x81$\xe4t:\xdd\xa4\x0bA\xa4G\x15\xec\x03\xc8\xcc\x04\x14\xb5V(\x14\xc8f\xb3\xd0\xeb\xf5\x05bFZ\xf0A\xad\xca\xe8hw\xf9\xf2\xe5\xb8\xfe\xfa\xeb\x11\x0e\x87q\xe4\xc8\x118\x1c\x0e\x1cW\xc5\x95\xbfk\xd7\xae\x85\xd7\xeb\x85\xd3\xe9\x84\xc1`\x98\x15\xb9\x7f\x0cS\x89\xd0=u\x22-\x08K9S0\xcc\xbc\xba\x8f\xf8-`\xe6;\x94\xaf\x07@\x18\xe3\xe6\xf3y$\x12\x09D\x22\x11\x0c\x0f\x0fC\xa3\xd1\xa0\xa5\xa5\xa5`\xb1\x88\xc7\xe3hkkC$\x12A,\x16C4\x1a\x15\xfe\x7f\xd2\xe8_>\x9fGuu5V\xacX\x01\xa7\xd39k*\x7f\x19\xa6R!\xcf\xccr\x1e\x01\xf3\xbd\xc8\xb0\x00d\x98y\xb8\x98\x90\xcd\x00\x1d\x07e\xb3Y\xe1\xff\xd7\xd5\xd5\x85\xb3\xce:k\xcc\xcfuvvb``\x00\xc1`\x10\x91H\xa4\xe0\xf8Wj\x7f\x94\xcf\xe7q\xe1\x85\x17\xc2n\xb7\xc3\xe9t\xce\x9a\xca_\x86\xa9\xd8\x85\xeb}\xe1&\xad\xd6\x97\x1e\xe5N\x16\x8e\x002,\x00\x19f\x1eS\xdc\xc1\x82L\x9cC\xa1\x10zzz`4\x1a\xd1\xd4\xd44\xe6\xb8\xa9\xad\xad\x0d}}}\x08\x87\xc3\xa2\x8b\x08\x89?\x22\x97\xcba\xe1\xc2\x85\xa8\xa9\xa9\x81\xd7\xeb\x85\xcdf\x83N\xa7\xab\xd8\xb6o\x0c3[6mr\xb9\x1c\xf1x\x5c\x08\xc0P($\x22\xf9\x93\x15\x91l\x04\xcd\xccg8\x07\x90a$bM*\xfe\x06\x07\x07\xd1\xd5\xd5\x85\xeb\xae\xbb\x0e\xb9\x5c\xae`\xb1\xe8\xed\xedEkk+\xba\xba\xbaD\xf4Oj\xfd\x22=\x8eZ\xb3f\x8d\xc8\xfd3\x99L\xa2\xcb\x0d\xc30'\xb6i\x8bF\xa3\xd8\xb3g\x0f\xd2\xe94\x06\x06\x06\xa6l\x0c\xcdG\xc0\x0c\x0b@\x86\x99\xc7\x90`\x93\x8a\xbf\xfe\xfe~\x1c\xe8gs\xb9\x1c\xd2\xe94\x12\x89\x04FGGq\xf4\xe8Q\x1c\x9f\xc7\xf0\xf00\x06\x07\x07100\x80\x03\x07\x0e\xe0\xc0\x81\x03\xe8\xec\xec\x14\x85&\xc1`P\x08?\x12\x7f\xd2\xc2\x0f\x85B\x81L&\x83\x8d\x1b7b\xc9\x92%\xa8\xa9\xa9\x81\xc3\xe1\x80\xc1`8\xa6Pe\x18\xe6\xf8\xe6\x84\xf16qS\xb9\xdf\x5c.\x17\x16/^\x0c\xadV\x0b\x8f\xd7\xc3o,\xc3\x02\x90a*\x09\x8a\xe6e\xb3YD\xa3Q\x0c\x0d\x0d!\x9dN\x17\xe4\x00\x92\xa9\xeb\xd0\xd0\x10\xe2\xf18\x86\x87\x87\x11\x0e\x87E\xa4 \x9f\xcfC\xa9T\xa2\xbf\xbf\x1f\x81@\x00n\xb7\x1b\xfd\xfd\xfd\x88\xc7\xe3\x08\x85B\x88\xc7\xe3\xe8\xed\xedE$\x12A\x7f\x7f?\xa2\xd1(b\xb1\x18\xc2\xe1\xb0\xb0\x9e\x91\xb6{\xa3\xe7\x94\xc9d\xc8f\xb3X\xbat)\xce>\xfbl\xf8\xfd~x\xbd^\x98L&\xf6\xfdc\x98\x93(\x10'\xb5\x00*\x95X\xb8p!\xbfa\x0c\x0b@\x86\xa9\xe4\xc9\x9c\xaam\xa9\x02W&\x93\xc1h4\xe2\xe8\xd1\xa3\xe8\xee\xee\xc6\x8b/\xbe\x88\xa3G\x8fb``\x00\x89DB\xe4\x0a\xd2sd\xb3Y\xb8\x5c.(\x95J\x8c\x8c\x8c \x91H@\xa1P\x08\x91\x17\x89D\x90\xcdf\x91J\xa5\x84\xc7 \x09?i\xab7\x12\x7fd\xf8\x5cUU\x85\x8b.\xba\x08\x0b\x17.Dmm\xad\xc8\xfd\xd3h4,\xfe\x18\xa6\x82\x05\xa0t#\xc70,\x00\x19\xa6\xc2\x90\x8a\xae\x5c.\x87@ \x80\xbf\xfd\xedop:\x9d\xc8\xe5r\xe8\xec\xecDww7\xf6\xee\xdd\x8b`0\x88t:\x8dT*U\x10\xa9\xcbd2\x00\x80\xde\xde^$\x12\x09qTL\xad\xe0\xf2\xf9\xe8\xf5zTUUA.\x97\xc3l6#\x1c\x0e\x0b\xc3\xe7l6[\xb0\x00\x94\x8a\x00\xd01\xb1\xb4\xb8C\xa1P\x88|\x22\x8dF#\xa2{Z\xadV|\xacR\xa9\xa0\xd1h\x0a*|9\xc2\xc00'\x17\xa3\xd1\x08\x83\xc1\x80\xba\xba:ttt\x08\xafO\x99\x9c\xab\x80\x19\x86\x05 3\xa7v\xfcj\xb5\x1af\xb3\x19\x1a\x8d\x06V\xabUt\xfc\x18\xaf\x95S\xa9*`\xea\xe0AB\x8e\xa2\x80$\x04\xe9\xb8Y\xadV\x8bJC\x85B\xc1G\xbd\x0cS\x81P\xe4_Z\x11,\xcb\xf3}\xca0,\x00\x999\x83L&\x83Z\xad\x16\x22M\xa7\xd3\x89N\x1d\xc5\x22o2\xcf%E*\xf0H\x0c\x96:6f\x18\xa6\xf2\xe6\x851~\x9f\xb2\xf7\x1f\x0c\xc3\xb0\x00d\xe6\xcedO\x11\xba\xe2N\x1f\xe5x\xeeR\xe2\x90a\x98\xcaG\x9a\x8f+\x93\xc9 \x97q~.\xc3\xb0\x00d\xe6\xfc\x84\xcf0\x0c\xc30\xcc\x14\xd7Q~\x0b\x18\x86a\x98\xd9\x0cG\xee\x19\x86\x05 \xc30\x0c3\x8f\x05 \xe7\xee2\x0c\x0b@\x86a\x18f\x1e\x8bA\x86aX\x002\x0c\xc30\xf3D\xf4\xb1\x08d\x18\x16\x80\x0c\xc30\xcc<\x13\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb8\xf8\xe3\x1c@\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe62\xd2N \x1c\x01d\x18\x16\x80\x0c\xc30\xcc<\x11\x80\x0c\xc3\xb0\x00d\x18\x86a\xe6\xb1\x18dA\xc80,\x00\x19\x86a\x98\xb9\xbe\x90qkH\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe60\xc560\x1c\x11d\x18\x16\x80\x0c\xc30\xcc<\x15\x83\x0c\xc3\xb0\x00d\x18\x86aX\xf41\x0c\xc3\x02\x90a\x18\x86a\x01\xc80,\x00\x19\x86a\x18\x86a\x18\x16\x80\x0c\xc30\x0cS\x99P\xe4\x8f#\x80\x0c\xc3\x02\x90a\x18\x86a\x18\x86a\x01\xc80\x0c\xc3\xcce8\x02\xc80,\x00\x19\x86a\x98\xf9\xb6\x90\xc9\xe5\xc8\xe7\xf3\xfcF0\x0c\x0b@\x86a\x18\x86a\x18\x86\x05 \xc30\x0c3'\xe1#`\x86a\x01\xc80\x0c\xc30\x0c\xc3\xb0\x00d\x18\x86a\xe6*2\x99\x8c#\x80\x0c\xc3\x02\x90a\x18\x86\x99\x8f\x22\x90a\x18\x16\x80\x0c\xc30\xcc<\x16\x83,\x08\x19\x86\x05 \xc30\x0c3\x0fD\x1f\xa1P(\xf8\x0da\x18\x16\x80\x0c\xc30\xcc\xbcZ\xd4\xe4\xbc\xac1\x0c\x0b@\x86a\x18f\xce#\x8d\x00\xf2\xf1/\xc3\xb0\x00d\x18\x86a\xe6\x99\x00\xe4\x08 \xc3\xb0\x00d\x18\x86aX\x002\x0c\xc3\x02\x90a\x18\x86\x99{\x0a\xb0\xb4\x18d\x18\x86\x05 \xc30\x0c3g\xf5\x1f\xe7\x002\x0c\x0b@\x86a\x18f~\x09@.\x02a\x18\x16\x80\x0c\xc30\xcc\xfc\x15\x80\x9c\x03\xc80,\x00\x19\x86a\x98y,\x06\x19\x86a\x01\xc80\x0c\xc3\xcc\x87EM\xc6\xcb\x1a\xc3\xb0\x00d\x18\x86a\xe6<\x05Q?^\xd5\x18\x86\x05 \xc30\x0c3\xbf\x04 G\x00\x19\x86\x05 \xc30\x0c3\x1f\x162\xb9\x1c\xf9|\x9e\xdf\x08\x86a\x01\xc80\x0c\xc3\xccGdr.\x02a\x18\x16\x80\x0c\xc30\xcc\xdc\x16|2Y\x81\xf5\x8bB\xae\xe07\x85aX\x002\x0c\xc30\xf3A\x042\x0c\xc3\x02\x90a\x18\x86\x99\xaf\x8b\x1a\x1bA3\x0c\x0b@\x86a\x18f~\xc1\xd1@\x86a\x01\xc80\x0c\xc3\xcc#\xf2\xf9<\x0b@\x86a\x01\xc80\x0c\xc3\xcc7X\x002\x0c\x0b@\x86a\x18\x86\x05 \xc30%\xf8\xff\x82\xe5f\x8a\x9e\x05\x9f\x09\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x1a\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x03\xcfIDATx\xdat\x8c\xcb\x0d\xc00\x08C\xcd\xe7\xca\x0e\xd9\x7f)n\x9cX!\xa1\x82H=\xb5\x96\xccC2\x86\xaa\x0a_b\xfcH{df\x99\x19\x88\x08\xfd!\x22n\x83\x99!\x22p\xf7\xa1\xaa\xde\xa0\xaf\xce9/\xdb\x13\xf4\xb2\xf7\xc6Zk\xd8~\x04\x10#y\xae\xfa\xfe\xfd\xfb\x7f\x90\xa5 \x002\x86\x93\x93\x93\x91\x05\xe6*\x90K`\x0e\x81\xeb\x80\xb9\x06C\x02\xa4\x1d&\x00S\x00\x10@8]\x85\xd7Q p\xef\xde\xbd\xff|||`w\x800\x0c\xc0\x02BDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\x9d;wP4\xc3leA\xb7\x1a\xc6VTTd\xf8\xf3\xe7\x0f\xdc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80t2X\x01\x18\x06a\xe8\xd8\xc9\xff\xffMO^\xbcvU\xc8\xc8\xec\x0a\x93\x15\xa4\x17\x1fM4mO\xe9<\x9a\xa7\x0d\xdc\xa6Uu\xf0T8\x02f\xb6zp\xf7\x81f\x06\xa2D${\x1f/\xa01b\xc8@\xe4\xe9U\x12\x00^R\xc8\x01\xfc\xdf4\xf4\xd6\xac\xd7=-@\xfe\xc4bz\x0b@\xf3' \xe2\xcb\xe6+0\xf7\x90\xf7%\x00set\x03 \x08\x03Q?\xfcb\x10V \xec\xff\xc5\x0a\xac\xc1\x02Rb\xc9y\xb41\x9a\x98hB0jyr\xbd\x96\xc7^\xfa\xdcJ\xbf\x03\xec\xfc@<\x1eB\xb8\x18K\xb5\xe6K\xe5U\xed%\xae\xd7\xf8\xac\x03\x13\xa0N\xb5\x00\x08\xc1\x84\xe2=6@\x13\xa0\x1f @F)e\xcc\xbd(\xc7\x9f\xca\x82)\xa5\xc5f\xb7\x00\x96C\x019\xe71\xd7Z\xb7\x18\xe3\xb2\x03+\xd6\x05H\x00z\x9b\xdf\xf1\xb0\xf2\xe2\x02\xf8\xccB \x9e\x15^\x0e4\xde\x05pI\xb3\x8b\xa4B\xb5\xa7\xbd\x02\xb4\xd6f\x12Q*\xd6\x97wxZ|\xcd\xe7\xd7\xad\xe2\x10\x80\xfdj\xcb\x01\x18\x04a\xc7\xf0\x00^\xc2\xfb\x9fla\x09\x89\xab-\xe0\x92\xed\xcb%\xfe\xeca\xe9\x80R\x8f\x16\x1d\x80\x0f\xe4\xda,\x95;i\x5c\x99dX\x0f\xcdR\xbd\x00\xd8\xe6\xad5*\xd1\x0c@\x0d\xee\x90\x01n\xce\xa2\xf7\xe7L\xafB\x00\xb59\xb2P\x11\x97\x00\xa2\xa5\xa2\xdf\x02`\x808\xcd\xec\xb2i\x86\xf9H\x01\x14\xd8\x18\xe3\x1e\x85>\xcdP\x96\x15\xa3R\x1f\xb0\xdf\xc3\xee\xbfn4,\xc3jy\xa6\x00\xd9,\x8e\xde\x959\xa8|\x88\x0c\x18\xbb\x10@E\xc3\x18d\x0eC2P \xbd\xf7G\xf5l\x03\xcc-\x9f\xc9\x00Kv\x09@\x81D\xb9\x88X/Ud\xdd\xca\x22\x9c\xcd\x163^nyRW\xe1Gl\xd6XY\xbep\x16\xfcb[.\x01\xda\xb1\x82\x14\x88A\x18x\xf1\x03\xad\xf4\xff?\xf4\xd0\x17,9\x08\xd9\xe0$c\xdc]\xba\xa0P(\x22m&\xea\xcc$_\xff\xc1\xdfk\xfe\x06\xb0\x01<|\x14f\x91\xa6&\xe4(-]E]\x07\xafQ`;\x8bi\x16\xea\x81\x8b\xcf\xbe\xae\xeb\xa7\x99\x95Z\xb5\x13\xbf\x07$\xdc\x01\x09^|bW \x94\xe5\x91\xdfd<\x10\xda\x1dI\x98|\xe3\xbe\xef\xfc\x11B\xb6\xdd\x03\xe0\xd9\xc8\xc8\xf6\xa3\x9e\xd5\xd2\x1d\x88\x82G\x9e\x981\xdc\xba\x9b\xde\xdf\xf5z\xc6W\x97l\xf0\xa3\xb9\xd6\x1a\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04YIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x98\x22r2\xa8j\xc7\xb0\x17\xd8\xad\xb5\x8e\xd9e\x03\x06-\x86^`q\x0c\x19 \xc0'\xb5\xd6\x96mW\xe8/&\x85\x88B\xe8\xc9`\xad@-\xf7'Y\xee\x0f\x06\x00\x8e&n\xd0\xa1\xad\xbe\xdb\xbf\x04\x16_\x0f\xbf\x0b\x10\xc8W\x00\xe6\xaa \x05`\x10\x86\xed\xb0\x93\x1f\x12\xff\xff\x1f/^<\xce\x0a\x91\x18\xea\xc6\x06\xc2\x84A\xd9Z3\xd34\xbe\xd6\xd2v)\xfd\x0e\xe0\xd4\x17\xa6\xf1\x10\xc2$,p\xad\x0b\xf4\x82{\xab3\x9f\xc4\x1c\xb8\x00P\xaa\x07\xc0 \xdcP\x8e\xd9\x00]\x00$0\x80=\xe6k9\xe7\x91\x17cte\xf6\x08\xa0t\xf0\x9f\xa7\x94z\xdc.\x80\xa3\xd6\x8a)\x9f@\x94J\x17\xc0\x0aX\xdbJK\xbb\x9cF\xac'X\x0e\x0e\xbb\xf7jc|\xbb\xeb\x01\xe7\xb8\x00:\xd28\x89M\xa6z\xd9'\x80RJ\x97\x1b\x8a\xb4\x1f\x1e\x15\x88\x9b\xc4\xd7v\xb7k]\x02\xb0c\xc5*\x14\x830\xd0\xa1\x9d\x0b.~\x89\xe0\xd4\xb1\x93_\xec\xa7\xf85\xaf\x11\x84r\xbd\x18;\xf4M\x1d\x84BC.Qs\x97\xf8q\xd1\x07\xf0\x02]KK\xd5;i\x5cXd\xb8\xa4\x86\xaeT}\x03\x10\xe7!\x04J\xd1\x0c@\x13\xeea\x06\xe8\x9cE\xdf\xffc53\xbbe\xc69f\xa1E<\x050ZZ\xf4\x8f\x004\xc0R\x8a\xdb\xb6\xad\xf5q\xc7q\xb4i\x09\xcf\x83\x01\xa8\xd7\x14\x8d\xc5\xb9(Z\xce\xb9\x81Y\x91\x0f\x15\x8de\xc1\xa4tt\x1e\xd3\x00\xdd\x81\x8cl\xe7\xb4\xe9j\xadn\xdfw\xf3z\x9a\x00x\xed\xd6uu\xde{\x97RR\x05\xc7\x04\x18\x098F\xccD\x9f\x01-\xac\xfc\xad\xed\xc2*\xd6:\x0c5\x03f\x18c\xbc\x09\xfac\x80\xeb\xde[4\xc0\xb8h\x0a@\x03\x99\xd9\xae)=8\x07B\x1aa\x7f\xbd\xc0\xef\xab\xad\xd0\xb5\xd9U\xf4\x11\x9b\x11\xa0u^\xa8\x05\x7fi[~\x02\xb4gm'\x10\xc2@\xd0\x0f\x0b\x10D\xd0*\xec\xff_\xf0\xc72\xac\xc0\x0a\x8e\x09\xac\x84e_F\xcf\xf3\xc0\x80(^N\xf7\x11wg&_\x7f\xc1\xdf\xf7\xfc\xd7\x81\xd7\x81\x87\x8f:2)/M\x1a\xa2\xe4\xe5\xcaS\x1d,\x92\xca\x95\xc5b\x07\xc8p\xe0\xec\xae\xebn\x8d,\xb8*\xbd\xdfr\xc4\xcd\x00\x8c\x07a\xa6\x0e\xa4EY\xc2\x9bZ\x06,\x92Mg\x04\x0c\xcf\xd8\xb6\xad<\x03\x1al\xb7\x1c\xb0\xf0i\x14fr\xcd\xea\xd47\xe0\x19/Q\x06\x8b\x00I\xbf\xe5\xe0%\x9f\xaf\x91\xaaC\x19\xf0\x8c\xe6g\xe8_Xn\xd8\x0b@\xfa\xdb\xb6MK\x9041\x0bVK\xf7N;\x10]6t\xbd\xaekR\x0cA\x81\x86a\xd8\x11\xdc4M\x09\xf7\x13\xe6\xd7\xa0\xe3\xd1\x0c\x14\xf5\x01-B\x1aq\xc4\xbe\x10\x98\x10H#\x8e\xe8\xff\x1f\xd5\xc8\x96e\xa9\xb0#\x86%u{#\xf3\xa4Z^]\xb054\xcf\xf3>\xbf\xef\xfbj\x1c\xc7\x9d\xaekL\xae\x04\x19\xd7\x9e\xa1y}\xce+\x85E}\x10i\xa8\xe5\x96<\xaf\x91\x0a\x8b\xca\x169 \x19\xcf\x1f\x9a\xdf\xe3\xd0@\x9a\xaf\x19\xe99tJ\x9a\x90\x9c\xe0u\x1c\x03\xea\x02\xa9\xfd\x9a\x01\x96\xa1\x97g\xc0\x8a\xa44\x9a\xa6I\x87ex\x04J\xd05\xd7\x12\x8a?b\xa9NG\xe6G\xe7x\xce\x5cV\x85\x8e:r\xc4\xc9\x92\xe7\xb9\xa4>\x87\xd4\x11!2\xd2\x8c\x22\x86\x93\x86\xe2q\x82W\x95\xf8\xf5\xf8\x00\xa0\xbc\xef)_\xde\x1f\xb5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00Y>\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x01X\x00\x00\x01O\x08\x06\x00\x00\x00\x08\xbf\xd6c\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x1e\xc2\x00\x00\x1e\xc2\x01n\xd0u>\x00\x00\x00\x07tIME\x07\xdf\x03\x09\x16+\x08u\x14\x9b\x99\x00\x00 \x00IDATx\xda\xec\x9dw\xb8\x5cE\xf9\xc7?\xef\xde\x92\xdc$\x84\x90\x84\xde\x12@\x04\x04\xe9M\x8a\x14\x15\xa9\x82 *\x22\x0a\xa8`\xe1'\x0a\x22X\xe9 X\xc0\x02\x82\x88\xa0\x14\x05\x95^\xa4\x83\xa8\x80 5\x02\xd2;\x81\xf4r\xfb\xdd\xf7\xf7\xc7\xcc\xb9w\xb3\xd9\xdd\xbb3{\xce\xee9\xbb\xf3}\x9e\xf3,\xe4\x9e:g\xe6{\xbe\xf3\xce[DU\x09\x08\xa8\x07D\xa4\x0bX\xb1\xcc\xb6\x92\xfd\x9d\x02\xe4\x00\xf5\xdcnP\xd5S\x1d\xef+\x07\xb4\xd9kO\x01&\xdb\xcd\x05\x8b\xed\x86\xaa\xfe+\xbc\xed\x00\x80\xf6\xd0\x04\x011\x91\xe7d`\x13\xe0=\x15\x08t\x99:\xdc\xca\x8c\xf06\x02\x02\xc1\x06d\x99L\xa7[2-\xdc\xd6H\xc9\xed\xe5\x1b\xd5,V\x05\xc7\xdd\xd696\xa7\x8dGh+\x18\xaf\xfdV)\xf7\x87\xde\x18\x086 \xbbD\xda\x09lP\x82L\x97M\xf1m\x07\x9bW@ \xd8\x80\xd4\x91\xa9\x00[\x01\xdb\x14\x10\xe9\xfb\x80\x8e\x8c=J\xa3\x086g\xb7\xf8\x94\xab9\xdf\x14\x9ed\x0a\x9d\xe4\xe8G\xec\x9f\xbb\xed>\xaf\x05%\x1b\x086 \xbd\xa4:\x09\xf8\x08\xb0'\xf0Q`\x85&x\xacZM\x04\x92\x12\x13A\xce~\xdc\x96\xa7\x9f\xb5\x8b\xfe6\xdf\xfe\xce,4\x19\x04\x04\x82\x0dh<\xa9nd\x09u\x0f`\xdb&\xec\x03\xc1D\x10\x10\x086\xa0n\x84:\x1e\xd8\xd5\x92\xea\xee\xc0\xeaM\xfe\xc8\x8d4\x11\xc49\x9e\xa6`\x19zz \xd8\x80\xfa\x91\xea{\xacB\xdd\x13\xd8\x11\x18\xd3B\x8f\x9f\x0f= \x10l@\xdc\xa4\xba\x1ep8\xb0/\xb0N\x0b7E#M\x04\x12\xe3\xb9\x96\xc5\xb8\xbeI\x19\xb5L\x8b}8\x03\xc1\x06\xd4\x9dT;\x81\x8f\x03G\x00;\x85\x16\x09\x0a6 \x10l@\xed\xc4\xba6\xf0%\xe0P`\xf9&z\xb4A\xe0]`\x81Ui2\xcao\xa9\x7f\xeb\xaeAyJ\x02J4q\xc5l]\xba\x00TC\xec{ \xd8\x00/Rm\x07>\x06\x1c\x89Y\xb4\xca\x0a\x09\xf4c\xdc\x89Jm\xef\x14\xfd\xff\xec\x06\x12D\xd6\xc8\xb5\xd0T\x90+P\xef\x81`\x03\xc1\x068\x10\xeb4\xe0\x8b\xc0a\x98U\xe5\xb4N\xcb\xff\x07<\x0a\xad\xf5\xceE\x10\xd0\xaa\x04[@\xaeG\xd4x\xaa9\xc0GU\xf5\x9c\xd0M\x02\xc1zb\x0d\xe0x\xe0I\x11yLD\x8e\x15\x91UC\x13\x07\x82\xcd2\xb9\xfe:\x06r\x9d\x01l\xa5\xaaw\x84.\xd2R\x18\x07\xac\x9f\xd0\xb97\xc6dU{UD\xee\x14\x91\x83l\x82\xa1b\xb4\xd9\xadZ7\xadz\xa8K\x97H\xae\xe1\xb1\x18U\xd7\x0d\x04\xdb\x5c\xe4\xfa\xa5\x1aOu\x1d\xb0m(\xdf\xd2\xb2\xea\xb5\xbd\x0ecn\x17\xe0r\xe09\x119RD\xc6\x84\xa6\x0f\x04\x9bvr\xbd\xa0FrU\xe0\x14\x8c\xa7@\xa8\x88\xd9Z\x88\x94\xd66u\xbe\xeet\x8c9\xeb%k>\x98@:m\x9d>J\xd9%\xafm \xd8\x94\x93\xeb\x85\xc0\x17k8\xcdb\xe0@U\xfd\x81\xc7\xaao@\xf3\x90\xec6\x0d\xba\xf6\xca\x91\xf9\x00\xf8\x0a\xb0\x9c\xa7\x89 I\x9e\xc89^'\x10l\xd6\x1b\xa0\x80\x5c\xbfP\xc3i^\x05>\xa0\xaa\x7f\x0e\x1c\xd3\xf2x\x12x\xb9\x81\xd7_\xce\x0a\x85\x9b,\xd1N\xf6P\x98A\xc1\xa6\x89\xa3\xb2*\xd8,\xb9\xfe\x068\xbc\x86\xd3\xbc\x0c\xec\xa4\xaa\xaf\x04ni\xe1A \x12\x959\x99\x0a,\x0f\xaci\xd5\xec\x86v[\xbdA\xb76\x08\xdc\x85\xa9\x0f\xf7v\x89\xbf\xf7\xda\xdfg\xec\xef@\xf2\x09\xa6\xdb0\xd9\xbdv\x04~[p\x1f\x85SxH\xd7\xc2\xd8X\xfb\xdb]0n\xa3-\x87YTV\xabr\x9b6\xd5g\xe6L\x04\x22R\xeb\x82\xd6\x8b\xd6,\xf0Z\xe0\x8d\x802&\x82\x89v\xab\x06\x9d\xc0\x96\x96\xfc\xea\xe1\xe6\x05&\xef\xc1\x85\xb6/\x03<[`\x22\x98\x97\x12\x13\xc1\xdf-y\xceie\x82m\xcf\xd8@8\xb6Fr}\xc1*\xd7@\xae\x01q\xa1\xdf\x9a\x12\xee\xb5JvG\x84-\xd1\xe1D0I`\x13\xe0\xe7\xc0_\x80+R\xda.]v\xccv\x15|\x88\xc6\x14l\x83\xc0\x80\xddg\x96%\xda\xbeQ\xc6\xff\x11\xc0\x0bY\x0a\x00\xca\xcc*\x9f\x88|\x14\xf8Q\x0d\xa7x>(\xd7\x80:\x98\x12nD\xd9\x15c\xab\xfd\x01\x90TB\xf6v\xe0\x93\x98\xb5\x88\x8f\x92\xbe\x1c\x01Qm\xae.\xbb-kg\x08\xab\x00\xd3\x80\xd50\xeei+\x17\x90n\xd9\x19\x86\x88\x9c\x8f\x09$\xfa\x93\x88\xac\x1d\x086^r}/\xa6\xac\xb6\xef\xfdF\xe4\xfaz\xe0\x80\x80:\xe1%U=EU\xdf\x8bIk\xf8GL\xf2\xa0\xb8\xb1\x02&X\xe1w\xc0Z\xcd\xd6\x88\x22\xb2<&qN\x94\xb8i2p\x9d\x0d\xcaH\xff\xfd\xa7\xdd\x06+\x22\x930\xb9X\xd7\xf5<\xc5s\xd6,\xf0F\x86:\xd5\xa4$li\x01\xe5\x15\x92\x9d\xc2F6\xd8e\xa8\xde\x06\x1ba\xd0nX\x93\x01\xaa\xba\xa8\xe8:k\x03\xdf\x06>g\xaf\x177\xfa\x80S\x81\xd3\xe3\xac\xb6\xe1i\x83\x1d\xb0\xbfs\xec\xefxL\x1e\xddr\x02\x08\xac\xbbYd\x93\x15\x91M0\xa1\xebk\x948\xe6:L\xd4e\xaa\x09,\x97\xf2\x8e\xdf\x06\x5cY\x03\xb9\xfe\xcf*\xd7722\xd0\xdbE\xe4t\xe0\xd9\x90u\xa9\xf9\xa0\xaa/\xa8\xea\x970!\xb2?\xc1TF\x88\x13c0\xe1\xdew\x88\xc8J\x19\xff\xe8\x1dh?Tk\x94\xd9\xe5c\xc0\x89\xc1DP\x1b~\x84\xb1/\xf9\xe0YK\xaeof\xa4C\xad\x06\xdc\x0d\x9c`\xa7}W\x96\xc9\xb6\x14\x90}\xa2}SU\x8f\xc5\x044\x9cX\xa0\xf2\xe2\xc2\xce\xc0\xe3\x22\xf2\xe1\x06sK\xce*\xd7\xf1\x15\x15{'9:\x87Cq\xdbD\xe44\x8c\xcf\xefh\xeeo\xdf\x17\x91\xfd\x03\xc1\xfa\x11\xce!\xc015\x90\xeb\xceY)\xef\x22\x22{`\x5co\xb6/\xf8\xe7\x1d\x80\x93\x03\x1deKx\xe1\x10N\xaa\xaasT\xf5$\xab\xd2\x8e\xc1\xd4|\x8b\x0b+\x00\xb7\x8a\xc8\xa9\x0d\xfaPGi\x17'\xd8\xad<\xc1*9\x94\x1c0\x09\xb8\x06\xf8\x8eC{_*\x22\x1b\x05\x82u#\x9c\xad1~~>\x88\xfc\x5cSO\xae\xd6$p\x16p#0\xa5\xc4.'4X\x85\x04\xd4F\xb4\xd5*\xda\xc5\xaa\xfaSk:\xb8\x00\xe8\x89q|\x7f\x17\xb8+\x03&'\x01n\x06\xf6p\xc7\x01\x9f\xf1<\xfc*U=-\xe5\xe4:\xd6N\xff\xbe\xe9\xfb\x01\x11\x91]\x02\x07\xb6\x1c\x860&\xb3\xaf2\x92\xdd\xabV|\x00\xf8\xa7\x88\xac\x9f\xd2g\xfe<\xf0\xb8\xe7\xb1\xe7\x89\xc8\xb6\xa9\x19\xf7i\xf0\xd3\xb5\xab\xe87x\x12\xfe\xa3\xc0\xf6i\xce;i\x83%n`I/\x01\x1f\xbc\x0dl\xa2\xaa3\x03\xef\xc4\xfa~\xa2@\x83\xe5\x19\x094X\xc6\xf14\xc3\xc9K\x80Y\xf6w\xb6\xfd\x8d\x02\x10\xfa\xed\xd6\x8bY\xc8\x1a*\x0e\x08(X\xd4\x8c\x12\xd0\x14ccLi\xa48\x16\xad\xe6\x00{\xa9\xea\xbf\xca)X\xfb\x9f\x91\x1f\xfaz\x09\xbd\x82(\xab\xdd\xd3\x00\xaa\xda+\x22\xd3\x80\x7f[\xd3\x86\xcf8\xd9\x22\x0d\xfe\xef\xb9\x14t\xee\x95\x81\xcb<\xef\xe5\x1d`\xdf\x94\x93\xeb\xaa\x98\xccB\xdb\xc7p\xba\x97\xc8h\x0e\xdf\x161/D\xbe\x9fS\xed\xb6f\xd1\xb6\x8a%\xf0\x09\x96<}\xdc\xa7\x1e\x07\xben?\xd8\xb5b2&(a\xcf*L&\x92p\xdb-q\x1dU}\x19\xf8D\xc1\xc7\xc9\x05+\x01\xd7\x8aHg\xa3;E\x1aL\x04\xbf\xc6\x94\xcapE?\xb0\x7f\x9as\xba\x8a\xc8z\x98\xdc\x9d\x1b\xd6x*\xc5,z\xec\x98\xa5\x90\xdf\x80\xc40\x88\xc9\x0b{*\xc6s\xa6\x16\x8c\xb3d\xf4\xb9\xb4=\xa4\xaa\xdec?&>\xd8\x02\xe3\xa2\xd6\xba\x04+\x22\x9f\x01\xf6\xf1<\xfc\xab\xaaz\x7f\x8a\xc9uk\xe0~\xca\x87\xfaU\x8bY\xc0\x9e\xaa\xfamU\x1d$\xa0.\x0a*\x86q\x95c$\xa3T\x94_ve`\x9d\x025;\xd1.z\xf9\x8e\xc3\x7f\x03\xdf\xc0\xdf^\x19\xa1\x1d\xb8\xc4\xae\x83Tj\x9f\xa4\x10-\x0e.U\xc7KU\xcf\xc3d\x0c\xf3\xc1\x09\x22\xf2\xfe\x98\xc6s\xcen]v\x1b[\xb0\x80Yv\xe12\xd7@\x02Z\x11\x93\xd3\xd2\x07\xbfT\xd5\x8bRL\xae\xbb\x03wR:x\xc0\x05\xf7al\xae\xb7\x04\x0e\x0c(\x83w\x81C\x80\x8b\xf1\xf3!-\xc4\x8fD\xe4\xa7\x0e^\x0e\xf5\xc2\xd7\xf0[\xe0\xeb\x00~\xd7H\xb7\xb4F*\xd8\xf3qsU\x8ap\x97\xfdj\xa7\x95\x5c?\x8bq\xc3\x1a_\xc3i\xf2\xc0i\xc0.\xc1$\xd0\x94h\xc3,\xaa\xb5G\xea\xad\xc0%\xca\xb7\xbf\xfc\x0c\xe3\x853\xab\xc6{\xfb\x06\xf0\x07\xbb\xf0W\xef\x19D9SA?\xb0?~>\xc1\x9b\x01\xc7\xd5\xaa\x5c-WMf\xc4\x9e>\xcdn+ar\xdd\x8eM\x0d\xc1\x8a\xc8\xa705\x86\x5c\xf1\x22p`Z\xa7\xca\xb6\xe2\xc2\xa5\xd4\xb6\x105\x13\xd8MU\xbf\xd7\xcc\xa54\x02\xc1\xd2\xc9\xc8BW\xc9\xe9\xb1\x87i\xe3NL\xb5\x83\xbf\xd7x\x7f\x9f\xc1,\xa2M\xa0A\x8b\x5c%Hv&\xb0/~a\xc4?\xa8\xc1%-z?\x11\xc1F\xc4\x1a\x91\xec\x8a\x96`\xc7\x94\xfaH\xe6\x1a@B+\x00\xbf\xf08t\x11\xf01U\x9d\x9dRr=\x198\xbb\xc6\x8ex\x8f5\x09\xdc\x118(\xc0\x07v\xc6\xb33\xc6a\xbf\x16\xec\x86\x89\xaa\xecL\xd1\xb3\xfd\x07\xbfJ\xd2c\xac\xa9\xa0\xee|\xd7\x08\x05\xfb+\xfc|\xdb\x8eR\xd5\xa7RJ\xaeGa\xc2\x1ak\xc1\x9f\xacr};\xd0D\xd3\xa3\x13cB*,\xa9\x12\xf9\xbd\xd6\xac\x16UuHU\xbf\x83\xa9\x02PK\xe2\xed\x9d\x81\xdf\xd3I\x1b\x9d\x89*\xd8\x5c\xd1V\xe9\xd9\xae\x04\xce\xf5\xb8\xc6\xd68\x98\x16\x0b\xd4h\xe4\x13\xbd\xbe\xdd\xa2\xf26c\xed6\xc1*\xd8\xf1\x94(}\x93\xab3\x11}\x028\xc0\xe3\xd0[T\xf5\x92\x94\x92\xeb'\x81sj<\xcd/\x81\x83\xac\xad) .\xc5w\x01\xc6\x97\xb4\xaf\x86\xd3\x1c@?'\xa4\xec\xd1N`\xa4\x0a\x82\x0bN)\xa8\xce\xd0\x5c\x0aVD\xa6Z\xf5\xea\x8a\xf9\x98\xc8\x954\x92\xeb\xae\xc0\xefkl\xc7\xef\xa9\xeaQq\x96\xf8\x08\xa8\xed\xb5\x92|\x01\xc1\x8e\x22\xf5\xda\x85\xb1\xdb\xb7\x13\xb3\xcdSU\xffj\xa7\xfb\xf3k8\xcd\xa7\xe9\x1f\xae\x89\x95\x9c\x82\x9dd\xb7\xd1\x9f\xa9\x078\x0cw\xaf\x89.\xe0\xb7\xa3-&\x16\xe5_\x18\xad\x84{\x8e\x91\xc5\xca\xa5\x14x=\x15\xec/1Q,\xae8&\x8d\xc5\x0aEd3L\x8c\xb8\xaf\x8dj\x08\xf8b\xdas(\x044\x85\x92\xbd\x17\x93\xb2\xb0\x96\x1c\xc9_\x03>\x92\xa2g\xfa\xbb\xa7`\xdb\x01\x93\xd7\xa1y\x14\xac\x88|\x1cSb\xd8\x15\xb7\xa9\xeaoSH\xae\xeb\x00\xb7\xe0\x1e\xaf\x1e\xa1\x178 \xcd\xbe\xbc-\xac^\xeb5\xee\xda\x0b\x94\x8f\xaf\x17\x81\x0b!=\x01l\x87)\x02\xea\x8b#1\xb6\xccdf\x0d\xf3\x00\xb7R\x9f\xc7c\xc2\xc7]q\xa6\xcduP\xcdL\xa6bI\xf1\xd1\xfaN\xe2\x04k3\x8d\x9f\xe7q\xe8B\xe0\x8b)$\xd7\x95\x80\xbfaR\xd2\xf9`>f1\xeb\xda\xc0g-I\xae\xd1\xb8\xeb( \xd9\xf6\xa4\x09\xd6\x92\xecK\x96d\x1f\xae\xe1\xbe\x8f\xc1\xad\xbal\xf5\x04\xebQ\x09\xc2\x93#\xc6cR\xa3Vs?\xd1bV5}g\xa9\xfb\xaf\x87\x82\xfd9\xc6W\xcc\x15\xc7\xa6-\xcf\x80\x88L\xb4\xca\xd5\xb7\xfe\xfc[\x98|\x02\xf7\x05>\x0bh\xd0\xd4\xfa]\x8cw\xc0\xed\x9e\xa7\xe8\xc4\xc4\xf8\xaf\x99\x92\xe7\xb9\x13\xbf\xf2R\xbb\x8aH\xe2\x02.\xd1t\x85\xb6D\x85O\x98\xe7\x1d\xaa\x9a\xaaZT\x222\xc6>\xcb\xce\x9e\xa7x\x11\xd8\xd5f\x09\x0aX\xb2m\xc71\x92*pj\xd1\xef\x98\x83I\xde\xfdn\x0c\xed\x10\x19\x06\x9e\xb2\xa49\xdb\xe1\xdd\xb5a\x16\xa1f\xe0^\x09e\x01\xf0\xbe\xe25\x1e\xeb/\x1be:\x8b\x0a\x00\x94[?Zd\xb7w\xa2\xb6P\xd5\xe1r\xec\xed\x09vZ\xc1\xd4\xd8q\xc5\x22\xe0\x0b)\xe4\x81\x0bk \xd7\x99\xc0GZ\x9d\x5cmL\xf8\xba\xc0\xfb\xed\xb6\x11&\xbf\xe9\xea\x15\x0e{\x16\x93I, ^\xe57`#*o\xc3,\xfc\xb8b2p\x12p,\xd0\xe8t\xa1\x0b0\x95h\x7f\xe7x\xdcD\xcbQ\x9fI\xea\xc6\x92L\x82\xf0i;\x88\x5cq\x9c\xaa\xbe\x922b8\x14\x93P\xc3\x07\x0b\x81\xddU\xf5\x85\x16$\xd4\x951.B;Y\x22\x8d\x1c\xb5]\xb0B\xb37S\xd1o=I\xb6WD\xf6\xc1$\x15\xf2)}\xbd\x0afE\xfe\xec\x06\xb6A\xa44\x9f\xc0\x84\xf7\xee\xedx\xfc\xa7D\xe4\x8c\xa2 \xa6B[jG\x95\xf7_\xd2~\xdc\x9e\xd0\xc0\xea\xc0\xafx\xd9\xdd\x98\xfc\xb0i\x22\x89\x0d0.f>\xe8\xc3$\x04\x7f\xb4E\x08\xb5\x03\xb3\x88\xf2Q\xbbm\x1c\xc3i\x97\x13\x91\x0eU\x1d\xa83\xe1\xd5\xebZ\xd1\xf5\x1a\x92\x17DU\xe7YS\xde?\xf1\xb3\xabng\xa7\xf6\xb7\xc4\xd0\xe6>m\x1fq\xd8r\x98\x1c\xb9\x1f\xc0-\x8b]\x0e\x93Ww\xdf2\xf7\xd4Y\xe5;,I\xb0I\xbd\xd4\xc3\x81\xb5\x1d\x8fY\x0c\x1c\xaei\xa8a3B\x18\xe30v\xaaq\x1e\x87\xe7\x81\x83U\xf5\xae&'\xd5\x89\x22r\x98\x88\x5c\x8b)\x91r7\xc66\xb7q\x8c\x97Y\x9e\x80$I\xf6M;\xd3\xf0\xcd\xc4u\x18\xfe\x0b\xbfqb\x91\xa7\x18\xfa\x98\x88l\x95\xc4\x0d\xe5\x12\x18p]\xf8U@=\xde\xba\x91\xa4\x09\xbf\xc4\xdf%\xe5(U\xfds\xb3\x0eJ\x11\xd9VD.\x06\xde\xb4\xca\xe1c\xf8\xfb\x05\xb7\xaa\x99\xa0\xb0\xccL\x22\x91\x5c\x0e$\xfb,\xb0\xa7\x15:\xae\xe8\xc0\xd8b\xbbj\xe0\xa1\xaar\x11\x14\xce\x96\xec\x8ci\x9a\xdd\xa2\x85\xc9G\x81{=\xee\xa1\x5c\xc0\xcf\xb8Q\x04V\xdd#\xb9\xfe\x0f\xf7\xd5\xbc\xfb\xf1\x8b\xcaH\x92@>\x0b\x1c\xeay\xf8\xc96\x13{\xb3\x91\xea\x14\x119ZD\x9e\xb2S\xcaC\xa9-\xefm\xb5X\x91\x80z\x90\xecC\x98\xbc\xab>\xe6\x98U\x80\xaf\xa4\xe4Q.\xc25d\x01>$\x22;\xc7}#\xb9\x98\x07\xe0$;=t\xc5q)3\x0d\xac\x87I\x08\xee\x83\x0bT\xf5\x87MF\xac[\x8b\xc8\x95\xc0\x1b\x98\xc4\xce\xef\xab\xf3-$\xad`\x1b\xb6\xd0T\xa0V\x13\x0f4\xa8\x92d\xfff?\x9c>\xe3q\x07\xfc\xc2i}\x14\xfb\xcav\x8b\xf2\xb4\x16b!&G\x88+N/\xba\x9fj\xee\xa9\x1d\x13\x88\xd0e\x7f\xc7\x16\xe6\x85\x8d\xfb\x85~\x0b\xf7\x02\x86\xd7\x97+\x1b\xdc 2\xe9\x02\xae\xf2Tf7\xa4\xe8+\x1eG[l!\x227\x01\x0f\x00\x9f\xc2\xdd\x03 K&\x82F\x92kj\x08\xd6\x92\xec\xe5v\xca\xef\x83/\xd8)\xbbo;T\xfb\x1eV\xb1\xdb\x14J/j\xdd\x03\xb8\xe60\xd9FD\xf6v$\xfd\x88`#\x92\xed*|\x97\xb9\x18\x07\xe3\x8a\xb8W\x80\xcc\x93\x82\xca\x8fE\xf89~.+/\x03\x874CV,\x11\xd9TD\xae\xc7\x14\xd5\xdb#\x05\xb7To\x1bl#<\x09RC\xb0\x96d\x7f\x0a\x5c\xe1qh\xa7\x15Zc=\xda\xa0\x22\xa9\x89H\xa7\x88,#\x22\xcbX\x014~\x14n\xb9\xd2\xe3\xfeO\xb5\xa4\x19\xe5\xe7\xad\xf6\xfe'`LY+\x02\xab\x02\xab\x8a\xc8\xd48_\xe8\xf7=T\xdf\x15iJ\xa2-\x22\x07\xe1\x17\xe40\x00|RU\xe7\x91a\x88\xc8\xfbE\xe4\xaf\xc0#\xb8\xfb\x136\x0b\xc16\x82\x5c\x0b\x09\xb6\xee\x0b\x5c\x15\xf0uL\xa0\x87+V\x05\xbe\x9c\x80\x82\x1d\xc3H\x02\xecj\xa2\xed\xfe\x89{2\x98\xf7\x03\x07:\x10\xac\x94 \xd8\xd5\xec\x16\x0f\xc1\x8a\xc8t\xdcs\xb6\x0e\xe0\xe7m\x90\x14\xb9\xac\x86\xbf\x0f\xeeqv\x81 \xab\xc4\xba\x82\x88\x5c\x06<\x86\xa9\x95\xd6\xe8\x01\xde\x8f\xb1\xf7>\x86\x89\x99\x9fA@#\xb0\x188\x18\xbf\x84\xdd\x1f$\x99\xcc[NB\x1c\xb8\xdcS,\xc6\x12#\x10W\xa0\xc1I\x0er:\xc2\x85)s\xcb\xfa)~nF\xd7\xaa\xea9Y\x1dA\x22r\x88}\xf6)u\xbet\xaf%\xce\xc7\x80\xc71\x918o\x02\xef\xa8\xea|Z\x03R`\x16\xe8H\xe9\xfd\xfd\x17\xf81~\xa6\xbc/\xdaw\xdb[\xe5\xaca\xb4\x0f\xfb2\x18\xbb\xab\x0b\x1e\xb6*\xfc\xbd\x0e\xc7\xac\x03|\x1eS\xc6\xc9\x07\x91\xdf\xf6\xf2\xed1\x0c\xd0\x0dq\x8f\xe5\xed\xb6\xb6\x8e\xb4\x90\xcc\x870\xa55\x5c\xf12\xfe\xae\x5c\x8d~\xe6i\xc0\x05\xd4/\x89\xf23\x984\x8f\x0f\xd9A\xf7lZ\xab\x03\x07,\x85k0IhvsD\xc8\x9c\xdd\xd5\xd6\x86\xff\x975\x09$A\xae\xb31\x05 w\x04\xa6\xa8\xea\xc7U\xf5\xc2\x8c\x91k\xbd\x89\xad\x5c$WZ\xda$\xdaz0I]\x5c\x83\x10\xda\x80#\x1c\xae#U\x10\xac\x0f\xc9\xfa\xd8b\xbf\x8c\x9b7D\xbc\x04\x8b\xdbJa\x84\xb3\xd2\xa2\xfaDd\x0d\xfc\xcamg\xce\xee*\x22\x07\xda\xafq\xdc1\xd7\x8a\xc9?p\x10\xb0\xaa\xaa~CU\xff\x1e\xa6\xffM\x89\x970a\xd1\xae\xd8\x08\xbf\x94\x88q\xe2\x19\xe0\xef\x8e\xc7L\xf60\x8b\xc4C\xb0\xd6\x17\xed`\xc7\xc3\xde\xc2\xf8\x99\xa6\x05?\xc3=\x91\xcb\x1bd\xc8\xee*\x22m\x22r6\xc6`\x1f\xa7j\x9d\x09\x9c\x09\xac\xab\xaa\xbb\xa8\xea\x95\xaa\xdaG\x80\xeb\xf8\x8b\x94k\x94\xec\xbb\xb0FW\x9a\xd4}\xa4,o\x05\x1e\xf48\xc7a\x15\xc6Z\xb5\xb9\x08\xda\xa8\xbeFV)\x9c\x8f{\x84Zc\x08\xd6\x92\xabk\xd6\xf7ST\xb5;%\xc4\xb3\x1b\xf0q\x8fC\xbf\x91\x15\xbb\xab-\x95~\x1b\xfeQ9\xe5\x88\xf5\x9b\xc0tU=AU\x9f'\xa0\xd5p\x11\xa3{\x06\x14c9L\x8e\xe8F\xe2i\x8c=\xd8\x05\xab\x00\x1b6\x82`]\xcd\x03/R\xb9\xd0X=\x89g\x0c\xf0\x0b\x8fCoW\xd5\xab3B\xae\x9bc\x02\x06v\x89\xe9\x94\xefZ\xa2^KU\x7ffk\xd3\x07\xd4\xae`\xebRU6f\xbc\x8b\x09'w\xc5\x9e\x94\x0e\xa3\xad\xd6\x06[\xd8^\xbeJ\xfc|L\x94W]Tl\xces\xf0n\x87{8\xe9O\xeb\x984y4|\x0bx\x8f\xe31}\xd4\xb1\x9ez\x8d\xe4\xfayL\x86\xb25b8\xdd,L\x02\x9f\xe9\xaa\xfa\x93\xb4\xcc@\x9a\xd0D\x90F\x82\xadD|\xd7\xe1\x1e\xeb\x9f\xc3\x94\xfe\x962&\x82\xd1\x08Vb \xd8\x97p\xf7o\xdd\x16S^\xa6n\x0a\xd6U\xbd.\x06\xfe\x90\x12\xf2\x99\x86\xa9\xdf\xe3\x8a\xb3T\xf5\xb9\xb4\x8fX\x11\x89j\x13\x8d\xad\xf1Tj\xbf\xf6k\xab\xeaY\xb6Dr@@\x84!\x8c\x1f\xb5+\xd6\xc3x\x994\x12\xa7\xe3f\x8bm\x07v\xad\x0b\xc1Z\xbb\xde\x01\x8e\x87]\xae\xaa\x0bR\xd21N\xc2=1\xf0K\x8c\xa42K3\xb9\x9eH\xf9\xc4\xc1.x\x16S^\xfc+)zo\xf5Vl\xf5@;#\x19\x98\xda\x0a\xb64\xbai\x95\xba\xa7'q_\x99\x07\x13\xd4\xd3\x88g\x8c\x94\xf2\x8b\xb8\xdbb?\xecs\xcf>\x0a\xf60\xdcW\xf1RQgKD\xd6\xc4\xb8\x13\xb9\xe2(U\xedM53\x88\x9c\x06\xd4\x9a\x87v\xc0\x12\xf4\xc6\xaaz\x7f\x10iuG\x07p\x02p\x14\xe9\x0b4(\x87\x8bq\xaf*\xbb\x1a\xa6vV#\xe1\xean\xb6\x0a\x1eY\xf6r\x8e\x83X\x18\xddi\xb8\x18\x0f\xa6\xa8\xe8\xdf\xb7p\x8f^\xbbVUoJ9\xb9\x9e\xedi\xf6(\xc4\xc3\xc0\x16\xaa\xfa\xbd\xe0nU7t2\x92vo\x9c\xfd@n\x81Y\x98<2\x85\xca\xbe\x14\xe6\xe2\x97\x16\xb0\x11*\xb6\xd0\xd6{3\xe0\x1aM\xba\x9b\xcf\x05]/\xe0Z\xdc\xec\xfcT\xf4\x10\x91\x15\xac\xfavA7\xee9n\xeb\xfd\x5c\xe7P\xbb\x1b\xd6\xd9\xc06\xaa\xfaD\xe0\xbc\x86`,&\xb3\xdc&Ec\xed\x0b\x19\xb9\xff\x9b0y9\x5c0\xcd~L\x1a\x85A\xab\xbe]\xb0\x0d\xb0l\x92\x04\xeb\xba\xb85\x07\xff\x8c4q\xe3h\xdcm\xaf'\xab\xea\xab)%V\x11\x91\xf3j\xfc\x00,\x06>\xa5\xaa\xc7\xa9\xeaP\x0b\x13\x5c#\xa7\xe3]V\xb9\x96\xf2\xb5\xdc\x0b\xf8\x5c\xbd\x9f\xa3\xa0\xe4\xc9\x98\xa2\xad\x1c\xf2\x183\xa0\xab\x13\xff\x81um\xe9\x8e\x0e\xa1\xa3\xa3\xb0\x8d.r\xbc\xe76\x1c\x17\xbbr\x0e\x8d\xbe:\xc6\x8f\xcd\x05\x97\xa4\xc1v)\x22\x13q/\xe5\xf2\x1a&f?\xad8\x15\xbfP\xe5\x08/\x00\xdb\xaa\xea\x9f\x08h\x14\xc9\x8e\xc7,\xba\xae_a\x9f\xfd0\xe5z\xaa\x9a\xc2G\xb5\xa0j!\xd7\x82\xf3\x8d-\xda*\xe1\x19\x8ck\xa0\x0b\xdeS\xa4\xda\xebe\xea\x10@l\xba\xd4\xdb\x1c\xcf\xf1\x11\x97\xbe\xe2\xa2`\xbf\x84\x9b\xff\x99\xe2\xe7\xc6\x91\x04\xbe\xe2*\xed\x81\x9f\xa4\xc8o\xb7x\x10\x1cLm6\xd7[\x81-U\xf5\xc9\xc0\xab\x0d\xc3\x04L\x0a\xbdu\xab\xd8\xf7S\x96h\xd3\x8e\xabS\xafb\x97\xc6\x85\x8e\xfb\xaf\x04l\x1c+\xc1\xda\xfa\xe3\xae\xf6\xa0\xbbT\xf5\x7f) \xa3\xb1\xd6<\xe0\x82Y\xc0oRJ\xae\xdbR[D\xdc\xb9\xc0\x9e\xaa:7p\x5c\xc30\xd1\xce@\xd6v8\xe6\x10`\xcb\x04\xfbU\xce\x8e\xf3\xa9\xf6\xbe\xd6\x066\xb7[5\x0a\x16\xe0U\xdc\xf3\x14l\xc0H\x95b)\xb0PH\x19e\xed\x8f\x81\x81\x1c\x03\x03\xc5\x01\x0d\xd7\xe3\xbe\xd8Uu\x0e\xe5j\x15\xec\xde\x96\xb9]p~J:\xf3a\x98:9N$\x94\xc6\x88%\x9b\xfd\xeb\x1a\xfc\x93]\x9c\xa6\xaaG7Ca\xc6\x0c\xa3\xdd\x92\xeb4\x8f\xe9\xed7p\xcf\xc7\xda\x08\x15\xeb\x8aO4\xeafm\xd67\xd7\xc5\xae\xad\x81Iq\x12\xec\xfe\x8e7\xf0\x16&\x94\xae\xd1\x84\xd4\x8eq\xcdr\xc1B\xfc\x12p'\xfd,\x130e\xc1W\xf4<\xc5\xf7T\xf5{\x81\xdfF%\xb1\xa41\x08\xdc\xe81\x95\x06\xe3\xca\xf5\x1d\xca/\xd6:\xdba\x0b\xd4\xe28Lz\xbe)\x8cd\xf6\xf2\xc1\x0b\x80\xab[\xe6&\xd6T2\xc9n\xe3\x80q6\x19>\x22\xd2\x86\xf1\x11\xae-\x94\xb8\x1d\xa1\xbd\xe4;\xfe\x0dn\xf9\x09\xda\x80\x0f\xc5B\xb0\xf6\xe1vw|\x94\xdf\xa4$\x1f\xe8\xa7<\x94\xc2yi\xcb\x96e\xab\x10\x5c\x8e\xa9x\xe9\x83cT\xf54\x02\xd2B\xb2\xb7\xe1o\x82Z\x0d\xf8\xbf\x0a\xf7\xe9s\xff\xb9\x02\x82\x9dZ#\xc1\xfa\xaa\xd8\xfd1\x19\xb7\x96c\xc47\xb8\xc3\x92\x7fD\xb0m1\xbc\xdb\xa5\x22\xd3T\xf5eLqM\x17|\xa0\xda\x86\x1d\x0d\xdb\xd9\x87\xae\x16C\xa4\xc7~\xe9\xea\x1f\xda\x8b\xc9\x11\x9b6\x9c\x84{\xe5\x08\xacJ\xfa\x8a\xadq\x1f\x90.\xdc\x8c_\xf2j0\xc9G\xf6O\xf1\xb3\xfd\x17\xf7J\xc0[R\xff\xc2\x9b\x85p]\x90_\xcb~\x90j&\xd8\xbd\x1c/|\xa3\xaa\xbe\xde\xe87,\x22\x1b\xe1\xb0\xdagq\xb1\xaa\xceL\x99z\xdd\x16\x13>\xe9\x83/\xab\xea\xf9\x04\xa4\x157\x00\x97z\x1e\xfb\x19`\xb3\x22e\xe6\x83(\xa3\xd7TL5\xd5\xc91=\xdb\xd5\x1e\xf7\xb1\x07\xa6r\xec\xb6v\xdb\x1c\x13\x9e\xba\x1ef\xd1mrM\xcf:H\x8e\xc1\xb2Y\xbbn\xc0\x986]\xb0y\x1c\x04\xbbw\x1d\xa6\x07I\xe03\xce\xcdo\x22\x9a\xd2D\xae\xe3\xec\x00\xf4\x99\x1a\xfdDU/ \xed\xb8\x06\xbfzQ\x82I|>9\xa5\xcf\xf5\x18\xe0\x9a\x8c}\xe7F\xdd\xac5i^RW\x82\x15\x91\xb5\xed\xd7\xc3\xc5\x08\xac\xee\xf2\x01\xb3d\x96&\xf5\xba\x0b\xf05\x8fC\x9f\x00\x0e\x0a\xaeX\xde\x83\xafQa\xb3\x97\x00Oy\x1c\xb75K\xaehK\x8a\x9e\xff!\xdc}L\x0bU\xecXF\x16\xbe\x96\xb3\x84+1\x7f\xf1\xc5\x5c\xed\xaf\x8f\xa4\xe4\xa5\xb9\x12l\xc3\x15\xac\x88l\x82[\xec\xf8\x10\x10\xeaj\x05\xfc\xc9\xf6\x05\x17U\xb5a\x8a\xee\x7f.\xf0\x9a\xc3\xfe\x13JM\xbd\xeb\x04\x05^r\xd8\xbf\x13S3\xad,\xc1\xba\xda_\x1bN\xb0\x22\xb2\x02nI\xc1_MIbmW\xf5z\xbd\xaa\xbeF@b]\x89\xf4\xda_\x0b\xf1\x16\xee\xeb\x1e\x1f\x0bf\x02`\xc4U\xad\x9c\x92\x95\x82\xbfGx\xb1\x163A\xad\x0a\xf6\xe1\x14\xbc,W\xfbk\xc3\xcd\x03\x222\x19\xf7\x8a\x0b\xbf$ \xc0\xe0\xcf\xb8\xd9bw\xa1|\xa9\xef,\x10l#\x03\x0e^r\xdc\x7f\xadJ\x04\xeb\xb2\xc0\xd5O:\x16\xb82g\x1e\xc0\xe4\xabu\xe9\xf0\xffU\xd5\xbb\x02\xaf\x04X\xbc\xed8\xf6\xba<\x84H\x92x\x0a\x13xP-\xd6\xa5>A\x12\xa5<\x0c\x5c\x15\xec\x12\x04\xdb^\xa0\xaa\xa6\xe3\x16M\xf4\xa4\xaa\xf6\xa7\xe0ee\xd1\x83\xc0u\xca\xf6\xab\xc0)u5\x15d\x01w8*\xbb\x0f\x03\x7fK\xc9\xbd/\x06^\xa0|\xbd9\xc5\xf8\xcc>a\xb7\x19\xb8\xd9\x9d}\xdf\xad\x94\x10\x9d\xafa\xf2\xc3V[\xf1azI\x82%\x9b\xf6\xd7\xc9\x98U\xc6j\xf1\x86\xaa\xbe\xd0\xe0{\x9e\x00\xec\xeap\xc8\x02\xe0\xf7\x81\xf7\x02\x8a\xf0\x80%\xaa\xf1U\xee\xbf\x0e\xc6q?-\x99\xd7\x9e(\x22\xd87\x0b\x08\xf5I`aJ\xees\x08x\x85\xea\xfd\xec'\x14\xb6s!\xc1f\xd1\x83`\x1bG\xc5qO\x0a\xeey7\xdcR*^\x1ajl\x05\x94@?\xf0w\xdc\xfc\x5c7\xc5$\xf3N\x03\x1e\x06\xa6\x14\x90\xea\xac\x94\xcc^\xdaJ\xa8\xd8\x97p\x0bdZ+\x22\xd8\x5c\x0d\x0a6\x0d\x0b\x5c\xeb\xb4\x80y\xe0\xb2\xc0%\x01ep\xbb\xe3\xfe\x9b\xa6\xe8\xde\x9f\x06\xce\x01\xeeJ\x09\xb9V\x82\xb7\x1d\xd6\x97`\xfb\xf1+1\xdch\x82}\xb0\x917kCc\xf7t8\xe4ML)\xe1\x80\x80Rx\x01c\xab\xac\x16\x1b\xd3\xd8\x8cZiG\xb90ZWO\x82\xe9K\x10\xac\x88\xacn\xed\x06\xd5\xe2\x89\x94,p\xb9\x10\xac\x02\xcf7\xf8~\xb7\xc7m!\xf1\xfa\x90\xd4%`\x14\xfc\xd3a\xdfq\xc0{C\x93U$\xd8R\xfe\xb1/\xe1\xe6\x16\xb7\x94\x82\xcdj\x04\xd7{\x1c\xf6}KU\xbb\x1b|\xbf\xfb:\xee\x7f]\xe8\xf3\x01\xa3\xe0Q\xc7\xfd7\x0bM\xe6\x8c^\xdc\xea\xa3M\xc5\x96\xed\x89\x08vm\xc7\x0b6\xdc\xfej\xa7\xdb\xd3\x1c\x0ey.\x05/j'\x87}\x17\x92\x82Le-85\xccJDW\x84\xe715\xf1\xaa\xc5\xa6\xe1u\x8f\xaa`K\xbd\x7f/;lD\xb0\xae9\x17\xd3\x10`\xb0&\xd5\xfb\xa6\xd1h\xf3\x80\x88t\xe1V)\xe2\xd6\x14\xd5\x0b\x0bH/\x14\xb7\x82\x82k\x91\xbeD\xdcY\x80+\xc1\xaeRH\xb0\xab8\x1e\xfcF\x0a\x1e\xd8u\x81\xab\xd1\xf6\xd7M\x1c?\x08\xd7\x87>\x1dP%\x5c\x16o\xc5c\xc6\xdaJ\x0a\xb6\x94\x9b\x96\x0f\xc1.K\xc1\x80w!\xd8\x87h;\x92/<&G.T|\x18\x81\xab{\xdf\xb2MD\xb0\xfd\x05\xfd\xaf\x8d\xda\xbc\x08\xa4\xccuz0\xc9\xb7\xc7\xba\x98\x08\x22\x05\xfb\x8e\xdd\x9e\xacp\xf11\x94/-#u\x1c\xc8\xae$?\xae\x22)/\x06:\x11:\x0b^S|p\xbd\xd7I\xf6\xe3Vk\x9b\xd6\xfb\xbdH\x99\xdf\xe1\x0e\xfa2s'\x83.\x13\x11\xe8\x08\x91\xb2\xc4\xff\xb7\x91\xcb\x9b_\xc9\xb7\x91\xcbw\x90\x1b\xea\xa0m\xa8\x93\xb6\xa1<9\x01\x18K[R\x09\x87\xb2\x9a\x8b\x00`\xae\xe3q\xcb4\xd1\xc7%2]\xb6\x01\x1d\xa3(X\xf1$X0v\xeej\xd7T&\xb9L]\xd5\xb2w\x1a\xd0\xe5\xb8\x7f#\xef\xdb5=\xdfP\x10b\x01\x9ep\xfd\xe8\x04\xf5\xef\x0e\x97yn{;\xeei\xff\xd2\x00W\xdf\xdd\x15FU&\xc9\x95p\x5c\xcd\xe3\xd9\x9a\xb2\xe3\xbf\xcd\xa2\xc9\x13\x19\xe3\xed\x7f\x99\xb3\x8a\x16`E&\xcc\x05X\x95\x89\xf3\x00:i\xab\xe5\xc3\x94U\xf7\xacbE\xe6Z1\xb6\x19\xf2\xe2\xe6\x8bDT\x0e\xb3\xd05\xaa\x82\x15\x11\x01((,ZI\xc1v{|\xc44\x97\xd1F\xedt\xdc\xbf\x91\xa5W\x82\x82\x0dHk_\x0b\x0a\xd6\xffcVU\xfb\xb6{L+\xd2\x80!\x8f\xfd\x1b\xe5^\xe6\xaa\x12\xfa\xeat\xafu\x1f\x5c\x1d\xe4\x06\xdb\xc8\x0d-}#\xbaD\xe7\xcd\x97\xf9\xf0+\xc8\x10\xf9\x1c\xc0B\xfa\xba\x00z\x18X\x1c\x83\x82m\x96A\xdf\x96\xf6>Ptm\xad\xf1\x03Q\xf8\x0c\x83\x05c}\xb4PY\xf1\x98\xc9\xf8\xb4\x95\xb6\xd7\xa8\x98\x1a\xf5\x82\x16:\xee?\xb6\x81\x1f\x12W\x82\xedo\x12\x82]\xea\xfc\xed\xe4\x86\xda\x0b\x08VQ)l \x1d\x1e`Z\xe6\x84*j;\xff\x22\xfa\xbb\xcc\xbcm\xa0\x13`\x19:\xfb\xcc\x08mI\xaf\x82,\x9a\x084\xe6>8T\xf0\x9b\xa7\xb6E\xaeX\x15l\x16\xe1\xeaP5\xae\x81\xf7\xfa\x16p&9r\xe4\x87_`\x94s\xb2\xd4\xb6\x88\x80\x80\xfa\x98\x08\xf2\xa1\xc9\x927\x11\xdc\x99\xc1\x87\x9c\x0e|\xc1a\xff\x17\x80\xbb\x1bx\xbf\xd7%\xdc\x953\xa1\xda\xf6c\x83\xa9\x1d\x05\x0a3_\xa6\xb3\xf61\xd0\x06\xb0\x80\xfe\xaeE\xf4\x8d]\xcc\xc0\xd8\xc5V\xb1\x8e\xc8\x95|\x1b@/\x83\x1d\xd6\xac \xd1YkT\xb1Y\x5c\xec\xca\x0dO\x12\xb2\xd3o\xfa\x19Y\x1b\x89\xd4\xe7\xe4\x1a\xfa\xfe@\x81\xa9\xa0Z\x13A\xb4\xd8\x15-tE\x0bd\xb9\x0a\xd7q\x22\xd8\xac.r\xb9FT\x84\xb8\xeb\x80V\xc0\x04\xc7\xfd\x83\x82\xad\x83\x82\xcd\xe2\xe2\x80\xabC\xf5D\xc2\xea|\xc3\xd1I[^\x0a\x94E\xaeHeD*t\x0c\x1dC\x00c\xc9\x0f\xe4Q\xe9'\xdf\x9eC\xf2\x0a\xa2\xc3JuX\xc9\xe6\x0a\xd5p\xae6U\x96u7\xad\x95\x1c\x8fkt\x14\x97\xc6\xa8\xa4\x0b\xcf\xa5\x9e\xefy\xf4E.A\x1c\xeeV\xdbU5s_1\x11q&\xd8,>g\xf3}\xfbO\xad\xd8\xf9\x8b\xc9\xb1\x8b\x8e\x01\x80n\xfa\xc7\x98(/E\x8b:\xff\x08\xc1\x0e/\x98I\xce?L2\xeb\xaaj\x05\x87c\x06=f\x82q\x93kR\x04;\xda9+\xbd\xeb\x5c\xc5\xbf\xbb\xddi\xbeUL\x04\x93\x02\xbb\x05\xb4\x00\x5c\x08v\x0e\xc1\x0f6\xf1YNV\xbd\x08\x02\xc1\xb6\x00\xda\xc9\xe5;i\x1b\xea\xa4}p,\xed\xfd\x03\x0c\xb5\xf73\x94[R\x86\x99\xc5\xae\x11\x05+R\xcf\x01\x94\x12\xe4<\x08vv\x0a\xee;\x22\xf8|\x8c\xe7\xaa\xc5D\x10\xe51\x88k\x91+\x1f\x16\xb9\x02\x02ZS\xc1\xce\x0a\xcd\x95\xf8\x078\x9b~\xb0\xaa\xda-\x22\x03\xf6k\x13\x14lS\xf7f\xd16$\xdfNn(R\xab\x85\xe8'\xdf\x0e#\xb6\xd8\xe2\xd4\x87-#\xf6\x0d\x5c2\xb7E\x04\xbb\x18\x13g\x1f\xb5\xdbx\xdc\x13\xda\xfb*\xce8l\xb0\x83^\xdd\xaa\xbc\x82\xed\x1cE!\xb7\x84\x9b\x96\xab\x8a]\x81\x80\x80\xe6'Y\x97\xc4B\xb3C\x93\x05\x05;Z\x07\xa9\xb6\x1a\xc3z\xa1_d\x139D#\x15+%\xd4\xe9H\xb8\xad\xc6\x91\xf76\xad\xf6W\x1d\xe5\xde\x04\x13|\xd3\xe1p\xce\xb71\xae\x8b\x0b\x81w\x19\x89.\x14F\xd2\x81\xe6\x12|\x9e\xc2\xb2.y\x0f\x95\x18\xed;T\xc3\xfb\x1e\xfe\x7f\x9bX\xab\x9a~\xe0\xd2&\xf9,+\xd8\xe7\x1d\xf6\x9d(\x22\xab\x10\x901rEM\x15\x83\x5c^\x10-\xe5~5\x84\xe6\x86\xe2\x99\x89\xa5\x99\x5cG\x9bF\xe7\x80u=\xc6O\xde\x12\xecLK\xb2s\xac\xb9`\xb4X\xfe\xb8\x9e\xa7x\xf3\xc1\x90\x07\xc9\xe6\x0a>&\xa3%\x8a/\x86K\x89\xad\xfe,\x13\xec\xd3\x8e\xfb\xaf\x1f(+\xa0\x89\xe1\x92\xd7y\xbe%\xd4\x007\x8c\xc7-\xd9\xff\x9c,\x9b\x08\xfe\xebA\xb0w\x86>\x92=\x08\x945\x11\xe4\x97\x0a4\xd08\xdc\xb4\xd2\xa4f#\x85\xd6Fe\xb7J\x17\x82}\xde\x9a\x08\xc0DEv\xdbswX\xf2\x8djN\xad\x98\xc0\xf3D\xea\xb8\x8f\x91$\xd6\xc5\xf9\x04\xaa\xcf\xf7\xdca\x17\xb9\x06\x9c\x14p\x94\xb0\xbfw\xf8,f[n\x14\xe5=\xc5\xf1Ygg\x99`\x83\x82\xcd\x1c\x06\xff\x09\x17;\xecJ\x22\x12\x02\x0e\x02\x9a\x11\xdb\xb8\xa8*\xdc\xb3\xd1\x05\x18Lu\xdc?\xf3\x04\x1b\xcc\x04\xad\xa1\x5c\xa3\xe9d\xa9)d\xb4@Q\xe8z\xe3\x8b4&\xdb\xcec\xa2\x95\xcaM\x9fW\x03Vu8\xdf3E\xd3\xf4%\x15\xdfX\x94\x0e\xf2t$\xea\xa6\x95\xa7\x83!r|\x916\xb6\xc3\xd8\x90\x07ps\xb9\xd2\x02\xf3\xc9P\x99\xbf\xc5}\xdf\xae\x0a\xf6\xdd\xf6\x8c\x0f@WO\x82\xf5\x80\x7f\x05\xde\x0ahQ\xf5\x0a\xf0`J>\x1b+\x93\xe7\xe8\x02\xb2\x7f\xda\x92\xff\xe3\xc0\x8b\xa4\xb3t\x92\x8b\x82\x1d\x04\xe6\xb7\x1a\xc1\xbe/\x8c\xc7L\xaaX*(\x12i\x81\xe7\xafd\x83u!\xd8!\xe0\x91\x0a\xed\xaa\xf4.\x11a\x95\xdc3\x0d\xb1I\xc1\xff\xe7\xec\xd8|\x1f\xb0\xbf\xbd\xa7W\x80\x19\x18\xaf\x93\x19\xc0\x82\x0a\x0a\xbf\xf8y\x0ag>q\xcdJ\xdc\x14\xac\xf0\x0ej\x13e\xb4\x90\x89`\xc7\xc0W\x996\x15h\x05\x82m\xd64\x85\x95\x9e}\x0a\xb0\x8e\xc3\xb9f0R0T+|\xc8\x92^\xe4\x82\xca\x0bs\x02L\xb3\xdb\x9e\xf6\xdf\xce\x06\xfeQ\x81`K\xb5\x97\xc6\xf8n\xd5I\xc1\xaa\xf1\x96\xc9\xb4\x0dVU\xdf\xc5-i\xc5fa\xa1+\xb3\xe4Z\x8f\x8a\x14i\xf5D(\xf7\xec\xdb;\x9e\xe7\xa1\xa2s\xe6\xcb\x10m>\xe1\xf6V`s\xc7c\x9e\xaf\xd06y\x96\xce\xce\x15\xf7\x87b<#\x01\x18\xd5\xe0-`A\xae\x09\x06\xe0?\x1c\xf6m\x03v\x0a\x9c\x95I\xf3@=\xfc3\xd3H\xae\xe5\x9e]\x80\x8f\xd6@\xb0\xe5\xce\x9bt[GSm\xd7\xcc_3G!\xd8r\x0a6\xae\xe7p\xf5 x\xb3Y\x08\xd65\xfcu\xd7\xc0[\x01M\x80M\x19\x09\xf9\xac\x06/\x03\xef\xa4\xe4\xde7v\xdc\xff\xa9\x14\xdc\xb3\xab\x07\xc1[\x90\xdd\x921\x81`[K\xc5\x96\x9b\xce\xb6\x02\xca\xb9\xa8\xedQ\x83z-4;h\x09\xf5\x97\xb49&N\x82\x1d*3\xdb\x89\xfaL\x5c3\x92\xe5\x1d\xf7\x7f\x0bx+\xf3\x0aVUg0\x92\xb8\xa2\x1a\xac\x9f\xc5\xd4\x85\x22\xd2I@\x80\xc1\x8a\xb8\xdb0\xefK\xd1\xfd\xbb\x12\xec\x8c\x14\xdc\xf3j\x8e\xfb\xbf\x05\x19_\xe4*\xc0]\x8e\xfb\xef\x92!b\xddSD\x1e\x01Nla\x05[M\xb8hM\xcd\x9c\x816(\xc4\xee\x8e\xf7\xfc\x04\xf0z\x09\x05[\xcd\x0a|\xdcX\xce\x91\xac\xe6P9\x7fE9;21?\x83k\xd2\xfe\xd7\xc9x>\xd8\xa66\x13\x88\xc8n\x22\xf2 p#\xc6\xa5\xe5(\x11Y\x9e\x16\x82\xaa\xd6s\x91KHg$W\xf1\xb3w\x02\x1fr<\xc7-U\x9c\xb7^m\xbdQ\xcc\xeaUK\x989H\xe0\xfe\xdf\xeb\xb0\xef\x00\xc6G?\x10l\x0a\x89u\x17\x11\xb9\x1f\xb8\x15\xd8\xaa\xe0O\x13\x80o\x87\xd9q\xcb\xe3\xc3\xb6/T\x8b\xd9\xa4%z\xcb\x8f`\xd3\xb0\xc05\x197/\x82\x19\xaa\xda\xd74&\x02U}\x05\x93!\xa8Z\xac.\x22\xeb\xa6\xe9\x19Dd\x07\x11\xb9\xc7~,\xb6+\xb3\xdbWDd\xa5\x16T\xb1\xd5\x98\x08\xd2\xaa@\xe3P\xb0C\xf6w\x1c\xf0I\xc7\xe3o\xa5\xf4\xa2U%?X\x9f2,\xd5\xc2\xd5v<\x9a\x82-\xbe\xd7\xc2\x05\xae8\xccJy`-\xc7c\xfec\xfbn>\xd7D\x1d\xd1U\xc5\xee\x93\x12b\xddFDn\xc7,B|p\x94\xdd\xbb\x80\xef\x04\x11\xd7\xb2\xf880\xd1a\xffA\xe0\xb6\x14\xdd\xff\xba\xb8\xb9\x96\xcdci\xdbq#\xb0\x8e\xe3\xfeQ82\xadL\xb0\x87\xa4\xe4\xbe\x0f\xc7\xcd\xa6\xf6%\x11Y\x8d\xd6\xc3hJ$N\x05+){\xee\xbc\x9d\xa2\xba\x8a\x82\xbfS\xbe\xbc})[k\xd2\x8b\x5c\x1ft\xdc\x7f\xc6(\xed2\xbcE(\xea+q=\x83+\xc1\xfe\xa7\x19\x09\xf6.\xc7\x06\xddHD6I\xc1}\x9f\x0a\xf4;\xec?\x068%\x90kY\x82\x1d\xae\xc1\xecA\xaai^\xe4:\x0c\x97ZU\x067Tq\xdej\x887\x0e\xe4p\x0f\xed}\xd2\xa1}(C\xaeq<\xc7\xda\x0e\xfb\x0ea2\x825\x17\xc1\xaa\xea,\x8c;\x8a\x0b>\x97\x82\xfb~\x05\xb8\xd8\xf1\xb0\xcf\x8b\xc8\xcea\xc6\xdc2X\x17\xd8\xcd\xf1\x98\x7f\x01\xcf\xa6\xe8\x196\x01\x96u\xd8?\x0f<\x90\x82\xfb\x9eB\xe5b\x88\xc5x\xba\xb0dQ\xae\xc9:\xe25\x8e\xfb\x1f$\x22i\x88f;\x0dSe\xd3\x05\x17\x88\xc8X\x02\x22D\xf5\xbbrV\xc4\xfa*\xd1\xb4)X\xc1\xd8\xdd]\xc6j\xbe\x8a\x8fv\xf1\x22Pqr\xf3\xb8\x17\xb9\x5c\xcd\x03\x8fcl\xb0\xd5(\xc6$\x17\xb9\xd6v\xdc\xff\x91b\xd9\xdeL\xf8\xbdc\x83\xae\xe0\xa1\x0c\x92P\xb1\xaf\x03\xbfq<\xec=\xc0\xf7\x03\xaf6=>\x8d\xc9;\xe0\x82\xbf\x01\xaf\xa6\xe8\x19\xc6\x00[;\x1esOJ\xee\xfd=\x8e\xfb\xff\xa7i\x09VU_\xc2\x18\xf6]\x90\x96\xc5\xae3\x18\xa9\xd3^-\xbe%\x22\x1b\x11P\xac`}Tb\x1a\xed\xaf\xd30\xb6W\x17\xf4Y\xa11\xda\xf3\x94Rx\xa5\x0a\x12\xc6\x81\xadpK\xf5\xd7G\xf5\xbe\xbb\x95\x22\xd2\x82\x82M\x00\x97:\xee\xbfO\x1ar\xc4\xaa\xea\x9b\xc0\x05\x8e\x87u\x00\xbf\x11\x91f|\x8f\xad\x8ev\xe0h\xfb\x8e]\xf0g\xdcr$\xd7\x03\xae\xe6\x81\x07=\xc4FRp\xf1 \xc8\x03\x8f5;\xc1^\x0dt;\xec?\x1680%\xf7~2\xe5\xf3^\x96\xc3\xd6\x84\x08\xaf\xb8Uh\xa5s\xd4+/\xed\xa7\xad\x82u\xc1\x02\xe0O\xd5~\xd3)\x1f\xc7\x1f\xa7\x82\x9d\xe8a\xe2\xb8\xd7E\x9b\x90\x5cN\xdb)\x80\x8b\xf8zVU\x1775\xc1\xaa\xeaB\xdc\x17\xbb\x0eI\xc9\xbd\xcf\x01\xfe\xcf\xe3\xd0SE\xe4#\x81`c\xa9H \x0e\x03;)\xac\x07\xec\xe7q\xdc\x95@O\x95\x1f\x9az\xb9im\x8f1\xddT\x8b\xb9\xc5*\xd0\xf19\xe2|/\xde\xfe\xaf\xcd\xac`}\xcc\x04\xdb\x89\xc8\xdai\xb8qU\xbd\x0a\x93\xe0\xc5\x059\xe0J\x11\x99\x1e\x84l\xe61\x01\xf8\x86\xc7\xd8|\xc1\xa3\xdf\xd4\xe3\xa3\xe7Zu\xe1v\x92\x0b\xd3u\xc5{\x1d\xf7\x7f\xa4U\x08\xf6N\xdcC\xec\xbe\x99\xa2\xfb\xff\x0a\xb0\xd0\xf1\x98\xc9\xc05\x222\xaeI\xdfi\xb1\xfbM1\x96p\xd3\xf2P\xb1\xd5\x9a\x18\x16\xdb\xad?\x01sA\x1bp<&\xdf\xab\x0b\xfa\x81sl\xbb\xe4\xaa|\xf6\xc8\xbd)o?\xec\xc5\xd5X\xe30\x11l\x05\xac\xe1x\xcc-N{/C\x9eeJ.r\xd5\xe2\xa65\x84\xc9\x88\xe5\x9a7\xa15\x14\xac\xaa\xe6\x81\xcb\x1c\x0f;LDVN\xc9\xfd\xbf\x06|\xd7\xe3\xd0\x8dqw\xf7\x0aH\x0f\x8e\x046\xf48\xeer\xe0\xb5\x14>\xcf\x01\x8e\xfb\xbfF:\xb2ga?rk:\xec\x9f\x07\x1em\x15\x05\xebc&\x18\x0b\x1c\x9b\xa2\xfb\xff\x15~\x91,\x07\x89\xc8\xd1M\xa8^GSWq,rUc\xc3\x1d\xb4[\xdc\x09\xc0?\x86IE\xe8\x8a\x19\xc0\xf5%\xda`\xb4\xb6\xa8d\x83\x8dC\xc1n\x8c\xbb\x0f\xe9-\x0emj\xee\x7f!y\x16\x96T\xb0\xd4\xf0~\x16\x01\xeb;\x1e\xf3\x80\xaa.h\x19\x82U\xd5gX\xba\x0e\xd1h8BD\xa6\xa6\xe4\xfe\xf3\xc0\x17\xedT\xc5\x15?\x16\x91O6\xdb+\xa5\xf2\x02L\xb5\xc4R-\xc9VC\xb0qa\x0b\xe0\xf3\x1e\xc7\xf5\x02?/j\x8fj\xdb \xe9\xaa\xb2\x07x\xbc\xdf\x9b=\xae\x9bD.\x82E\xb8\x07F\x94\xb4\x7f7\xbb\xff\xe4E\x8e\xfb\x8f\xc7,0\xa4\xe5#\xf1\x14\xc6\x17\xd2\x15m\xc0e\x22\xb2\x1f\x01i\xc7\x1a\xc01\x9e\x1f\x85\x8bqw\xeb\xab\x07\xde\x8b{b\xed\x1bRd\xe6\x18\x07l\xebq\xff-G\xb0\x97\x02o8\x1e\xf3\xb54\x04\x1e\x14\x90\xecy\xc0%\x1e\x87\xb6\x03\x7f\x14\x91=\x9bH\xbdVZ\xe4\xcaQ\xfd\x02\x8f\x0f\x06\xec\xd6g\xb7\xc1\x98\xce\xfb.\xa6\xbc\x88+\x1ea\xe9\x5c\xaf.&\x82\xa5\x16\x81\x8a\x16\xbajQ\xb0\x9f\xf08\xe6\x0c\xcfk\x95\xeb\x0f\xb5Drm\x8e[\x80\xc7\xcbV\x0c\xb5\x16\xc1\xaaj?\xf0c\xc7\xc3&\x02G\xa5\xecQ\xbeL\x09\x17\x90*\xd0\x09\xfcED>L@Z\xd1\x83I\xf6s\x83\xc3131^\x03i\xc44k\xf2p\xc1]\xc0\xbfS\xf4\x0c;\xc5a\x1eh\x05\x05\x0bp\xa1U\x09.8ZD&\xa4\xe5\x01T\xb5\x17\xd8\x1f\x98\xe5q\xf8\x18\xe0:\x11\xf9`\xc6\xdf\xe3h\x8b\x5c\x85\x0a6\x89\xbc\x02=v\xeb\xb7[\x9c\xbe\x9ay\xe0\xb7\xc0\xf9U\x9c\xb7\x1b\x93C\xb8\x94\x1b\x9f0\xe2\xaa6\x9a\x9a\xaf\xe4\xc6T\x8b\x82=\xc0\xe3\x98\xd3\xf1_\x5c+\x95\xf9\xab\x16\x15.\xc0\x0eq\x98\x07Z\x82`U\xb5\x1b\xf8\x99\xe3a\x93\xadjL\xd3s\xbc\x02|\xcas`w\x017\x8a\xc8n\x04\xa4\x19\x7f\x03N\xc2,\xb2\x94#\xc5\x1f\x93N\x97,\x80U(_O\xae\x1c\x1e\x04\xeeN\xd13\xbc\x17\xb7\xfc\xaf\x8b\xa8\x90\xf9\xabU\x92\x84\xfc\x8a\xearK\x16\xe2\x18\x11\xe9J\x19\xc9\xde\x89\x7fM\xae\x09\xc0M\x22\xf2\xd5\x8c+\xd8j\xbc\x08\x92@\xa1c~\xdc.Z\x85x\x02\x93[\xe2\xcd\x12\x7f\xfb\x1d%\x9c\xd9K\xb4A5\xb6\xe8$r*|\xdc\xa3\xfdO\xa7\xb6R5\xe59\xb3\x06bM\x02+\xe2\x16\xda\x9b\xc7\xf8\xee\xd6\x87`Ed{\x11Y3\x8d=@Ug\x03\xbfv\xa1i\xf6\xbbv{\xd1*\xd8\xcf\x02{`rkD[\x9c\x8a\xbc\xf8\xfe\xfb\xad\x10YD\x07o\xd0\xb1\x84\x9bc\xb4\xd0\xf76\xf0?L\x14\xd9c\x98\xa8-\xd7\xc8\xa7Wk\x98\x89\x95z\x0e\x97\xbe\x12\x97z}HU\xdfI\x94`Ed9\x11\xf9\xba\x88\xcc\xc0T\x13\xf8z\x8a\x07\xe9OpO\xe4\xbb\xaf\x88\xec\x91B\x92\xed\xc7\xd8\xbc\xee\xa8\xe14\x1b\x02\xff\x16\x91}\x08H3zS|o\x13\xf1K\xb1y\x9e\xaa\x0e\xa5\xecYb5\x0f\xd4D\xb0\x22\xb2\x9d\x88\xfc\x1ec\x8c?\x07\xd8\xc0\xfe\xe9si-\xc6\xa7\xaaoc\xdca\x5c\xf1\x8b4>\x93u\xdf\xfa\x18n\x09\x8a\x8b\xb1<\xc6\x8d\xeb\x12\x11Y6\xc5\x03y4\xdbZ\xd2\x8b\x5c\xf5\xb2\xc1F\x0av\xa1U\x93\x83\x8c,\xb0E\xc1\x0eq>O1F\xae\xd3\xceL\xda\x99\x09\xbcb\xb7W\xed\xf6\x16\xc6ep\x9e\x15T\x93\x1d\xaf=\x0b\xb8*\xe6\x99M9\x05[-\xa6\xe2\x17}\x16\x1f\xc1Z\xb5\xfa\x7fV\xad\xdeo\xa7/\xc5\xc43\x19?_\xb8z\xe1Gv\x9a\xe3\x82\xb5\x80\x13R\xfa\xd1\xe8\x06\xf6\x02\xfeY\xe3\xa9>\x07<\x95\xd2\xc4\xddiX\xe4*\xf6\xc3M\x8a`g\xd9m\xa15\x05E\x04[h\x9e\x88\xdb\xe4R\xd8\x9f\xf2\xaa:\xa0\xaa\x03\xf40\x93\x9e\x8a\x04;\x0d8\xd4\xe3\xda\x17\xda\x8fG\x12\x04[M_)\x85\x8f8\xf2\xe1+\xaa\xfad,\x04+\x22\x9b\x89\xc8\xa5V\xad\x9e[\xa0V\xcb\xe1K\xa9\x95A&\x15\xe0i\x1e\x87~[D\xd6I\xe93-\xc2\xd8T\x1f\xaa\xf1T\xab\x01\x7f\x13\x91_\xa7)\xd0\x22 \x95\x10\x8c\xfb\xa3\xeb,\xf8i\xdc\xa2\xd6\xea\x816\xdc3\x99U\x95\xdc\xbc\xda\xc6\xf9\x00f\xe5\xba\xdai\xf2\x0e\x22\xb2A\x8a;\xc7\xd9\x18#\xbd\x0b\xc6\x00\xbfL\xf1\x87c\x01\xb03\xa6\xe8]\xad8\x02\x98!\x22\x07\x8bHZ*\xad\x8e\xb6p\x91t.\x82\xa1\xa2-\x9f\xd0u\xa2\x5c\x07\xa5\xa2\x93\xe2\xac\x955\xaa\x12W\xd5~\xbb\xcd\xb5[\xb7\xdd\x060\xd5n\xb7\xf2\xb8\xe6\xb91\xa9\xff\xa8\x1f\xc4\x11\xc9\xb5\x0dn\xc1\x050\x92\x222\x16\x82\xbd\xcccZ\x9df\x15\xdb\x8f\xf13t\xc5n\x22\xb2\x7f\x8a\x9f\xab\x1b\xe3]pJ\x0c\xa7[\x03\xf8\x03\xf0\xb0\x88\xec\x1a\x04[@\xc1\x8cv2p\xa6\xc7\xa1\xb7\x02\xcf\xa7\xf0\x91\x5c\xcb\xda\xbcL\x95\x8b\xcb\xb9*\x07\xee<\xaa\xafV\x19\xe1\x90\xb4.v\xd9g\xba\x1d?C\xfb\xb9\x222%\xc5\xcf\xa5\xaa\xfa\x03\xe0 \xe2Y}\xde\x0c\xb8CDn\x16\x91\x0d\x1b\xf9hU*\x938\x0a\x1f\xd6r}_D\xa5h\xca+\xcbN\xbb\xd5I\xc1V\x1a\x03\x98\x8a\xab.\x98\x8f\xa9\xbc\x90T\xbf(\xfe7\xaa|\xb6\xd5p_\xdc\xba\xd0\xe6k\x8eM\xc1\x02\x5c\xe0x\x13\xcb\xe1\x97\xb6\xac\x9e\xf8&\xee\xc1\x07\xab\x02\x97\xa6h\xea\x5c\x8eh\xaf\xc4d\x05z;\xa6S\xee\x0e<.\x22\x97\x8a\xc8f\x0d \xd7Q\x85U\x82\xe4Z\x8a`\xe3&\xd9n\xbb\x95>\x7f\xfc\x16q/\x82\x15\x91\xcf\x03\x07{\x5c\xefR\xfb\x01IBP\xd4\x92p\xdbU\xbd\x0e`\xf2\xf0V\x85\x9c\xc3\x83<\x00<\xe9x3G\xa4\x9c\x84\xde\x00N\xf48tO\xd2U$\xb1\xdc\xf3=\x88\xb1\x93=\x16\xd3)s\x18[\xfc#\x22\xf2/\x11\xf9\x8c\x88t\xd6\xebqhl.\x82\xa4\x15\xec<\xbbE6\xc5%\xaf\xb1\x08E\xec\x16\xe3\xf3\xb8DR\x89\xc8\xfa\x98\x85-W<\xcd\x92\x09]\xb4\x0e}\xa5\x9a\xeb\x8c\xc1=\xbc\xf7\x1aU\x9d\xe92`\x5c\xe0\xaab\xb7\x13\x91\xf7\xa5\x9c\x87\xce\xc5\xaf\xd0\xda\x19\x22\xb2U\xca\x9f-\xf2\x9a\xd8\x0e\x93\x8b!\xce\x8e\xbd\x0d\xc66\xff\x9a\x88\x9c*\x22\xab\xd7\x99d+)\xd8,b\xbe\xdd\xca+\xcb>\x94\xbex\xde\xa1k\xfc\xbfM|t\x15&\xba\xd1U)_@\xedu\xb2\xe2\xec+\x11v\xf4x\x9e\xf3]\x15\x89\x0b.\xb3\xd3\x18\x17|)\xe5\x044\x08\xf8d\x98\xea\xc0T\x0cX6\xed#\xd7\xae\xfc\xfe\x1f\xf0!\x8c\x1fc\x9cX\x01S\x01\xf7%\x11\xb9^D>g\x17A\x02\x9a\x0b\xbf\xc0\xaf\xe2\xed\xcd\x98E\xa14\xc2\xd5<\xf0\x8c\xaa\xde\xe3r@\xbb\xe3@\x9d/\x22\x7f\xc2\xcd\xb9\xf8\x10\x119^U{\xd2\xdasT\xf5>\x1b\x95v\x88\xe3\xa1\xd31\x91a\x07da\x84\xa8\xea]\x22\xb2\x91U\xed\x9f\x8f\xf9\xf4m\xc0\xdev\x1b\x12\x91\xfb\x80k\x80k\xad\x8aNzz\x9e+\xf8M\xca\x06\xeb2\xfd\x84\x11g\xfa\xe8\xa3\xb6\x00\x13\xa1U\xca\xc5\xa8/R\x96%\xcf\xb4L\xc1\xd9\xfa\xea\xdboD\xe4 \xe0p\x8fC\xe7\x02W\x14\xb5W\x9e\xf8\x8bF\x96\xea+\xa3\xd9\x98\xdf\x03\xac\xedxn\xd7\x5c&^\xa1\xb2\xaef\x82I\x18\xd7\xa1\xb4\xe3[v\x8a\xe6\x8a\xfdE\xe4+Y\x91!\xaa\xba@U\x0f\x05\xf6\x05\xdeI\xe82m\x18\x9f\xdc\x9f\x03\xaf\x8a\xc8\xc3\x22\xf2c\x119PD\xa6\x071\x98\x1d\x88\xc8\xba\x1ec>\xc2%\x1e3\xdezaw\xc7\xfd{0\x0bu\xc9\x12\xac]8y\xc2\xf1\xb0#\xd2\xde\x91lV\x9c/z\x1e\xfeS\x11\xd98K\x03GU\xaf\xb3S\xbe?\xd7\xe1r\x9bc*\xa7\xfe\x09xQD\xde\x11\x91\x9bD\xe4\x87\x22\xb2\xa7\x88l\xe0`jit\xa8\xac\xcb\x22W\x94;`\x81\xdd\x16c\x5c\xe7z\xad\x0e\x8dr\x0d\x0cVi\x13Mj\x81\xad\x1c\xb9\x8e\xc5\xd8]}|\x18\xeeg\xe9\x1c\x19\x85\xea\xb2\x1e\xcfQ\xee\x1a\x13\x80\xed\x1d\xcf\xf5G\xeb\xae\xea\x84v\xcf\x1b\xbf\x00\xb7\xd5\xc4mEdsU}$\xe5\xa4s\xb5\x88\x9c\x07\xb8*\xd21\xc0U\xf6\x19\x17\x91\x11\xa8\xea\xbb\xc0'DdGLt[\xbd\x16\xed\x96\xc7\xa4\xe0\xdb\xa3h@/\xc2$\x9b~\xdd\xfe\xbea\x95CD\x9c]v\x1b\xb2\xffvo\x11\xc1B\xba\x16\xba\x06\x0aL\x03\x00\xbd\xaa\xea7\xc1_\x882\xa6\xee\xf7\xffSL*BW\xbc\x0d\x9cW\xc5\x87\xaaQ\xd8\x15w\x8f\xe2_\xfb\x5c\xc8\x97`/\xb3\x03\xd2e\x05\xee\x94\xe2\x01\x95R|\x13\x13\x1a\xbc\x89\xe3q\xeb\x02\xbf\x13\x91OV\xeb\x84\x9c\x22\xa2\xbd\x0f\xd8ZD\x0e\xc4\x94\xf0X\xbbA\xb72\x01S\x13\xe9\xbdU\x0e\xe2{\x09HJ\xbd\x1e\x89_]\xbaA\xcb\x0di5\x0d\xb4y\xf0\xd0\x7fT\xd5+\xcfG\xces@.\xc0=Y\xee\xee\x22\xb2]\xda;\x96U\x18\x07\xe2\x1e\x80\x00f\xb1\xeb\xe7Y\x1dT\xaaz\x15&q\xf2\xd7\xf1\xab`[O\xe4K\xf4\xe5$\xab\xca\xfa\xdec\xb4\xa8S\xeb\xc2\x8e\xd2G\x9e\xbe\x9a\x17\x87\xa2\x5c\x07\x95\xc8u\x7f\xfc\xfc]\xc1\xd4\x0d{\xa1\x82r\xadG\xc9\x9dJ\xe6\x9c\x0fc*\x17$\xae^\xbd\x09\xb6\xc0L\xe0\x8a\xd33B4\xcf\xe1o7\xfe\xaa\x88|?\xc3$;\xa0\xaa?\xb7*\xf6tJ\x97\x87N\x03\x86\x08HB\xb9\xee\x84\x09i\xf5\xe1\x86\x7f\x007\xa5\xf8\xf1:q_p_\xc0\x88'D\xfd\x08\xd6J\xe6\xc7\x1d\x0f\xdb1\xa5\xf9FK=\xdf\x15\xc0E\x9e\x87\x9f,\x22Gdy\xa0Yo\x83\xefbb\xb5\xff\x0fx&\xe5\x0av\x98#\x1a\xa0\x8a\xe2<\xa6\x9a\xf3\xd5\x82\xb29e\xedB\xedu\xe0e\xed\x9diM\x03\xa3\xdd?\xd4'\x92\xabT[\xed\x89{r\xf0?\xa8\xaaw\x88o\xad%c~\xe2q\xcci\x19\xe2\x99\xff\xc3/\xca\x0b\xe0<\x11\xf9x\xd6\x15\x8d%\xda_\xa8\xea\xfa\x98@\x85kS\xa2\x1e\x87J\x10k=\xcd\x02\xea8\xd0\x1b\xbd\xb0S\x91`\xad\xfb\xdc\xad\x98\x120>\xe7<\x95\xea\xccjI\xb7E\xb9\x84\xdb\xe3\x00\x9fLx\xbf\xae\xe5fj%\xd8\xcb1\x85\xcf\x5c\xb0\x85\x88\xec\x97\x11r\xe9\xb1S\x8an\xcf\xb6\xbd\xc2N\xb9\x9a\x02\xaaz\xa7\xaa\xee\x87\xa9\xf0p\x06\xf1%\x92\x89S\xc1\x06\xb8\x9b\x05V\x00n\x03V\xf2<\xc5\xd9\x98|\x03i\xc6\xbe\xb8\xbb\x9b\xdd\xaa\xaaO\xd5r\xd1\x5c\x8d\x03.\x0f\xf8\xd8\x1bO\x11\x91L\x94\x0cW\xd5\xa7\xf1\x0b\xa5\xc5N\xb5\xae\x13\x91M\x9ai@\xaa\xea\xab\xaa\xfa\x1dk>\xf8 \xf03\xe0\xa5\x06+\xd8\xe2E\xae\xa4M\x04\xd5\xa8\xb08\x17v\xe2R~\xfd\x14\x94k\xb1\xa5\xdbo\x06|\xabu\xdc\x8c\x9b)\xad\x92\x1fla2\xf3Z\xda\xab\xb8\xad\x96\x05\xf6\xf18\xc7wj\xed4\xb9\x18\x06\xdb5\xc0\xc3\x8e\x87\xbd\x0f\x93\xaf4+\x84r\x09\xf0{\xcf\xc3'\x02\xb7\x88\xc8Z\xcd\xa6|TuHU\xefS\xd5o\xaa\xeaZ\x18\xd7\xb6\x13\x89/{WP\xb0\xc9*\xd7N\xe0\xaf\x98@\x10\x1f\xbc\x82\x09/\xd7\x94?\xea\x01T_\x8d%\xc2U\xaa\xfah\xc3\x09\xd6\xe2{\x1e\xc7\x9c$\x22\x1d\x19\xea\x8f_\x04\xee\xf2\xae\xaa'\xa9\xea\xa6\x98\x82x\x87clX\x0f\x13_\x91\xbbr\x04\x9bt\xc9\x18\x1f\xf5X\x9cs \x0dD4\x00\x0c\x88H;\xa6b\xc5\x87<\xcf3\x1b\xf8\xa8\xaa\xcevl\x8bJ\xea\xb4\xf0o\xa3\xb5w\xa9R1\x14]c\x08\x13\xd4\xe2\x9a\xd4e\xd0sf\x9e\x0c\xc1\xaa\xea\xdf\x80\xfb\x1c\x0f[\x0bS\xd7'+\xe4\xd1o\xed8\xbe_\xb5\xb5\x81\x7f\xd8\xd8\xee\xa6\x87\xaa\xbe\xa2\xaa\x17\xab\xea\x97UuKL\xba\x92-1\xce\xeb\x17c\xc2\xadk)A\x1d\xdc\xb4\xfc1\x16\xe3-\xe0\x9b#d1\xb0\xa7\xaa>\x93\x81g=\x1c\x93\xf9\xce\x05\x7f\xb4\xae\x9a5\xa3=\xc6\x07\xf9.\xf0w\xc7c\xbe\xc0\xf8\x04#\x00\x00\x18\xb3IDAT/\x22\x97\xaajoFHc\xa1\x88\xec\x8e\xf1\xf7\xf3\x89v\x9afIv\x0fU\xfdw+\x8dh\xfb\x81z\xd8n\xbf\xb6S\xd4\x1c\xc6\xe9{\xb52\xdb\xb2\x98\xc8\x9b\x0e\xdbW;\x0a\xfe\xbb81\x8f\x94\xf9MB\xc9\xba\xee\x1b\x97{R\x1c\x0ax<&\x18f#\xcf\xe3\x07\x80\x03lN\x92b\xd5X\xebs\x14\xb7\x93\xb8~`UUm\xb1\x11\xb5\xe3\xcd5\xa9K\x1ffM!\x16\xb4\xc78\x80\xee\x17\x91[\x1d\xe5\xf8\xaa\x98\xb8\xff\x9ff\x88(f\x8a\xc8n\x96dW\xf48\xc5T\xe0.\x11\xd9_Uoke\x19e\x17I\xdf\xb2[\xd9\x0f\x8e5%u\xda\xb6\x9bj\xffy\xb5\x0a\x04\x9b\xa6J\xb8q\x11c\x1c\x98j\xc9cz\x0d\xcfs\xa8\xaa\xdeZfZ\xeeb*\xd0\x1a? \xf9\x0a\xfdJE$\x0f|\xc3c\x96~\x151z\xc7\xc4\xbd\x92\xff=\x8f\xcet\x82]\xc9\xcc\x121\xbc`\xbf\x8c\xbeQN\x13\x80\x1bm\x9e\xcd\x80\x80z`\x0d\xe0G5\x90+\xc01\xaazyF\x9ew\x13`7\xc7c\x16aR,\xc6\x86X\x09\xd6f\xcb\xfa\xab\xc7W\xf5\xbb\x19T_\x8fbl\xb2\xbe\x8b7\x1d\xc0e\x22rt\x18\xfb\xce\xcaG\xcb\xf4\xe5z,r9\xbbK\xa9j\xden\x8dr\xd3Z\x0f\x13\xf6\x5cK5\xe4\x1f\xa9\xea\xcf*\xdc[\xb9E\xa7R\xcf\x10\xc7\x22W\xd9\x884\x8b\xef{\xf4\x85?b\x92\x84\xc7\xe6\xa1\x92\x84/\xea\x0f\x89I\xf8\xee\x82\xb9$\x90\x1b9v\x82U\xd5\xff\xe2^\xff\xbc\x1d\xb88cn[\xd1\xf3^\x8d\x09\xa9\xad\x05\xdf\x16\x91\xdfY\xd7\x99\x00?\x05\x9b\xf5\xaa\xb2\xa3]\x1b\x8fk\xef\x0a\x9c\x80{\xee\xd3B\xdc\xc0\xe8\x89\xe8\xabU\xf7\xd5\xfe\xbd\x1a\xe4K\x09\x1b\x11Y\x1e\xf8\xa5\xc7s^\x85I\x84\x1ek\xa6\xaf\xa4\xa2\xa9N\xc4\xdd\x05\xe7\xfd\xb63do\xe4\xab\xfe\x0a\x13\x8b]\x0b>\x0f\x5c/\x22\xcb\x05.-K0\xa3\xf5\xe5zGr\xd5\xa3o\xa9\x87\x89 \x87\x09\xe49\xaa\xc61~?\xf0IU\x1drh\x9fj?\x12\xb5\xb6_\xbe\xcc\xcc\xf1W\x8c,\x84V\x8b\xb71\xa1\xc2\xb1W[\xc8%\xd4)^\xc4\xf8:\xba\xe2\xbb\x22\xb2a&\x19@\xf5\xfb\xd6-\xd8\x0c8\x07\x13\x8a^\x0b\xae\x00\xf6vH\xd3\x97\xa7\xfaE\xaeh\xdfj\x16\xb9\xaay\x1f\x11\xb9N\xc5/I\xf8\xcb\x8c\xf8\xefW\xba\xb7t\x11\xac\xaa\xbe\x81\x9f\x7f\xeb\x96\x98\xb2-\xd9d\x01\xd5\x0b\xecW\xb4\x96\xe2\xca\xed\x18\x97\x9a\x9bl\xc7\x09\x08\xa8\x846\xe0s\x98\x95\xf3\x895\x9e\xeb\x1c\xe0`U\x1d\xc8X\x1b\xfc\x02X\xc1\xe3\xb8\xcbH\xd0\xdc\x93tF\xab\x93\xf1K\xd4|r\x96CJU\xf5\xaf\x98\x80\x8b\x051\x99\x0cv\x0c\x1c2\xeaBO\xae`K\x02\xa5\x14\xec,\x8c\xefd\x7f\x91z\x8b\xaa\xc9\xce\xc7\xaf\x14|\xa56(~\xf6\xe51.X\xfb\xc5\xa0\xdaOP\xd5ox\xba\x93\xe5q\x0f6(\xf7\xef\xd5(\xd8\xe1\xeb\xd9\xf4\xa7\x9f\xf2\xb8\xe7\xc7X2Q\x95fF\xc1Z\xa2\xe9\x03\x0e\xf5\x982\x8f\x05.\xca\xf2\x14YU\xef\xc1\xa4\xf2\x9bY\xe3\xa9V\xc5D~}/+)\x1e\x13&\xd7J}9\xc9E\xaeh@G\xe4\x19\x11\xecB\xfb\xff\x85\x04\x1b\xf9h\xc6I\xb0\xa5\x9e}kLd\xd6{k<\xf7\x10p\xb8\xaa\x9eY\xc3\xbdU\xbbp\xe5Z\x11\xa2\xe2yDd2p\xbe\xc7=\xf7\x940)\xa8\xe3\x87\xa2\xe1\x0a\x16U}\xc0N;\x5c\xb1\x03\xfeyX\xd3B\xb2\x8fa*\xd4\xbe\x10\xc3\x14\xf0\x14\xe0o\x22\xb2\x22\xad\x89\xd1\x14l\xd2!\xb2\x85\x19\xb1\x22\xa5\xd3\x87\xc9*\xf5\x0a\xf0\x1a\xa6\xdcxa\xc9\xf1E\xf8\x15\xcf\x1c\x8d\x9c:\x81/`\xbcn&\xd4x\xce\x1e\xe0\xe3\xaazq\x8d\xf7U\x0d1\x8d\x16h\xe0S\x8e\xe7\xe7\xf8\x85\xac_\x82\xb1\xa1\x17\xbf\xe3\xfeL\x11\xac\xc5\xf7\x80\xe7=\x8e;CD\xa6e\x9cd_\x04\xb6\xc3?\x0bW!>\x04\xb4\xb2\x87A\xa5\xc1\x97\xb4\x1fl1\x81\x0c\xda\x19\xdal\xe0\xd5\x22\x82}\xd3n\x8b\x89\x7f\x11l\x17\x8c\xbdq\xaf\x18\xce5\x0f\xf8\x88\xaa^\x1f\x13\xf1\xd7\xd3MK\x81\x1d\x81\xcfx\x1c\xfb\x04\xc6-\xab\xd4G\xb4x6\x92~\x82\xb5\xae\x1e\x87y4\xea\x04\xe0\xc2\xcc\xb3\x82\xeaL`'\xe0\xee\x1aO\xf5+U\xbd\x96\x80V\xc4j\x98\xca\x01\xbf\xf7Tl\xc5x\x13\xd8AU\xef\xcfh{L\xc4\xaf\xe2@/&\x10\xa1.~\xccu\xb3\xe9\xa9\xea\xdf\xf1s\xa3\xf8\xb0\x88\x1c\xde\x04$\xbb\x00\xb3\xf0\xf5K\xcfS<\x06\x1c\xd3\xe2$\x13\xbb\x8d\xccS\xa5i\xb4\x10d\xab:\xf4\xa9\xea|U\x9di\xb7\xb9v\xcb\xdb\x8ca\xde\xb0\xd1\x8d\xc7c\x16c>\x1a\xd3\xb3\xfc\x1d\xd8\xb2\xd6zSE\xea>\xb2;\x8f\x16\xa9\x15\x97\x9b\xd67q\x0f(\xc0~\xa4\xde)\xf3\xb7>\x8c\xcd<\xb6\xf4\xa9\xf5^49\x1e\xbf\xdaM?\x11\x915\x9b\x80d\xfbU\xf5(L\x09\x0b\x97\xc5\x8f\x85\xc0\x81vJ\x1a\xd0\x22\x10\x91]\xect\xf6t\xa0+\xa6\x8f\xc4\xe9\x98\x00\x8273\xdc4[\x00{{\x9a\x06n\xaa\xe7\x8d\xd6\x95`\xad\xe3\xf2\x17<\xe4\xf9\xb2\xc0\xb5\x222\xae)d\x98\xea_\x80M\xa9\x90\x03\xb5\x08G\xc6\x95a=\xe3\xea\x15\x1aW\xfe\xban\xd7\x15\x91\x95E\xe4\x0a\xe0NL&\xac80\x17\xb3(v\x12\x95\xb3P\xf9\xb6M\xb5\x81\x06\x95\xda\xb2\x1a7\xadI\x98\x1c\xd2\xae\xe8\xc6T\xbf\xad\xe4\xdf\x9b\xd9E\xaeBr\xb9\x0b\xf8\x8d\xc7\xa1\x9b\x00\xbfk\x1a\xb6P}\x09\xd8\x1e8w\x94]\x7f\xab\xaaW\x04r]\xe2\xb7Y\x15k\x97\x88\x1c\x83\xf1\x1d\xfft\x8c\xa7~\x02\x13a\xf8H\x82\xefg4\xd3\x8d\xcf\x07\xb2x\xbfv;\x0b\x9e\xecq\x8f\xa7`\x16\x22+}\x5c\x86Tu\xa0\xca\xdc\x0b\xe9$X\x8bc\xed\xc3\xba\xe2@\x11\xf9N\xd3\xb0\x861\x19\x1c\x8dq\x12\x9f[b\x97\x19\x98\x84\x1d\x01\xcdM\xac\x13D\xe48L\xd8\xe6\x8f\xa9=\x1a\xab\x90\xa0\xfe\x08\xfc\x10\x98\xd3\x04Mu\xa4\xa7\xa2\xff\x17n\xa5\xc5\xb3i\x22( \x96\x85\xc0\x97<\x0f?ED\xf6j\xa6\x01f=\x036\x05\x1e,\x9a\xd2\x1c\x98\xa5d\x1bu\x9a\xa27\xcaD\x90\x04\xb1.+\x22\xdf\xc7\xf8\xd1\xfe\x08\xbfP\xcfJ&\x81\x1fX\x82\xadv\x0a\xef\x8b\xc2E\xaej\x94\xeeP\x19\xb5[\xe9\x1d\xef\x85_\x05\xdcnL\xb0\xd3\xf8\x1e\xb5\x07\xfax\xa3\xd1\x09\x9e\xbf\x89\xa9\x9b\xb3\x8a\xe3q\x13\x81\xebDdkU\x9d\xd7D$;\x00\x1c+\x22W\xa8\xea\x7f\x02\x15-\xa5nh\xa0z\xad\xf9\xba6\x0a\xef\x18L\xe9\xf2\x09\x09\xdcc\x9fU\xac\xd7\x96\xb8\xdf|\xc2m\x93\xafr\xbf\xd1\xdc\xb8\x8a\xb1\x12p\x1c&\x9a\xd1\x15\xff\xc0DzE\xe7\x9d]$,\xa3s.\xb4[\xec\xd5\xad\xdb\x1bL(\xf3D\xe4H\xc0'\x92d]\xe0\x0a\x11\xd9\xabV_\xc3\x14\x12m \xd7\xa5\x07\x9ed\xd5< \x22\xdba\xb2]\x1dL<\xeeV\xa5\xf0 \xc6\xce\xf8nR\x1f\x88\x1a\x09\xb6\x9aH\xae\xe2\x7f\xeb\xc2\xd4\xeb\xf3\xf9\x18\xf5\x00\x87\x15-X\xcd\xb6\xef\xc3D\xd7M\xb3\xff\xfa2\x83\x98\x05\xae\xd8\xdb(\x97\x022\xb9\x01c\x7f\xf2\xc1\xee\xc0\x19\x81\x7f\x02RH\xaa\xd3E\xe4\x07\x22\xf2<\xa62\xc0\x17\x13\x22\xd7\x99\x98j\x1agT \xd7L6!\xf0-`u\xcf\xe3\xbf\xa5\xaa\xffk\xf4C\xa4\xa5\x06\xd4w\xac\x9dew\x8fc\x8f\x13\x91\xc7T\xf5\xca0\xac\x9b^\xc5\xa6Z\xc1\x8a\xc8DL.\xe0C0\xf6\xf4$\x93\xcf\x0cb*8\xff\x99\xea*\x1b\x0f%\xa8d\xa3E\xaej\xce_)%`\xa1\xca=\x14\x93-\xcc\x07\x17\xd92N\xe5D]o\xbd\xfaD*\x08VU\xf3\x22\xf2i\xe0!;\xf5w\xc5oE\xe4\xd90\xb5\x0eh\x00\xa9\xb6aV\xb7\x0f\xc1\xb8\xdbu\xd5\xe1\xb2\x8f\x03\x17`\xf2\x094#v\xc2\xdf\x0f\xf8~R\x94\x85/5ULUu\xbe\x88|\x0ccKr\xf5\x03\xec\xc2Dzm\xa1\xaa\xef\x84a\xdf\xf4*\xb6Q\xd7\x8e\xb0\xacM\x82\xfe!`\x7f\xdc\x17i}\xf16\xc6\xf5\xca\xa7\x8f\x0f%x_\x85\x0av\xb46\x1c\xcdMk#\x8c\x9f\xbc\x0f^\x05\xf6W\xd5\xfe\xb4t\xd8T\x95\x89V\xd5gD\xe43\xc0u\xb8\xdb\x87W\x07\xfe\x22\x22\xbbd\xb0\xdcE@\xba\xd1i\xa7\xfc;`\xd2En\x8e\xdf\xaa\xb6/\xfa1Y\xe5~\x8a\xc9/\xfb\x81&m\xe7\xe51\xae\x9bc<\x8e\xed\x06\xf6M\x9b\xc0jO[\x0b\xab\xea\x8d\xd6\xf9\xfa4\x8f\xc3\xb7\xc7d\xab:\x22pBS\xaaW\xea\xa4`s\xc0{0\xeb\x02\x1ba\xa2\x87:\x1b\xf0\xcc\xbd\x98\xb0\xf2\xb31\x91X\xb5\x98\x1fR\xed\xa6%\x22\x9d\x18/\x08\xdf\xd9\xc0\xe7U\xf5\xd1\xb4u\xda\xf6T\x8e$\xd5\xd3Edc\xfcJ\x0f\x7fID\xe6\xa8\xea\x09\x81\x93\x1a\x07[~}7\xe09L\xb2\xf5\x17|\xb3\x81\xa9\xaa\xda\xf2A\xb1\x9b\x08Dd\x05Lv\xa6\xf7c\xf2]\xbc\x17\xb3\x0e\xd0\xc8\xc4B\x8b\x80_\x03?V\xd5\x99\xf6\xd9k\xb5\xed&\xed\xa6\xa5U^\x7f)\x92\xb5\xe4z5\xa6\xe0\xa9\x0fNU\xd5\xab\xd38\x0e\xdaSg\x9f#V\xc5-\x88\xd8\xff\x88\xc6\xe2j\x98$\xe0\xb5\x98\x05\x1e\x02\xben\xc9\xb5\x0dS\x12\xa7\xbf\xe89R=n\xdb\xb3<\x90U\xf5\xb5\x02%\xebK\xb2_\x05\xd6\x13\x91\x03\x83\xed,6l\x83I\x96\xb2N\xc1\x16\xfd\xff\x8a)\xb9\xc7!\xe0%\x8cm\xefEK\xa8s1\xa5F\xba\x9b\xf0\x9dh\xb2'7JM\x90W1\xb9\x1c~[\xc3\xec\x12\xccb\xf6\x01\xf6\xbe\xdb\xed\xd6m\xdf\x0fY\x11D\xed\x99\xef5\xf1\x90\xec\xae\xc0C\x22\xb2\xb7\xaa>\x1d\xf8\xb1\xe6w\xb2\x00x\xc4n\xc5Jl\x99\x22\xe2]\xc7\x0e\xc4\xa9\x98\x90\xd7h\xeb\xf2\xe8\x9fj\x07\xe1|K\x96s\xed\x7f\xcf\xc7\x94m~\x07\xf8/\xf0\x0c\xf0\xbf\x025\xb4\xac\xddV\xb2[\x80?\xf6\x00.\xb5\xed\xe9\x8b\xfb0~\xb2y\xea\x93\xc0<\x10l\x15$\xbb\xb3%\xd9\xe9\x9e\xa7Y\x1bx@D\x0eR\xd5\x9b\xc28I\xec]-\x04\x1e\xb5\xdbh\xd3\xe2\x8e\x22\xd2]\xd1nc1)\x04{-\xa1.\xb6\xbf\xdd\xc0\x1b\x98L\xff\x0b1\x8b\x22Z.V\xddf\xa9\x1a\x99\xe5\xa6_\x81\xa6\xd5M+j\xcf\xe31iFk\x89\x10\xbd\x17\xd8\xb3 \xbamQ\x96\xfb{{\x13\x0d\xdcW\x0b\x94\xac/\xc9N\x04\xae\x17\x91\x13T\xf5\xac@\x87\x0d\x7f\xa7\x03\x05\x0a\x14\x11\x99\x83\x89\xdf\xa7I\xa7\xf1\x99\x84\x88\x8c\x03.\x06>Y\xe3\xa9\xee\x01\xf6j\xa6\xbc\x0c\xb9&\x1b\x90\xafb\xea\xf9\xbc\x5cc\x9b\xfcHD\xfe`\x17\x16\x0228\xe63\xa0H\xeb\xadb\xa3\x5c\x04\xb1\xe6\xd4\x15\x9150u\xb0\xe2 \xd7=\x9b-\xe9M\xae\xe9z`<$\x0b\xa6\x86\xfd\xbd\x22\xb2r\xe0\xab@\xae-N\xce\xe5\xc8uG\xe0a`\xd3\x1aOu\xb7%\xd7\xa6\x9b\x95\xe4\x9a\xb2'\xa9\xbe\x12\x13\xc9n\x05<,\x22[\x86\xf1\x19\x10\xb0\x04\xb9~\x19\x93\x15l\xf9\x18\xc8u\xaff$W\x80\x5c\x7f\x7f?\xfd\xfd\xfdM\xf7`\x05$[kJ\xb3U\x80\xfbD\xe4\xa00\xac\x9aL\xc5\x9e\x88p\x22\xc2\xb2\x99P\xbc\x1a\x93\x89\xa0Vb\xed\x10\x91\x0b0I[jM\xe8\xd3\xd4\xe4\xba\x84\x82\x8d\x88\xb6pk\x12\x92\xdd\x16\xe3\xb0\x5c\x0b\xc6\x02\x97\x8b\xc8\x19\x22\x92# \xa05U\xeb\x8a\xc0]\xc0\x97b8\xdd\x15\xcdj\x16X\x82`s\xb9\x1c\xd1V\x8c\xfe\xfe~\xc4 \xb3\xa4\xa2\xaaob\x22_.\x8b\xe1t\xc7\x03\xd7\x89\xc8\xf2a\xb85\x5c\xc5\x95SsF\xbd\xba\xa8\xd2\xf9K\x1c\x9b\x85\xe7\xaf\xbb\x82\x15\x91\xcd1\xe5\x9b\xb6\xaf\xf1\x19\xf2\xc0\xb7U\xf53\xaa\xda\xd3\xec\x1d6\x07088\xc8\xe0\xe0 \x85d\x1b\x11n__\x9fb\xea\xe7\xe4D$\xab\xb9\x0bzU\xf5\xb3\xc0q1L\x93\xf6\x02\xfe+\x22\x9f\x0a|\x97\x0a\x92]\x12\x1d\x08c\xc9U\x95\x0a\xe4D\xbbe\x83\x5c\x1bB\xce\x22\xd2&\x22\xdf\xc2DV\xad^\xe3=\xcc\x07\xf6n%\x17\xc8\x5c__\x1f\xf9|\x9e|>O\x7f\x7f\xff0\xd9ZyK.\x97\x1b&YK\xb4\x99\xf5\x9dU\xd5\xb31\x11\x22\x0bj<\xd5T\xe0J\x11\xb9FDB\xe4O\x9a\x14\xec\x00&\xfc`b\xf0$\xa8\x95`m\xe9\xf5\x7f\x01gQ{D\xd5\xff\x80\xadU\xf5\xe6Vj\xf4\xdc\xc0\xc0\x00CCC\x0c\x0d\x0d\x91\xcf\xe7\x19\x1c\x1c\x1c\xfe-$\xda\x81\x81\x81%H6\xabf\x03\xfb\x82\xb7\xc6&\x8b\xa8\x11\xfb\x023D\xe4\xe00~S\xa2`#5\xba 4T\x0d\xe6\x80\x0e\x11\xf9\x01&\xd49\x0e\x0f\x9a[\x80\xadT\xf5\xd9Vk\xcb\x5c__\x1f===\x0c\x0d\x0d\xd1\xdb\xdb\x8b\xaa\xd2\xd7\xd7W\x92h\x0b:\xf5\x90}\x11Y%\xd9g,\xc9\xde\x1e\xc3\xe9&\x03\x7f\x10\x91\x1bDd\x950<\x032N\xae\x9ba|[O\xc2T~\xad\x15gc<\x05\xe6\xb7b{\xe6\x06\x07\x07\x19\x18\x18`\xc1\x82\x05\xf4\xf5\xf5\xb1p\xe1B\xf2\xf9<\x03\x03\x03\xc3\xa6\x83\x88h\x0bT\xac\x90pv\x9e:\x90\xec\x5c`w\xe0\xdc\x98N\xb9\x97U\xb3\x87\x86aZ\x17\xf5Zn\xd1F0k\x0b!\xe0`IT\x5c\xe4\x12\x911\xb6\xfa\xf2\x83\xc0\xfbc\xb8^/p\xb0\xaa\x1e\xa7\xaa\xf9Vm\xf4\xdc\xe2\xc5\x8b\xe9\xe9\xe9a``\x80\xc1\xc1A\x86\x86\x86\x222]B\xc9\x96 \xd9\x5c\x96U\xac%\xd9!U=\x1aS\xad6\x0e\xbf\xb4I\xc0\xc5\x22r\xab\xcdU\x1b\x10\x90\x05\xd5\xfa\x01LB\xf1\xe3\x89'?\xc9\x1b\xc0\x0e\xaazy\xab\xb7mn\xf1\xe2\xc5\xcc\x9f?\x9f\x9e\x9e\x1e\x22\xb2\xed\xed\xed\x1d\xb6\xcb\xf6\xf7\xf7\x0f/\x80E$[\xa4(\xc8\xbao\xa8\xaa^\x0c\xec\x02\xcc\x8c\xe9\x94\xbb\x01O\x89\xc8\x97\xc2\xf0MT\xc5\x96w\xd3rS\xaf\xd2\x8a\xed&\x22\xe3D\xe4\x1c\x8c\x87\xc0z1]\xe7_\xc0\x16\xa1\x8an\x01\xc1vww\x0f\x93kww7\x03\x03\x03\xf4\xf6\xf6\xd2\xd3\xd3C__\xdfR\xe6\x82\x02\x15K\xd6M\x05\x05$\xfb\x0f\x8cA\xff\xd1\x98N9\x11\xb8@D\xee\x10\x91i\xa1\xab\xc5\x80\xc9\x98\xb5\xec\xd1\xd7\xb3}\x08\xb3\xa5\xcc\x096\xbd\xe7\x93\x98\x8a\x01q\x09\xa4\x8b\x81\x9dT\xf5\xed\xd0Y-\xc1\xce\x9d;\x97\x85\x0b\x172\x7f\xfe|\x16/^\xe0\x86\x18O\xbb:\xa6V\xd1S\x22\xb2\x7f\xe8~\x01u@;\xb0'\xf0\x00\xf0\x1d`\xb9\x98\xce\x9b\x07~\x02l\xac\xaa\xf7\x87f.\xd3\xf8\xf3\xe6\xcd\xa3\xaf\xaf\x8f\xae\xae.\x86\x86\x86\xe8\xeb\xebc\xdc\xb8q\x00\x8c\x1f?\x9e\xee\xeen\xc6\x8d\x1b7\xece0~\xfcx\xfa\xfb\xfb\xe9\xec\xec\xa4\xd9\xd4k\x09\x92}\x13\xd8\xc7\x06\x12\x9c\x8b\xb1\x02\xc6\x81\xf5\x80?\x8b\xc8\xc3\xc0\x09\xaazG\xe8\x8a\xd5\xbf\x96Q\xfa\x9d\xcf\x22W\xe1\xb1M#\x9e0\xd9\xe4>M\xed)\x05\x8b\xf14p\x98\xaa>\x10\xba\xe3(\x04;{\xf6l\xc6\x8e\x1d\xcb\xc4\x89\x13\x89\xa2\xba\xa2\x12F\xaaJ.\x97\xa3\xad\xad\x8d\x5c.GWW\x17\xf9|~8OA\x7f\x7f?===\xda\xd5\xd5\xd5\xd4\xd31U\xbdLD\xee\x00\xce\xc7Do\xc5\x85-\x80\xdbE\xe4.K\xb4\x0f\x85.Y\x15\xb9&A\x94\xcd\xd2\x87\x05\x93A\xee L\xe9\xec81\x84\x09\x1c8QU\xfbBw\xac\x82`\xe7\xcc\x99\xc3\x84\x09\x13\x86}_#\x92\x15\x11\xc6\x8c\x19COO\x0f\xb9\x5c\x8e1c\xc6D\xd9\xb5\x18?~\xfc\x12D\x0b044\xa4M=\xb2\xcd\xca\xe8~\x22\xf2i\xe0\x17\xc0\x94\x18O\xbf\x0b\xf0\xa0\x88\x5c\x03|7T\xb6\x0d\xf0\xc4f\xc0g\xa8\xad\x5cv9<\x09\x1c\xaa\xaa\x8f\x84fv\x98Ftvv\xb2p\xe1B\x1e\x7f\xfcqn\xbc\xf1\xc6\xe1\x05\xae\xde\xde\xde\xe1\xff\x1e\x1c\x1c\x1c\x0e:\x88l\xb0\x91\xeb\x16@OOO\xe4W'\xcdf\x8b-A\xb4W\x02\x1b\x00\x7fM\xe0\xf4\xfba<\x0e~'\x22k\x86\xeeYV\xc5\x8e\xe6\x07\x9b\xa3\xb5\xa2\xb96\x00\xce\x00~\x90\x00\xb9\x0e\x00'\x03\x9b\x07r\xf5 \xd8\x05\x0b\x160o\xde\xbca\x9f\xd7h\xa1\xab\xbb\xbb\x9b\xee\xeenz{{\xe9\xeb\xebchhh\x98T\x0b\x93q\xb7\x92\x8a- \xd9wTu\x7fL\xa1\xb7Y1\x9f\xbe\x0d\xe3\xc1\xf0\xac\x88\x9c\x13\x12|\x07T\xc0\xda\x96TO\x07\xd6O\xe0\xfc\x8f\x02[\xaa\xea\x0fm\x85\xdf\x00W\x13\xc1\xec\xd9\xb3\xe9\xeb\xebc\xd1\xa2E,^\xbc\x98E\x8b\x16\x0d\x9b\x06\xc6\x8f\x1f?L\xac\x03\x03\x03\xb4\xb7\xb7\x0f\x07\x1b\x00\xc3\xbfK\x18iZ\x84d-\xd1^%\x22w\x03\xbf\x02>\x11\xf3\xe9\xc7\x00k\xb5r\x1cw\x05\xf5\x0a\xe5\xe3\xea]\xdc\xb4\xa4\xe07+Jw,f\xf1j\xb7\x84H\x15L\xd8\xf8\xc9\xc0\x8fTu0t\xbb\x1a\x14l\xa4\x5c\xfb\xfb\xfb\xe9\xeb\xeb\x1b\x8e\xe0\xea\xed\xed\xa5\xb7\xb7w\xd8<\xd0\xd7\xd7\x87\x88,\x11\xd1\x05\x14\x87\xce\xb6\xde\x88W}WU\x0f\x04\x0e\x00\xde\x89\xf1\xd4y\x8c[M@\x00\x18\xcf\x9331yU\x8fN\x90\x5c\x1f\x026S\xd5\xd3\x02\xb9\xc6@\xb0\x03\x03\x03\xcc\x9d;\x97E\x8b\x16-\x11\x1e\x1b%~\xe9\xeb3\x8b\x85\x91\x1fl\xa1z-$\xdaf,\x9c\xe8H\xb4\x7f\xc1\xd8\xc2.\xc4D\xb6\xd4\x8a\xcbU\xf5\xa9\xd0E\xcb\xaa\xd8\xd1r\x11d\x1e6\xc3\xd5A\x22r/\xf08\xf0U\xe2\xf3c-F\x0f\xa6\xe2\xc7\x07TuF\xe8f1\x11\xec\xacY\xb3\xe8\xee\xee&\xaalP\x98I\xab0\xcbV.\x97#\x9f\xcf\x0f\x9b\x0c\x02J\x92\xeclU=\xc2\x12\xed\x9f\xf0\xf7\x11\xee\x07~\x18Z\xd4\x9f\x9b\xb2L\xb2\x22\xb2\xae\x88\xfc\x18\x93\x95\xear`\xc7\x04/7\x80\x09\x9d]GU\xcfV\xd5\xa1\xd0}b$\xd8\xee\xee\xee\xe1P\xd8(UaD\xb2\x91+\xd6\xe0\xe0\xe0\x12Q\x5cmmm\xc1DP\x99h\x9fS\xd5O\x01\x9bc\xb2\xb9\xbb\xe2BU})\xb4d\x0b}\x11D:E\xe4S\xd6\xa6\xff,p\x0c\xf1\xba\x02\x16#\x0f\xfc\x01XOU\xbfl\x83j\x02\xe2&\xd8\x88\x5c\xf3\xf9<\xaa:\x9c\xa60\x22\xcf\xe8\xff#RU\xd5\xe1@\x84\x80Q\x89\xf6QU\xdd\x03S\xd5\xf6\x1fU\x1e\xb6\x1885\xb4^Y\xf3@\x94\x83`47\xadL(X\x11YGD\xce\x02^\x07\xae\xc4,`\xfd\x7f{\xe7\xaf\x225\x14\x85\xf1\xdf1\xf7Q\x14\x9bE\xb0\xb7\xf6\x01\xf6\x01\xf6\x01\x04\x1ba\x0b\xb1\x11l\xb4\xd2B\x10\x1b\x0b\x85-\xb6\xb1\xd0b\xb1\xb0P\xb0\x16\x15\xacV\x0bY\x16A\x11A\x161\x83\x9f\xc5\xcd\x8d\xd7\xd98\x7fv\x93M\xc2\x9c\x0f\x86If2\x19\xb8I~|\xf7\xdc{\xcf\xe9Z\x8f\x81s\x926$}\xf0\xdb\xaac\xc0\xe65\xb9\x12<\xcb\xb2D\x12eY\xd6pm\x9aA\x90f\x16\xb8f\x82\xf6\x85\xa4\x0b\xc4\x82\x8bo\xe6\x1c~[\xd2go\xb5\x95\xd1u`\x93\xf6\x97\xb36\xe99q\xda\xd5\xba\xc7YO\x08\xb0)4\x90\x96\xc8\xa6\xfd\xa4\x14s\xcd]k\x0e\xd4U\x1f\xdcZ\x12\xb4O\x81\xf3\xc4\xd56\xbb\x0d\x87|%.Et\xcdv\xb1]%\xdc\xee\xc3\xf5\xde?\x81\xffxOL\xf8\xb2\xee\x89\xb0{\x00l\xde\xf5\xcf_\xe9\xf3ys_]KA\xf6\xb7\xa4-\xe24\x9bK\xc0~\xf6\xf5MI^\x0fu6\x5c\xbb\x02\xa6\xf5t?\xbc\xac\x00\xd8\x85>\x027\x80\xab,\x1e\xa2r\xb5\x09\xd8\x1c\xa0\xf9vZ\xf1\x9a\x06\xba\xf2\x81\xad&'\xebZ\xfa\xc1*%\xdd\x03N\x13k!\xbd\x05\xeez\xcb\xac\xa4\xdav\xb1{UO\xe8\x0a\xb1\xf4\xb6\xabO\xc0N\x0f\x5c\x99Y\xbd\x046\x815\x9f\x9a\xe5`m\x15\xb4\x07\x92n\x11\xf3j\xfe\xf4\x16i%D0\xb6\x84\xdb\x0f\x89UX\x8f\xabO\xc4DD\x97+\xc7\xea\xa3\xd1=+\xe4p\x95\x84\x99\xd5\xfby,\xf6\xbf'\x08\xc1[\xb1\x1d\xd0\xfa\xc3\xb0\xba\xd7\xfe\x9b\x99m\x03\x1bG\xf8\xf9/b\xa1\xc1\x9d\x0eC\x0d\xae\xa3\x026\xbb\xc8\x7fmm\xb5\xa8 \x84@Q\x14\xff8\xd7\x10B\x0d\xd5\xc9d\xe2\x80u\xf5\xe1`\xa1\x9b\x84\xdb}\x87\x09\x96\x01\xec\x1e\xf0\x8cX[\xeb\x87\xdf\x16\x03v\xb0\xd3\x8e4A\xb5(\x0a$9D]C\x83+s\xba\xbf\x8b\xc2u0\x10\x96\xf4\xca\xcc\xde\x01ks\xdc\xea\x13\xe051f\xef\x1a\xb8NM\xbbW3\xab\xc3\x04\x1eku\x0d\x14\xb2]M\xd3\x1a\x82\x8bm\xd2.p\x0d8KLe\xe9p\x1d\x0b`\xa7\x1dl\x82j\xeeb\xd3\xbb;Y\xd7H4\xd6\x921\x8f\x80\x83j{BL\xea~\x118C\xacJ\xfc\xc5/\xed\xc8B\x04\x87\x88[\xcd\x1e\xc8\x13i/t\x22\x87\xaf\xcbu\xdc0\xc1w3\xbbSA\xf6\x81\xa4\xfd\xbcg\xe9\x1a\x9f\xfe\x00\x89\x0bY\xd4\x9e\xe4\xf2`\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x13\xdf\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x04\xc8iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \xef\xa3\xbf Made on a Mac! exclusive for Smashing Magazine\x0a \x0a \x0a \x0a \x0a icons\x0a flavour\x0a smashing magazine\x0a addicted to coffee\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski \x0a \x0a \x0a \x0a \x0a\x0a\xee\x04Q\xd2\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0e\xadIDATx\xdab\xfc\xff\xff?\x036\xc0\x02c\xdc\xf5r\xff\xcf\xc4\xc2\xc2\xf0\xef\xcf\x1f\x06\xe5m;\x19\x19A:@\x82\xec\x82\x02\x0c\xcc\x1c\x1c\x0c\x7f\x7f\xfc`\xf8\xf9\xfe\x03\x03\x13H\xf5\xaf\xd7\xaf\x19\xfe\xbe\x7f\xc7\x10~\xec$\x98\x06\xf1\xc1\x12 \xed\x7f\xbe\xff`X\xac(\x0b\xa6A|F\x98\xe5W\x0d\xf5\xe0vh\x9f\xbf\xc4\x08\x10@\x8cx]u\xdb\xcd\xe9?3;\x07X`\xf7\xd7\x9f\x9f\xd2\xf7\xed\xe5g\x0aK\xcc\x9c\xcf\xc6\xcb\xc3\xc0!\x22\x0c\xc6\x1e\x82\xdc| \x05\x8c'\xb45\xfe\x08\x0b\x0b1'=\x7f\xc9P\xaf\xa6\xcc \xff\xf9\x0b\x83\xee\x95\x07\x07\x199\x84\xa4\x94.\xa8I\xdde\xe7\x03+d\xf8\xf9\xe9\x13\x83\xfa\xf1\xd3\x8cL\xdf\xdf>\xbd7\xf9\xf9\xdb\xc7\xdf\xde\xbec\x00a\x83[\xcf\xe6\x83\x14\x00\x04\x10\x86\xab@v\x06?\xbc\x19j\xc4\xc2\xc0\xbd\xf5\x17\xe3\x87\x97\x0eNn\xed\x0d\xd5\xa7a\xf2p\x0d`\x17\xb3\xb130\xb3\xb320\xb2\x001\x13\x13\xc3\xff\x7f\xff\x18\xfe\xff\xf9\xcd\xf0\xf7\xe7o\x86\xd5_~\xbd/?\xb0W\x08\xaca\xb5\xa3\xf3Wc\x1ev.\x16.N\x06\x16..\x86\xc2[w\x18X\x7f\xffa\xf8\xcd\xca\xc2\xd0\xaf\xa6\xc2\xf0\xe7\xdb7 \xfe\xce\x90\xc3%\xb6\x09\x1c\x1e'\x1e0 \x92\x18@\x1a`\x18\x08@\xc1\xae\xc7\xc6/\xba\x94]P\xe26\x1b\xafP7\x90\xaf\x86\xac\x06 \x80p\xc6\x1d.\xc0\x82.\x90j\xeb\xfc<\x85\xfd\xbf\xc8w`\xaa)\xfc\xc3y\xe7\xfc\x81\xad\xda\xc8\xf2p\x1b\x8c\x1c}.\xafb\xff\xa9\xc3\xc8\xcc\xcc\xc0\xc4\x04Nf\x0c\xff@\xc1\xfa\xf7/\xc3l3{\xc7\xce\xa6\x9a\x03p\x1b\x22\x92\xb3g\xac\xe2\xf8\xa5\xc3\xcc\xc6\xc6\xc0\x04\x8c\x0b&\xa0&\xb0\x06\xa0\xe2\x7f\xbf~2\xa4\x9e>\xb4\x1fd8\xdc\x86\xdb\xee\xce\xffY@\x91\x06\x8c\x07fv\xa0\x06`\xc4AR2(\xd2~2\xfc\x05\xc6\xc1\xbe\xaf\xbf>%\xef\xdb\xc3\x0f\xb6\x81\x91\x89\x99\x81\x11\x18IL\xac\xac\x0c\x13\xdf\x7fbx\xf4\xfe=X\xc3d;+\x86\x9f\x8f\x1e3\xfcc\xfd\xcd`\xcf\xfe\x87\x0f\xee\xa4\x9f/^0\xfc\x05F\xda\x7f`\xa4\xe5qs1D\xbdy\x03I\x89W\xaf2\xfc\xfe\xfa\x8d\xe1\x17(\xa6\x81\x11\x07\x02`\xdf\x81\xc3\xf8\xdf\x7fpDE^\xba\x06\xf2-\x18\xcf\xb9\xff\x00\x92\x9e\xfe\xc1\xe3\x09b\xc3\xdf\x7f\xff\xfe3\xff\xf9\xc3\xf8\xf7\xf7o\x86E\xca\x0a`\xa7\x81\xfd\x00\xe4\xff\x01\xfa\xe1?0\xb6M\x1f\xbf;\xfd\x1df\x83\xc5\xd3\x0f\xec\x7fAY\x15(\x09J3 g\x801\x90\x0d\x12\x03\xc9\x01\x93\x85\x19JpD\xb00C26\x083C,c\x00Y\x061\x1f\x9cn\xfeC3\x0b(\x19\xfc\x03\xe3\xbf`~\x0e\xaf\xd4\x96\x1d\xab\x17\xfa\xa2X\x90h\xed\xfc\xbc\x9a\x97I\x02d\x183\x1b\xc8``F\x02&w\x06I)\x86\xe2\xc3G\xc1\xaeg\xff\xf7\x87\xa1\xc3\xc8\x80\xa1\xf0\xd2U\xa0\x05\xff\x19\xb89\xd8\x18\x9a\xe5\xe5 \x86\x03\xcb\xb0\xff\x7f~\x01K\x97?`Kf\x98\xd8\xf8\xf4\xb4\xd4o\x85[p\xc2\xc2\xec\xb7\x00+\x0b\x0b3\x0b$\x0f\x811\xb0xb\x06\xd2\xac\xc2B\x0cI'\xce\x80}\x00\x03\xc6@\x8339\xd9\x18\xfe\x02\x0d\x06%\xf0\x7f\xc0b\x0b\x94\xb0A\x18\x94N\xbd>\xfd;~\xef\xe2\x09+x$\xbf\xfe\xf8\xe9\x17\x0f;\x1b\xcb\x7fP\xd0\xb0B\x0c\xfe\x0f\xb4\x80\x01H\xefz\xf8\x88\xe1\xf3\xd7\xaf\x90\x88\x86\xc6\xf2\xfe\x8b\x97\x18\xd2\xb4\xd5Q,\xf8\x0b\xb7\xe0/\xc3\xf3'o\x1f\xc0\xf3&\xb8Bx\xf5\xf9\x18(\xd2 \x11\xf6\x17R\xb6\x00]\xd2u\xf7\x1e\xc3\xbcWo\xc0\xdenw\xb6gX\xae\xa5\x06f\x83\xcb\xa5\xcb\xd7!\xae\x06\x05\x11(.@\xfa\x80\xfa?\xfe\xfd\xfb\x1b\x94\x101\x22\x19\x98\xdb\x8e\x03s\x9b\x05<\x92\x99Y\xc04<\xf7\xa1E2\xacD\x04E\xee\xbf\xbf\x90H\xfe\xf2\xfb\xcf\x1f\x87g\x1f\x94\x80Y\xf91\xd6d\x0a\xb4D\xa5Q\x90\xeb\x98\x17\x0f\x87(<\x992\xe3I\xa6\x7f\x11\xc9\x14X\xa0\xec\x07\x1a\xec\x847\x1f\xa0YVQ-\xc0U\x10\xc0\xc3.\x8e-\xa3}\xfa\xfb\xef\x8f\xf3\xb3\x0f\xe7\x81\x1cw\xa0\xc1\xef\xb1\x99A\xf3\xa2\x02 \x00\xf1\xd5\x16\x12E\x14\x86\xcf\x5cv*Mm\x8b\x14\xcb$B\xb4\xa2\x88\x08+27\x0d\x02\xa9\xb7.F\x10H/\xf5\x10=\x15\x85\x14b\xa4 \x06\x11\x05\x15Q\x16\xdd\x08_J\x8aX%\xdd\x84\x902\x10\x1f\xacvki\xcd\xdb\xd6n\xa4\xdb\xaa{\x9bK\xff\xb9M\xcej\xa6\x0f\xd2\xc0\xcf\xfc3\xe7\x9c\xff?s\xce\x7f\xbe\xef\x9b9O \xa29\xbe\xe4\xe9\x1a+\xabk\x0b\x9d\xaf:\xeeV\x18\xd1e\x1blFJ\x8e\x88\x94\x9f:R{u\x14{\x99\x10\xc2\xa1\xbc|g\xe3\x9d\xebGf\xbdD\xb5\xf5\x97R\x1e59\xbb\x9f\xa4\xaay\x02\xb9$R=\xa4\x8a\x10\xaf |\xd7\x903\x86F\xde\xe4\xaf}\xf0\xf0\xe6\xd5\x133Jp\xa6\xaa\xa6d\xd5kW\xd3N\x1bJ\xe7\x88*\xb0\x13\xcc\xe2\xd3*\xe2\xa4\xc2\x0e\xe6\xc1\xf8\x82\x9e.\xd7\xf3\xf5\xd3&\xa8\xac\xaeY]\xd0\xde\xd6\xb9]\x91\xd2p\xed\x93\xe0\xec\x1c\x104\x158\xad!\x86\xa4\x0cQ\x99_\x1e\x9f7)\x89e\x93\x17\xb9\x5c\x1dfp|z\xb1xP0\xf8) $l\xe4n\xf1I\x1b\x83s\x18\xd3\xa8\xc4\xd6aY3e\x82\xb2\x03\x15\xcf\xf6\xceGv38\x86\x08\x8c\xac\x0c\xb6M\x845\x8d\xb6\xd1>\x92\x99d\x97\xcfsh\xca\x04\xfb\x7f\x0c805\x9a\xcbB\x06\xcaD\x02\x09\x0c]yp\xec\x0b\xc4\xe4?\xa4\xc4\xc6\xedP\x84t\xac\xa4-ez\xea\xdc\xf9=\xc7\xc8\xa6\xd2\x0d%3\x12\xe9\xac\xaa|}h\x0c\x10\x13\x89\x02\xaaYS\x80\xae\xb8=(\x80\xb7\x0d\x08\xe7bI1\x12\x07\x07\x91\x00\xbe(SL\xc21\x16z?\x95Y\xbe\xe0\xc3g\xdfQ\xb2\x81\xb8L8j\xb2\xa5\xaa+\xda\x8a\x90\x84\xdfK\xe8l\xcfG\x14\x14e\xe2\xe7fe\x22\x1bh4\xbe4|\x1c\x8e\xb1[A\xe9\x96/p\xbb\xddK\xb5T\x98\x94M\xa2D\x13\x07\xb3Q\xb2Q\xfb\xbe\xa2\xfc\xcc,\xd4\x05\xaa\x90L \x12!%zze.J\x04\x03\x16\xa2!d\x03\x96\x13\xd7\x14K\x02\xff@\x7f\x5c\xcd\xb1\x93\xf54\xc0&\xb2\x19\xb6b\xc0\xfcv\xceh\x8c\x0b\xe2\xfd}\x84\x875\x16\xd4L\x04\xe4#&\x12B\xf2&\x0f\x99\x18\x0f\x8e\xc14\x0f;Q\xe8\x82\xc7\xcb1\xda\xbcZ\xfc\x01S\x1b\x91C\x87\xd8X\xf0\x03\xaa\x1eKN\xd0B;$\x9dR\xb0[\xbdti\xb0\x7f\xdcQL\xe5\x0a\xf8\x0d\x81 %\x1a.#\xc9\x9d\x06sE\xe2\xc3\xc9\x09\xee\x855C%=\xb8>\xd6(\x1d\xb6\x8e\x8e!\xfe\xbe(\xe0G'\x1dETO\xc3U\xef\xf5Q]\xa4\xd11\xe4=\xf4\xbd\xf6+\xfa~\x12T\x00{u\x02\x1f\x17\x8aLhI\xac\xc6\xf93\xe5d\xbe\x07\xc6\x04>\xa6\xa2\x0b\xab\x0b\xfc\xac\xaa\xaa\xb1e`8\x0f\xff~&\xc3\xf5\xbeQ]\xff\x92\xa6\xeb2^\x12L\xe0\x1cw\x04\x1c\x8c\x83\x9e\xf9\x87\xc0\xf8\x98\x91=\xc7$\x08\xfe\x96\x07\xb7\x9cd\xac\x02J\x87B\xf7\xf9\xd2\xe8L-hj\xc2R-f\xd5\xe0w\xb8M\xa5}\xf1\x98\xc7\xa1\x88\x1fB9\xa6\x85kX\xaa\xb6w\xcb\xed\xa5\x164\x15\xff\x82\xa6\x13\xecE8\x1a\xac\x1a\x1e\xdb\x06\x13\xf5\xfe\x93p ICsv\xc6\xe1\xc5\xb2d\x9b\x09\x1f\xdc\x18\x19\xef\xbf\x1d\x8en\x86\xe0\xdffL\xfa\x90\xc4\x0e\xb7\xe6\xd6\xec\x8c\x8d\xe9\x12(\xb0)\x18\xed\xe9h\xf4{\xed\xc8\xf8e\x08\x5c7kU\x01\xc1\x96\xe0\xdf!\xb0\x0c\xf8\xcd.G\x92\xbc\x09\xd0v\x05D\x0f\x19\x86\xee\xd1\xe3\xd1f5\x12\xee\x86v\x5c\xf3A\x88\x13\xfd/\xb2\xe5\xb7\x00\xddZklTE\x14>\xf7\xde\xdd\xeen\xdb\xdd\xd2\xad\x14[\xa8\xf2\x08\x10\xd3F\xa2\x16$\x0a\xe1\x19\x1b@B\x9a\xa0\xc1\x1f\xe0#\x1a4&\x9a\xa0`CQ\x8b)/\x8d\x1a\xe3\x8b?&*\x88\xc44)Jh\x03>\xd0\x00\xc6\x07\x06\xaa\xad\xb0\x81\x96\xb6\x92\x02\x05J\xbbw\xbblw\xf7\xcexf\xee\xdc{g\xb7]\x9e\x02\x89\x9b\x9d\xdc\xb9\x8f\xdc=\xdf\xcc9s\xbe\xef\xcc\xde\xf0\x1f\xb8\xa5\xb4\xe8j>+V\xd7,\xea>\xd73\xebBX/\xe9\xed\xd3\x8b\xe2\x89\xb87\xacG\x82\xec^\xc0\x9f\xdb\x93\xe5\xce\x8a\x0d\xcb\xf3\x9f\xca\x0f\xf8\xff)\xbc-\xb8\xf7\x9d\xf55_\xdf\xb2\x19x\xadv\xe3\xe8\xe6P\xdb\xba\x96c\xad\x0f\xac\xf4A\xf0^\x8d\xe6\xf8U\xe4f\x8a\xf4b\x90\xc2\xd2\x0aM \xf6i\x04\x97\x88?\x08\xf4\xbf\x15\x85\x9e\xd2\xf1\xe3~.\x9b8\xb6\xfa\x8d5U\xed7\x0c\xc0\xea\x9aue\xfb\x0f6}\xbc@?_:\xd7M\x03\x01M\xd1\xcc`QE\x88+V\xf4\xc8\x81\x94R\x7f\x90N\xa4he\xcb\x01\x81\x08\x05cO\x1c\xc2\xbbr\x0bZ\xa6\x95Ozn}Mu\xf3\x7f\x02\x80\xf1\xd4\xc6\x1f\xf7o^\xa1\xc5\x8b\x1ft+~\xdb`Uq\x0c\xe7_\xc5Q\xa0\xbc\xa4\x04\x90>\x05\x94H\x00\xac\x14K% \xc4<\x1e\x88\x1b\xfa\xdbFV\xd7\xbc\x99\xd3\x9e\xb5*aW\x0d\x80\xb1\xf7=\xfb~i\x5c\xa2\x9f\x9f47\x0b\xf2\xb8\xe1\xaai\xb0\xa2J\xa3\xce\x8e\x9a\xa4\x0d\xb9\xe5\x16(\x0b\x84H\xf6v\xfd\x84\xd8\x86\xb3\xd4\x03rJ\x17)\x9e%\xcc\xef\x12\xa4\xef\xcb\x9c\x82\xa6\x87\xa6O\x9d7T\x8d%#\x00\xa6i\xeaw\xff\xb0\xfd+?\x1d\xedC\xcb\x99\xf4`#n\x1b.\xfavi\x84\x1b\xaf\xa6\xceJ\x9a;\x0dr\x1b\x22\x04\xb0M1\x88]\x8a\xb3\x81`?\x8a\x08\x1f\x0dC{e\xc5\xec%r\x8d8#\x80\xaa\xd7k\xa7\xd7\xef\xde\xfb\xf9\xce\x00\xbd\x93\xcb&I\xde\xa4\xa8\x90AyT\x91\xc0(\xa2\xa0\xe9\xcc\x80\xf8:\xa3-\xb8\x8d\x9coAR1\xd6L\xf0\xe201\xe8\xc2>\xe8\xa8\xac\x98\xb5l\xe3\xda5\xfb2\x02@\xb7q\xd55|\xdf\xf4\x89\xeb\xe2D?\x06\xa9m8\xd4d:\x92\x1d\x07\x04f\x96\x8c\x82\xca;JL\xc3\x0dS\xe3\x99\x0d\x9fd\x14\x88\x12\xbbp\xaa\x1b\x86\xf1T\xdc\x1bZ<\x7f\xce$t\xa7\xe4\x90y\xe0h[\xe7\x9b\xcb V\xcc\x8dgn#\xdc\x81\x1b\xaf\x09 \xaa\xcb\x99\x05$z\x0f\x8f\x19\x03E\xa5e\xb0\xf5\xf7\x83\x0e\xe1\xc3\xe7w\x5c\x08C\x17R\xa6b\x9f\x17vt\x9f\x03\x0d\x99?\x88\xba\x09\xfb,\x9b2\x19\xee\x8bF\x91Z\xc5\x81\x0e\x9a1\xb4\x8fI\x0dF\xf9\x14\xc2g\xd4\xafj\xdaR\x88\x15\x1dj\xeb\xd8\x80WW\x0e\x09\xe0H\xe8\xd8\xe4'I\xdcG\x19\xa9W\x09w\x09*\x5cD\xa5\x9a\xe9\x03*\x8a(\xd6g\xd7\x85+\x94\x93$\x8c\x981\x1d6\xec\xfdI\xc4\x00\xe5`\x0e\xe8\x11P\xf4~QU2@\xa1f\x8d\xf7\xe5Y3`\xec\xd93\x9c\x03*\x9c^\x1bNY\xd1\xa6\xd0D\x94\xb6\x1c\xd7\xba\x87&\xb3\xb7\x85\x8eO\xcd\x98\x89O\xb4\x1e\xd7\xfc\x05\x1eWR\xd3$\xff6E\x99j\xf1G\xcd){\xb1\x99 \xbc\xaf\xc1\xc8H\x04\xd6\xcf\x9c\x06/4~\x8b\x0b\x08MYR\xad \xc6\xd5\x00\xde_P\x01\x81\xb6VA\xc9\xb9\x7f\xdbF\x13\x19D\x8a[\x99n\xe47\x0c\xd7\x89\xb3\xddZF\x00\xd1\xfeH\xefE\xbfJ|.\xa2\xc9\x00\x14\x95\xbd\xdc,\xa9Y\x05|\xca\xfb\xaa\xe8\xb3\xd1Bi\xfb\xcd.\x18\xa0\x90j\xbcS\x16\xe3\xc7\xda\xfa\x9d\xb0\xe1\xae\xf1|\xf6l\x03\xa5Q\xb7\x00\x90!\x00D\x93\x06a6^\xaa\x06u\xb8%nD\xe4\x8a<\xa5r\xc5F\xce\x9eB\x86\xe1\xcbu\xf4\xe3g\x9aZ\xa0\xd3 \xf6}\xd6lA\x97\xdaZ1\x07\x0a\xb2}\xceR\x89\xf7\xa3\x08l\xf9_G\xa1\x1dc\x84\xa4I\x17\x13\x90\xf5\xac$\xa0\xf1\xbb5\xc2m\xfb\xe2r\x9a\xe9\xa5W\x87\xf9V-\xcc\xf1\x14\xa6\xb8\x91\x22\xfc\x1f\xfb\xdc\xff\xc52\x9a=a\x02\xb8\x83A\xbe\x221\x09\xca\x82\x9b\xeduE[\x9a\xc1\x08\xeb\xfc\x9d\xee\xe1\x85\xe0\xc5e\x15D`\x82\xd8\xf8K\xf6\xf4@4\x14J\xf1wb\xeb\xe6\xd4et\x87\xce\xd5\xcd&T7\xef^\x89&\xdbT=\xcc\xf7\xf8\xa2\x1c\xcf\x08\x1b\x84\xa2\xa4\x89\xc0\xb4\xf3kHd4-\x91\xd1\xb4\xd9\xb5\xee\x0b\xe3?C\xe3_\xb9\x22=\xc0\x1eD\x10\x1d\xa7\x0dR\xb5<\xe0+1\x97OG\xef)82\x0aa\x86S\x1e\xe0\xd7K%l\xc3\xe9`*\xb1\xb9\x8f\x0b\xd6\x8dh\xd3GW\xcdF\x11D!\x1e>]\x9b\x9f]>?\xdb3<#\x99S\x1d2\xa7\x88\x9a\xfc\xa5\xc8\x9c\xb3(P\xdb\xd7\xd3\xc9\x5ccd\x80\xc9x\xcc\x8e\xf0\x04\x1a\xdf}]z\x80\xedP\xe0\xe1\xc3Uy\xbe\xbb\x1f\xc9\xf5\xde~EtZ\x19\x82N\xd3\xcb\xd3\xe9\xbaH\xec\xf4\xa6\xde\xe8\x9f\xf8\xd4\xf3\xe95\x88\xebVd\x08\x84m\xca.e\xa3\xf2b\x9eo\xdcc\xb9\x9e\x22\xcd\xe4\x1a\xd7,h0\xe9\xd1mz\xec\xd4{}\x17[\xf1\xd2\x166\xe3hx\xe2\xa6\x88z\x04T\xc2\xea\xc3\xd8\xe6O\xf1\xb8F\xdd\xefu\x07F\xbbT\xefHM\xf5\xe6k\x9a;\xa8)|\x17\xba\x87\xd0Do\x92$N\x1aF\xac=Ib\xbf\xc6\x12\xe1\xdf\x06\x92'\xf1V\x03\xb6:ks\xea\xa6hb\x1cU\xf6\xd7\x86\x1cl\xd9\x0c\x83\xf8\xab\x83\x07[\x96h.\xd1T)\xcf\x10\xd1\x92\xa2\xc5Ec\x85NV\x8fa\x1b\xebL\xb0\xf4g\xaa\xcf\xfco\xcb*\xff\x02G\xbd\x8aL\x08S\xf3a\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04TIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\x02:\xac\xed\x98\xafs\x08t9g\x17ct\xbdw\x97R\x82T\x01\x8f\xae\x1e\xedB\x08[\x99\x8c\xf8\xe8\x9c\x94\xae1\x96v\x15;4\x80\xb8h\xb9E\x0c\x845\xbe,\xd0\xb7z n\x22\x0bG\x1f\xa4\xef\xe7\xb5B\xd7\xcb\xaeb\x8c\xd8\x88\x00\x99\x07\xf3\x08\xfe\xd7\xb6\xe5-@{\xd6\x8e\xc3 \x0cC\xdb\xaa\x88\x13\xb0#\xb1\xb0p\xffs0q\x02F\x16\x16\x90\x18\xda\xbeH\xa9\xdc\x88\xd8NB+*%\x12J)?\xdbI\xec\xf7^\xbe\xfe\x81\xbf\xaf\xf9\xd9\x81\xec\xc0\xc9\xdb]s\x13MM>D\xe9\xa6+Iu\xe0\x84\x02WY\x8c\xceB\xd6p\xe0\xec\xaa\xaa~\x1aYpU\x9b\xf89G\xc4\x11\x80\xf1 \xcc\xb6\x02\xf9\xa2\xccA@\x0d\xceq\xa3\x8f\x80\xe1\x1d\xf3<\xc7O!\x1fl\xe7\x1c\xf0!\x8f\x10\x98\xe9jVIk@2^B\xee\x1c(\xa1j:\x95\x9f\xde\x9b\x12\x0c\x14Se!\xc9hwt0g\xd7u\xfd\x10\xe0h\x8fk\xb8g/\x01H\xe4 \xda\x01\xed\xb4\xc11\x0c\x83\xd9\xda\xd9\x93+q\x8ek\xb8Gkp\xf2\x08h8\x1b\xb7\x0e\xa45\x14\x1a\xed\x5c\x89%\x0a\xe8+T\x1c{\x90X]R%v?N3\x85\x8f\xfal\xdb\xb6+\xb2\xe2|Y\x16l\x92\x069$9u\xd3D\x9a\x8b,\xfd\xaf\xeb:\xa3c[\xdel\xa9\x15\xfa\xbe\xef\xcd.e\xdb\xb6^Q]r(\x18J\xbc\x0cy\xd8J\x1cR\xc8\x10\xe9q\x1c\x0dG\xc7\x9e8\x04zl:\x17E!F\xde\xf6\x18\xa9i\x9aL\xda\xad\xeb\xfa\x9a4\x854,\x99>S\x96\xe5\xa5i\x1as\xd0\xe6\x0a\xfd\x9c\x03\xf6\xb7\xfbL\x14\x1a\xa5/\x0eq\xe4\xa8dp\x88\x03\xb1\x8e\x848\x19\xf3>\x91\xd4SH\x1d*D\xa6\x18\xae\x81\xd2Y\x958C{\x02d\xa1\xeem`\x07\xab\xd9\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10W\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05\x0cIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91]\xb9\xaa\x0e\x5c\xa1`_Y<\x0d@\x1b\x8e\xc51\x8a\xa2\x9fz\x16ZU'~\x0e\x88\x93\x10\xc1x\x08f\xbd\x02Q^\xb6\xf1M\x8e\x03Q\x22[\xf7p\x18\xde\xb1\xae\xebyFG\xd1v\x0e\x00G#]\xb4\x9f\xaaY]\xa2\xa4.\xe3m\x92\x81\x13@\xb6g&y1\xc7S\xa2\xeaP\x04\x5cF\xdbz\xec\x0b,\xcb\xb2\x85\x1fS0\x8ec\xcf\xf7}\xb2\xdcE\xdd\xbb\x0c@:mp@\xee\x8c\xe3\xe8\xd5u\xad4V\x92$\xdbXT\x0f\xfb\xbeW\x8a\xa5(\x0a\x92:~5\x02\x12\xcd\xa6\xcf\xa1\x1b\xe0\xf1\xb6m\xad`\x01\x08G\xd7u*B\xd0\x146\x83o\xdf\x8b\xa1\xda<\xcf\x9b>\xe4\x1a\xa2\x83\xb1\x7fZ\xc0\xa1\x94\x18\xb5Pq\xaa\xc1\xa5\xea.\x01\xb0}\x9c2\x18\xe2\x19\xd5r\x9b*1\xaf\x87a\xf0\xd24%\x81\xed\xcf]\xa0^\x12Os\x9e\xd5\xf7\x9a\xa6Q\x05r\x14I\xb0we\x1a\x8d\x1e\xe2\x1d\xcf\xc20\xf4\xaa\xaab\xdfu$*o\xe9t\xb1\xe5\xea}\x1e/\xcbR\x19\x07\xe9B\xee\xbe\xa6\x07LJ-\xa9\xbbJj\x82\x12\xc3%T\xfa\x91\x94\xff\xa1}\x00\xeeU\xc1\xd6Y\x5c\xc9\xf4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x12\x5c\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x11\xfeIDATx\xda\xec\x1dKl\x1b\xc7u\xf6GR\x9fFR\x92\xba@\x93\x149\x06=\x09\xf0\xa97\xe5\xd4\x18E\x13\xb5\xb7\xa4A \xdb)z(\x82\xc8I\x8a\xc4\xf2O\xb2\x1d\xc7\xf9\xb5v\x80\x06\x05\x1a\xa0v\xcf\x05J%=\xf4\xd0\x22\xd2%9\xa5\xa0zk\x03'rk\xd7N,QTD\xf1\xb3\xdcO\xdf\x9b\xcf\xee\x90\xa2\xa8\x0f?\xbb\x5c\xcd\x03\x06\xf3\xd9\xe5\xeep\xe6\xfd\xdf\xcc\xac\xe6\xfb>QppAS\x08\xa0\x10@\x8d\x82B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01Z\xfdX\xd3\x82\xf2\xf0\xf3\xbf{\x0a\xb2q\xcd0\xc5E\x9e\xeb\xf5u\x05\xbb\x031/A\xee\xf1\xcc\xc5,W\xfc\xfd/\xe6\xc3[\xfdh\x10\xe0\xb3\xcf>#\x13\xbf\xfdt\x04\x8aY\xcd\xb0&\x0cC'\x86iR\xc4\xd0\xa0\xacC\xae\xeb:\xab\xebXgH\x13\xe4P\xd0\x826\xad\xe1\x1a\xe4Dkr\x9f|?\xa9\xff\x9d^\xff\x8cf\xf7\xe3=\xc4g\x83\xe6\xf3\xdc\xc32\x1fk\xd6\x8em\xcd\xcaa\xee\xf1k\xe13\x9a<\xb3\xc5\xfd\xb4\x8d\xf0:\xfc\xd8#\xe1\xfb\xe85\xcf\xe3\xd7<^w\xe95\xd7u A\x83c/\xc0O&\x17~\xf9\x83\xf5\xc3\x87\x0f\xef{\x0e\xcdv\x90\xf4\xa1\x87\x1e&\xc4\xb0\xb20\xc0\x13\xe9\xa1ab\xc0\xe0\x9a\xe9\x0c\xcd\x8dT\x8a\xd5\x01!h\xdd4\x88\x09\x93j\x18\x06\xab#\xb2`\xc22\xcc\xb6\xc9\xdbp\xb2MzMc\xbf\xd7\x19\x12\xb1:\xdc\xc7sQg)\xfc\x8dN\x9f\x83\xbf\x0b\x9f\xad\x1b\xfc:\x7f\x07\xce\xb6\x0b\x03\xebz>\x1dL\x8f\x97=\xac\x8b\xf6\xa0\x8e\xf7\x84m.P\xa2\xe7J\xf7\xba\xd8\xc6\xee\xc16\xcfg\xcft\xa4\xe7\xe1\xfd\xae\xb8\xdfo\xf2N\xf9\xf9A\xbf<~\x0d&\x1c\xb0\xc3\xad\xd5X\xbb]\xa1\xb9]\xda\x9c\x80Gea\x0e\x1e\x8fL\x04\x0c?\xf7\xeeSZz8k\xc2\xcc\xbex\xe40\xf9\xee\xe8\x10P\xbe\xc1\xa8M\xd7\xa5\x5c\xa2jMo\xa0z\xb2\x85\xfa\xc3{\xeb\xdb\xe4{\xb7\xfb\x1dJ\x1c\x9d\xc8\xf9\xd6{ej\xf7x\xa5\x91\x8a)u2\xb2\x0c\xcax\xebV\xca\xaf\xa7j\x9fs\x13\xda\xee\x85\xf5f\xdc\xc4\xa3?\xa8\x7ff\xfd\xf3\xf0~\xf6\xe2\x80#\x00B\xdc.\x94\xc8{\x7f\xfd\x07q\x00\x91\xfc\xf2\xfad\xf1\x8f/\xcfG\xc2\x01\x98\xcc7\x08\x9d-\xdd\xa4\xf2^\xd3\x0d*\xef\xb1\x9d\xb1\x5c#d\xbd\xc1\xe45a\xe5z\xb3v\xf9\x9a<\x89MD\x82\xbe=b\xd0\xeeI\xef\x16r\x13\x07[\x97&D\x93&B\x93&\x82He-\x98\xa8\xc6z\xfd\xc4Q1\xa3\xfb\x12\x921d\xd2}\xc6\xee\x83\xb2\xc7\xde%\xae\xcbeZ\xf7\x10\x81 \xd7<**(\xd5\x1a\x16\xd1\xac\x0c\xfc/\xe4.\x83\xe3\xd0\x14\x19\x02\x04\xf0\xfe\xe2\xbf\xe8$X\x99A\xc6\x82Si\xc6\x9a-K\x12\x01Z \x02\xccF\x11`hA=\x14\x01\xa1hh&\x02(\x9bo\xf2\x9b\xf0\x9aTo!\x02\xdcF\x11\xe0ne\xc72\x8b\xaec\xe9\x92\x08\x90Y\xba\xd3\xeay^(\x02\xe8\xb5\xba\xe7\xbbu\xfd\xf2\x1c^\xaf\xd94w\xb8\x08`\x98\xd0\xfe\xbc\xb5\x8f\x00\x1aS\xf2\x00-\x19\xbb\x05N@s\xc4R\x9a\xf3:*\x83[r\xbd\xc5\xb5\xdd\xe6{\xff\x0d\x22\x80F\xc7\x8fQ\x5cP&\xbc\xecKezO\x8b\xeb\x9cj\xeb\xee\xf1wz\x9e\xcf9\x18\xe4z\x93\xfb\x83~\xe1\xf8i\xf4>\xce*(\xa7\xa5\xd7\xd1\xda\x02\xae@\x8c\xf6\xa6PW\xf6\xd6\xc1\x06S\x0dA\xdf\xb8lBwJ\x07]*\x8a\x03(\x0e\xd0\xae\xc7\x0a\xcd\x13\x94\xab.\x14A:y\x0e\xf1\x01M}\xb7\x06\xed\x90\xe3%\xccAG\xa0R\x8e\x9aa\xa2\x9d_k'\xf7H\xf8\x5c|/\xd9&\xd7\xc2\xfb\x88p\xb0\xa0\x86M\xd3Ne\x9eS\x0d\xdf\x93\x1c6\xfb+\x87\x1d\x0f\xdb\x85\xd6\x8f\xef\x22\xc1;=\xea\xf9\xa3\xda?\x98\x7f\xb4\xddeu\x9f\xd6=\xd6\xae8\xc0Au\x17G\xcf\x01r\x88\x91\xa8\x9d\xfaN\x0d\x8dm\xe2\xd7\xaa\x80\x9d\x1au\x9e\xa0\xd6\xed\x01G\xa0\xda\xb7k\x10Og.b\xac\xbb\x8e\x01\x1a,5\xda\xa9\x1fA34\xeeO\x80\x1c\xee\xf1\x0d\xe4\x12\xac\xeeC\xdd\x83\xba\x07u\x1f\x9f\x89\x89\xd7\x85\x19\xe8If\x1fz\xfe\x5c=\xf4&\x1a\xbc\xae7\x98\x81^\x9d\xf7\x8d\xd4\x99m\xf5^\xc1z\xf3-4\x03\x85w\x10\xda[\x98\x81\xde\x16/_\xe8e\xf4d\x93P*{\x0e\xe3\x00\x1e\xa7x\xafVc\x94oW\x19\xd7\xa8Uh?|\xbb\x94\x8b\x8c\x03,\xbe8\x81\x0e\x88\x05\xea\x9bV\xd0S\xe0\xbe\x80\x85[\xef=?\x1f\x19\x07\x18\x18\x18@'\xf5\xa4E\x9c\x85j\xd1\x1fG\xear\xaaU\xea\x86E\x0f #\xeem\x82A\xba\xd6\xd2\xad\x1bx\xff$w\xae\xb8\xae7\xf3(n\x09,\x91m\xbd\x89\x84H\x9e\xbb\xba@L\xf3\xe0M\xa3\x97o\xab\x1b\xb8Ep\x88\xbb\x91\x83\xebD\xe8-\xad\x03FA9\x08\x061\xfd\xc1\x05N\x8b\x04\x97r\xcb9\x9b\x98\x93\xed\x86\xf3\xdbB\x80\x91\x91Q\xf2\xb4\xf9\xcf+\xd0\x89\xf1\xffx#d\xa5\x96\x06\xf6kps\x85\x86\xf3XNB_=u\x07\x07uf\xdeh\xda\xf6\x89M\x9c.!\x8c\x1e\xb4\xe9\xba@,\x9d\x22\x9a\xce\xd9\xbc\x8e\xc8\x17\xe4:\x0f&\x89\xdc\xe0Q5\x8fF\xd8X.\x92+\x95\xbd\x80=\x8b\xc1g9k\x17~\xfaP9l\x9d\x84\xfb\xd9'a\xec\x81\x89q?\x90\xe7>\xf1\xeb\xe4z\xa00\xd22\x0b\x07\xa3Hx\xd0\xa8\x92\xef\xe9\xeb\xe30>W\x08\xf9\xd9\xd1\xc8\x82A\xc7\x8e\x1d{\xd14\xcd+\xc5\xcd2\xb1,\x8ba\x94\xc5p\xca@\x0f \xa5<#\x98H\x16\x1c\x12uy\x22YT\x8fM\xa8\x11L\x94!r\x9at\x8a\x5c\xf8\x5c\x13\xdd\xca\x90S\xf72\xb6\x99&\xb1L\x8b\xb5C\x19\xdd\xcf\x16\xb6Y\x16\xadcnY)H&I\xa5Rt\x22k5\x9b\xd85\x87\xd4l\x1b\xca5Rs\xb0\x8c9P\x18\x96\xe1\x9a\xe3\xb0\x84\x119\xc7q\x89\x83\x919\x9a\x03\xa2\xd0(\x9dG\xeb.\x97\xd5\xae@ D*\x81(\x02\x99\x02\x04\xf2\x03$\x22\x9c\x8b\x10\xd2\x10G\x90\x83G\x1e\x9bx\xd7uY\xee0\xad\xdf\x81~\x0e\x0f\x0db\xfb\xf4\x07\x1f|p5*%p\x14)\x08\xbb|\xe4\xc8\x13dll\x8cN\xa2\xe0\x00\x82%3*g\xf1}\x22\xb1\xe5z\xea\xaf\xbf\x1e\xb0o]f\xf7\x9c\x13P\xb1\x22\xa2\x8d\xac]7\xf4\xba\xeb\x06\xe7\x02ZP\xe6\x1c\x82+\x81\x94\xfa\xc5dQ\xc5\xcb\xe5\x14\xcf&\xd1\x97\xa8_P\xba\xc7Y\xb2\x17\xb0f_b\xdb\x0d\x14/\xd8\xbf\x1cP\xe2u1\xe1Dp\x04N\xf9\xbe\xa4\xda\x87\xebAx\x9d#\x02\xbeomm\x8d|\xf8\xd1G\x94{\xe1\x1cDi\x05dS)k\xb6\x8cr\x9f\xcd]\xc0\xf2\x0d\xbd\x99\x08\xd8\x8a\x04\xf5uy\xb2\xb5@w\x08\xeau!f\xad\x1e\x19\xeaD\xc0\xd6IGNdH\x08\xe0z\xf0;\xa4d\x9d!\x81\xe6i\x1c\x01\xb4\x80\x8a\xe9\xbb=\x8f&\x1f\xeec\xd1;x>\xde\x87\x16\x89\xef\xb1h\xddN\xec_\x0a\x09\xcb\xac\xbd\xe5\x847\xb2~\xae\xafk\x80\xa0\x82x\x90\xa3U\xab\xd5ld\x22\xa0PX'33'?.W*\x13\xc2\xa0\xb0\x80\xc5\xd2\x1c\xd8n\xbd\x08\x08W\x08\xe1\x05d\xeb[D\x00\x9f\xa8\xa6\x22\xc0\x14\xa2@\x88\x00\x83\x8a\x04\xca\xf2\x03\x11`R\x11dI\x22 L)`\xffV \x02l\x1bE@\x8d\x8a\x00\x9b\xb3~!\x02\x1cl\x97D@ \x06\x90\xcds\x11\xc0V\xe6\xb8ME\x80\xacW\xb4\x12\x01!\xcb\x97\x94GN\xe9\xe2\x9a`\xfd\x0eg\xfd(\xae\x08\x0f\x18\x0dd\xd2\x0b\xc7\x8f\x1f\x7f\xbc\x9d\x15Am;\x82\xa0\xc3\xb3).\xff}B\xea\x947=\x90\xf5a\xd2D\xaeim&}\xcb\xb3\xbb\x994\xad\x13}\xde9m\xff~\xaeD\xf3\xe8c\x0a\x10\x1d\xc7>\xf2X\xc0\xe5\xcbo.B\xc7\xb2\x83\x83\x19e\x9c\xf7\x08p\xacq\xcc_x\xe1\x85E\xe4z\x91\xbb\x82\x81\x9dN\x01\xb5/\xe3\xa2\x8b\xd0l\xea\x88\xa7R\x01\xe1\x22\x82\x8b\x13\x5c~\x07\x1ca\x19\xc7\xfc\xd0\xa1Cdtt4:\x04\x10+\x9d\xde\xfd\xf5o\xd6!\x9b\x04\x99T0\xb9\x1f@A\x17\x22w\xa0\xfb\x0cd2\x05\x1c\xeb\xb7\xde~g\x1dM\xe6t&\x13!\x07\x08\x22[>9\x7f\xfe\xc2\x12(*\xd3\x99\xb4\x05U\x97)H\xe8\xc7\xf6\xbd@\x93U\xb0\x97\xa1\xf5\x03\x87\x95P*\xd3\xe9\x14*\x83\xd3\xe7f\xe7\x96B\xa4\xb0\xe2\x13\x0d\xbct\xe9\x8d\xeb\xe8\x9d\x1a\x1a\x18 j\x1bH\xe7\x00\xc7rhh\x00\x95\xea+\x17.\x5c\xbc.\x13\x1ei\xd3\x15\xdc\xf1p\xf0\x993gO\x00\x96.\xa0>\x80&\x0b\xf5\xa09\x8a\x13\xec\xc1\xaa\xa2I\x98\x9f8\x868\x96.\x8c\xe9\xcc\xcc\xa9\x13\x9d~_\xb7\xd6\x03L\x82\xcd\xbd,\xccC\x05\xfb\x874\xf5]XT\xeew\xe3\xf9\x1dQ\x02\x1b\xd3\x85\x8b\xaf\xaf\x97\xcb\xe5IBw\xd18\xd4\xef\x1er\x02\x9f\xa8\x0d\xa9\xadd>\xd3\x9d\x90\xf2\xd1\xb9\x84\x0e\x9fJ\xa521;w~}\xbb\xf1\x8e\x85\x12\xd8\x98@V\xa1\xa2258\x90a\xee^\x05{\x86a\x90\xfb8\x860\xf9K\xddzGW\x97\x84\xa1\xc2R\xadV\xaf\xe1\xbav\x1aq\xc3\xfdm\x1c\xc3\x15lO\xf9T\xee\xc3\xcc\xe0\xd8\xcd\xce\xce]oEh\xb1S\x02\x1b\x01\xcc\xc3\xa3\x86\xae\xe7\xd0\xf1\xd2\xcbKIC\x80x:\x82\x1a>\x9f\xb2\x0f\xc8m\x14\x8b\x8f\x8e\x8e\x8du\xa4;U\x16s\xc8%q\xdbz\x22?\x1d\x0b\x94\x9f+\x16\x8b\x1d9\x97\x10\xbf\xec\xc3?\xd3\x92K\xe2X\xa9o\x07\x1fpH$\x02\xb0\xe8\xa0MY\xb7\xf8 \xe4~u\x00\x22>\xef\x96\xd0\xf3\x8d\xdb;$J\x1d\xfa\xac\x94\xc0\xee\xe8\x80~\xbbJ\xe0\x96O\xc0vb\x9d\x80R\x02\x15(\x0e\xd0K\xe8\x04\xc56\xfb\x88\xb3\xe2\x00}\x82\x00q;L:\x89\x87[\xc7S\x09\xe4\x8fu\xda\xb1\xdf\xa5\xbeu\x8ar\x1d\x85\x00\xbdR\x029\xc59\xf1:\x1f n\xfdI\xbc\x08\xc0ux\xaa?\x07Z\x07pT\x7f\x14\x07\x88\xd6\x92P\x1c \x0a\xb3\x88?\xd7\x89\x19\xc59\x8a\x03\xf4\xc8\xfe\x8f\x15\xc5\xf91\xeb\xcfA\xd2\x01\x94\x15p\xc0u\x80\x18\xd8\xdd\xbe/\x8b$\xc5\x01z\xac\x04:\xaa?\x07Q\x09\xd4u\x16\xa3\xc2c\xd5q/^4\x94/\xc5\x10\xb8\x16\xe0\xc6P\x07H\xa7S\xc9\xe3\x00\x06L\xbceY\x04\xbf;\xbcY\x8a\xcf\xe9`q\xb4\x02\xc6\x06\xeeK\xa0\x19\x08044L\xd7\xe2\xdd?6JVV\xd7\xf6\xbf\xb2\x97\xec/\x1aX\x1fE$\xfc{?\xf1\xe1\x00\xc8%\x1f|`,Z\x0e\xd0\xd5\xe8\x98\xa6\x91\xe1\xe1o\x11\x0d\xfe(r\x82r\x17v\xfb\xec\x15\xbes\xe8\x81\xd8 \xc0@&\x13\xbd\x0e\xd0u\xad\x18&\x7f\x08\x90\x00O\xe1J\xa5\xd2{\xd7\xde\x03\xa7\xc2\xee)_\xac\x22\x12\xab\x89E\xc2\xb6\x91\xfbF\xf6\xf57\xf0\xfb\x07\xdd\x00\xcf\xd7\xa2E\x80^\xd9\xc5\xf4\x98x3zue\xdf}\xe0\x08`\xd7\xe2\xa7D\xb6\xc7\x01b\xec\x19\x93w\xf6\xe0\x9a\xc0\x9dv\x08m\xf9\xc2\x07\x9e\xff\xc7s\x97\x7f\xed\xbc\xdd\xff\x1b\xc7\x05%m\xea\x00\xc9\xb3\x8b\x9bAq\xb3\x88Y\xae\xdd\xff\xeb\xb8\x8a\x03\xf4\x94\x03\x08Y\x8e\x1a3~\x7f\x08\xbfQ\x89\xfb\x05\x90#\x88S\xc5\xd8\xd9\xc3\xec\xd4/\xfcZ\x19\x9e\xf5\x8b\x94\x8f{\x0b\xd0\xec\xc3\x93\xca\x8b\x1b\x1b\xcb\xc7\x8e\xff|~\xbf\xffW\xd7Hl\xfd\x08m*\x81\xc9\xe6\x00\x88\x0c_\xdd\xbd\x83\xc5\xe9v\xfek\xca4b;^\x89\xe5\x00\xb8\x1bH\x9c\xec\x81 v\x09\xa1B\x89\x07N\xe3)\x22x\xad\x0c\x93\x8c\x94_*\x97\xa8\xb5\x81\x8e'<\x08\xe2\xee\x9d;\x94\xfa\x81\x1bL=\xfb\xdc\xd4|;\xffU @\xf2t\x80\x04\xf9\xc6\xf1d\x91{_\x7fMVVV\xe8\x07\x9e`\xe2\x17\xa0y\xf6\xe9g\x9e\xbd\xd9\xfe\xffL\xc5\x96`\xe2\xed\x07h\x03\x90\xe2\xc5\xb7{\x90\x1bh\x9aN,\xcb\xa42\x1f\xf5\x03\xa4\xee\xbbw\xbf\x22\xb7o\xdd\x22\xf7\xee}\x8d\xd4\x89\xbb\x7f\xb3x\x06\xd03\xcf>\xb7\xd4\x8d\xff\xe8&O\x09\x8c-\x07\xc8\xae\xe5\xf3\xb3\x8d\xfd+\x14\x0a \xd3\xef\x92[\xb7\xfeK\xf2\xf9<\xbd\x0f\xc4B\x16\xf2\x85''\x7fz\xb3\xdb\xff+\x8eV\x93\xd6\x8e?\xffo\x7f\xff8\xb2h\xdd\x8e\x18\xf0\xe7?\xfd!\x9dNO=\xfc\xc8#\xc4\x86>\x16\xd6\x0b\xa8\xd4-\xc3\x84#k\xcf\xfe\xf8\xc9\x9f\xcc\xf7\xa2\x1f\xe8\xab\x1f\x1b\xbd\xaf\xab\xef8|\xf8p4\x08\xf0\xc9'\x9f\x92{\xab\xf9\xd8\x8a\x81\xbf|\x98}\xca4\xcdq\xfaG5-\xfb\xc3#?\xea\xf9\x11/\xdf~\xe0~\x8a\x04O|\xb8\xb6\x93\xe1\xbaM\x95o\x94\x15N,\x97\x11\x9co\xb3\xd8\x88_\xde \xf9\xf7\x8fG\x83\x00\x0a\xfa\x1f\x14\x02(\x04P\x08\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@\x81B\x00\x05\x0a\x01\x14(\x04P\xa0\x10@A\xe2\xe1\xff\x02\x0c\x00)3_$\xa0\x879\x5c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1cT\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x11\x09IDATx\xdab\xfc\xff\xff?\x036\xc0\x02c|\xbb\xc6\xfc\xff\xee\xc3\xff\x0co~108\xfa\xffcd\x00\xe9P\x92c\xf8\x1f\x17\xc4\xf8\xff\xd6a\xa6\xff\x02\xbc\x0c\xff\xe3\x83\x19\xfe3\x82$\xf8y\x19\xffss10,\x98\xc6\xc2\xf0\xf2\xf9\x7f\x86\xe7O\xfe1\x80u\x9c\xdd\xc8\xf8\xff\xd3e\xe6\xff\xe6\x86\x0c\xff7\xccf\xfa\x7fk/\xe3\x7f\xb0\x04\x08\xa7E0\xfeW\x90a\x00\xb9\xe4?\x88\x0f\x10@\x8c\xb8\x5c\xc5\x04\x22\xcenf\xfe\xff\xfa\x0c\xd3\xff\xcd@c\xd8y\x04^\x81\x9d\xcb),]\xa8+\xf3\x8f\xe1\xe0\x1a\x16\x86\x0f\xdf\xff1\xc8\x89~\x14\x05k\xe1\xe3a\xfc\xc3\x0ft\xa2\x8a\x02\xc3\xff\xcdKY\xfeOl`\xfc\xcf!$u\x80\x01H(\xdd;\xc8\xf8\xbf<\x8b\x09l\xf1\xc9\xf5\x8c`\xcb\xc1.\xaa-\xe2{\xe4d\xc9\xf0\x9f\x93\x83\x01\xa4z\x1eH\x0c \x80\xe0\xce\x85a\xa0D\xe1\x85\xe3b_\x7f~e\xfa\xdfT.\xfe\x1a\xc8\x97E\x96\x87\x87\xa1\x87=\xe3\x7fA>F\x86\x95\x1d\x0c\x0c\xf2\x9c\x8c\x0c\xb7\x8f\xfcgP\x97}%\xa2(\xf4\xff\x11\x0f\x17#\x03\x1b\x0f\xef\xe3w\xaf>\xc9\x81\xfd\xcd\xc1\xc5\xf9\x8e\x99\xe1\x87 \x07;P\xb1\x0c\x03\x83\xa733CF\x02\x03\x83\x8d\xd7_\x06#}\x06\x06V\xa0\xb1\xec,\x8c\x0ck\x0eI\xb6\x83m\xf0wc\xfd\xe6\xef\xf0SP\x80\x8f\x81AN\x96\x91AI\x99\x89!4\xfd7\xc3\xd2\x99\xcc\x0cr\x12\xff\x198\x99\xff3\xb02\x03581\xdc\x82\xbb\xad\xbd\x8a\xe7\xd9\xc7\x8bL\xff\xab\xb3\x19\xff\x07\xb93\xfcWUd\xf8\xcf\xca\xca\xf0\xbf\xa5\x88\xf1\xff\xcaI\x8c\xd8=\x0d\x04\x1c@\xac\xc7\xc6/\xba\x94]P\xe26\x1b\xafP7\x90\xaf\x86\xac\x06 \x00\xddd\xf3\x92@\x14E\xf1\xf3t\xb42\x9b\x19\x13\x1b?\x92\x08\xe9?\xd0E\x84\x10D\x85\xeb\xdc\x05\xd1\x22\x08\x0a\xa2U-\x8aV-\xda\x05\xad\x5c\xf5\x07\xb4Oh\xd3\x22h\x11J\x8b\xa04\x0a\xa1$\xc5DM\xf3\x83\xd0\x99\xd7u\x84\xc1\x84\x06\x86a\xee\xbb\xf3\xce\xbc{\xce\xef_\xef\xfe\xbbL\x83\x85\xa5EW\xaa^\x11\xb5\xb7g\xa9M\xb6^\x0d\xae\x1b\x0a\xb4\x18o\x16>#\xdfO\x1a*U\x8el\x1e\xc8\xbc\x03\xc5\x0a\xc7a\xcc;\xd3*}\xbc\x1a\x0a\xd4\xbc\xb5\x12\xceE\xb6\xd7U\x08fJ\x8c\x05Pi\x9fj\x1d\xd8'_\xfcR\xee\xe5\x8f\x82\xdf\xcb\xb8h\x07|\x0a0\x1b4aw\x03xH\x03\xf3Q\x0d\x93n\xc0!\x01m\xd8\x0a\xa9t\xc3\xad\xfbP\xa9\xd2n5\xa0\xde \xb5Q\x0d6\xd1\x8a\xfa\x8f\x8a\x8505\x937f\xc6`\x1bn)\x86\x82,\xf6\x8eBs\x87\x9d\xa0\xe9\x82sJ\x9e\xd6\xbe8\xca%\xae?5\xe2g\xefDc\xba\xc2\xd9\x11\xd3\x9b\x14\x8a\xf6t\x80\xe1\xe2\x92a9\xdaA\xea\xc6\x0c\x99\xeaVR\x10\x84>\x80\x03S\xe0\xf4\x9f\xcc\xe9\x00$;C\xe2^\xc5\xb8\x0c\xdc\xde\x11\xc3>\x0e\x17\xd5C\xab\x9eD\xab\xd4C\xaf\x1biK1\xc9x\xe3\xd1\xc4\xbbT\xce\x05\xc1\xe9\x03\x1d\x90\xd81\xe3\xf1\xf3\x1e$F\xbci\xc6]\x93<;\xd1|\xbeL\x03h6\xe9pT7\xd3\xd03YS{\xf3\xa0c5\x9c\x1b\xc8\x92_\x18\x19[\x1b\x92\x95\xa4U\x9e\xb8\xa6\xf7\x10\xdd\xce\xfe\x9e_\x01h-\x7f\xd0\xa6\xa20\x8a\x9f\xf7^\x9a\xd4\xbc\xa4%ml\xfe\xd4\x82\xff@\x07)\xa2\x0e.A(Am\x07'\x15\xa1N\xe2\xd2E\xaa\x88\x16'\x11\x04-\xddb\xbbI\x07\x97\x0e\xe9\xaa\xa0\xa0\xe8`Qh\xa4\x94P\xb4C\xd5\x92?Mm\xd2(1\xb6yy\xd7s\xef\xd3bm+t0\xf0A \xef}\xdf\xbd\xe7\xfb\x9dC\xb6\xcd\xd2v?\xfaV?\xf0J\x97F\xee\x07r4\x85\x5c\x90\xa8d\x0d\x91M\xe9\xe2\xfdK]\xcc\xbe\xd5E\xa9@S\xc1\x10\xe9I\x97\xe0\xb3\xafX\x0d\xff\x8c\xd1?\x1a?N\x5c\xcfw\xdf\xe8\x15\xf8|\xa4yW\xb2\xb1Z\x0d\xab\xa6\xdf\xbbh\xbb\x03\x87\xa8ya\x83\x19\x94\xa0\x1df>v\xe4{\xe8S\x06\xf0\x9b\x92:\x0dm\xad\xc0\xbd\x01]\x0d\xa9T\x05J$\xb4\xf0\x05\xc8\xe4\x81\xe7\x13\x02\x0f\xc7\x04\xcc\x1d\x1d\xa3I\x9b\x01ec*\x0d\xbcy\x07\x0c\xdf5\x18/BIb\xf2y\x0fa\x93\x12\xc9\x88\xffZ\xd1k\x87\xcf\x87\x1ay\x03{\xdd\x929db:\x99;\xfe{\x88\xc7\xad\xa9\x17\xee<\x10X\xa0\x8fs\x05\x81\xcc\x02\x90e-\xd3\x91\xb7\xafj\xca\xe7-\xdcU\x93\xdfI\x1a[h\xd6\xc9\xbe\xf0^6\x9f\xdf@\xd1\xaf!\xfb\x87o\x95__8]\xdd)\x1b&\x1eA\xe1\xb8X\xe4?\x81\x22\xbf\xf3\xe2\xe5o\xb4{\x95\xc8\xd6h\xddN\xe0l\xb7\xa6\x80\xb82\x14y\xc1\xc6]\x9bf\xf6\x16f\x1b8v\xa0\xdc\xdfjVB\x12O\x19\xa22\x96e\x90V\x7f\xd0\x1b\x96n\xb9\xfca\x0a\x86Sl\x5c\xda\xac\xc7\x7f\x8f\x8a\x9f\x02\x10_u\xa1m\x95a\xf8\xfd\xce9Y\xba\xa4ij\xda4Y\xb5C\x1c\xe2\x85?\x9b?\x9d7RP\x14VA\x04\xdb\x09\xd2\x0dq:E\xaf\xbc\xd4\x1b\x05A\xd11P\x18S\x19S\xf1\xef\xc6\xe9P\x06\xd59\xc1Y\xd9\xac\xe8\xe8\xa4\xd8\xcd^T\x9d\xd2-\xcb\xbadK\x9b\xe6$9\xe7\xf3y\xbe/E\x1b\xe3\xa67\xee\xe2\xe5\x90\x9c\xe4}\xbf\xef\xfdy\x9e\xe7\xbdtX\xf4\xbf\x04@\x0d\xfaH\xb6o\xef\xc8\xe6s\xbf&k\xd5\xb2\xab\xcf\xcdu\x84_\x8ff\x16\xd6\xdf\xda}\x8c\xfa\xea\xa2\x11\x9a\x05\xd2\x12\xa3\xc0\xc6\xaag<|\xf4tm\xde\xd1s\xd3J\xcf\x1cRz\xea\xa0\xd2?O:\xf8\xde\xd1a\xe8\xe8\x91\xe1\xe4I\xfcvk+?-\xf9\x80mzg\xff\xc2\xe1\x0f^.\xa5\xbdN%>X\x13H)\x8b\xbe6\xdds~\xde\xd22\xbb\xaa\x08\xdb\xb8A\xc9\x1d\x9bB9\x9e\xeb5\xd0p\xc1\x14\xc1y\xcf\xba5\xc5\xc3\x8f\x0d\x95\xd2Oo\x83\xbc\x98\x0e\x815\xf8\x91c\x8d8\x03D\x90 \xc0\x0cTm\xab\xde\xfdH(G\x8f\x01^\xfc\xd9AB\xfd\x05\x03d;K\xdf\xdf3PN\xbf\xf1\xa16\xc3\xf4\xea{\x7f\x82\x17i\x9c\xc6@@n\xa9\x01\xd8\xa6\xc1\x17\xe3\x13\x16\xb7\xa2\xc0\xa5T\xc4\x04y\xa2e\x00\xbcx\xa9+V\xea\xfb\xe4\x0b^]\xdb4\xc0^xM\x8b\x1b\x81N\x888\xe2\x02:\x1cHIdVj\x90\xf6\x1f\x1f\xd0\xc69\xb9\x80\xcfk\xae\x12I\xb7\x17\x9fi\x19 \xee\x14\x1ft]\x9bW:\x9e\xa3jA \xe6< \xf5\x11\x97\xe0\xc4\xf1\xec\xe7\xb1\xefB9[\xb4\x9cA\xedD,:\x8f[\xafN\x973\x7f\xed\xae%\xb5vm\xdd/gH$KE\x0e\xc3C\xb6\xa1\xe0=]\xdai\x0e0\x8b\xf5\xc08\xa3c\xb6#\xb9\xb8#\xa1$\x89\xdc\xf6t+\xe9\xc5\x89\xf7~\xaa\xe4\xae\xfb\xea2s\xc4\x93$\x0e\x14A>\x1dR\xdam\x0b\x02m\xb1\xa1\x11\x0d\xa1\x08\x9a\xd8\x18\xca\x0f\x11\x15\x110F\x8c\xa8\x89\x22\x04\x01\x95&\x22FMC\xfc\xa3?\x8c\x01\x91()H5\xe1%B@%\x92 \x89\x9a\xa01\x01,\x88 \xa4\x11\x09\x06\x03\xa1%\x0d\xdan\xbb\xaf\xee<<\xdf\xbdw\xbaC\xa5\x85\x12\xf8c\x93\xb33\xbb\xdb\xde{\xce\xb9\xe7\xf1}gz\xd37\xb8\xd9?\xd6\x8dZ\x88c\x82Q0\xd5\xb1\xd4\xb00[!T\xecj\xfd5\x806G\x07q\x13\xa7\xb3,G8.~\xbb!\x1b\x0f\x04\x97\x06\x13\x86Y\xe5,\x8d,-\xdb7\x94ut\x9d\x8f:N\xaf%\xa1\xd8\xb5H6ex'\x0fG\x9c7\x1a\xca0\xf7j\xd1k\x95_\x8f.\xd7\x1cB\xeca.\xff\xf4n\xfd\xc4\xd4\xf4\x8dM\x99\xca1\xe3]p3r9\x9d\xf3\x0c\xfdr\xccN\xec\x1c\xea\x88'\x01\x13Z\x1b\x1a<\x08\x9f/\xc8L\x08\xb3:\xaad|s\xfbDA\x09\xce\xe5<\xa7Q\xd3\x1a\xcb\xf9p[\xa4\xad\xbd\xb3\xf8;\xfe\x8d\x15W*\x09\xd7\x95\x03\xc0\xa9|i\x9ezw\xf7\xa4\xc6\xc5\xa9\xe1\x15\x9c\xd7\xa79\x10\xcc\xb0G\xd3\xee\x15z\xf2Ir\xda\x038)\x0d\xd0\xcaC|\xa5!(\x96\xe9l\xa0p\xea\xcfN\xfc\xc1\xb1u\xce\xa3#'\xf93~\x1f\x8e\x15_\xa2p\x02!\xb6\xc4\x9f\x84\x0d\xd9\x00=\xd7X?\xb6\xa2k\xd6\x8c\xfaT\xe5\xb8\xdb\x0c\x86\xa0\x9el\xed\xd1@\xc3{\xabA\x1b\x01\x03\xb4\x11\xbd\x8c^\xa4\x01\x1a\x1bK\xf4\x93Q\xca\xa7R\xaaN\xa2\x03\xe0\xfdO\x87=:|\x9c\xc9kG\x01\xc4\xa09\x1a|\x9fq\xe2m\xbd\xa2\x14X\xfa\xe5\xfe\x1d\xe0j\x9d\x00\xc9\xb7s|e[\xdd\x84;<\x0bJ\xa1)\x86\xc1\x9c\x19<\xfa\x1d\x1b}\x0f#\x90\xe5\x0b\x19\xdc$\x846\x82!1[\x82S@\xb7\x90\x8ag\x94\xd2\xe8 =iU\xecq\xbf\xe7{O\xe2r\x7f\xae\x10\xd2\xd8\x1dW\xc0\x0c\xcc\x18:\x93\xc2nOW\x1ea}\x9e\xf2Y\xf7\xa0\x06\xb0\xf2\x1c\x9d\xb4\xa3\xaa\xf8B]\xe5\x08\x12\xe8\x8d\xf0\x22\x16\x06\x8c\x80\x11>V\x07\x22\x00\x9c\xc6\x1c\xe1\xfez\x88\xa1\x99\x09#\xe3\xbc\xdbg\x00\x94\xed\xd6\xb4\x08\x88\x01\xe8\xe0\xf3\xaf]j\x09\x90\x0a\x8ci|\xf2!g\x11 \x1a\x0c\xc8J\x13`\xff\xe4\xb5^\xa8\x82\x11\xcf\x04\x91\xc4\x7f\x0c`\xe5\xd1\xfc\xf7\xc5\xa9}Z$\xe4\x86\x10*\x19\xed\x1d\xffx}\xd4'\xbd\x1fW\x03\xab\xb2\x84A\xc3\xca\xd5\xb8g\xd9\x0b\xca\x88<\x1b\x91\xcd:\xca\xe3)!\xc9Lg\xa7\x9a\xf4\xae^\xaf8\x05f\x1a\x91\xa8r\x0a\x94\x96p\xd5T@\xc2\xdf\xabf\xb4:\xbd\xae\x1e#\x7f\xf0T\xc5~\xd6o&\xf0\xdb@}\xe0y\x91ON\xc8:n\xc8s\x94\xd7D\x80\xfb\xf9\x98\x18\x1b\xa6\x02\x09\x99eo\xf7:\x08\x1f\x83\xd6nr\xe9\x95E&\x09fX.s\x00\xcb\xc6\xe3\x14P9A'N\x13\xbd\xbd\xca\xa1Q\x5c\x08\xc6\xd6(\x5c\x07O\x87\xb4\xf2\x92g\xea\x19\x18\xae\xf8,\x93\x13tW\xad\xa0\xdf\xcfx\xa1\xda1\xdd\xb5\xad\x7f\x95<\x07\x84;\x90\x01\x0f\xdb\xd9l9b\x19I\x88\xec\xc4b\xc1\xe1\x1a\x165\xb5\x87\x92a\x95\xd0\xf1>\xa4\xc4\xa0\x92\x95]\xb9N\xd0\xcc\x87\x88j\xc7Yd\xe6l\xca\xb2\xf2\x9bw\xd9t\xe0 \x83\xbd\xc9\xea\xa4b1\xb5\x86$\xc5\xb2!\x19r?O\xbd\xa8\xbd5\xac\x8bE<\xba\x85O8\x14\xce\x0ec\x03\x1e\x19\xcc\x80\xaa\xbc\xed\x14\xc9\x05\x9c@\x9c\xf5\xbd\xf4c\xd6F\xc1(\xdc#\x0c\xe0\xd5\xe1\xe5\x0e\xfd\xca%\xf1\xbe)\x82\x9e\x9e#h\xfe\x8b6=:\x9bh\xf1B\x8c\xcfL\x90G\xc5\xe4\x1d\x94_OM\x12\x1d\x05\x0f\x1dG\x95d|\x07G\xfa\xfb\xc2\x80D\x82uc\x1d\x07\x83\x12\xe9\xd7\x17\x1bn<\xe2\x9a`5H@\x11 \x83\xb8\xf1\xab\x04\x1eF`\x8e\x17.R,(\x1aE<\xaba\x7f\x09'\xb5$0#M\x9a\xb74G;6YT=\x1a\x15\x0c\xa7gJ\xf7\x8a\xbe\x06\xd2\xff*\xe4E\xbe\xe8\xf4\xc4\xa3\x04\xcf\xc6S\x12\xc3]\xb3\x1d\x019\xb0\x01\xc7R\xb9\xd0\xe4\x9a\xaa\x5c9\xbc\x8a\x045\xfd\xf87\x0bc\x8a\xa2\x22]=\x22Jq\x08\x14GX\xc4\x8b\xf1\xde\xa4/\xbfq\xe8\xcf\xb39\xd9\x0b\xa6>f\xd3\xe6\xb5&U\x8d\x82q\xb6\x1c\x0e\xa3\x10XX\x1bt\x8et\xec{B\x9f\xb8\xd7\x17B\xf2\x13W]\x8f\xb6\x86\x80\xa7\x8e\x0df\xc0\x8e\x9fO\xc6\xe7L\xbd'W\xeeW\x02?\x99,\xb3\xe0\xfd\x82\x01\x1e++\xe4{0\xe20\xd3C\x9cL\xf36\x87:\xe4d\x95\x930\x83\x90\xe2\xfa\xb7\xc4\xa1\x8fW\x9a2\xfeK\xa5\x91jV,K\xa6\xaer\xa6p\x95\xb3\x02\x89\x8c\x03cJ,\xef\xd7n\x8f_d\x1d\xb7\x0dh\x00\x10\x22\x97\xd2O\x8f\x9e\x8a\xbd9\xa3>=\xf22\xc5\xf53\xacP\xa0\x1f@B\x98\x8a\xc8\x1a.Xi\x87\xb6\xecV\xb5\xfe\x9f\xa4K\x97\xfeV\x06\xe0o\xc1w\x17\xbd\xe6\xd0\x1c&\xcc\xb3\x1f4\xd4\x03\xa8\x88?R\xe6\xb5L\xafo\x1f\x7f_\xff\xd4M\xfen\xd7\xb7\xb1\x8b\x07\x8e\x87\x9b1\xc2\x1f\x14N\xf3/\xac\xc6(\x8d7\x9c\xff\xf8\x03\xe9QV?\xef\xcbf#{\x82`\x92\xa3\x167\xd8=\xef7;\x5c\xaf\x85\xac\xfb==\xae$\x82\xc9\xa4\xca#$&\xc2\x05\x86\xee\xdeG\xf4\x0bS\xe1e\x0b\x0c\x15v8\xc5\xa2\xcb\x1bZ8T\xe8\x03\xd8o\xcf\x8f\xb1\x8b\xefl(\xdd\xcc\xba\xad\x19\x0a\x16Z\xfa\xd2\xdc\x9e\xc6W\x9f\xed\xae\xb6\xac\xa0W\xf4\x8c\xc4P\xf48\x95\xf2\xe8\x83\x8d\xaa\x1f\x00\xdf(\x9c\xc3l6]\x80\x0e>\xfe\xc1i\xf4\xeaJ\x83N\xbeb\xa9\x90'\xe3\x0f\x05\xe4$\xce?\x11m\xc4g{K\xcem\xd9[\xfc\x1e+\xbfn\xc8h\x14'\xc1\x97M\x1f5v\xd5\xcd\x9d\x9e\x19\xe174\xbf\xd9\xec?\xe4\xd1\x0f\x874H\xcb\xa8\xe7yxl$\x95\xcd\x16\x00\x9bD\xa3\x98\xcdk\x84\xea\xe8\x12\x89\xb5V4(#\xd0\xd9\x11NR\xd8\x80\x96\xd6hG\xd3\xce2\xc0\x87\x05\xc1gX\xd7E)5\xa4nZ\xb9<9i\xd1\x13\xe9\x0a(\xb1\xea\x13\xaf\x0f\x0e\x17\xc4\x93\x0a\xa73\x81\xcfrj\xd8\xe7+\x8fZo\xfb\xb5^\x03\x02L\xbe\xa2\xba\x10p\xfe\xb5\x7f\xb5\xbf\x14P\xba\xe1jPz\xc8\x9cXC\xecy\xf0\xca\x94;\x93c\xc7U\xa7*\x19I\x0a\xdf\x10\x0c\x9e2\xda\xdb\xbe\xe2\x00t(\xa50 \x1f0\xc0\xd1\x06\x80XY\x91\xe26+\x968\xc3\xebn\xc5\x89\x0f\x04\x9do8\xa9\xd7\xb0\xfbI\x96Y\xc3Jr\xb7&\xa2\xb9D\xc8\xb0#\xdcu\x22.\xe3)\xdbv\xe5#\xb8l\xde\xc8\xe7!\x8e\x95\xb5=+\xeb\x8a\xa2$\x99E\xe7\xf9\xef\x80\xf5\xbf\xb8\x12L\xbei\x06\x08!\xf0\xaf\x0d\x98\xa7\xc6`\x83\xfeW\x07\xb4\xf8\xb0\x16K\x8b\x11\x18m\xb9Zl-\xbdZ@01\x8f\xc9(\xc8G\xa9\x81\xe63\xff\xdb\xb1\xca\xbf\x98y|\xd8\xf0]\xd6\xbb\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0fv\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04+IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xc8\x18\x05`\x10\x86\xa2\x11\xba\x09\xba\xb8\x07\x0c\xcc\xdb\xb2\xd0Q G@V\xc6\xa3\xa2\xbe\xa5\xd4\x8a\x11\xa9\x14#%p\xf3P\x98&\xb8]\x89o\xb7't\x1e\xca\xb0\x8e\xf8\xea\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1a\x1b\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0d\x90iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a Oliver Twardowski\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x08\x8b\xe74\x00\x00\x0c!IDATx\xdal\x8bA\x0a@\x11\x18\x84\xc7\xa3\x94\x5c\xe4Y\xb9\xb7{(\x1bWp\x02%\xc9\x06\xfd/\xca\xee\xcdj\xfaf>FD\xf8\xcbsK)\x85j\xad\xe4\xbd?Ov\x8d\xd6\x1a\x09!0\xe7\x84\xd6\x9a\x89\x0ds\xce\x14B\x00\xe7\x1ck-\xbc\xc6\xacc\xa4\x94hC\xa5\x14z\xefg<\x86s\x0e\xd6ZH)1\xc6@\x8c\x11\x9f\x00b$\xe8*\x90\x8b@\x18]\x22\x95\x95\x95\x95\x01\xe4* \xd8\x04w\xee\xd6\xad[\xc1\x96\x83\xc0\xbf\x7f\xff\x18V\xacZ\xc9\xc8r\xf3\xd6\xed\x9fZZZ\x0c\x9c\x1c\x9c\x0c s~\xfc\xf8\xce\xc0%(6\x81\x99\x99\x83\xb3\xf2\xf6\x8d\x1b\xac\x5c\xdc\x5c\x0co\xde\xbce\xd8\xbd{7\xc3\xf4\xb9\x0b\x8e\x03\x04\x10\x86\xab\xde\xbf\x7f\xff\x9f\x91\x91\x11\xcc~\xfc\xf81\x83\xae\xae.#V\xd7\xc2\x1c\x07r\x18\xc8\x1d G*))1@}\xb0\x09\xa6\x00\xecl\x98\x89\x99\xb9\x85\x96\xb22R\x0c\xcc@M\x8c@\xf8\x1f\x08\xff\x02\xc3\xfa\xd7\xef\xdf\x0a\x0d5U\x0cp\xdf5w\xf6>\x88\x8f\x0c\x95\x07[\xc9\xc4\x04\xf6>\x88\x06\xf9\x14\x149w\xbe\x070(\xb1\xafch\xec\x9a\xc0\x0d\xb6AGSM\xfc\xea\xb5k\x0c\x0d\xf5u\x0c\xdf\x7f\xfc`\xf8\xf5\xf37P\xe1o\x06V\xa0Fv66\x86\xae\xde^\x86\xab\x7f\xaf1\xdc\xbdw\xa7\x0d\xc5\xd3'\x8f\x1f\xff\xcf\x02\x0aG\xa8\x13\x19\xa0rO\x9e>\xfd\xe5\x1f\x18\xc8\x0eb\x03\x04\x10\xce\xb8\xc3\x05PB\xe9\xe5\xcb\x97\xffA\xc1\x0aJu z\xce\x9c9\xff\xf1j\x80y\x1a\x04@A\xeb\xea\xea\xca\xb0p\xe1\xc2\xb7\xc8\xf2,\xc8\x1c\x90\x22XZ\x00i\x14\x16\x16f\xe0\xe0\x00\xa6\x01\x5c\x1a@\xf1\x01R\x08\xb3\x05\x1b\x80G\x1c#\x0b\x87oCC=\x03;\x07\x1b\x033\x13\x0b\x03(0@\xa9\xfa\xec\xd9sL\x11\x11\x11\xd0@\xfb\x0f\x898#\x1b\x87\x9b\x0deEj\xa0\x08\x03k\x86\x06+H\x0e\xa6\xb1\xa0\xbaQ\xf2\xfe\x95s/\xc0\x1a^\xbdz\xf5\xff\x070\xc2\x80v\x01c\x99\x19\x96\xd0\xc1\xb1\x0c\xc2\xbf\xfe~f\x10\x17\x91c\xe0\xe5\xe5e\x04;\xf6\xeb\xd7\xaf\xe0\x9c\xf6\xef\xdf_\xb0\x89\xff\x18@\x18b\x03(y\x00\xf3#0\x89\xbfA\xf8\xe1\xc6\xcd\x9b\x0c GTWW3\xfc\xf8\x09J\x1a\x7f@y\x86\x81\x03\x98,\xd888\x18Z[[\xc1\x8a\x15\x15\x15!~\xe8\xea\xea\x02G\x90\x8e\x8e\x0e8\x0b\x83\x82\x16\xe4\x0f\x90\xdbO\x9d:\x05wbYY\x19#\xd8\xf4\xfa\xf5k_\x90\x05\xb0x\x87\x19\x0e\x8b\x7f\x98\xa5\xdb\xb6m\xfb\x0dL<;\x80\x5c?\x82\x99\x01\x09l\x86\x19\x08K\xbe0\x0c\x8b$\x18\x1f\xc8\xfeCT\xf6\xc4\x9b\x8f\x11\x86A\xc2\x96\x85\x05\x9e\xce\xf0\xc5#F\x10\xf1\x89\xcb>\xb9r\xfa\xa84(\xe1\xfe\xfd\xf3\x97\x81\x81\x113\xab\xc3Jr\xf4\x8c\xba}\xf7\x9e\xbf\x05\xa5\x95)\x7f>\xbf[\xc0\xcf\xcf\xcf\xf0\xf1\xe3G\x84\x05n\xfe!&+\x17\xcc>}\xe3\xc6\x0d\x14\xcd\xb0\xa0\x00\x07\x17\x13#\xbc\xb4\xfd\xff\xef?V\x1f\x80\x0c\x9e\xb9p\xd9\xf3\xa9\xd3fI\xff\xfb\xfa\xe6?<\xf6{\xda\x9a\x0e\x01S\x048w\xa0\x1a\xce\x08,\x0d\x98\x19\x98\xa0\xe1\x8e\x1c\xc9\xff\xfe\xfe\x03f\xd1?\x0c\xff\x90\x22\x1d\x04\x1a+K$\x81b\x19@\xe6t\xb8\x05\xef\xdf\xbee\xfc\xf3\xf3\x07\x86\xb7A\x18V\xb0\xa1\x07\x118\x18\xff\xfeEIU0\xf0\xe6\xcdku\x94|p\x1d\x184\x1cl\xacp\x05\xb3f\xcdb055e`f\x05\xd6)\xa0\x88\x00\x05\x0f\xdc\x8c\xff\xe0\x0a\xe0?(~\x80Au\xfb\xee\x1d\x86W\xcf^0dde\xc2\xf5_\xbct\x19\xe2H \xe6\x00b6&V\xce@\xa0k}A%1(>gL\x9d\xe4\xe9\xef\xef\xc7\x0ar\xf9M`\x9d\x04\x8b\x07t\x97\x82\xf8\xf2\xc0*\xd3\xd5\xcb\xe7\xc8\x8dk7\xde\x81J=\xa0\xb1_\xff\xfd\xfeV\x0c\x94\xfbJ\xf3\xa2\x02 \x80hn\x01\x13\x03\x8d\x01\xceBj\xc5\x8a\x15\x1f\xdc\xdc\xdc\xf8\x91\xcb!\xa46\x0e\x03\xa8A\x07,\x0c\x8b\xf3\xf3\xf3\xfb\xc8\xf6\x01rY\x04K\x9a >\xa8\xfe\x05\xd6W\x0c!!!\xbd\x13&L(\xa3\xc8\x02\xf4\xfa\x1a&fkk\x0b\xae\xe7\x80\x19\xb3\x93\x9c \xda\x04L\x9e\x5c0\x1f\xc0\x0c\x05\xb5\xe2`I\x13V'\x82|\x03m\xd1\xf9\x91\xe4\x03`p\xfcA.\x87p\xf9\x0c\xd8T\x01\x17\xed\x14\xa5\x22\x98%\xe8\xcd\x1aB\xcd\x1c\xa2#\x19V\x1e\xe1\xb2\x88,\x0b`\x05\x18\x0c#\x17\x13\xb0 \x22T\xd9`\xb4\xb2\xc1l.a\xc6\xf9\xb3'\x8bo\xda\xb0\x99\xe9\xfe\xa3'H\xf5\x01\xa2,\x02A\x90\xe1\x90\xe2\xfa/\xc3\xa3\xa7\xcf\xac\xe7\xce\x9au\xfd\xff\x9f\x1fG\x90\x1d\x08/*`\xb5\x0f\x0b\xafPL_g\xeb\x82\x00\x1fofP\xab\x01W\xe3\x15\x5c!\xfdg\x80\xd7v\xa0:\x03\xd4,\xd504\x7f\xfa\xe9\xe5c\x19\x0c\x1f\x04\x06\x060\xc8k\x1b\x8a\xdd\xb8tn\xe1\xb5\xcb\x97\x98.\x5c\xb8\x00om\x81\x1b\xba\xf0\xa4\x0a\xec\x0a0\xfcC\x04!\xc8\x96\xff\x0c\xf0\x16\xe6\xdd+\xe7\xa4M\x80-\xd1s\xe7\xafh\x80j3\xb8\x05\xa1!\xa1\x0c<\xa2R\x17~|\xfd\xc2\x04M\xd7X\x1b\xc5\xc8\xcdV\x06P\x9f\x03-\x0e\xee\xdc\xb9\xc3\xb0{\xf3z\xb5\x88\xc4tS \xf7\x14\xdc\x82\xe5\x1b\xb7\x0bO\xef\xeb\x90\x04\x951|||Xk4t\x0b 5\x1a\xc8\x82\x7f\x0c[\xef\xe8\x80\xc5\xbd\x94/\x83\x1bd\x96\xe6\xa6+A\xf9\x10n\x81\x90\x90`6\xc8\x8bl\xc0\xd6+J\x98\x03#\x96\x89\x19\xb5M\x04\xaf.AA\x04j2\x03q\x80\xe6M\x94\xd4\xe7\xe1\xea$\x82\x12\x07\xc0F\xab&(\x92q\xb5\x85\xc0u2\x13\xc42P\x98\x83*yX}\x0cNMhM\x18\x0eV6\x16\x14\x0b\xde\xbf{\xc7\x04\x0a\x1el\x19\x0dn\x01\xb4U\x01s%\xb2\x05\xe8y\xe1\xed\xbb\x8f\xffP,x\xf1\xea%\xc3\xeb\x97\xaf\x81\x958\xa8S\x021\xa4\xa6\xae\x86\xe17\xc8\x10`\xd0\x81\x1a`\x7f\xffA\x0c\x84t\x9f@]X`\xf7\x03\xd8\xb9\x017\x0a\x80)\xae\xa5\xb9\x19\xdeF\xfb\xf4\xe5\x1bj2}\xfe\xec\xf9\xdf\xe7/\x9f\xc3\x05g\xce\x9c\xc9 ##\xcd\xa0\xa8\xac\x08L\x98\xb0\xc8\x85\xb4, =#\x06p\xe4\x82\xc4@\x96\x1e;v\x8c\xe1\xe5\x8b\x17p\xfd\xef>}A\xb5\xe0\xd9\xf3\x17\x1f\x81\x1d#\xb8``` \xb8\x15\xa7\xaf\xa7\x0f\x0e\x22`\xd3\x82\xe1?\xa8\x01\x06\xce\x07\xff\xa1\xfd\xe8\xbf\xf0\xb6\x91\x88\x88\x08\x03\xb2\xfe\xe7\xaf\x10\x9dG\x90\xd3\xb8\x80\x98\x93\x99\x8d\xab\x0bH\x0b\x80,\xb5\xb3\xb7\x93]<\x7f\x8e!'''\xd8\xf5\xa0f\x0bz\x91\x0d\x8b\x0b5MM\x86\x8d\x1b7\xfeN\xcd\xc8\xd9\x0e\xeeH\x01\xed\x06\x9az\xf4\xef\xafos\x80\xfc\xdfX[\x15\xabV\xac\xf8\xa6\xa4\xa8\xc8\x096\x14\xad\xab\x0d\xe6\xa3\xb5\xe4\xfe\x02}r\xfa\xec\xd9\x0a`G\xa4\x93\xee\xcd\x16\x80\x00\xd5Y]l\xdbT\x14>q\xe2\xfc\x96&\xa4\xab\xda\x15\x125C\xeb\x16\xd4\x07`aU\x10B\xea\x03\x8ci,\x0f0uT\x14\xf1\xd2\xbd\x80x\x00UE{\xe2\x05\x09\xc4\xf2\x02\x12\x12o\xa8Bh\x08\xd1\xc1\x84\xd4\x02BCle\x12\x0fL\xea\xd4\xf2\xb3\xad\x1b\x1d\xd5\x92%M\xdb8\xd9\x92\xd4\x8d/\xf78\xbe\xce\x8dcg\x0c\xd4V\xb3d\xd9\x8e\x1d\xfb\x9e\x9f\xfb\x9d\xef|w\xd3?\xb0m\xb4\xa8\xd5F)\x93|\xb7\xffE$\xc2\xee\x1d;u\x9a|\xab\xb4\xf5\xfbntttx[\x22\x80\x9copp\xd0\xcf\xa0\xd1\x8cs\x98\xbd\x17g\x8c$I0;;\xab2*j\x10E\xcb\xea8\xe5\x86\xc9ma\xbe<#\xe3\xb9\x0d_0\xf8j\x85\xac\x0d\xab\x19\x0a\x16\x94L\x11\xe4\x9e\x81@\xe0\xfd\x89\x89\x09\x85v;\xefnY\x0a\x19\x99\x9d\x19\xfbfB\x15\xdf42\xc3\xba\xba\xba \x91H\xd80\x22\x85B\x01VVV\x80r\x80\xd7\xe8\xed\xe3\x9b\x1d\x01\xe4\xb7\xc7\xe8\x80\x9c\x0d\xad\xb3\xc1\x08\xbeF\x19\x89 \x7f\x8d\xf7i\x04 \x14\x0a\x01\xc5Tt\xe61^\x15\xdd\xb2\x08\xf0t\x8c'\x99\xc6\xb9a\x9c\x17f\x91\xdb\xd2\x142\x0e\xd2\x18\x89V\xaaI\xab\xc9\xbe\xe9\x06\xd0\x81\xd9\x8c\x9e\xb6\x9a\x0b<\xd5o\xa0\x9c\x06\xa3\xf0\x9d[f@.\x97\x93)\x9e\xbb1\x87q\x10\xc6\xb9\xc0{\xde\xd8\xbb\xb0\x9a\xc0\xa8\x15nHc\xf1\x9d\xff\xbb\x0e\x98\x85T\x10=\x03\xd4W\x1f\xf6E\xfbw\x07\xda\xefks8]\xd5\xb66/\xadCNJ\x88\xed\x8d9n\xe2C\xfe\x89\xaa\xfe\xd1\xa6\x0a\x01\xa5\xca:\xb9}\xbb\x5ci\xf7y\x1c\x17\xe7\x7f\x97\x96\x16\xaf.\xd0\xaf\xbf\xa1\xc8\xa5\x9f[\xa5]C\x04b\xfb\xf6\xe9\xe7\x81\x07#\x89\xce\xe0\x8e\x8f\xcf\x9e\xfb\xb1\xab\x7f\xef^\x01\xe1\x0e\xd4%\x9b\xb2\x88\x15\x15_\xa2\xaaS\xa0\xc1\xe3\x7f\x98\x88D\xa7|j\x13f\xa3^qct:;;\x83\xa2S\x0c^\xba|\xf5l\xf4\xd1\xfd\x99T6\xfb\xea\xda\xd2\xb5\xaf\xf0\xd9\xc7b1\xeb\x14::4\x04\x82o\x87\xed\xc9'\x06>\x99\xfa\xf2\xf3\x17w\xf5\x86]W\xae,\xc0\xaf\x17.\xc0\xadb\xb1\x0e\xba\x8a\x9e\xb8M8\xcc\xe7:\xdeE\x96Njz[CJib\x9c\xaa\x5c\xe2-\x85u}\xf4\x85\xd7\x16\x16\xc0\xe3\xf3a\xb1\x13\xceL\x9d\xee\xfe\xeb\xfa\xf5\x93O=}\xe8\x8b\x99\xf3\xbf\xbc\xf2\xde\xdb\xe3\xc42\x85\x9e}a\xd8\x1e\xea\xd9\xf9\xd9\xf17_\x7f\xde\xe3v\x8bX\xf6\x91\xbb\x08\x9a\x87\xf9g\x05\xcd\x00#\x225Ld\x1b\x13V5w\x13\xa2\x1d\x88a\xafu\x00\x99\xf29\xf8Sz\x0b\xfa\x03\x1f\x81_\xec\xa7\x0d\x82\xa8\xb6\x1f~\x7f;\x94\xca\x15\xf9\x9d\xe4\x07\x937\xd2\xe9\x91o'OVM#\xd0\x1b\x0e\x1d:r\xf8\xb9\x03\x1d\xc1\xa0X*\x95\xd4\x96\xfc\xdf\xc0\xa8\xd9\xde\x80\xf3\x84\xe8Ak\x1a\xbcR\xeb0\xf0\xdcY~\x84\x1a\x80\xaag\x8e\xb6\xa2A\xda\x91\xd7\xdb\xd0\x8e\xe0\xfdb\xe2\xe0\x81g\xa6\xbe\xff!N/gL\x0d\xf0z\xbd\xc3\x91]a/.\xe3\xdc\x09\x9f\xf5\xc1\xe29W]\x9b\x8c\xd0\x92\xc78p\xd6-\xe2:\x0c\x8b\x8a\xd7\x11\x82\xc3}\xbf\xe9iZ\x85:R\xe1\x98\xfa\x1e\xea\xf5\x9e\xf1\xb8\x8eX\x1aP\xc8KB~u\x8d\xac\xbb]wU\xc8\x10\xc2\x99\xfa\xc2_\xf3Q`\xdeV\x88\xc2\x19\xa2\xa8\xdd\x10\xd37\xee\xe4\xb4\xd5\xd5<\x91\xa4\x82\xf5$\xcef\xb3\x90]\xce\x12oMv3\xdd\x10\xfbQxqPfI\x1bxp\xb8D\x10\x1dNUu\xa0\xc8\x01lI\x9a\xad\xfe\xb2\xb5Y\xdc\x11a\xd8\xea\x1d\xae\x1c \xfe+\xf47Y\xae@e\x9d\xde\xa3\xe7\xf8\xbb\xa5\x01\xf9\x22a\x0bx\xa6\x06\xdc\xa0\xadw\x8e2\xc3\xa2\xc3\xbc\xbe\x9d8\x91\xacy\xb0\xaa\xa8G\x94?\x00{~R\x1b z\xb8\x8a\xb1\xdf \xaa\x8a\xc4\xbc\xca\x16\x03\x98N\x86\x08`\xb7i\xaa\x07FL\xed\xba\x05phdol|\xbc\x01\xdd\xf03\x18\xc8\xb5b\x09\xd27\xd3\xd6\x06\xa4Si\xf9\xef\xc5%\xf0\xf9\xdc \x10\x1d\xf8\xf4^xd\xe4%8uj\x12\xa2\xd1\x87\xe9\x1e\xd5=\x5c\xe4e\x19\x04\x1f\xb05\x17\x1d[\x1d\xfc\xeb\xe9B\xa0\xcd\x1fPE\x8f\xb9\xb99\x98\x9f\x9b\x87\xa1\xa1\xa3\x90I\xa7T\x80\xad=lC0\x06\x85\xbekye\x0dn\xa6Z\x18\x90JgNO~\xfd\xcd\xc1\xee\xee\x0e\xd9\xe5t\x9bR\xed\xc7\x07\xe2\xee\xfd\xb1\x98}\xcf\x9e>XZ\x5cT-\x13\xc2aK%\x18\xc7\x81\x91ah\xb4\xa1\x19\x84Z\x13\x93\x80\x1e\xa0\xff\xef\xd9\xd9\x03^\x8fO\xfei\xe6\xfc\xba\xd9w76d\x92\x97nU\xd3\x99\xe5\xd9\x86\xefh\xcb5\x82f\x8c\xa8\x1d\x9d\xdc9;\xda\x93\xc9\xe4\xa7\xf1x|w$\x12\x11\xd9R\xb6\x91\x16\x1b\xa9\x89\x95\xbek\xa6\xf7\xd29\xa8LOO\xff166\xf6\xb2\xc6\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10`IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cbi\x8b} :@\xf8\xd9\xbb\xff\xec :))\xe9?\xd8^\x98\x042\x06I\x02\x04\x10#^W\xa5O\x7fo|\xf0:\x83\xf1\xd2\xa5K\x0f\xc3$\x98\xa5\xa5\xa5\xff'\x87Z\xcf*\x9fz\x8aAA\xc7e\xd7\xed\x93+oo\xdd\xba\xd5\x8a\x85\x93\x93\xcb\xb8v\x15\x03c^\xa4\x19\xc3\xbb\xabKO\x1d\xfc\xbf\x82\x81A\x86\xc1\x1b\xd9R&ss\xf3Xcc\xe3[ >\x86\xe5&&&\xd9@\xaa\x06 \x80P$\xe6\xcd\x9b\xf7\x9f\x95\x95\x95\xe1\xcb\x97/\x0c?~\xfd\xd7,\xcc\xcf\xba\x81\xeeZ\xb0\x86)S\xa6\xfcy\x22\x90`\xae\xaf\xc4\xc3\xa0/\xcf\xc0\xa0(\xca\xc0\xb0\xfd\xf8c\x86\xff\x1f\xafO\x0e\xf6s\xb3B\xd6\x00\x0eBNNN\xe6\x9a\x10\x1e\x86'/?2\xa4L|\xcf\xe0a\xa9\xc0\xa0\x22.\xcb \xc4\xf7b\x11##\xa3\xf2\xf6{K\xee\xa0\xd8\x00\x023f\xce\xbe\xc6\xcd\xc3\xffA\xc5$(\x9f\xf5\xeb5\xe3\x87\xf7oO?q\xee\x02\xc3\xa53\xa7\xfe\xbe|\xf92F__?\x7f\xd1\xa2EAX\x83\x1c\x19\x03C\xc1\x15\x14\x05@\xfa2J\x1c\xe1\x88\x0d! V\x01b[\x05\x05\x85\x1e -\x06\x10@8\xe3\x0e\x17`\x811\xfa\xa7\xccQ\x17\xe6c\xbb\xf1\xe7\xcf\x1f\x86O\x9f>1\x14\x14\x140b\xd3\x00\xb6a\xc6\xccY\x87\xdeqZ\xf5\x7fcUx\xa4\xa7\xc8\xc3\xa0'\xc7\xc0p\xe5\xd8\x9a3!!!\x8cXm\xe0\x17\x14g\xf9\xce\xad\xfe\xc8V\x99\x95AM\x92\x81A\x80\x8b\x81!a\x9f\xa4\xc9\x97/\x0b\xfe$$$\xb0`$\xb3\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\x0d\xf2\xcc\x17\x8d\xb5e\x18\x18|*O0\x1c\xb8+\xc2\xe0c\xc8\xc8`\xac\x004\xe8\xf7o\x86\xedWV>\xf8\xfa\xff=\xc3/\xde\x0f\xa0\xa4\xf3\x12l\xdd\xb7O\xaf\xa7;\xd5\xbd4\xb1\xb7\xb2`P\x00&\x0bM)\x06\x06\xee?\xf7\xe4\xae}\xffn\xb4n\xf3\xda\xad\x8c2\xdf$A\xea\xee-\xfb\xb1\x1c\xac\xc1\xd3\xd3\x93\xf1\xe9\xd39\xff\x85\xb8\xf42\xb5\x95\x8c\xce>\xbeq`\xe2\xd3O\x9f,_\xbc}\xcf0\xbbu\x15kpp\xf0\xe6\x8f\x1f?\xde\xdd\xbd{w\x0eJ<\x5cx\xc8\xc0\xaa-\xcd\xc0\xce\xca\xc2\xf0\x05\xc4\xf7\xf7\xf3\x03&tF\x86\xa7O\x9f\xf2\x9f9s\xe6\x13\xdc\xd30` \xcf\xf0\x1b\xa6\x18\x046n\xda\xc4\x08L\x06\x0c\xd2R\xd2\x1fQB\x09\x1f\x00i\xfa\xcf\x08\x0c5\x7f\xbf\xff(1\x0d\x0c2t\xb5\x9c\xd0\xb4\xc4\x0fr\x1d\x10\xbf\x02\x09\x02\x04\x10\xce\xb4dW\xf9\x98\x91\x9d\x85\x91\x81\x87\x8b\x85ML\x80\xfd\xaf\xbc\x04\xe7\xbf\xaa@\x8e\x7f\x0c$\x02\x0c\x0b\xe6\xcc\x9dwQP\x80_\x8f\x99\x8d\xf7\xf8\x8b\xef\x82\x8b\xee\x7fW9+&\xc8\xca \xcdt\xcd\xf8\xdf\xe7\x07q\xdf\xbe~\xb2\xfc\xf7\xef\xdf\xa5\xb4\xb44}\x92,\xe8\xea\x9d`$-!z\xf6\xc9?\xfd\xa0\x97\x7f\xe5\x1f\x09\xf3\xb12H\x0a\xb13H\x080\x021\x03\x83\x08\x0f\xd0\xff@\xcc\xc3\xc1\xc0\xb0\xf3\xc85\xb9\x7f\x9f\x1f\xae{\xf5\xea\x95a||\xfc\x05\xa2r\x0f\x1f7\xc7\xd9\x1f\xecJ\x99\xfc\x02\x9a\x8f4\x04\x99\x19\xc4\xf9\x18\x18\xc4\x81\xa1\xc9\x07L\xe7B\xdc\x0c\x0c\x1f\xbf\xfef\xc8\xee\xbb\xc8\xf0\xe0\xa3\x10\x83\x9a\xaa\xd6#s\xc1o\x99\x8c\x7f\x9e\x9d\x079\x12\x1a'@U\x0c\x12@\xcc\x0e\xc4o\x81\xf85\xd0\xf1\xff\xe0\x16\xb0\xb0\xb00\xa8i\x9b\x9c\x15\x13`\x06\xbb\x16\x94\x81\xbe\xfc\xf8\xc3\xb0x\xebU\x86\x85\x07~0(k\x9a3\xa8\xa8\x980\xe8\x02\xc5\xf9\x81F\x89\xf2\xe8\x9f}\xfd\xfe\x02\xd8``Q\xfd\xea\xc0\xd3u\x5c\xc8.\xff\xf7\x83\x91\xa1\xb8\xb8\xb8\x1a\xd9\x82\xbf\xdf\x1f\xef\x9f\xa8\xac\xe6\x9a\xdb\xb3\xea>\xc3\x8e\x93\xaf\x18$U,\x80%\xa6>C\x10\xb0>\x11\x01\xfaH\x98\x1b\xe2+PP\xdd<\xb3}\xe2\x8f\x1f?\xfe\x82\xb2\xdey\xceml\x17\xce20ln<\x0e\xb7\xc0\xb7\xde\x92\xe1\xcd\x1bf_\x94H^\xb4x\xe9q.Nv\x8b\xc7\x7fu\x83~p\xa8?\x12\x00\x1a(\xc2\x0b4\x18h (\x1e\x04\x81n\xfc\xfb\xfd\x85\xc3\xe9\x93\xc7{\x1e=y\xc2`fb\xf2\xd3\xca\xca\x8a\xcb\xde\xde\xbeA\xc6\x9f\xad\xe4\x1f\xc7/N\x98Yl'\x15N-\x5c\xb80\x00k2\xed\x9f\xbaH\x97\xf1\xf7\xbbK\xec\xec\xec\x0c \x0c\x0a>\x10\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab6\xa6\xad*\x0c\xbf\xdcK\xbf\xeem\x07\xed\xed7\xeb\xed\x82\xdbb\x04\x87s\x8e!?V5T\x9d\x08\xd1\xc4\xed\xc7bBp\x89\x05\xdd\x12\xc7\x92\xc5\xe9~\x18e\x12\xc3\x22.\xeb\x9f\x85\xc5\xa8\xc9\x12\x11\x13\xc7&\x8bs]R0E7\x83\x8aF\x8c\x84\x0czA>C\xcb(-\xdc\xd2\x16\xdf\xb7\xed\xc86\xc9>\x0c;\xc9InNr\xdf\xe7=\xefy\xcf\xf3<\xe7\x81\x03\xdc\x91N\x9f\x7foL\xf1j\xcb\xb4fMw@,\xbaw\xc3\xc56N\xa3\xdcM\x94\x8c\xca\x96\x16+Y\x96A\x8e/\xb5\x1fj8\xb8\xe7\x7f\x03||\xb2\xd5n\x15\xf8\x7ft:\x1dD\x92\xeb\xbc\xc3\xf3\xf6\xce\xa4\xca2\xb3\x9e\x0f\x09\x9a\xc5\xbf*\x17\xe7&\xf7\x13\x18\xde\x8b\x82\xda\xda\xda\xb1\xfb\x02hiiQ\x9aL&Y\xa5\xb3\x9e\xed\xb8V\xd2h\xd1\xab\x80\x18\xd5jP\x83]\x9faT\xa2\x88?~\xbapt~>\xf2Rx>\xa9\xae\xdb\xb7W\xbeg/\x82&,\xa6\xe2\xf4?_\x1e\x7f\xbcQ4+\xc1*\xa8\xc1\x94\x97\x0b\x0e\xd4(\x13\xf2\x90\x9e\xcfp\x91\xc9\xbd\xab1\xf0CW\x01\xc3LGo\xfe\xff\x8e;hj\xfe\xc4a3\xe5K\xbf&\xaaK\xcd\x06.e\xccS\x80C`\xc1\xa4\xcb\x90\x1c\x05'v\x0d\xcf'\xe0\xb5c=\x10\xc9\xdd\xc0x\xb6\x5c\xb9:\x13\x9e3\xd6\xbd\xbeo\xe6\xae;P2\xa9\x93\xc47\x0eA\x9b\xb2\x90zaI(k!+2j\x05\xc0\x99\xef\x87\xe0\xd3\x8b\xd3\xc0\x1a\xcb\xe1117\x95L\xf6@\x22\xbep\x0a\x7f\x7f\x05\x9b\x81\xba\x11-\x09\x088\xa9l\x13\x98xt\x05\x80e\x99\xa7\x91\xc4`\xa3M\x99\xae5eN\x81\xf3\xb0A\xa7\xae/\xc1\xf1\xb6!\xf0\xf7\x85`\xeb\xb62\xb0\xe5g\xd6S\xd7S\x80\xd2\xe9&\xbf\xd4\xd4\xd4t\xb6\xf4\x85G+5\xe6\x1cH,\x02\x8c\xf4\xcc\xc6p\xfd\x11\x04\x09\xa6\x01(8\x0d\xd1\x88\x96r]&\x80\x0eg\xffp\x04\x1aN\x5c\x81\x84\xa1\x1c\xcavl\x06+jA\x1e\x97\xa1\xf0et&\x08\xb0\xdc\xd0\xd0px\xc7\x9e\x87+\x19u\x1c\xe2d\x09\x94\x00\x05Oi8\x14\xa1~\x041gw\x90\xebG\x90\xea\xf5\x86\x8c<\xe6\xb2\x00\x1f|\xf6'\xf8\xfa\x95`\x14+\xa0\xd8\x81M\xa0\x84\x95\xb2Q\x12CC\xcbtG.\x15\x17\x17\x1f\x92s\xa2\x99\xc2\xdc4\x0c%\x0aR8k\x1a\x80\xd7j\x0f\xb0,[m\xe0\x92\xcc\xb5\xf1x\xeax\xdb \xf4OpPR\xf4P\xba\x5c\xe9\x0e\xd2f\x00(x\xbe&\xc9\x0c\xe0}@}\xf0\xf8\xfd\xfeN\x8b1Yv\xbb\x9a-'\x80([\xb5r\x0f\xda\xdb\xdb\x13\x82\xd1\xfc\xcb\xe13r\xbd\xb0\xe9Y\xd8\xe2\xc8(\x18\xa9\x9a9\x9b9u\x14\x01\xf6\x06.|9::R\xe8\xf1xX\xb7\xdb\xed-\xacQ\xbfy{\xf7,\x8fr\xe3\xadG\xbe\xda\xba\xc2E\x9cV\xaf\x9b\x9e\x9a\xd8~\xf0\xb9\xc4\xd1'\xd1\xe7S\xbd\x0b\xb0'\xc8\xae:\xf1l\x1c\xf8M%\xfc\xfb\xf7\xee\xf3\xe1ph\xe3\x13\xdbK\x99\xfa\xfa\xfa\x1a\x9f\xcfwL\xf6\x09W#\x91\x08\xdc\x98\xd1iya\xe0\xbb\xc9\xd3\xe4,n\xa1\x8af\xef\x17\xce|\x95\x01\x15\x19\xae\x95\xf8\x83\xafO\xdau\xf0\xfc\xc4}lq\xbftK_)Z\x95\x103a\xb7+\xee]N1\x91L&^\xac\xd9\x0f\x9f\xae\xb8\x17\xb4v&i\x7fT\xd1\x8bsrr\x16\xa4\xa5\xa5MT\x14\xc5I\xb8\x02\x81@\xd0\xe7\xf3\x9d\xe9\xe8\xe8\xa8[\xb3f\xcdz\xa3G4\xf0\x17\x02\x08>\x9f\xc0S\x86\xc9\xce\x1e\xdb+\x8b\x16\x0d0\xf9\xb7`\xb8\x05\x8dvcL\x19Yh\xd5gg\xa1\xfeX\x08\x12 \xc0\x80E\x84\xb4\xb1w\xc1\x0c\x97\xaeg)\x1eh\xa5l&ykVo*\xa27\xa7\xf1\xd9\xfc\xcdo\x942\x95\x85\xdb\xc6`m\xf1\xc1\x94)S\x0a\xd3gXY\xf6\x8a\x829\x132\xa8\x80\x1b\x95Psg\xce\x9e=\xbb\xaa\xa5\xa5e\x1fb\x5c\x8a\xb7\xba.\xef\x003\xeci\xbc\x91G\xd7i\xac7\xdd\x9d>\xae\xc7\x04K.C\xb3M~\xfdK\xf7\x00\xec=\xdc\x09\xbftF\xe1\x88\xe77PY783o\x82\xeb\xd3t\x85o\xc6\x80m\xd0\x8a\xd9\x8c\xdf`Uo\xfaE\x1d<\x99\xe7\xfb\x83\xdbFWWW\xef\x9cV0~&\x97\x81\xa9\x97\xde\xb1k9<\xba\xef\xcd\xc0N\x1f\x97[\x5c\xcdW\xef\x5c\xb9r\xe5\xdc+\x5c\x88{\x17W\xa0\x86\xdc(\xda\xdb^\xec\xce\x1f\xf71\x01\xd0\xfa\xc9\xde(\xf8B\xfd\xb0\xb5\xe1\x0c\xd4\x1f\xe9\x05^F\xd0\xee|\xc8\xc9\xcb\xd5:{\xe4b\xdaL\x0f\x22L\xb3/\x1b\xae\xc3sz\xec\x9c;\xf5s1\x817\xf6\x8b\xd7I\x83\x8c\x1f?~z\x5c\x0aA<\xa67c_\x5c\xb8\xfa\xaa\xf8_\xdb\xf8\x9c~!1@\xcf\xd0\xb3\x97\x05qO\x10,{\xeb6\xd4\x89\xa28\x8f\x0293\xd3\xfdR\x9f5g\xcf\xfa\xbd\xbf\xc2y\xbf\x00\x81H\x02xG\x0e\xb833\xb4\xaa\x84V\x87R'o\x04\xb3\x96\x89\x8c\x14j\x827\x89\xd0gg<\xdf\x14uy/Ti\x95I$\xb2\xb3\xb2\xb2r\x01\x1d\xaf\x94\x94\x94\xd4\xba\x0b\xd9Y\x8c\x92p\x10\x8eico\xbc*\x81S\x17N\xea\x9bg\x80\x0du\xeeK\x1c\xda\xbe}\xfb\xf2\xcbV\x00k\x80TYY\xd9\xfc-[\xb6\xbc\xaf\xaa\xea\x93\x1d\x1d\x17\xaa\xc2\xf1\xce\x19}\xc9\xb1\xb5\x98E\xfc\x93\x1c\xfa,\xd2\x0c\x9b\x19\x88\xdcdpp\x9b\xf7Mr\xf4\xb9\xaf\xe7\x5c\xd6\xd7?\x1e^\xc5\xb1\xccT\xca>\x9c\x95Kx<\x9e\xe3(\x8ce*\xd4\xa9\x0a\xb26\xe4\xdbm\xb3\xc2\xb7\xa6\xec\xfd\xf6o=__;m\xaaV5v\xc8y\xb4\xad\xad\xe5\x13z\xf6\x9a;\xf1{\x1flr\xdb\xad\x89\xefX\xc62N\xab\xcd\x19\xdb\x0f\x03\xdc\xa8]AG\x01\xee\xc4l\xd2\xcc\xfd\xa2\x01^\xdb\xc4\x0c\x12\xbcF\x10\xab[\xcf\xb1\xc2h$\xbc\xd4\xef\xf7_\x87\x13\x02\x81`\x08v\xd77@^\xeedx\xb8\xb4\x04z{z|8\x8b\xaf\x1d8p\xe0\xc3\xd6\xd6V\xa5\xa0\xa0\xa0\x22++\xeb\x0efR8w@\x8a\xcaI\xa1O\x0bg\xa6OP\xb9\x88\x18N\xb6\xc9?{\xbd\xde\xaf\x1a\x1b\x1b?\xc4\x8f;\xaeZ\xf6]\xf9Z\xbdn\xbf\xd3\x16?\xbb\x1a\x92\xf1'06X*\x03I\x0b\x99\xe5\xa0iD\xd2lB\xd0o\x92\x1e\x22\x8b\xc7\xe3\x09L\x81\x0d(Kn}\xa2b\x89\xebDS3|\xb1u\x07\xfc\xd4r\x1a\xac\x96$\x5c\xec\xea\xc6\xb2%B\x1d\xd6\x17\xa8>54\xbal\x98`\xc0\xa0\x8a0l\x98Jm\xf6\xff$\xa7w|\x0f\x96\xdf\x8eo\xce\x19\x88\x05\x8aS\x89\xfey,\xaaQ\x84\xebF\xe0\x19h=h^\xfc\xday$\xb8\x0bs\xf8\x97\xa5\xa5\xa5\x1d\x83\x9f/**~`\xce\x9c9k\x9b\x9aNd\xabj\xcc\x82\xe2\x90\x08\xe2\xca\x04!\xe0\xf7S\x5c\xd0Q\xe32\xac`\xeaGl=\x80~O\x81\xfa \x16\x87\xcf(.e\xaa\xe2P\x80\x17x\xe8C\x22\xc1?\x89\x84\xf0;K\x90\xc8\xb6\x11[\xd0 \x11j\x1fU \x91*$\xe22\x89\xfc\xb1\x22>?\xb9\x96v\x04\x8eD\xd6\x8d\xe8\x8a\x0c\xc9,\x16E\xe9#W\x9a\x02\x8a\xc3\x89D\x04\x8dH0\x18\x00\x9f/\x80\x9a)\xd2I\xb5\x14\x1d\xc9 \x99\xd0\x88-)\x91\xc8<$\xb2\xde\xe5RFa\x0c\x81\x19#\xc1\x00\x12\x09\x04R\xd1H\xa4\x89\x0e\x0e\x91D\xcd\x88\xae\x89\x91\xc8\xdd\xa2$\xaeE\xcf\xca\xfb\x83H\x9f\xe6Z]\x81\x80\xef\xae\xc6\xc6\xaf\x9aG|Q\x8f$('\xcf\x91D\xe9\x15%M\xc9V\x9c\xca\x9d\x1b7n\xf4\xfc\xab\x18\xf8\x87}\x96\x7fr`\xc3\x1a\xca\xd7:h4\xcd\xbcG\x7f&\x1a=\x1a\xd1\xe8\xd7p6\x9bM\x88\xc5b~\xbc\xf6\xa1]2\x8c\xf6\x82\xd0\xdfv%\x86hu\x92\x86\xd1y\xa3:\xd4+\xf5;\x16r\x1c?\xc6H\xf1\x9e\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xbf\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04tIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xdd*6\x01\xd8/\x83\x14\x88A\x18\x8a\xba\xe8E\x04/\xe0)\x04\x8f\xad\xe0\xd2\xad\xe0ifR\xaaHL4.fV-H\xc1V_4&\xf9\xbe\xb9\xe8\x05\xfc ]\x83\xa4jJ\x1a7\x1c\x90\xb8A\x0c\x8d\xa9z\x02\xc0\xe4\x90\xc8\xa8\x14M\x01\xb8\xc2\xbd\x5c\x01\x9e\x9c\xb2\xbe}\xc7\xd1L\xfdwI&\xc7\xab\xe0,\x16\x01V\x8d\xb3\xfe\x08@\x01\xc1y\xa5\x94\xde\xff\x95\xfc\xca{?\xf9c\x0b\xe0`p\xe1\x80\x8a\xf6\xe8\xd8.\xbav+\x12\xc5\x01\xf6A\x8cQi\xadY\xe7\x1f\x03\xc6c\x98RR\xce9\xd1\xf1\xdc\x02\xf0`\xd8\x1a(\x99\x92:\xcc\x02\xa8\x81\xad\x85\x10n5AE\xf0\x0atq\xdb1>9\xe7\xdb\xfaZk\xef3\xc6,a,\x80\xb2\xc4Z\xcb\xfa\xe6\x080\x86\xfc.\x0dP\xb9H\x04\xe0 +\xe1\xbbr\xf4t\x8a\xe0\xb4P\x16\x8e\x97*|\xc1jo\x88\xf8\xad\xaahWl*\x01\xee\xfc\x85k\xc1_d\xcbG\x80\xf6\xac$\x87A\x18\x06\xf6P~\x00_\xe0\x19\x9cy8\x0f\xe0\x09\x1c9s\xe5RM$W\xa9\x1b/\x09iK%\x22UA\x08\x81\x9d8\xe3\x99\xe9\xc7?\xf0\xf7=\xffJ\xe0J\xe0\xe4\xe3\xeey(\x86&\x89Qr\xb8\xb2\x5c\x07\xcd(\xe0\xceb1\x0aQ\xe0\xe0\xd9]\xd7}ue\xa1U\x09\xf8\xb5D\xcc\x1d@\xf0\x10\xcc\xd4\x81\xa4UN\xf1M\x8b\x03\xa5\xa8\x08\xcdX0\xbcc\xdb\xb6\xf2\x12\x92h\xbb\x96\x80F#-\xda/yV\x87\xce\x80\x15|J2x\x09w\xec\xa6\xd3u\xfc\xbc\x87W\xdfK\x83O\xdd\xdb\xf7\xfd\x85\xb0\xa4<\x99\xa6iBYj4N[\x90\xa2\x12\xf2\x94\x0d~\xf0\xc2\xdb\xb6M~x]\xd7\xdb<\xcfO\xc3O\xa2\x8eUw\xc0\xa3\xd9\xb4s@3\xd4\x10\xc68\x8eo\x87U\x0a\xbej\x1f(\x1d@.\x88\xa1\xbe\xef\x83V\xfcY#\x93\x14\x83t\x8dyY\x96\xe0\xaa\x0e\xc3\x10`X\xe2\xe0\x96\xaa;\x94\x00\xef\x8e1Rh\xd2g\x9a\xa6`\x05K\x22Z\x93C)\x1f\xfbP\x02\xa9\xe0\xf9K\xe9\x1e\x9a\x0e\xea\x1d6?44Y\xd2V\x03\xf3&TTBZ\x12\x1c\xc7\x91\x00\xf48f\xa9\x83r\x93^\x93xUv\x807\x1amP`9d\xceJ\x80\xff9P|\x88s\xa1.\xf7\x19+\x99j(t\x04\xb3s\xc8]5Q\x1fSj\x8f\xef\xea\xf1\x04=\x81{\xa8\xf4\xe5J\x9ca<\x00\xdb\xfa\x15g\xec\x8b\x8b\xb0\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10\x91IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfeg\x01I\xde\xb8tX\xee\xe2#\xdbGy\xee\x0c\x0cJ\xe2\x8cb\x1a\xe6\x8a\x10\x1d \xfc\xec\xdd\x7fv\x10\x9d\x94\x94\xf4\x1fl/L\x02\x19\x83$\x01\x02\x88\x11\xaf\xab\xd2\xa7\xbf7>x\x9d\xc1x\xe9\xd2\xa5\x87a\x12\xcc\xd2\xd2\xd2\xff\x93C\xadg\x95O=\xc5\xa0\xa0\xe3\xb2\xeb\xf6\xc9\x95\xb7\xb7n\xddj\xc5\xc2\xc9\xc9e\x5c\xbb\x8a\x811/\xd2\x8c\xe1\xdd\xd5\xa5\xa7\x0e\xfe_\xc1\xc0 \xc3\xe0\x8dl)\x93\xb9\xb9y\xac\xb1\xb1\xf1-\x10\x1f\xc3r\x13\x13\x93l U\x03\x10@(\x12\xf3\xe6\xcd\xfb\xcf\xca\xca\xca\xf0\xe5\xcb\x17\x86\x1f\xbf\xfek\x16\xe6g\xdd@w-X\xc3\x94)S\xfe<\x11H0\xd7W\xe2a\xd0\x97g`P\x14e`\xd8~\xfc1\xc3\xff\x8f\xd7'\x07\xfb\xb9Yax\x8f\x93\x93\x93\xb9&\x84\x87\xc1P\xfa#C\xda\xc4\x07\x0c\xbd[\x19\x18~\xb1\xca2\xf0\xf2\x09.bddT\xdeq\x7f\xe9\x7f[7\x8b\xff \x1a\xee\xa4\x193g_\xe3\xe6\xe1\xff\xa0b\x12\x94\xcf\xfa\xf5\x9a\xf1\xc3\xfb\xb7\xa7\x9f8w\x81\xe1\xd2\x99S\x7f_\xbe|\x19\xa3\xaf\xaf\x9f\xbfh\xd1\xa2 \xacA\x8e\x8c\x81\xa1\xe0\x0a\x8a\x02 }\x19%\x8ep\xc4\x86\x10\x10\xab\x00\xb1\xad\x82\x82B\x0f\x90\x16\x03\x08 \x9cq\x87\x0b\xb0\xc0\x18\xfdS\xe6\xa8\x0b\xf3\xb1\xdd\xf8\xf3\xe7\x0f\xc3\xa7O\x9f\x18\x0a\x0a\x0a\x18\xb1i\x00\xdb0c\xe6\xacC\xef8\xad\xfa\xbf\xb1*<\xd2S\xe4a\xd0\x93c`\xb8rl\xcd\x99\x90\x90\x10F\xac6\xf0\x0b\x8a\xb3|\xe7V\x7fd\xab\xcc\xca\xa0&\xc9\xc0 \xc0\xc5\xc0\x90\xb0O\xd2\xe4\xcb\x97\x05\x7f\x12\x12\x12X0\xe2\xe1\xb7\x84W\xbe\xab\x1e+\x83\xa9\x12\x03\xc3\x89\xabo\x18\xfcz\xff3\xf8\xb9[3\xfc\xfd\xfb\x97\x19\x18\x0f\xf2\x87\x1fl\xfb\xbf\xef\xd2\xd6\xff@6\x0bX\x83<\xf3Ecm\x19\x06\x06\x9f\xca\x13\x0c\x07\xee\x8a0\xf8\x1822\x18+\x00\x0d\xfa\xfd\x9ba\xfb\x95\x95\x0f\xbe\xfe\x7f\xcf\xf0\x8b\xf7\x03(\xe9\xbc\x04[\xf7\xed\xd3\xeb\xe9Nu/M\xec\xad,\x18\x14\x80\xc9BS\x8a\x81\x81\xfb\xcf=\xb9k\xdf\xbf\x1b\xad\xdb\xbcv+\xa3\xcc7I\x90\xba{\xcb~,\x07k\xf0\xf4\xf4d|\xfat\xce\x7f!.\xbdLm%\xa3\xb3\x8fo\x1c\x98\xf8\xf4\xd3'\xcb\x17o\xdf3\xccn]\xc5\x1a\x1c\x1c\xbc\xf9\xe3\xc7\x8fww\xef\xde\x9d\x83\x12\x0f\x17\x1e2\xb0jK3\xb0\xb3\xb20|\x01\xf1\xfd\xfd\xfc\x80\x09\x9d\x91\xe1\xe9\xd3\xa7\xfcg\xce\x9c\xf9\x04\xf74\x0c\x18\xc83\xfc\x86)\x06\x81\x8d\x9b61\x02\x93\x01\x83\xb4\x140U\x22\x87\x12>\x00\xd2\x04L\xa2\x0c~\xfe~\xffQb\x1a\x18d\xe8j9\xa1i\x89\x1f\xe4: ~\x05\x12\x04\x08 \x9ci\xc9\xae\xf21#;\x0b#\x03\x0f\x17\x0b\x9b\x98\x00\xfb_y\x09\xce\x7fU\x81\x1c\xff\x18H\x04\x18\x16\xcc\x99;\xef\xa2\xa0\x00\xbf\x1e3\x1b\xef\xf1\x17\xdf\x05\x17\xdd\xff\xaerVL\x90\x95A\x9a\xe9\x9a\xf1\xbf\xcf\x0f\xe2\xbe}\xfdd\xf9\xef\xdf\xbfKiii\xfa$Y\xd0\xd5;\xc1HZB\xf4\xec\x93\x7f\xfaA/\xff\xca?\x12\xe6ce\x90\x14bg\x90\x10`\x04b\x06\x06\x11\x1e\xa0\xff\x81\x98\x87\x83\x81a\xe7\x91kr\xff>?\x5c\xf7\xea\xd5+\xc3\xf8\xf8\xf8\x0bD\xe5\x1e>n\x8e\xb3?\xd8\x952\xf9\x054\x1fi\x0823\x88\xf310\x88\x03C\x93\x0f\x98\xce\x85\xb8\x19\x18>~\xfd\xcd\x90\xddw\x91\xe1\xc1G!\x065U\xadG\xe6\x82\xdf2\x19\xff<;\x0fr$4N\x80\xaa\x18$\x80\x98\x1d\x88\xdf\x02\xf1k\xa0\xe3\xff\xc1-`aaaP\xd369+&\xc0\x0cv-(\x03}\xf9\xf1\x87a\xf1\xd6\xab\x0c\x0b\x0f\xfc`P\xd64gPQ1a\xd0\x05\x8a\xf3\x03\x8d\x12\xe5\xd1?\xfb\xfa\xfd\x05\xb0\xc1\xc0\xa2\xfa\xd5\x81\xa7\xeb\xb8\x90]\xfe\xef\x07#Cqqq5\xb2\x05\x7f\xbf?\xde?QY\xcd5\xb7g\xd5}\x86\x1d'_1H\xaaX\x00KL}\x86 `}\x22\x02\xf4\x9107\xc4W\xa0\xa0\xbayf\xfb\xc4\x1f?~\xfc\x05e\xbd\xf3\x9c\xdb\xd8.\x9c\x05\x96\xac\x9d\xa7\xe0\x16x\x96\x9b1\xbcy\xc3\xec\x8b\x12\xc9\x8b\x16/=\xce\xc5\xc9n\xf1\xf8\xafn\xd0\x0f\x0e\xf5G\x02@\x03Ex\x81\x06\x03\x0d\x04\xc5\x83 \xd0\x8d\x7f\xbf\xbfp8}\xf2x\xcf\xa3'O\x18\xccLL~ZYYq\xd9\xdb\xdb7\xc8\xf8\xb3\x95\xfc\xe3\xf8\xc5\x093\x8b\xed\xa4\xc2\xa9\x85\x0b\x17\x06`M\xa6\xfdS\x17\xe92\xfe~w\x89\x9d\x9d\x9d\x01\x84A\xc1\x07\x02\xa0\xd2\x8d\x89\x89Is\xe5\xaaUA\x1b7lh\xcd\xc9\xc9ex\xf1\xf2\x05\xc3\xb3\xa7O\x7f\x02\xa5y\x81\xd9\xf17\xc1dJ\x0a\x00V_^\xd2RR[EDE\x19\xde\xbc~\xc3\xf0\xf4\xd9\xd3s@as\xa0E\x7f\xa8b\x01\x92Ea\xd2\xd2R+DDD\x19\xdf\xbc~\x0d\xb4\xe8\xd9K\xa0%\x12T\xb3\x00\xc9\xa2,\xa0\x8f\xa6\x02}\xf4r\xee\xdc\xb9\xd4\xb7\x00\x1b\x00\x08@{\xb5\xc6\xc4QE\xe1\xc3,\xeccf\xb7\xb0;\xbb\xec\xab;\xdbX\xac\x0f\xa8\x88\xb5\x94bR\xd4\xd0TE\x88&\xb6?\x88J\xb1\x89\x80i\x13K\xa3\xf8\xe0GU*ih\xc4\xa6h\xd2\xd0\x185\xb1\x09\xd1\xc4b\xc5\x88\xa5\x09\x0fA[%\x8aFL\x09)\xcc\xa0\xcbc\xc3.\xb0\xec\xc2,\xbb\x8b\xe7\xcc\xd0\xc6F\xd2\x87\xa17\xb9\xb9\xb3\xb3\x99\xf3\xdds\xee\xbd\xdf\xf7\xdd\xdb\x0ep]:}\xec\xb0/\xe5\xd9F\xbfaM3 \x16-\xdd\xd0\xde\xc2\x1a\xb4\xbb\x89\x92Q\xd9\x14\xb1\x92e\x19\xe4\xe8\xd2\xe7\x87\xaa\x0f\xee\xf9\xdf\x00\xef\x9dhv9x\xeeo\x93\xc9\x04\xa1\xf8\xba\xa6\xd1yW[\x5cg\x9f^\xcf\x05x\xc3\xe2\x9fE\x8bs\x93\xfb\x09\x0c\xcf\x85\xbb\xbc\xbc\xdcwK\x00\x8d\x8d\x8dZ\x9b\xcd&\xebL\x8e3\xad\x97\xb3\xeb\xecf\x1d\x10\xa3:,zp\x99UF%\x8a\xf8\xfd\xc7oj\xe7\xe7CO\x05\xe7\xe3\xfa\xca}\xa5\xf2M{\x114a\x11\x1dk\xfe\xe9\xfc\xf8\x03uB\xba\x16\x1c\xbc\x1el\xa9\xc9\xe0A\x8d\xb2!\x0f\x999\x95\x8bl;\x1f\xaf\xeb\xed\xe9r3\x8c?\xfc\xef\xef\xaf\x9bA}\xc3\xfb\x1e\xa7-M\xfa%V\x92\x9bna\x13\xd6\xd4\x14\xf0\xf0\x1a\xb0\x99T\x92\xa3\xe0\xc4\xae\xc1\xf9\x18\xbcp\xa4\x0fB\xc9\x1b\x98\x8a\xfb.\x5c\x9c\x0e\xceY+_\xdc7}\xc3\x0c\xb4L\xe2\x04\xf1\x8d\x877&\xec\xa4^X\x12\x9a5\xbf\x222\xfa\x14\x80\xcf\xbe\x1b\x81\x8f\xda\xfd\xa0\xb1\xe6\xc3\xfdBr\x22\x1e\xef\x83Xt\xe1$~\xfe\x0cn\x06\xda\x8dhI\x80\xc7Ne\x9b\xc0\x89\x87\xaf\x02h4\xcc#Hb\x90\xe1\xd4*\xb5\xa6\x99S\xe0T\xdc\xa0S\xb3Kp\xace\x04:\x07\x02\x90\xb3%\x0f\x9ci\xea\xfb\xc4l\x02P:w\x92_\xaa\xaf\xaf?\x93\xfb\xc4\xe6\x22Cz\x12\xc4\x16\x01\xc6\xfaf\x22\xf8\xfe^\x04\x11\x15\x00\x0aNM\xb0\xa2\xa5\x5c\xa7\x060a\x1f\x1c\x0dA\xf5\xf1\x0b\x10\xb3\xe4C\xde\xb6M\xe0@-HeU\x0a_Fg\x82\x00\xcb\xd5\xd5\xd5\xafn\xdbsw\x11\xa3\x8fB\x94,\x81\x16\xc0\xfd\xb0\x81E\x11\x1aD\x90tF\xcd \xb9\x93@\xd6\xe3\x82R\x10\x0a\xfe\xce\xc7\x7f@\xd5\x87\x13\xc0\x09\x85\xf0\xd0=\xac\xf2\x9f\xd3\x0cXFT\x18\xabj\x85\xf1\x8c\x9c\xcb\xca\xca:$'\x85aA\x8e@\xed\xde\xa3\xcaH\xdd\x92\x9dL\x0a\xe7P2\xe0\x8c\xc6\x03\x1a\x8d\xa6\xc4\xc2\xc6\x99\xcb\xe3\xd1\xc4\xb1\x96a\x18\x9c`!;s\xa3R.e\x07\x19\xd5\xddD\x19\xa6\x19\xe2\xcc\x10\x9e\x07\xd4\x87\x8a\xce\xce\xce6\xbb5\x9ewE\xcd^y\xee-E\xcd\x96c@\x94\xadS2\xd8[\xf6\xbc\x84C\xbc\xbb\xe7\xfb\x0fJ\x0f\xf7\x80\x18\xdb\x0c\xdbs6\x82;M\x0d\xe86\xab\xdd\x85\x9d\xd6`\xf0\xe7\xf6\xd3x\xb2\x13555\x01\x9f\xcf\xd7\x8fF\x11\xf2+\xefR\x04\x89F\xfa={i\xc9O\xe2\x7f\x95\x8bX\xa3\xd9\xe4\x9f\x9a\xd8zpW\xacv{\x86Z*7\x96\x83\xec*\x95\x84JCe\xba\xf4[\xf7\xd9`0\x90\xf1\xe0\xd6\x5c\xa6\xaa\xaa\xaa\xac\xa3\xa3\xe3\x88\xdc\xc1_\x0c\x85B\x90S&\x00\x8da\xbf\xbc0\xf4\xed\xe4)r\x16\xd7PEC\xd3\xa7\xde4\x9d\xfb0v\xe8\xb6\xdb\x96\x7f\x04h\xce\xcac\xa3\xa8\xa3\xf0\xdb\x99\xdd\x99\xdd\x99\xd9\x9d-\xb5t\xdb\x02)\xa0\x85\xa2\x09\x88\xc2\x1f\x8261\xa8\xa5\x15D\x9bz+wD(5\x91\x18\xe2\x85\x09 Z\xb0Eh!\x04\xc2\x11\x10\x0f\xa0\xa6\x04\xb9B\x08\x94\x84C<\x10\x8b-\xb5P\x14,\xdb\x83\xb2Wwg\xbb\xed\xee\xfa\xde\x1cP\x10\x03\x98\xd6t7/\xbf\xc9\xce\xce\xee\xf7\xcd\xef\x1d\xdf{\xd3\xe3\x7f\xd0\xd3/\xf3\xdd^\xb0\xb8ds_\xc9\x12\xceaM\xd1<\x86e\xfa\xc7c\xd1d\x88\xc7\x5c\xd1X\xbc\x11\xcbO3\xd6\xa0\x8b\xd1\xb8\xa9\xdcd\x91\xf6\xbcS8\xbd\xb9\xa7\x09\xdcv\x07>*\xfeZJ\xb1\x87\x8al<;\x93\xe38\x0bI\x032\xc3\xdf\x10\xb0Q\xdb\xd4j`\xac$\xcf\xc8\xf0|\x07*\x82\xb5v\x87s\xfe\xe4\xd7_\x09\xfeo\x04JKK\x93l\x82xB\x14l\x83P\x91\xe1^\x09?x\xda\xc5]\x87/\x0d\xde\xc7q|L\xc0JhC\x13\xadf\x90l\xacfVVm\xa2I@\xf1l\x94i\xac\xab\xccV\xda\xaeN\xe8\xech\x1fE\xc4\xf0\xbf\xeacq\xe6\x91\xa9S^k\xeaQ\x02e\xabV\xaf\x94Da\xae$I\x103\xcb\x15?\xb7\x0c*\xbb\x12\xe9\xe3\xe5,\x04\x98\xd1\x01\x9b\x81Hh\xc0M\xaa\xcc\x11\xb0\xfd\x16\xd1\x04N\x13jtLkkk\xb3\xf3l\xcd\x99\x02%\x14\x9aD\xbb\xa2\x84#e/\xbf\xf4\xc2\xdc\x1e!\xb0\xfc\xf3\x15\x15\x0e\xbb\xf4\x0c\x81o3\xb9\x16\xfc\xe2\x1f\xb1G\xbb\xdb,\x02dAD\xe0\x0e\x81\x05\xbbU\x03\x88\x85\x0e$^\x1b_\xd8\x10,g\xd1@\x93\x91\x97E:bPU\xef\x83\xe5\xdb\xeb\xe1\xc9aJ\x8eKl[H\xa3S,~\x15\x93'O~\xb6[\x09,[VQ%c\xd4\xd8\xb1c\x17\xd6\xd6\xd6\xeeC\x8c3\xf1T\xd3\x8d\x13`\x86=\x8b'\x86\xd2q\x02\xebNt%\x0eh1\xc0\x92\xcb\xd0\xdd&\xbf\xfe\xa3\xb9\x13\xf6\x9eh\x84?\x1aC\xf0S\xf5ePX\x178\x92G\xc0\xc0\x04M\xe1\x1b1`\xed\xb2cV\xfd7X\xc5\x9dxE\x03OV}\xf2\xf0\x8e{\x8a\x8b\x8bw\xde\x9f\x95>\xca\x9c\x84\xa9\x97\xdea\x0d\xcf\x07\xd3\x8a\xb4\xda\xb3a\xbe\xee\xf0\xe8\xbe\x0f\x01\xfb\xe0\x80\x8c\xdcb\xaex\xe7\xbcy\xf3&\xdc\xe4B\xe6U\xb8\x03\xa5\xe4F\xa1\xd6\xfa\x5cW\xe6\x80M\x04@\x9d'\xbbC\xe0\x09t\xc0\xf6\x83\xe7\xe1\xc0O\xad\xc0I\x08\xda\x95\x09iC3\xd4\xc9\x1e\xb9\x98z\xa7\xbb\x10\xa6\xbb/\xe9\xae\xc3\x99\xb5\xd8\xb9\xf8\xdb\xef\xb9\x04^\xaf\x17KH\x83\xa4\xa7\xa7?\x18\x11\x03\x10\x09k\xc3\xd8E\xb3Vh\xb32\x96W\xd7w\xa7,Q\xd7\x0f\xd7\xbc\xa5\x81\x11\x19\xa0k\xe8\xda\x1b\x82\xb8\xc5\x0f\xa6\xbd\x15\x9b+\x04A\x98H\x81\x9c\x9c\xecZ\xd0nI\xdb\xb3q\xef\x9fp\xc9\xcb\x83/\x18\x05\xce\x9e\x06\xae\xe4$\xb5+\xa1\xdd\xa1\xd4\xc9\xe9\xc1\xacf\x22=\x85\x1a\xe0\x0d\x22\xf4\xd9\xf9\xeac9M\xee\xbf\x16\xaa\x9dI0\xb8\xb3\xb0\xb0p\x12=^\xc9\xcb\xcb+se\xb3c\x189j'\x1c\x03\xfb\x0e\xd6&\xc3\xef\x96\xab\xeb\x8cO\xf2\xd4\xf5B\xf3y\xadx\xfa\xd8@\xe3\xbe\xe8\xd1\xf2\xf2\xf2\x82[\xd6\x81m\xdb\xb6\xad\xc1f\xe3\x0d\xf5iQ\xc4\x5c\xb1\xaf\xae_Y\xbb\xf8\x80\x97\x9a@\x87\x1e\x0fF\x06\x227\xe9\x1a\xdc\xc6y\x83\x1c}\xeei\xb9\x98\xf2\xf3\x8f'\x8a\xcc,3\x8cR\xa8\x891E\x8f\x1f;\xbe\x18e\xe6g\xd8\x11\xc5\x87\x0f\x1f>!33s\x96u\x8c\x7ft\xdc\xd6q-\x02\x06\xb9\xee\xd5v\xbf\xf1\xdc\xf5\xb4\xa9X\x94\xf0Q\xc7\xc9\x9a\x9a\x9a5\xd89\xed\xfa\xd7J\xbcz\xdd\x17.\x9b%\xfa=\xcb\x98\x06\xa8\xbd9c\xfd\xa1\xd3\xdcg\x97\xdf\x9e\x85\x95\x98\x8d\x19\xb9_\xd0\xc1\xabEL'\xc1\xa9\x04\xb1\xbb\xad>\x95\x1d\x0a\xb6\xcd\xf4z\xbd\xfd\x15E\x01\x9f?\x00\xbb\x0f\x1c\x84\xa1\x19\xf7\xc1\x8b\xf9y\xd0\xda\xd2\xe2\xc1\xbb\xf8\xf1\xa1C\x87\xd6\xd7\xd5\xd5\xc9YYY\xd3SRR\x1ec\xeem\xcb\xe8\x14CR\x8coW\xc90\xed\xbcb\x0e\x0am\xb1s\xd2\xefn\xb7\xfbHee\xe5z\xfc\xb8\xe1\x96m\xdf\xcd\xaf\xa5k\xf7;\xac\x91\x0bK!\x16\x99\x81\xb1\xc1\xd2\xae\x90\x162\xdaA\xc3\x88\xa41\x84\xa0\xdf$=D\x16\x89D\xa2\x98\x02\x0f\xa2,\x19=c\xfa4\xe7\xe9\xaa3\xf0\xcd\xf6o\xa1\xa6\xf6,XL1\xb8\xd2\xd4\x8cmK\x90&\xac\xefQ\x7f\xaaktI7^\x87A\x1da\x9bn\x0a\x8d\xd9\xff\x93\x9c\xfe\xf6$\x98.\xff\xb25\xad3\xec\xcb\x8dG;&\xb2\xa8F\x11\xae\x0b\x81'\xa1\xb5\xa0\xb9\xf1k\x97\x90\xe0.\xcc\xe1\xdf\xe5\xe7\xe77t\xbd>''\xf7\xb9\xf1\xe3\xc7\xaf\xac\xaa:\x9d\xaa(a\x13\x8aC\x22\x88;\xe3\x07\x9f\xd7KqA\xbe2\x1b]\xeb@\xaf\xed\x07\xb0!\xa4@}\x1e\x9b\xc3\xb7e\xa7\xaf1[\xadV>\x1c\x0e{\xf1\xd8\x83vU7\xaa\x05\x81\xdbN%\xbaiwb\xba\xd1\xf3F\xa5\xbbw\xeao@:<|3(U\x02\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\xa2IDATx\xdat\x8c\xb1\x0d\x001\x08\x03\x0d\xa1\xcd4\xd9\xbfe\x94T\xac@x\x99\xb4yK\x87\x91lY\xaa\x0a/)~d<\x11QsN\x88\x08\xb8\xb0\xf7\xbe\x81\xaab\x8c\xd1d&\xcc\xecN\xb1u\xce\x81\xbb\xb7\x93\x0e\xf8\xb0\xb9\xd6j'\x9f\x00\x9c\x8e\xb1\x0d\x000\x08\xc3\xa0\xea\x00\xff\xdf\xc35\x1c\x01\x03\x12U\x90x\xa0\x9e\xb2$1\xffYED\xe3\x18`FU\xf9\xac\x95\x99\x91\xbbO\x06\xf3\x91\x99\x0dEPU$\x22|\xb7\xbe\x120\x04O\x00\xe1t\x15^G\x81\xc0\xbd{\xf7\xfe\xf3\xf1\xf1\x81\xed\x86\xd9\x0f3\x19\x84EDD\x18Qlx\xfb\xf6\xed\x7fnnn\xb8\x06X8\xc34\xbc|\xf9\x92A^^\x9e\x11n\x14H\x01\x08\x83\x14\x83\xdc\x0e\xd3\x08\xd3\x0c\xb3\x95\x05\xddj\x98\xe6\xdf\xbf\x7f3 \xc7\x13(40<\x0d\xf2\x07\xb2m 9\x98\xbc\x92\x92\x12\xd8\x0f\x00\x018%\x83\x14\x80a\x10\x08\x86\xfe\xffA\xf9B\xde\x92\x93\x1e\xbcZWj\xb0\xad\x14\xd2@H\x08\xbb\xc4q\xdd\xee\xd2\xd16\xd7\xb6aA\xcf95w%\x03\x13\xd1\x9bAD4\xc4\xd9\x80m\xb3\xe2\xda\xdb\x0f!\xc4\x18f\x032)K\x0aC\x0e\x09\xe5\x84\xf9\x13\x9a\x99[\xef\xbd\xa6\x8e:\xc1\x809\xb7\x84u\x8c\xa1\x96\xac\xdf\xf1v-\xd7\xfd\x87\x8e\x16V\x86\x12\x1a\xe3\x9b\xe1\x9f\x06\xcb\xc1\xcfS\x00\xe6\xaa\xd8\x86\x81\x10\x06ZQD\x81X\x82\x1d\xe8\x18\x81\xd5i\xd8\x81\x0d\xa8\xe8\xf2\x8eb\xcbo\x8c^)^\xfa\x0e,\xc1\xe1\xbb\xf3\xf1\xb7\x97n\xb7\xd2\xe3\x00\xde\xba\x80\x1e\xf7\xde\x9f\x8cE\x5c[\x16\x91\xdc\xe3\xb9c\xc6y\x0eL\x00r\xaa\x05 A\xa4\xa0r-\x03\xd0\xa4\x88\x13Z\x00\xe0\xfc\x1c\x9d\x9dr\x0d\x9do=\xe4\x12@\xd3\x81k\xe7\x1c\xf4\xde\xb9\xbb\xd6\x1a\x94R\x96\xae,*_\xbb\xf1\xd3T\xe0\x1f\x87\xff\xd0\x9c\x13b\x8c\x5c\xd76\xd7\xfb\x05\x80\x92[^@\x89\x9es\x86Z+\x84\x10\xb8\xa6\x81\xe8\xfcVd=\xd2Rd\xa4'\xa5\xc4\x99f\x89|\x090\xc6\xf8\xda\x8d\xd3G\xe9\xb1\xa3\xf1g\xf1U\xcf\xbb\xa3\xe2#\x00\xfbU\x8fC!\x08\x83\x8dy\xbb\xa3\x03!qu\xc1cpd\x8f\xe0\x09\x98%\x9e\xe4Y\x12^*\xf4\x07\x07\xdf\xe4@4Z\xfa\xb5\xd0\xf6k\xdfZ\xf4\x02AA\xb6[\xbb\xddLZ\x93\xcc\xf4\xed\x7f\xf0\xf79\xff\x06p\x03\xf8\xf1\xab\x88\x0c\xe2\xa1I\xab(e\xb8\xf2T\x07K(\x90\xcabv\x14J\x86\xa3\xce\xae\xeb\xfa\xa3\x9e\x05WM\x81\xdf\x02\xe2\xae\x00\x8c\x07aN\x19H\xf3\xb2'Vx\xe5\x8a\xf4>\x1c\x869\xa05fo!\xadl\xb7\x00Xe\xa4W\xf6k\x9a\xd5\xa5w\xc03\xde\xab\xdc\xad\xa2\x84\xab\xe9\xa9\xcd\xc7G\xea\xeaG\xae\xf1ru\xa0\xfdo\xdb\xb6\xd3\xa6\xa5\xf2\xb7\xae+\xd1\x16k.\x8f\xca\x9c\x02\x10\xdd6\x10\xf3P\x11O\xd3t(W\xe2\xfb<\xcfts\xad\xd42\xf8\xf2\x0aD8\x1bo\x83\x98@\x91\x84\x97\xa5w\x97e!f\x04Fd\x19\xfc\xd5DV\x96%\x81\x18\xc7q\xf7l\x18\x06zVU\xd5\xf73\xb1\xc6\xc4\xd0\xee\xba\x8eX3\x18\x5czAa|\xd34\xf4\xcc\xfb\xfdK\x01\x1ceG-v\xf3\x1bR\x00\xb6\x0b\xce\xb8\xa0\xef\xe3\x13}\x1a\x89\xb3\x00y\xa0\x8a\x88\xa7\xb50\xa7\x85J\x9c\xd1\xc2`l%d\xd3\xbe\xefCFz\x80\xb2\xf2\x80\x05B\xc6q~\xb5mK\xfd\xd8\xf38G\xb3\xb2\xae\xd5wy\x05d\xa2\x89\x02\xc6x\x80\xb0\x98s\x04\x80<\x1c\xc8\xaaF\xa5ag\x80\xe4\x06\x83\xe8\x1cEn\x04\x8a\x029\x032g>\x97\xd4\xf3\x92:\xa2\xbbF\x92Q\xc4\xf0H)}\xab\x12\xbfp=\x01\xc0\x18?~\xda\xb0\x86\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xe2\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x97IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91;\xc1\xe7\xbb\x87\x8b\x1e\x80?\xd05$\x95(i\xbdtC\xea\x85\x1e\xaa\xa9\xfa\x02\x80\xe0)%J\xd1\x0c\xc0\x1a\xdcn\x05:8\xcb^\xde\xebnf\xdf\x0d=\xc1u\x15V\xc6]\x00\xde\xb2\xb2\xff\x0a\xc0\x02\x84J\x07\x09B\xc3\xad\xebz\xfa\x13}\x1fM\x00\x0f,\xe7\x1c\xe6y>\x9f\xb7m\x0b\xcb\xb24+r\x01\xd8\xc8\x94\xfdm\xbf\x9a\xf7\xd1\x0d \x010\xc1p\x84\xe8R.\x90\x12B\xee'\xda\x96\x16\xcc\xa2\xd8h\xefr_\xbe\xfb\xee\xf2\xf1?\xf8\xfb\x9c\x7f;p;\xf0\xe3\xe3!\x99dS\x13\xa6(]\xba\xe2\xba\x0eT\xa3\xc0\xed,\x9ef!c8d\xe0,\xcb\xbe\xba\xb2P\xab\x1a\xe2\xa7\x1ca#\x00\xc6\x83V4\x19\x08[e\x9f\xde\xa4z\x13X\x91m\xae\xb0`\xf0\x0d\xd0G\xa7!\x84\xc9v\xca\x01LyHd?\xd6\xb3\xba\xb4\x078\xe3}%\x03U\x00\xf9~\xb3\xc5\x8b=\x9f\x92b\xe2\x08pF\xbb\xd7m\xdb^p\x03\x0cGQ\xa4!\x08P\xe4d\xb5\xef\xd9e\x07\xa4\xb01\xf7\xeb\xba\xeakUU\xaf\x8d\x0f\x0e\xf5}\xaf\xd24Uu]\x93\xd214\x02\xa7\xf2\x00\xb6BX\xa4\xe0\x5c\xa8m[5\xcf\xb3\xae\x8a\xb8\xf7\xdf\x9e\x07\xae\x0e0z\x18\x06]uA\x14\xbe\x9e\xc8(\xec\xba\xf7\x062\xe6\x98\x05hp\x9a&\x05\xb5RY\x96(}\x86\xb0\x93\x18B\xbe\xec\x88q\xb7=\x176n\x92$\xba\xb6\xcd\xf3\x5cu]\xa7\x8e\xe3P\xe38z\xabD\xca!\xce)\xd6\x01J\x02\xf8\xa4\x00\xd6\xe3.\x8aB-\xcb\xa2\xf6}'\xdf\x0b\x8d\x8a\xb85\xe1\xe3j\x97\xc7a\x98\x8e\x82k\x0c\xd0h\xd34\x1aR\x10\x1d.\x13K#\xf0\x90@HR%\x9b\x11\xc71\xbarp\x00 \x81\xa3\xfd\xcc=\x1c8\xbd\x89}<-\x99/\x9d\xc39\xf36\x16\x0au$\xc4\xc93\xdfc\x8bz[RK\x1a\x91\x92d$1\x5c\x22\xa5\xef\xae\xc4/\x8c'\x7f\x08B\xb3\xa3\xd0\xea\xb7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xd6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x8bIDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02\x88\x91\xedE5r\x8fCi+Ej^\xc9\xe5.N|\xb6\xf3\xb5\x96n\x97\xd2\xdf\x01\x1c\xba \x1a\x17]\xb0\xb0\xc0\xb5>@/\xb8\x97\xb9\xa7\xc7\xa7\x0fL\x00\xc4\x8c\x05\xc0 \xbcP>\xe7\x004)B\x03\x03H\xad\x942\x03\x91\xaf\xf5\x8b\x5c\x02h:0\x1cct9\xe7\xe5\xa1\x9a\xba\x8f\xbc\xc9\xd2e\x0b\xa6\x94\x5c\xad\xd5\xf5\xde\xdf\xea\xbbYs\x07Hn\x0bD\xee\x85\x10\xdc\x18c\xe9E?j[\x00mi|\xb2\xf7~f\x99\xd0$\xbf\x8d\x9f\x00Zk\xa7\xdc0\xa4\xf7\xb1\xa3\xf1%\xf1u\x9fwG\xc5C\x00\xf6\xab\x18\x87A\x18\x06\x22\xc4\x0bX\x92\x7f\xb0!\xe5\xdd\x90\xd7d\x809oh=\xb8J\xcd\xd9q+\xb5\x13C\xa4H8\xbe3\x89\xcf\xf6\xadE7\xc0\x0f\xe4\x9aZ*\xee\xa4-\xcdi\xa5\x82\x17\xe5P+\xd5\x17\x00r\x1eB\x80\x12\x8d\x00\xb4\xc2mF \x9d#\xf6\xfc]f3\xb2\x9b<\xcee\x14\x1ac\x17\x80\xb54\xf6\x16\xc0h\x19\xb0\xe3m\xdb\xde\x80j\xad*\xb0\xfb\x99J\xe3\x9c\xb3\xbb\x8a}\x94\x07|x]\xd7\xa1\x94\xa26\x01_\x03\xf0\xbf\xa6\xb1\xe98\x0e\xf7\xf3\xec\x02\xa0Z\x9bR\x1a\xf6}\xef\xd6a\x15\x00\x1d\x94\x8c\x97e\xa1\xb9\x0e\x16}\x044\xf6\xd2\xbf\xad\xb5\xb4\x9f\xe7y8\xcf\xd3\xb4U+\x1aIE\x8c\xd1\x95\xc9\x88\xd0shx\x8d\xb1fW\xe1\x91\x01t\xd9(\x82Ic\xa6e+\x02\xb0.\xfa\xf2\x8a(L\xc4\x90\xef@\xee[[\x92\xebnW\xc1#6\x12@+\x82v\x04\xffk\xdb\xf2\x10\xa0=+F\x95\x10\x06\xa2a\xb1\xb4\x13\x1b[\x1b\x8f\xa0\x9d\x95\x97\x16k\x11\xb1\xb6\xf5\x0c\x9e\xe0\xf3\x02\x81!?\x93\x19#\xbb\x7f?\x18\x90uw%\x997cf\xde\xbc\xbc}\x81\x7f_\xf3\x1f\x00\x0f\x80/\x1f\x99\xe6!\x9a\x9a8F\xe9\xa7+Iu\x88\x09\x05\xbe\xb2\x98\x0c\xc0\x19\x0e\x9e]\x96\xe5G=\x8b^\xd5\xad\x1f\x03\x22F\x00\xc6\xa3av\x15\x88\xf3\xb2\x86\xe6\xc5x\x8e\xef}8\x0cs\x9c\xe7\x99\x1e\x01\x8e\xb6\xc7\x00\xc4h\xa4D\xfb9\xcd\xea\xd6\x1e\x90\x8c\x97\x98{\x8c\x94P5\xdd\xdd\xd3\xe75\xbc\xfa\x95j|(:8D\xc0\x19\x00\x15\xe0\xe8'\xfe\x03\xa1\x09%\x00\xae\xcd\xbc\x0d@\xfb\xda\xe0Z\x96\xc5\x8c\xe3h\x8e\xe3\xf8%\xe8\xe1;\x8c\xdf\xf7]m\xf0\xed\x08hz6\x7f\x81\xb6m\xadX>\xcf\xb3\xb8\x87\xaez\xfbc\x85\xac\xeb:S\x14\x85\xed\xb0h\xf6\xfa\x9aJ,ub\xf8\x0d\xda0\x80L\xd3d%r\x8e\x83Ks\xdd\x02\x10\xaa\x8e\x5c\xee\x0eu(\xa8!\xc30X\x00\xeb\xba\xb2]b\x0c\x90\x04\xea\xa5\xf14G\x01bF8\x108R\xee\xfb\x1e\xa7\xbbf\xdb6\x16\xb0\x06PR\x1d\xa0\x93\xfa\xb9\xda\xcf\xe3u]\xdb3\xb3\x90\x01M\xd3\x98\xaa\xaal&\x92\xa2y%\x02\x99\xe6\x15\xd2t\xc9\x18\x00\xe0D\x86PF\xc9\xf3\xdc^W\x00\xf8\x87\x03Il\x94N\xac\x01\xa2\xdd\x90\xd2f\xd6\xcc\x91\xa5f -\x90+ S\xe6\x13\x9bzJ\xa95B\xa4\xa6\x18i\x0cw\x1a\x8a\xd4\x13<\xaa\xc4_\x8f\x1f\x06\xc6\x18\xae\x04\x8f\xa7\xff\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\xbe\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00\x08\x00\xa8\x08\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x0f\x0f\x00\x13\x13\x13\x00\x14\x14\x14\x00\x15\x15\x15\x00\x16\x16\x16\x00\x17\x17\x17\x00\x19\x19\x19\x00\x1b\x1b\x1b\x00\x1c\x1c\x1c\x00\x1e\x1e\x1e\x00\x1f\x1f\x1f\x00 \x00!!!\x00\x22\x22\x22\x00#\x22#\x00$$$\x00%%%\x00'''\x00)))\x00+++\x00,,,\x00---\x00...\x00///\x00000\x00111\x00333\x00555\x00666\x00888\x00999\x00:::\x00;;;\x00<<<\x00>>>\x00???\x00AAA\x00BBB\x00CCC\x00DDD\x00FFF\x00GGG\x00HHH\x00III\x00JJJ\x00KKK\x00LLL\x00MMM\x00NNN\x00PPP\x00QQQ\x00RRR\x00SSS\x00TTT\x00VVV\x00WWW\x00YYY\x00ZZZ\x00[[[\x00\x5c\x5c\x5c\x00]]]\x00^^^\x00___\x00```\x00aaa\x00bbb\x00ccc\x00ddd\x00eee\x00fff\x00hhh\x00iii\x00jjj\x00kkk\x00lll\x00nnn\x00ooo\x00ppp\x00qqq\x00rrr\x00sss\x00uuu\x00vvv\x00www\x00xxx\x00yyy\x00yzy\x00zzz\x00{{{\x00|||\x00}}}\x00\x7f\x7f\x7f\x00\x81\x81\x81\x00\x82\x82\x82\x00\x85\x85\x85\x00\x86\x86\x86\x00\x87\x87\x87\x00\x88\x88\x88\x00\x89\x89\x89\x00\x8a\x8a\x8a\x00\xaeh\xf1\x00\x8c\x8c\x8c\x00\x8d\x8d\x8d\x00\x8e\x8e\x8e\x00\x8f\x8f\x8f\x00\x90\x90\x90\x00\x92\x92\x92\x00\x93\x93\x93\x00\x94\x94\x94\x00\x95\x95\x95\x00\xb2{\xe6\x00\x96\x96\x96\x00\x97\x97\x97\x00\x98\x98\x98\x00\x99\x99\x99\x00\x9a\x9a\x9a\x00\x9b\x9b\x9b\x00\xba~\xf3\x00\xbd|\xfa\x00\x9c\x9c\x9c\x00\x9d\x9d\x9d\x00\x9e\x9e\x9e\x00\x9f\x9f\x9f\x00\xa0\xa0\xa0\x00\xa1\xa1\xa1\x00\xa2\xa2\xa2\x00\xa3\xa3\xa3\x00\xa4\xa4\xa4\x00\xa5\xa5\xa5\x00\xa6\xa6\xa6\x00\xa7\xa7\xa7\x00\xa8\xa8\xa8\x00\xa9\xa9\xa9\x00\xab\xab\xab\x00\xac\xac\xac\x00\xad\xad\xad\x00\xae\xae\xae\x00\xaf\xaf\xaf\x00\xaf\xb0\xaf\x00\xb0\xb0\xb0\x00\xb1\xb1\xb1\x00\xb2\xb2\xb2\x00\xb3\xb3\xb3\x00\xb4\xb4\xb4\x00\xb5\xb5\xb5\x00\xcf\x9d\xfe\x00\xb6\xb6\xb6\x00\xb7\xb7\xb7\x00\xb8\xb8\xb8\x00\xb9\xb9\xb9\x00\xba\xba\xba\x00\xbb\xbb\xbb\x00\xca\xad\xe7\x00\xbc\xbc\xbc\x00\xbc\xbc\xbd\x00\xd5\xa6\xff\x00\xbd\xbd\xbd\x00\xbe\xbe\xbe\x00\xbe\xbf\xbd\x00\xbf\xbf\xbf\x00\xc8\xb8\xd7\x00\xc0\xc0\xc0\x00\xd2\xb1\xf1\x00\xc1\xc1\xc1\x00\xc2\xc2\xc2\x00\xc1\xc4\xbe\x00\xc1\xc4\xbf\x00\xc3\xc3\xc3\x00\xc2\xc5\xbe\x00\xc4\xc4\xc4\x00\xc5\xc5\xc5\x00\xc6\xc6\xc6\x00\xc7\xc7\xc7\x00\xc8\xc8\xc8\x00\xc9\xc9\xc9\x00\xd8\xbc\xf2\x00\xca\xca\xca\x00\xcb\xcb\xcb\x00\xcc\xcc\xcc\x00\xcd\xcd\xcd\x00\xce\xce\xce\x00\xdb\xc3\xf1\x00\xcf\xcf\xcf\x00\xd0\xd0\xd0\x00\xd1\xd1\xd1\x00\xd2\xd2\xd2\x00\xd3\xd3\xd3\x00\xd2\xd4\xd0\x00\xd4\xd4\xd4\x00\xe1\xc8\xf8\x00\xd5\xd5\xd5\x00\xd6\xd6\xd6\x00\xd7\xd7\xd7\x00\xd8\xd8\xd8\x00\xd9\xd9\xd9\x00\xda\xda\xda\x00\xdb\xdb\xdb\x00\xe5\xd3\xf5\x00\xdc\xdc\xdc\x00\xdd\xdd\xdd\x00\xde\xde\xde\x00\xe9\xd4\xfd\x00\xdf\xdf\xdf\x00\xdf\xdf\xe0\x00\xe0\xe0\xe0\x00\xe1\xe1\xe1\x00\xe2\xe2\xe2\x00\xe3\xe3\xe3\x00\xe4\xe4\xe4\x00\xe5\xe5\xe5\x00\xe6\xe6\xe6\x00\xe7\xe7\xe7\x00\xec\xe4\xf3\x00\xe8\xe8\xe8\x00\xe9\xe9\xe9\x00\xea\xea\xea\x00\xeb\xeb\xeb\x00\xec\xec\xec\x00\xee\xee\xee\x00\xef\xef\xef\x00\xf0\xef\xf1\x00\xf0\xf0\xf0\x00\xf1\xf1\xf1\x00\xf2\xf2\xf2\x00\xf3\xf3\xf3\x00\xf4\xf4\xf4\x00\xf5\xf5\xf5\x00\xf9\xf2\xff\x00\xf6\xf5\xf7\x00\xf5\xf6\xf4\x00\xf6\xf6\xf6\x00\xf7\xf7\xf7\x00\xf6\xf8\xf4\x00\xf8\xf8\xf8\x00\xf9\xf9\xf9\x00\xfa\xfa\xfa\x00\xfb\xfb\xfb\x00\xfb\xfb\xfc\x00\xfc\xfc\xfc\x00\xfe\xfc\xff\x00\xfd\xfd\xfd\x00\xfe\xfe\xfe\x00\xfe\xfe\xff\x00\xff\xfe\xff\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc1\xcf\xc3\xf4\xf4\xc2\xb6\xeb\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe7\xc0\xd0\xc3\xf4\xd1\x97\xbe\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xa7m\xdf\xf4\xf4\xf4\xf4\xe6\xbf\xd2\xc3\xf4\x8f\x1a1\xc8\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xebW\x15p\xe7\xf4\xf4\xf4\xf4\xe6\xbc\xd6\xc4\xf4\xe6\x8b;\x09k\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd53+\xb4\xea\xf4\xf4\xf4\xf4\xf4\xf4\xc3\xe0\xcc\xf4\xf4\xbe\xa9\xa9\x17;\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd22=\xd8\xf4\xf4\xf4\xf4\xf4\xecI(!6\x5c\xc8\xf4\xbe\xad\xea\xdb\x1a)\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xddJ4\xe1\xf4\xf4\xf4\xf4\xf4\xf4\xeelt`^&\x01(y\xa7\xeb\xea\xa3%<\xf4\xf4\xf4\xf4\xf4\xf4\xee{\x1b\xd2\xf4\xf4\xe7f\xd6\xf4\xf4\xf4\xf4\xe9\xba\xdf\xce\xe1h\x05?\xd9\xe6\xa7\xb4\x0aa\xe6\xf4\xf4\xf4\xf4\xc4\x1d\x8b\xf4\xf4\xde8\x22\xc6\xf4\xf4\xec\xdd\xce\xb1\xde\xae\xc6\xf4\xa9\x128\xca\x9f\xc1w\x08\xb9\xf4\xf4\xf4\xe9_/\xf4\xf4\xeaE3\xda\xf4\xf1\xc2xH9\x80\xe6\xa7\xb3\xe7\xbf\x93%6\x92\xbf\xb2>K\xf4\xf4\xf4\xdd(\xa9\xf4\xf4o+\xc6\xf4\xeb|$-p\x93\xa3\xe0\xa9\x9f\xcd\xb6\x93\xdb\x12N\xc2\xb8q\x17\xd7\xf4\xf4\xe7\x85\xf4\xf4\xc71}\xf4\xf4b\x1d\x8f\xf4\xf4\xec\xb9\xde\xa4\x92sm\x89\xddz\x03\xa1\xb8\x87(y\xf4\xf4\xf4\xf4\xf4\xf4r9\xd7\xf4\x87\x19\xb9\xf4\xf4\xf4\xe1\xb7\xe0\x9d\x90]\x16j\xc8\x95\x1aE\xba\x93@>\xe0\xf4\xf4\xf4\xf4\xe1M]\xf4\xf4\x84\x95\xf4\xf4\xf4\xf4\xe8\xbb\xe5\x9a\x88\x94(;\xbc\x90[\x02\xba\x97S\x1f\xa3\xf4\xf4\xf4\xf4\xc4C\x87\xf4\xf4\xf0\xf4\xf4\xf4\xf4\xe3\xc5\xa0\xd4\x9e\x88\x93a\x17\xaa\x8e\x85V\xb0\x9de\x1c}\xe6\xf1\xe7\xeb\xa9:\x9c\xee\xe0\xe0\xe6\xe6\xe7\xe4\xbdun\xaf\xa5\x88\x8c\x88\x06~\x8c\x8d\x97\xa9\xac}\x1eo\xcac\xbf\xd3\x8b3\x8f\xdaiU\xbe\xcd\xcd\xcb\x98dv\xa2\xa8\x88\x8b\x95\x04r\x8c\x8b\x8d\x9d\xad\x82\x1bo\xe9\x15\xb9\xf4\xb1>\xa1\xf4\x80>\xd5\xf0\xee\xed\xc9\x91\x9b\xb5\xa6\x88\x8d\x81\x0bw\x8c\x8b\x8f\x8d\xaa\x80\x19\x88\xec*\x86\xf4\xc7D\x83\xf4\xcd\x18\xca\xf4\xf4\xf4\xf2\xef\xf3\xdc\x9f\x88\x92R }\x8c\x83J\x90\xaas\x16\xb0\xeeXL\xf4\xe7NY\xf4\xf45^\xea\xf4\xf4\xf4\xf4\xf4\xde\xa1\x89\x8e\x17T\x89\x8dR\x0f\x95\x9f]*\xc4\xf1\x9c\x11\xf4\xf4}6\xce\xf4\xcc\x0c\x96\xec\xf4\xf4\xf4\xf4\xde\xa1\x90? \xcf\xbc\x8f\x17O\x99\x8d7Q\xca\xf4\xde\x1a\x95\xf4\xcd5s\xf4\xf4\x97\x0dx\xd5\xe2\xee\xf4\xde\xa3\x8bPx\xe6\xf4b\x00\xc4\x9fk\x10\x85\xce\xf4\xeai-\xf4\xf4|+\xb2\xf4\xf4\xb7\x1d.b\x93\xf4\xdf\xa3\x87\x9d\x9f\xea\xd5\x0eN\xcf\x9d'8\xb0\xdb\xf4\xf4\xcf!\x7f\xf4\xf1M7\xbe\xf1\xf4\xf4\x8eMT\xf4\xdf\xa3\x86\xa1\xab\xc10)\x8e\xd1\x80\x13\xb6\xc1\xf4\xf4\xf4\xf0\x82\x1f\xbc\xf4\xdfF1\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xdf\xa4\xa3\xbal&@\x8a\x88\x93\x1a\x82\xf4\xd7\xf4\xf4\xf4\xf4\xe2U-\xc7\xf4\xe2g\xd1\xf4\xf4\xf4\xf4\xf4\xf4\xde\x8dlG\x1bZ\xe7\x96\xaa\x89T\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9I,\xb7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xcaB)Hj\xe6\xf4\x95\xd9\xec\xe7\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd9Z\x1dr\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xd6q\x8c\xd1\xb3\xe2\xf4\x96\xe6\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xe6\x87&$\x83\xf4\xf4\xf4\xf4\xf4\xf4\xe0\xa7\xc7\xdf\xb1\xe1\xf4\xc7\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xd3w%\x079k\x94\xae\xc3\xe7\xa1\xc2\xdf\xb3\xe1\xf4\xee\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xec\xdf\xb7h>#\x14A\xf4\xa3\xc1\xf0\xd8\xec\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf0\xee\xec\xeb\xf0\xe1\xa7\xc3\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x91\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05FIDATx\xdatL\xc9\x0d\xc00\x083\x84o\x06\x88\xc4\xfe\xabd\x13\xc4\x83\x15\x08\x15\x8a\xfak\xfd\xb0,_TU\xf8\x02\xe3\x07\xd2\x14\x115\xe7\x04\x11\xa1\x1f\xcc\xec.\x98\x19\xee\x8e\xbd7\xc6\x18\x10\x91\xbb\xe8\xd6Z\x0b\xaa\x8a\xcc\xc49\xe7\x06-^\xa3K\xad\x1f\x01\xc4H\x9e\xab\xbe\x7f\xff\xfe\x1fd)\x08\x80\x8c\xe1\xe4\xe4dd\x81\xb9\x0a\xe4\x12\x98C\xe0F\x818\x17.\x5c`\xd8\xbau+\x5c\x02l\xf9\xd7\xaf_\xff\x83t\xc1\x5c\xc8\xcd\xcd\xcd\x08\x10\x80\x0f2V\x01\x18\x06\x81h\x02\x9d\x9d\xdc\x05\xff\xff\x9b\xb2;\xf8\x07BZ\x03'i\xa0=\x10\x02\xe6|\xa7\x9f\xa9~C\xa5\xc6\x18\x93\x88V\x0eL\xc5\xe4,f\xee\x85N\xb9\xfb|\x90e\xc0\x9da0\xb3&\x22\xbd\x08\xf9!\xeb4`IP\xaf\x13\x8d7\x04JD\xb4W$\xec\xb1\xd3\xb2\x87\xbe\xaa\xae\x1dn\x018%c\x14\x80A\x18\x8aJ'7O\xe1\xfd\x8f\xe0\xa27q\xd2\xc9E\x04\x87\xd4\x84F\xd2\xeab\x03A\x90\xff1/\xfe\xe3-]\xea\xb0\x8e\x0d\x13:\xe7\x0crC\x12\xb8\x94\xb22\xd4Z\x81\xc5\xd2\x80\xad\xb5&\xed\xeb\x05\x16b\x0c\xa5\x01\xe3\xb8e`\x03vJ\x89F\x0b!L\xf3\xc2\xc0\xe5\x9cS\xc6\x18\x0c\xbd\xb2\xd6\xae\xd4<'2\xb4\xd6 \xc6\x08\xe3W\xc1{\x0f\xbdw\xba{\x8at\xff\xa1y\x85;\xc3\x96\x01\xe3+\xe1\xbf\x86\xf1\x0ft\xde\x020W\xc5(\x10\x83@p\xab\x04\xf2\x81\xb4>\xc0>`\x11\x82\xe0\x8fm,\x04\xdf\xe0\x07\xac\xd2\x07\xf2\x81\xcb\x0a+{j\xb8\xbb\x22p\xc5\xc2\xc6D';;;\xfe\xac\xa5\xc7\xa5\xf4w\x00\x8d.P\xe3\xd34\xbd\x09\x8b\xb8\xeeI\x84s\x8f\xfb\xae\x19/s\xd0\x05 \x9b\xe9\x01p\x10\xdeP\x9es\x03\xecRD\x1f\xd0\x81\xde\xfb\xe2k\x18!\x84\x92\x1f\xc7\x91\xad\x8a\x83\x7f\x04\xa8\xe90\xc6\xc0\xbe\xefy\xcd9\x07R\xcar`\x8c\x11\x86a\xb8\xdd\xdb\xa5\x88J\xe6\xdaN)\xc1<\xcf\xb0\xae+^L`\xad\x05!\x04l\xdb\xd6\x0cJ\xfd\xdcT@\x8eM\xbcb(\xa5\xb2)\x8c\xe3\x98\xdf/\xcb\x92\xc7\x95.V\xde\x03\xee\xf8\xdd\x0a\xea\x91\xa6J\xb4\xd6\xc5\xcb\xd0}0\xea\x9f\xf9\x0a\xe0<\xcf,\xb7\xe2>\x8c\xd3ZEu~I\xbc\xed\xe7\xd3V\xf1\x12\x80\x1d+\xc6a\x10\x86\x81\x08\xf5\x11\x9121\x91\x0c\xf9\x7f\xbe\x90\x91\x85\x01>S.\x92\xa34\xd8&\xa8j'*E\x22\xc5\xf8\xec\x06\xdf\xd9}\xb8\xe8\x01\xf8\x01]\xa3\xa5\xa2N\xba]\x9c\x9a\xd7\x0b5TS\xf5\x09\x00\xce\xc19\x1cEs\x00\x92p\xab\x19\xb4\xce\xb9\xe8\xe9~[\xcd\x9c\xdd\xab\xc7y\x9b\x85\x14q\x17\x80\xb6\xa4\xe85\x80Q3\xc0\x1e\xe3\x12\x01\xa4\x94>\x00c\x8c\x22\x19^\xbe\xa6d\xbc\xef{\xd9\x1f\xbd\xdep\xf4~\xe5\x9e\xf7^t|\xab\x0e\xc8\xc1<\xcf\xc3\xba\xae\xf9\x1a2:M\xd3\xf7\x85\x86\x96\x16\xbf\xf7\xb6mY6!\x99\xf8,\xcb2\xf40\xb1\x08@\x0f[k\xb3&\x03\x00\xdf\x11 \xc0$\x1d\x16\xdf\x22I\xc0q\x0e\x10yr\x8c\xe8C\x08\xa7\x22\xe3\x80\xc6\xab\xf2\xc7\xc2T\xe9\x9c+{\x1c4&]\xceVU4P\x851\xa6\xab\x92\xb9\x80\x8e\xa1\xa1\x8c\xb1jW\xd1C\x03\x1c\x17q\x19\xa8\x8d\x97vx\xad\xd3\xee\xff\x1b\x90&\x17a\xddd\xd5\xd7\xb5-\xe8\xfa\xb2\xab\xa0\x11\x9b#@-\x83z\x04\xffk\xdb\xf2\x16\xa0=\xabgq\x10\x08\xa2r\xe8\x0f\xb0\xb0\xb1\x8dX(\xd8\xf9\xf7m\xacS\x8a\x96B\xdaD\xc1J\xbb\xe3\x0dL\xd8\xdb\xdb\x8f\xd1\x5c\xeer\x90\x85%\x82f}og\x9c\x997\xfb\xf4\x17\xfc\xfb\x9c\xff&\xf0&\xf0\xe2#\x94<\xa4\x86&[E\xa9\x87+_\xd7\xc1\xd5(\xd0;\x8b\x87\xa3\x10\x03G\x9d\x9d$\xc9\xaf\xee,\xb4*\x07~\x17\x11\xaf\x05\x00\x1e\x82\x993\x90m\x97M\xf5\xa6\xcd\x02.\x91\xcd\xbf\xd80\xac\xb1,\xcbq\x17\xb2\x95\xed.\x02\xae\xfa\xd4W\xf6\xdbzV\x0f}\x03>\xf0&\xc9\xe0\x12@\xa6{j\xf1\xa2>\xef*\xc5DQ\xc8\x07\x1as\x9egR\x0b&\x1d\xb4m\x1b\xf9\xb1\xde\xf9\xe39M\xd3\x97F\x9c\xc45w\x13\xf0\xb9\xcd8\x8e\xc10\x0c\xdf\xc0\xa1c\xd54M\xd0\xb6-\xf5\xcbu\x12}\xdf\xd3\x8c\xa2\xc8\x0a\xf8a\x0b\xf8,\x83Q\x14\x05I(\x80TIv]GMqL\x96Z|\x7f]W\x22^U\xd5\xae\xdd~J\x22\x83L\x83\xb2b\x01\x8a\x81\xc8\x813\xc1\xb2,\x83<\xcf\xe9Z\x8d& \x07\x91\x04-\xf3'\x99X\x0f\x7fP\xc8\x10R\x1cjqB\x08\xf0\xb8\x07q\x05\x81\xcb\xb2\x11dn\xb7\x1bY\xce\x16J\x7f\x8c\x80);\x9a^\x08\x90\xd8Q\xb8\xca\xe5r\xb9\xeb]\xfe?\xb4.\x80_\xafWz\x06\xe0mZK\xbf\xf6\x91\xfa\x90\xec\xb4\xad\x04P_p:\x9d\xe8\xa0\x08\xae\xc4\x9a\x99\xa5\x15\x8e\xd7\xb2,\x0b\xce\xe73\x91M\xd3\xd4\xb9\xd6\x1e\xab\x84Rw1\xc5j5\x8e\x03X]\xd7\xf4\x81\x22{\xeb`@@\xb5\x8c/\x13K-\x10J\x5cH\xa2\x921\xe28\xbe7\xf4M\x11\x85\x8f\xea\xf6\x10\xd0\x0f\x07\x0eU\xa3\xea\xc2\x12\x22\xd2\x0f\xd2\xe5\xfb\xd25\xc2\xa3\x11HJd\x0f\xc9#\xebyE\xbdZRK\xfa\xae\x92d$\x01.)\xa5\xdf]\x89W\x18\x9f\xa4\x95d\xdf\xae\xac\xa9\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0f\xde\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x04\x93IDATx\xdatL1\x0e\x001\x08B\xeb\xea\xff\x7f\xe8\xe4\x17\xd4\x0bv\xed\x91 $\x8023xA\xf1\x03\xe3\xc9\xccqw\x88\x08\xf8!\x22n\xa0\xaa8\xe7,\xab\x0afv\x03\xb6\xba{\x17Tr\x03\x1a6\xa9,\xd1\x7f\x02p:\x06'\x00\xc0 \x10\xab\xa5\x0f\xdd\x7f?\xd7\xd0\x87`I\xc1\x05\x9a\xd7q\xa0\x17\xf9\xb3\x8a\x88f\x18xcf\xb2\xc7\xca\xdd\x9f\x0d\x19\xdeFf6%T\xd5RU9s>\x12\x98\xc1\x15@8]\x85\xd7Q \x00\xb4\xfb?\x1f\x1f\x1f\xd8n\x98\xfd0\x93AXDD\x84\x11\xc5\x86\xb7o\xdf\xfe\xe7\xe6\xe6\x86k\x80\x853L\xc3\xcb\x97/\x19\xe4\xe5\xe5\x19\xe1F\x81\x14\x800L\xc3\xb6m\xdbP4\xc3leA\xb7\x1a\xc6vww\x07\x87\x00\xcc\x16\x10\x1b\xc3\xd3 \x7f \xdb\x06\x92\x83\xc9+))\x81\xfd\x00\x10\x80s*H\x01\x18\x06ae?\xf0\xff\xcf\xf3\xe8\xc9\x8b\xd7N\x0b\x96\xb4+\x1b\xae \xbd\x18Lb,\xbbt\xb5\xe2+\x03\xa6h\x11\xe9\xe8\x0a\x06SU\x9f\x1a\xcc\xacg3\x02\xa2<+\xa3w\x99\x90\x8d\x11C\x04D\x9e\x8e\x94\x12\x80K\xca;\xf9\x14\xed1\x09\x8a\xef.%\xdf(\x22j\xcc\xbc,n2\xf9-\x1ao{\x07\x1cEG|Q\xfc\x0e\xf0=\x8c\xff\x16\x80\x99*F\x81\x18\x04\x82\x16\xa9,m}\x80o\xb0\xf0\xdd\xf66~\xc27\xd8\xf8\x81c%\x13\xd6\xcd\x0a\xb9\x83\xc0\x05$\x22\x0c\xa3\xb33\xf3\xb5\x97^\xb7\xd2\xdf\x11\x1c\xf2\x80\xcfs\xd9\x1f\xc7\xe1\xa6iR\xfb\xb0\x0a\xd0j\xe0\xfcP\xb0Q\xd3G\xe7\x07T\xfe\xe8\xd9\xb6\xad4\xfd\x94\x92[\x96\x05R\x05L]9\xday\xef\xbb*\x19\xf1\x11y\xc6c\xac\xa9*zh\x00qQ\xf3\x17Y \x96\xf0\xb5\xe2\xf5B\x01D\x16\xb2\x0e\x92\xfb\xfa,\xd1uSU\xf0\x88\x8d\x08\xd0\xf2\xa0\x1e\xc1\xff*[\xde\x02\xb4g-)\x0c\xc2PPJ7\x1e\xc0\xad\xe0\x09\xf4\x04zi\x0f\xe1-\xdcx\x02w\xe2\xa6L\xe0\x95\x10\xde\xcf\xd8/\x18(M\xdbh3\xcf\xe4\xbd\x99\xc9\xdb\xff\xe0\xefk\xfe\x05\xe0\x02\xf0\xe3\xed\xee\x19\x14\xa7&\x89Q\xa6\xe9\xcar\x1d4\xa3 u\x16\xb3\xb3\x10M\x1c\xf4\xaa\xaa\xaa\x8fF\x16Z\x95\x12\xbf\x06\xc4|\x02\x98<\x04s\xec\xf7qQ\xe6\xf8\xa6\xe6MH\x22\x9b\xde\x110\xdcc]\xd7\xfc%$\xd1v\x0d\x80F#-\xda/yV\xa7\xf6\x805yN2h\x02\x88\xfb-&/\xf1x\x0f\xaf\xbe\xe7N\xde\x02\x06\x15\x01\xcbv\xdf\xf7\xf0\x19\x92\x12\xc7\x19\x1a\xad\xe6\xbe;\x0d\xc0\xbbl\xa8\x0fCo\x9a\xa6\xa2\xef\xfb\xa7\xa7L,N\xda\x17i\xd4\x8f<\x81\xac: E\x08}\x18\xfc`\xe5\x88v\xbc\x87\xc8\xe0\xb5\xae\xffz!\x83\xaa\xc5\x01\xdb8\x8eA]YY\xe4#\x85L\x93\x84\x5c\xbfm\xdb\xa2\xeb\xba\xe7\xd2\x81\x1e\xc5q\x11\x96UY\x96\xa2\x92\xcba\xc67k\xa2\x9atI\xabh\xfc\x22U\x82\x09C\xef6M\x13\x9e\x08\xa7\x125@\x16(\x13\x80F\x018*\x80H\xa7 \xb6m+\x96e\x09\x99H\xba\xce\x03\xe8\x945\xc1\xe5\xea4\x8f\xa3\xe1\xccl\x9e\xe7@\x03\x90JQ\xc9\xb1\xa1\x87aPU\xae\xf4\xdd)\x00qd,\x95L\x0d\x87\x99u]\x8b\x9e\xa4\x97JP?=\x1c\xc8\xde\xc4\x5c\x9e\xf6\x8c\xf7\x8e\xb1\xc0\xbc,\x0b\x1d\x05r\x04d\xce\xfdLQ\x1fSj\x8f\x11\xe9)F\x9e\x89{\xa8\xf4\xe5J\xfcB{\x00&\x9b+\xe1}\x9eoR\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x1b\xa9\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x10^IDATx\xdab\xfc\xff\xff?\x036\xc0\x04\x22\xd6\xaf_\xff\x7f\xf2\x8e\xdf\xc6\xe7.\xdd\x0c\x84\xcb\x80t\xec\xb9\xfc\xdf\x18H\x1b\x17,\xfao\xbc\xe4\xc8\x7f\xc6c\xcf\xb7\xfe\x07\xeb\xb8q\xe9\xb0\x5c\xdf6\x06\x86\xf5\x14\x83\x82\x8e\xcb\xae\xdb'W\xde\xde\xbau\xab\x15\x0b''\x97q\xed*\x06\xc6\xbcH3\x86wW\x97\x9e:\xf8\x7f\x05\x03\x83\x0c\x837\xb2\xa5L\xe6\xe6\xe6\xb1\xc6\xc6\xc6\xb7@|\x0c\xcbMLL\xb2\x81T\x0d@\x00\xa1H\xcc\x9b7\xef?+++\xc3\x97/_\x18~\xfc\xfa\xafY\x98\x9fu\x03\xdd\xb5`\x0dS\xa6L\xf9\xf3D \xc1\x5c_\x89\x87A_\x9e\x81AQ\x94\x81a\xfb\xf1\xc7\x0c\xff?^\x9f\x1c\xec\xe7f\x85\xe1=NNN\xe6\x9a\x10\x1e\x06C\xe9\x8f\x0ci\x13\x1f0\xf4ne`\xf8\xc5*\xcb\xc0\xcb'\xb8\x88\x91\x91Qy\xc7\xfd\xa5\xffMm\x8c\xfe\x83h\xb8\x93f\xcc\x9c}\x8d\x9b\x87\xff\x83\x8aIP>\xeb\xd7k\xc6\x0f\xef\xdf\x9e~\xe2\xdc\x05\x86KgN\xfd}\xf9\xf2e\x8c\xbe\xbe~\xfe\xa2E\x8b\x82\xb0\x0692\x06\x86\x82+(\x0a\x80\xf4e\x948\xc2\x11\x1bB@\xac\x02\xc4\xb6\x0a\x0a\x0a=@Z\x0c \x80p\xc6\x1d.\xc0\x02c\xf4O\x99\xa3.\xcc\xc7v\xe3\xcf\x9f?\x0c\x9f>}b(((`\xc4\xa6\x01l\xc3\x8c\x99\xb3\x0e\xbd\xe3\xb4\xea\xff\xc6\xaa\xf0HO\x91\x87AO\x8e\x81\xe1\xca\xb15gBBB\x18\xb1\xda\xc0/(\xce\xf2\x9d[\xfd\x91\xad2+\x83\x9a$\x03\x83\x00\x17\x03C\xc2>I\x93/_\x16\xfcIHH`\xc1\x88\x87\xdf\x12^\xf9\xaez\xac\x0c\xa6J\x0c\x0c'\xae\xbea\xf0\xeb\xfd\xcf\xe0\xe7n\xcd\xf0\xf7\xef_f`<\xc8\x1f~\xb0\xed\xff\xbeK[\xff\x03\xd9,`\xdd\xf2\xcc\x17\x8d\xb5eLN\xbb\x96\x9c`\xd05\xb0`\xf01d`P\x93``\xb8\xff\xfc7\xc3\xf6++\x1f\x14\xc5\xd4\xc0\x92\xceK\xb0\x1f\xb6o\xdf\xfe\xbf\xeb\x98\x91\x89\xbd\x818\x83\x020YhJ10p\xff\xb9'\xb7{\xfb\xc6\x07\xd7\x7f\x9e\xd8\xaak\xad*\x09\xd2\xb0\xa9\xe9\xe4T\xb0\x0d\x9e\x9e\x9e\x8cO\x9f\xce\xf9/\xc4\xa5\x97\xa9\xaddt\xf6\xf1\x8d\x03\x13\x9f~\xfad\xf9\xe2\xed{\x86\xd9\xad\xabX\x83\x83\x837\x7f\xfc\xf8\xf1\xee\xee\xdd\xbbsP\xe2\xe1\xc2C\x06Vmi\x06vV\x16\x86/ \xbe\xbf\x9f\x1f0\xa132<}\xfa\x94\xff\xcc\x993\x9f\xe0\x9e\x86\x01\x03y\x86\xdf0\xc5 \xb0q\xd3&F`2`\x90\x96\x02\xa6J\xe4P\xc2\x07@\x9a\x80I\x94\xc1\xcf\xdf\xef?JL\x03\x83\x0c]-'4-\xf1\x83\x5c\x07\xc4\xaf@\x82\x00\x01\x843-\xd9U>fdgad\xe0\xe1ba\x13\x13`\xff+/\xc1\xf9\xaf*\x90\xe3\x1f\x03\x89\x00\xc3\x829s\xe7]\x14\x14\xe0\xd7cf\xe3=\xfe\xe2\xbb\xe0\xa2\xfb\xdfU\xce\x8a\x09\xb22H3]3\xfe\xf7\xf9A\xdc\xb7\xaf\x9f,\xff\xfd\xfbw)--M\x9f$\x0b\xbaz'\x18IK\x88\x9e}\xf2O?\xe8\xe5_\xf9G\xc2|\xac\x0c\x92B\xec\x0c\x12\x02\x8c@\xcc\xc0 \xc2\x03\xf4?\x10\xf3p00\xec\x06\x06q`h\xf2\x01\xd3\xb9\x107\x03\xc3\xc7\xaf\xbf\x19\xb2\xfb.2<\xf8(\xc4\xa0\xa6\xaa\xf5\xc8\x5c\xf0[&\xe3\x9fg\xe7A\x8e\x84\xc6\x09P\x15\x030\xbd2\xb0\x03\xf1[ ~\x0dt\xfc?\xb8\x05,\xc0T\xae\xa6mrVL\x80\x19\xecZP\x06\xfa\xf2\xe3\x0f\xc3\xe2\xadW\x19\x16\x1e\xf8\xc1\xa0\xaci\xce\xa0\xa2b\xc2\xa0\x0b\x14\xe7\x07\x1a%\xca\xa3\x7f\xf6\xf5\xfb\x0b`\x83\x81E\xf5\xab\x03O\xd7q!\xbb\xfc\xdf\x0fF\x86\xe2\xe2\xe2jd\x0b\xfe~\x7f\xbc\x7f\xa2\xb2\x9akn\xcf\xaa\xfb\x0c;N\xbeb\x90T\xb1\x00\x96\x98\xfa\x0cA\xc0\xfaD\x04\xe8#an\x88\xaf@Au\xf3\xcc\xf6\x89?~\xfc\xf8\x0b\xcaz\xe79\xb7\xb1]8\xcb\xc0\xb0\xa2z\x0f\xdc\x82\x88V\x17\x867o\x98}Q\x22y\xd1\xe2\xa5\xc7\xb98\xd9-\x1e\xff\xd5\x0d\xfa\xc1\xa1\xfeH\x00h\xa0\x08/\xd0`\xa0\x81\xa0x\x10\x04\xba\xf1\xef\xf7\x17\x0e\xa7O\x1e\xefy\xf4\xe4\x09\x83\x99\x89\xc9O+++.{{\xfb\x06\x19\x7f\xb6\x92\x7f\x1c\xbf8af\xb1\x9dT8\xb5p\xe1\xc2\x00\xac\xc9\xb4\x7f\xea\x22]\xc6\xdf\xef.\xb1\xb3\xb33\x800\x0b\xa4\x90`\x00\x95nLLL\x9a+W\xad\x0a\xda\xb8aCkNN.\xc3\x8b\x97/\x18\x9e=}\xfa\x13(\xcd\x0b\xcc\x8e\xbf\x09&SR\x00\xb0\xfa\xf2\x92\x96\x92\xda*\x22*\xca\xf0\xe6\xf5\x1b\x86\xa7\xcf\x9e\x9e\x03\x0a\x9b\x03-\xfaC\x15\x0b\x90,\x0a\x93\x96\x96Z!\x22\x22\xca\xf8\xe6\xf5k\xa0E\xcf^\x02-\x91\xa0\x9a\x05H\x16e\x01}4\x15\xe8\xa3\x97s\xe7\xce\xa5\xbe\x05\xd8\x00@\x00\xda\xab5\xa6\xad2\x0c\xbf\x9cCO\xdbs\xdaA{zg=]\xd0-Fp8\xe7\x80\xf0c\xa8\x01o\x08\xd1\xc4\xf1c1!\xb8\xc4\x82n\x89+\xc9\xe2t&\xc61\x89a\x8a\xcb\xf8\xb3\xb0\x18/Y2\xc4\xc4\xe1d:\xc7\x0c\xa0eNS\xb5:1A\xb2r\x0e\xc85PF/\xd0\xbb\xef\xd7\xd6\xe2\x22\xd9\xc5t_r\xd2\xe6$\xe7}\xde\xf7\xfd\xde\xefy\x9e\xef\x8e\x03\xdc\x90N\x1f\x7fcJ\xf6\x5c\xc7\xbc2\xab\x15\x10\x16\xdd\xbd\xe9|7\xabdv\x11JFe\x83H$\x02\xa1P\x08B\xe1HO\x8bc\x7f\xfd\xff\x06x\xf7x\x97\xc5\xc4s\x7f\xa9\xd5j\xf0\xc56t\x8e\xfb-}1\xb9qa#\xb7\xc8+W\xff\xa8Y]\x9e\xddK\xc0\xf0\x5c\x14466N\xdd\x16@GG\x07\xa3\xd7\xebCr\xb5\xe9L\xef\xd5\x92V\xa3F\x0e\x84QMZ\x05X4)F%\x14\xf1\xdb\xf7\xe7\x0e\xf9\xfd\xbe\xa7\xbd\xfe\x98\xa2i\xcf\xee\xd0-{\x114aA9\xab\xf9\xf1\xe2\xf4\x03\xad\x82\x81\x01\x13\xaf\x00}^.XQ\xa3\xf4\xc8C\x1a.\xc5E\xfa\xea'Z\x9d\xdf\x0e\x16P\xd4|\xe0\xdf\xdf\xdf\xb0\x82\xb6\xf6\xf7\xacf}\xbe\xf4s\xb4\xae\xd4\xa0e\xe3\xba<\x19Xy\x1a\xf4\xea\x14\xc9\x91\xe0\x84]\xbd\xfe(<\x7fd\x18|\xb9\x9b(\xfb\xd6\xcb?,x\x97uM/\xecY\xb8i\x05\x0c\x15?N\xf8\xc6\xca\xab\xe2F\xa2^\xd8\x12\x925\x9f\x16\x19\x85\x0c\xe0\xd4\xd7\x1ex\xff\xfc<\xd0\xba\x0a\xb8_\xc8\x8d\xc7b\xc3\x10\x0d\xaf\x9c\xc0\xcf\x9f\xc5a \xd3\x88\x96\x04x|H\xdbf0\xf1@\x06\x80\xa6\xa9\x87\x91\xc4\xe0n3\x93\xec5\xc9\x9c\x04\xce\xc3\x01\x9d\xbb\x16\x81\xa3\xdd\x1e\x18p/\xc2\xb6\xed\xe5`\xceO\xbd\x8f_\x8b\x03Jg5\xf1KmmmgJ\x9f\xbc\xafFi\xc8\x81\xe8*\xc0\xc4\xf0R\x10\xdf\xdf\x8b b\x12\x80\x04'K\xd0\xa1\xa5\xdc\x90\x0a\xa0\xc6gd\xdc\x07\x8ec\x97!\xaa\xad\x80\xf2\xb2-`B-\xc8cS\x14\x9e@g\x82\x00\x09\x87\xc3q\xa0\xac\xfe\x9e\x1aJ\x11\x860\xb1\x04\x0c@\xc1CJ\x16Eh\x04A\x0c\xe9\x0ar\x07\x10\xa4n\xa36%\x8f\xb94\xc0\xe1\x0f~\x87\xfe\x11\x06tB\x15\x14[q\x08\x18\xc8\xb4\x8d$\xe1\xf1$\xc8\x19\xb9P\x5c\x5c\xdc\x12\xca\x09\xc0\xeb\xf5\xefd\xfa~\xf8\xe3\x16\xd0\x96\xc8\x88\xc2\x99\x92\x00\x9cJ\xb5\x8f\xa6\xe9:-\x1b\xa3\xaeN\x87\xe3G\xbb\xc7`d\x86\x85\x92\xa2\xbb\x92\xedJN\x90*\x05@\x82\xe7+c\xd4(\x9e\x07\xd4\x07\xfb\xc0\xc0@\x9fQ\x17+\x9f]^;\x16\xdf\x5c\xf9\x12\x12Q \x94-\xcf\x9c\x83\x9e\x9e\x9e(\xaf3\xfct\xe0T\xa8\x99\xdf\xfc(l\xb5\xa6\x14\x8c\xa8\x9a!\x9d9\x99(\x02\xe8r\x9e;=99Qh\xb7\xdb\xe9\xea\xea\xea\xce\xc2\x06\xc5K\xc5\xb65\x17sEtCb\x92\x9d\xee:\xf8\xc9\xb6\xcc\x1c\xb3*\x8dz~n&\xb8\xff1\xf5\xa1?)hU+\xd6\x02f\xaa\xc0\xdf_]Cg\xbd\xdeE\xf3\x83;J\xa1\xb9\xb9\xb9\xa1\xbf\xbf\xffHCA\xc3\x8eKe\xdf\x95f\x08n\x95Y\x99\xfc\xcaw\x928\x8b\xeb\xa8\xa2\xbd\xf3#[\xbe<4N\xeeSr\xb5\xb1Sc.\xea3\x9a-\x0bLt\x8e_\x9c\xf5\xd4,y\x17\xf7\xfa|>(\xdc\xbc\x05\xde|\xebmx\xf9E;\xbcz\xf0\x95\x06\x97\xcbu\xa1\xaa\xaa\xea5\x8b\xc5\xb2=\x10\x08,\x8d\x8d\x8d}\xe8v\xbb?\xc5\xd8\xd1\xff\x90]\xfb\x17@S\xa3\xc7>\x933t-\xd1c\x02\x86\xfb\x93\xbc\xdb \x0f\xf5:\x9d\xce]\xb5\xb5u!-\xaf\xcd9\xd9\xd5\x05\xa2$B0\x10\xdc\x87\x0a\xd6y[zpi\x14(\xdc\xd4\x1c\xb4)JN\x01\x11*\x07\xae\xe3\x9d\xca\xca\x9daA\x10d\xc4wI\x92\x04\x98y\x13\x82\x9c\xc8\xaa\xe0T\xee\xac\x94\x04\x9b\xd5J@DQ\x8a\x07\x83\x81g\x10\xe4\xf3[\x16\x9c\x9b\xad\xc1\xa1AA\x12\xa5\xa47\xb5\xd9\x04\x8a\xe3\xd8^\xd4\xe5G\xb2\x06@V \x18\xac\x90D\xf1\x17r\xf3\x10\x04\x1bp\x1cw\x11A\x9e\xca\x1a\x00\xb6d\x05A*Eib\x924\x1b\xf7\x05X\x8e=\x9b5\x804\xc82\xf6_\x98\x10%R\x08\xd8\xac\xb6\xa2u7y\x9dk\xd1zK\x96\xbe.\xfd\xf3(\x08\xdb\xa4\xa9Ze\xb6\x98\xf9\xe9\xa9\xe9!\xfc?\x85\xb1}w\xdc\xb6\xfc-@wV\x1e\x1bE\x19\xc5\xdf\xce\xec\xce\xec\xce\xcc\xeel)\xa5\xdb\x16H\x01)\x14I\xb8\x84?\x00mbPK+\x876\xf5V\xee\xa8\x1c\xfdCb4\x9a@D4\x1c\x16\x94\x16B \x08\xe1P9S\x82\x5c!\x04J\xc2!\xc8e\xb1\xa5\x14\xcae\xd9\x1e\x94\xbd\xba;\xdbmw\xd7\xf7\xe6\xc0\x82\xd5\x80\x8a6\xee\xe6\xe5\x9b\xec\xec\xec\xfe~\xf3\xbd\xf7}\xbf\xdf\x9bG\xfe\x07\x8f\xfa\xf5\xd0\x04\xe6/Y\xdfE\xb2\x84sXS4\x8fa\x99n\xf1X4\x19\xe21W4\x16\xaf\xc5\xed\xa7\x1e\xf7\xa0\x1b\xd1\xb8i\xbb\xc9\x22\xedy\xbf`J\xfd\x7fN`n\xe1wR\x8a=\xb4\xd0\xc6\xb3\xd38\x8e\xb3\x904\xa00\xf2\x0d\x01\xab\xcb \xe9?\xb2'\xc6H\xf2\x8c\x02\xcf\xb7\xa0\x22Xew8?\x98\xf0\xd6\xeb\xc1\x7f\x8d@QQQ\x92M\x10O\x88\x82\xad'*2\xdc\xa5\x85S\x9efq\xd7\xe1\x9b\xbd\xf6q\x1c\x1f\x13x\x06l\x18\xa2\xd5\x0c\x92\x8d\xd5\xc2\xca\xaa&\x9a\x04\x14\xcfF\x99\xda\xaa\xd2l\xa5\xe9\xce\x98\xd6\x96\xe6\xa1D\x0c\xff\xab:\x16g\x86O\x9a\xf8f\xdd#%P\xbc|\xc52I\x14fI\x92\x041\xb3\x5cr\xa6\xa1g\xf1\xedH'/g!\xc0\x8c\x0e\xd8\x0cDB\x03nRe\x8e\x80\xf6[\xc4\x108M\xa8\xd11\x8d\x8d\x8d\xf5\xce\x8b\x15\x17f*\xa1\xd0x\x9a\x15%\x1c)~\xed\xd5\x97g=\x12\x02K\xbf\xfc\xaa\xc4a\x97\xc6\x11\xf8&\x93k\xce9\xff\xc0=\xda\xddf\x11 \x0b\x22\x02w\x08,\x90\x1e \x80\xb8\xd1\x81\xc4k\xed\x0b\x1b\x82\xe5,\x1ah\x0a\xca\xb2HK\x0c\xca\xaa}\xb0tk5<\xdbO\xc9q\x89M\xf3\xa8u\x8a\x9b_\xc9\x84\x09\x13^\xf8G\x09,^\x5c8C\x92\x84b\x02\x1f\xe7\x93\x8b+[\x87\xaf\xb3\x0b\x16\xb0\x0bf\x95\x00\x81&\xa0\x0e\x9b6\xf2fM\xebQ\x10\x19\x22@\xfa\x13\xcb\x02\x0e\x9e\xbd\x03\x17\xaa\xbdp\xbe\xea\x0e\x94]\x0f\x83\x9c\xd2\x1f\xba$:\xe1\xf9\xd4\xc3\x13\xb9h\xfd\xccP(\x04\xa1\x90\xf2\xc6\xf4\xe9\xefn\xfa;\x04\xeeq\x0ef33\x83\xb4\x09\xa9\xe0\x90\xad\xf7\xee\x1e\x92\x88\xea\xd7\xa4\x02'\x15,\xb6\x01K\xc1#`\x0b\xab\x8d\x01\x94\xd3?\xe1\x9d.\xbb\xdc\x08\x1b\xf7W\x83'\xe2\x00\xde\x91\x06\x9d\x93\x9e\x80'Gh3E\xb3\x12`z\xeevF\xdc3\xa9&b\xb1\xe8\xc7E\xfb\xe1\x9bY\xcf\x81\xda\xce$\xed\x8f*zRZZ\xda\xf8\x84\x84\x84^\xb2,;\x08\x97\xcf\xe7\xf3{<\x9e+555%K\x96,Y\xab\xf7\x88Z\x7fG\x00\xc1g\x12xZaRS\xbb6J\x82I\x05L\xf9\xcd\xebiA\xa3M\x1f\xe3\xfa*\xb4\xf0\xdb\xabp\xe0l\x00\xa2\xc0C\xabI\x80\x84\xae\xa3`\xb0S\xd3\xb3T\x0f4SV\x83\xbc%\xa51\x1e\xd4\x9a\xd3xm\xe6\xa6\x05\xf9LA\xf6\xb6.\xe8-V\xf7\xe9\xd3';q\xb0\x85e\xef3\xcc\xc9\x90D\x06\xaeST\xc9\x18:r\xe4\xc8y\x95\x95\x95\xfb\x10\xe34\xbbR~,\xa7\xce\xfd\xcb<2\x16\xb8\x0a\xed,((\x18O\x8fW\xf2\xf2\xf2\x8a]\xd9\xec\x08F\x8e\xda\x09\xc7\xfe\x05\xa7\xdb%\xf0\xdc\x87C\xb4\xcd\xd3\xc7\x06j\xf7E\x8fn\xdf\xbe}f\xbb\xfb\xc0\x96-[V\xa2\xd9x[}Z\x141\x97\xec\xab\xeaZ\xdc,\xf6\xf7\x92}r\xe8\xf5`\xac@\x94&m\x8b\xdb8o\x90\xa3\xcf=\x0d7R\xce\xfcxb\xa1\x99e\xfa\xd1\x12jbL\xd1\xe3\xc7\x8e\xcfG\x99\xf9\x05:\xa2\xf8\x80\x01\x03\xc6dff\xbec\x1d\xe1\x1f\x16\xb7\xb5\xfci\xcb\xc8\xa4X\x94\xf0Q\xc7\xc9\x8a\x8a\x8a\x95\xe8\x9cv\xfd\xe1N\xbcb\xf5F\x97\xcd\x12\xfd\x81eL\xddUo\xceXO\xb5\x9a;\xed\xf2\xdb\xb3p'fc\xc6\xda/\xe8\xe0\xd5ML'\xc1\xa9\x04\xd1\xdd\x96\x9f\xcd\x0e\x05\x9b\xa6y\xbd\xden\x8a\xa2\x80\xcf\x1f\x80\xdd\x07\x0eB\xdf\x8c\xde\xf0J~\x1e464x\xf0.~v\xe8\xd0\xa15UUUrVV\xd6\x94\x94\x94\x94\xa7\x98\xc7\x9a2Z\xc5\x90\x14\xe3\x9bU2L3\xaf\x98\x83BS\xec\xb2t\xc9\xedv\x1f)--]\x83\x1f\xd7\xb4k\xfb\xee\x7f-Z\xb5\xdfa\x8d\x5c]\x04\xb1\xc8T\xac\x0d\x96f\x85\xb4\x90a\x07\x8d \x92F\x13\x82~\x93\xf4\x10E$\x12\x89\xe2\x12x\x10e\xc9\xb0\xa9S&;\xcf\x97]\x80\xcd[w@E\xe5E\xb0\x98bp\xbb\xae\x1emK\x90:\xac\x1f\x91?\xd55\xba\xa4\x07\xaf\xc3 G\xd8\xa4\x87Bm\xf6\xbf\xa4Fw\x9c\x04\xd3\xads\x9b\xd2Z\xc3\xbe\xdcx\xb4e,\x8bj\x14\xe1\xba\x10x\x12F\x03\x86\x1b\xbfv\x13\x09\xee\xc25\xfc\xfb\xfc\xfc\xfc\x9a\xb6\xd7\xe7\xe4\xe4\xbe8z\xf4\xe8eee\xe7S\x15%lBqH\x04qf\xfc\xe0\xf3z\xa9..\xe3\xd7\xa6cj\x1d\xe8\xb0~\x00\x0d!\x15\xeaKh\x0e\xdf\x93\x9dr?\xd9.\x03\xc7s\xd0\x8cD\xfc\xbf\x11\x09\xe0w&#\x91m\x1d\xd6\xd0 \x11j\x1fMA\x22\xf3\x90\x88\xd3 rwF<^J-\xf5\x118\x12Y\xd5\xa1\x1d\x19\x92\x99$\x08\xe2\xd7\xce\x04\x19d\xbb\x03\x89\xf0*\x11\xbf\xdf\x07\x1e\x8f\x0f5S\xb0\x96\xbc\x14=\x92A2\x81\x0ek)\x91\xc8X$\xb2\xd6\xe9\x94;a\x0d\x81Q#~\x1f\x12\xf1\xf9\xe2\xa1`\xb0\x8c\x1e\x1c\x22\x89\xa2\x0e\xed\x89\x91\xc83\x82(,\xc3\xcc\xea{\x97H\xb3\x9aZu>\x9fgTi\xe9\x91\x0b\x1d\xde\xd4#\x09Z\x93G\x8b\x82\xf8\xa9\x9c \xa7\xca\x0e\xf9\xe9\x0d\x1b6\x94?T\x0d<`\x9f\xe5A\x1e\xd8\xb0\xba\xf2\xb5\xb4\x19\x8d0\xce\xd1\x9f\x09z\x8fF\xd0\xfb5f\xab\xd5\xca\x87\xc3a/\x1e{0\xee\xe8A{A\xe0\x7f\xd1V\xf9\x15N\x0d\x19H\xb5\x03\xcb\x8b\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x10\xaa\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x000\x00\x00\x000\x08\x06\x00\x00\x01 \x05\xc9\x11\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x0a\xe1iTXtXML:com.adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \xc2\xa9 2009 by Oliver Twardowski\x0a \x0a \x0a \x0a \x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0aI\xaa4\xe0\x00\x00\x05_IDATx\xdab\xfc\xff\xff?\x036\xc0\xc4\x80\x03`H\x18\xe4\xf1\x81\x8d`\x81\x09\xd8\xf5\x08\xff\xff\xf4\xe87\x03\x9f\x1c+\x98\xcf\x88\xcb\x0e\xb8\x0e\xe32\xfe\xff\x7f\x7f@\x14]\x98\xf4\x89\x11 \x80\x18)s\x15\xccEp;\xecz\x84\x80.\xfa\x83\xa9\xe3\xfb\xbb\xbfpK\xadZ\x05!\xba@\x96\x83\xb0Q)\xef\x7f\xfd\x5c\xde\xff0>@\x00\xe1t\x15\xd1AE\x08\xb0\xa0\x0b8N\x16\x81X\xc9\x08F`\x86\x82\x84\x08\xc3\xbc\x90\xeb\x8c(n\x05a\x8b&\x01\x14\xf7\xc2\xb0]\xaf\x10\x5c\x0c\xc5\x86\xef\xaf\xff2\xb0\xf131\x98\xd5\x0a\xfcg\x04\xda\xf3\x1fj\x0b\xb2/Q<\xcd\xc8\xc8\xc8\x0e\xa4\xe4\x818\x18\x88O\x00\xf1] \xfe\x04\xc4\xdf\x80\xea~\x81\xd4\x00\x04\x10\xedCi\x805\xa4l\xd0\xc6\xf0 \x8a\xa7\x1d\xa7\x88\x82\x02\x93\x01\x1e\xa4P\xa9w\xb7\x7f\x81\x13\x1a\x86\x0d\x7f\x7f\xfccx\x7f\xeb\x17X\x01\x88\xfe\xf3\xf3?\xc3\xbe\xdc7\x8c8\x93\xc6\x9f\xef\xff\xe0&\xc1\x80}\x9f\xf0\x7f\x9c~\xf8\xf6\xfa/\x86?\xfe\x00m\xc5i\x83\xba\x96\x10\x8390Y\x80\x8d\xfc\xcf\x00O\x17\xc8\xb6\xa2h\xb8y\xed\x1d\x033\x07#\x03,\x1b\xc3r\x15\x03\x9e\xb4\xc4\x0d\xa4\xf4\x80\xd8\x04\x88\x8f\x02\xf1#P\x9a\x04\xaa\xf9\x0aS\x03\x10@$\xa7%\x9a\xa7\x8c\xe1c\x01r!\x89\x0f\x10\x8c\x03|\x06\x09k\xb2\x83\xca(\x86\xb77~a\xa4\x06\x82\x16\x80\x0c\x16Pb\x03&\xbb\xff\x0c\xa8:\x19Q\x13%\x14|\xb8\xf7\x1b\xab%,\xb8\x5c\xc7#\xc9\x02\xce\x05_\x9e\xfd\xc1\xaa\x11\xe63\x90\x1c\x88-\xa8\xc2\x06\x16CW\x8b\xd3\x82?\xdf\xff\x83]\x8f\x0b \x1b\xc4#\xc3\xc2\xf0\x1b-K\x92\x14\x07\x1az\x22\x0c\x0f\x9f\xbdg\xf8\xfe\xe6/V5\x1c\xc2\xcc\xe0\xe0\xfa\xf1\xf6\x1f\xfe\x9c\x89\x0cL*\xf9\xff3\xc2\xc3\x1c\x16\xda\xb0:\x89\x11\x1a\x0b\xff\xe1\xb1\xf1\xfb\xeb\x7f\xacAITN\x06\xf9\x82\x11\xd8f8\xdf\x8b=\xa5P\x94L)\x05\x00\x014Z\x16\x8dZ0j\x01\x05\x16\x80\xdaM\xc4T:dW8\xc2\x1a\xec\xe0\xf6\x96\xa2\xb8\x18\xc3\xd9\x03\x8f\xc9\xabp\x04\xd5\xd8 \x0d7p\x13\x05\xa9z\xf9\x0f-\x02\x19\x11U\x8f\x82\x84(\xa2%NL\x10\xf1+\xb12\xfc\x07vC\xfe\xfd\xfd\xcf\xf0\xef\x0f2\x06\x89A\xf1\xef\xff\x105@|\xee\xd0S\xe2\xfa\x090\xd7\x83\xba\x5c \xdf}z\xfc\x1b\xabF^\xa0<\xc8\xb9\x9f\x1e\xfd\x82\xd7jD[\x00\x02 \xd7)H\x8b0,)\xbd\xc5\x88\xcb\x11\xb0pw\x9c$\xfc\xdf\xc0F\x9a\xb4T\xf4\x17h\xc1\xbf_\xff\x08V\x99 \x8b\xfe\x82\x83\xed\x1fi\x16\x80\xc2\xfb\xef\xdf\x7fD%\xd9\xff\xd0\xf8!\xc9\x02\x90\xa6\x9bW\xdf\xc0\x83\x03_\x9a\xff\xfb\x07\x12\xd9$\xc5\xc1\xa9\xb6\x8f\xe0 \x88\x98\xa3\xf4\x1fT\xa9\xffx\xfb\x17k\x9e\x00\xc9\xfd\xfb\x8d;\x88\xf0\xe6\x035-\x11\x86\x07O\xdf\xc1\x13;#J\xe5\xcf\x88\xe0\x03\x89_\x9f\xfe\x11_\xe9\x83\x0cg\xe1fDjA\xfc\xc7h\xd31\xa0uB\xcf\xb4\x7fd$\xb9\xa8\x00Y\xc4\xcc\x0e\x0d\xe7\x9f\x0cD5\xc2\xc8*\x8b\x98X\x19\xc0\xe1\x8c\xcf \xa2{\xfcH\xdd#N %\x08\xc4\x86@\xac\x0f\xec\xd8/\x00\x8a\xf9\x01\xd9\xa0\xa4\xf5\x1e\x88\xdf\x82Z\x98P\x0c\xeaz\xff\x01:\xf6\x0f\xdd\x9b-\x00\x01\xda\xb5\x9a\x95\x06b \x9c\xa8U(\x05Q\x16\x11\x17\xa1\x1e\xfc\xa1\x0aB\xef\xe2\xcb\xf8\x0e>\x96\x8f\xe0A\x8f\xeaE\x0f\x1e<\xb4hA\xa1\x94j\x11\xb5\xdb\xce:\x93MLv\xb7\xd8M\xeaV\x94\x0d\x0c\xcd\x12\x9a\xe4K6\x99\xf9\xbe\xd9\xdc\x07\xf8\xf3\x1e\xb3\x00P\x00(\x00\x14\x00\xdc\x1cA^\xc5\x8c\x18\x5c<\xd7\xaf\x03\xa0\xe2\xd5\x16D\x00qt\xb2+\xbc\xd0\xe5\xe9\xbd3\x18gG\xa6V\xb2~\xb8\x8e\x9d\x80\xd4|gd\x04\x03\xfa\xed\xe4\xc0\x92bP\xe3\xa9m\xe8)\xa4\x17sVE\xfe\xc2\xb0\x1f\x15\xfdg\x05d\x0d \xd2\xb4JB^\x13\x83\x1b\x9a\x1a\xe7\xba\xaf\x10\x11qn\x86gZ\xf6\xe1*`\xa3\x04\x03\xe7:|\x0b5\x7f\xea\xde\xf53\x81\xb0\x02 \xe8N\xb5\xc4R\xbd\x1aA\xa4\xaeF\xa8\xd4\xa4\xa26\x15\xc4\x86\xb1\xffR\xfbs#p:\x17\xd6g \x0c\xa2\xe1\x05m]\xf5\xd8\xf5\xc5\xa3\xd5\xa0\xb4\x08\x15\x7fNL\xba\xf70H1$\xb2\xfa\x81\xcf\xae\xceZ\x99\xfa\xb5\x06@\x14L\xa9\x800t;\xc4\xe7\xc7\x1d\x9e\x04EL\x99\xb4_\xda\x12\xc8\xc8\xd6\xdcv`\x00\x0c\x84\xc4\x09V\x03\x8d\xbb:\x896\xaa\x06\xa2\xfb\xb992\xc1\xf0\xa4\x860\x0c\xc0J1\xf9~a\xa8o\x90|9\xc73\xf0\xf1\x02\xa9\x95T\x13\xae\xed\xaf0WvAJ\x82:\xd5\x10d\xdf\xd9\x89\xfd\xc0\xf6\x9e\xc7\x9aH\x9d\x8d\x5c\x17\xab\xfaK\xb2F~\x01\xc6\xbe\x00\x8dV;\x96\xdd\xd8\xf0\x97\xd9\xedM;\x9fkts\xc7\x93\x83v\x8c\x8b1\xcd\xf7Y,G\x9c&\xeb<\xe6\x13\xf4\xc5J\xcf\xfd\x1e\xe4\xeb\xc8\xe8w\xb6\xcc\xbf\xb2\xad\xe9L\xb6tJ\xcay\xa9\x94z\x98h70Q\xfe\xc6\xc5\x0fL\xcc\x89\x85v1\xcf\xb5\x0a\x89\x15\xe8\x87?\x12\xa8M%\x98\x1bu\x98\xa7Y\xacw@~\xda0#m\x11\xad\x82VF#\x1df\x0bmM.L\x17\xad\x89\xd6\xa3\xcb\x0b\xed\x1d\xed\x0d\xedU\xd6!aCi\xe2y\x94F\xf3/e\x95O\x08\xef\x09\x03\x8d\x1a\xcd\xa5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xf9PyLoT - the Python picking and Localisation Tool\x0a\x0a

PyLoT is a program which is capable of picking seismic phases,\x0aexporting these as numerous standard phase format and localize the corresponding\x0aseismic event with external software as, e.g.:

\x0a
    \x0a
  • NonLinLoc
  • \x0a
  • HypoInvers
  • \x0a
  • HypoSat
  • \x0a
  • whatever you want ...
  • \x0a
\x0a

Read more on the\x0aPyLoT WikiPage.

\x0a

Bug reports are very much appreciated and can also be delivered on our\x0aPyLoT TracPage after\x0asuccessful registration.

\x0a\x0a" +qt_resource_name = "\x00\x04\x00\x06\xec0\x00h\x00e\x00l\x00p\x00\x05\x00o\xa6S\x00i\x00c\x00o\x00n\x00s\x00\x06\x07\xa7(\x98\x00s\x00p\x00l\x00a\x00s\x00h\x00\x0a\x08\x94\x19\x07\x00s\x00p\x00l\x00a\x00s\x00h\x00.\x00p\x00n\x00g\x00\x09\x0fG\xb4\xc7\x00k\x00e\x00y\x00_\x00T\x00.\x00p\x00n\x00g\x00\x09\x0fC\xb4\xc7\x00k\x00e\x00y\x00_\x00P\x00.\x00p\x00n\x00g\x00\x09\x03g\xa7G\x00p\x00y\x00l\x00o\x00t\x00.\x00p\x00n\x00g\x00\x0a\x0c\xad\x0f\x07\x00d\x00e\x00l\x00e\x00t\x00e\x00.\x00p\x00n\x00g\x00\x09\x0fH\xb4\xc7\x00k\x00e\x00y\x00_\x00U\x00.\x00p\x00n\x00g\x00\x09\x0fD\xb4\xc7\x00k\x00e\x00y\x00_\x00Q\x00.\x00p\x00n\x00g\x00\x0a\x0a\xc8\xf7'\x00f\x00i\x00l\x00t\x00e\x00r\x00.\x00p\x00n\x00g\x00\x0a\x08\xaa Date: Mon, 9 Nov 2015 16:12:12 +0100 Subject: [PATCH 0675/1144] first implementation of a checkerboard modification for a given vgrids.in file --- pylot/core/active/seismicArrayPreparation.py | 130 +++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index a26b659c..2a22dbcb 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -511,6 +511,136 @@ class SeisArray(object): print('Wrote %d points to file %s for %d layers'%(count, outfilename, nlayers)) outfile.close() + def addCheckerboard(self, spacing = 20, pertubation = 1, inputfile = 'vgrids.in', outputfile = 'vgrids_cb.in'): + def readNumberOfPoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + nR = int(vglines[1].split()[0]) + nTheta = int(vglines[1].split()[1]) + nPhi = int(vglines[1].split()[2]) + + print('readNumberOf Points: Awaiting %d grid points in %s' + %(nR*nTheta*nPhi, filename)) + fin.close() + return nR, nTheta, nPhi + + def readDelta(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + dR = float(vglines[2].split()[0]) + dTheta = float(vglines[2].split()[1]) + dPhi = float(vglines[2].split()[2]) + + fin.close() + return dR, dTheta, dPhi + + def readStartpoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + sR = float(vglines[3].split()[0]) + sTheta = float(vglines[3].split()[1]) + sPhi = float(vglines[3].split()[2]) + + fin.close() + return sR, sTheta, sPhi + + def readVelocity(filename): + ''' + Reads in velocity from vgrids file and returns a list containing all values in the same order + ''' + vel = []; count = 0 + fin = open(filename, 'r') + vglines = fin.readlines() + + for line in vglines: + count += 1 + if count > 4: + vel.append(float(line.split()[0])) + + print("Read %d points out of file: %s" %(count - 4, filename)) + return vel + + def correctSpacing(spacing, delta, disttype = None): + if spacing > delta: + spacing_corr = round(spacing / delta) * delta + elif spacing < delta: + spacing_corr = delta + print('The spacing of the checkerboard of %s (%s) was corrected to ' + 'a value of %s to fit the grid spacing of %s.' %(spacing, disttype, spacing_corr, delta)) + return spacing_corr + + + R = 6371. # earth radius + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + outfile = open(outputfile, 'w') + + # Theta, Phi in radians, R in km + nR, nTheta, nPhi = readNumberOfPoints(inputfile) + dR, dThetaRad, dPhiRad = readDelta(inputfile) + sR, sThetaRad, sPhiRad = readStartpoints(inputfile) + vel = readVelocity(inputfile) + + dTheta, dPhi = np.rad2deg((dThetaRad, dPhiRad)) + sTheta, sPhi = np.rad2deg((dThetaRad, dPhiRad)) + + eR = sR + (nR - 1) * dR + ePhi = sPhi + (nPhi - 1) * dPhi + eTheta = sTheta + (nTheta - 1) * dTheta + + nPoints = nR * nTheta * nPhi + + thetaGrid = np.linspace(sTheta, eTheta, num = nTheta) + phiGrid = np.linspace(sPhi, ePhi, num = nPhi) + rGrid = np.linspace(sR, eR, num = nR) + + # write header for velocity grid file (in RADIANS) + outfile.writelines('%10s %10s \n' %(1, 1)) + outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) + outfile.writelines('%10s %10s %10s\n' %(dR, dThetaRad, dPhiRad)) + outfile.writelines('%10s %10s %10s\n' %(sR, sThetaRad, sPhiRad)) + + spacR = correctSpacing(spacing, dR, '[meter], R') + spacTheta = correctSpacing(self._getAngle(spacing), dTheta, '[degree], Theta') + spacPhi = correctSpacing(self._getAngle(spacing), dPhi, '[degree], Phi') + + count = 0 + evenOdd = 1 + even = 0; odd = 0 + + # In the following loop it is checked whether the positive distance from the border of the model + # for a point on the grid divided by the spacing is even or odd and then pertubated. + # The position is also shifted by half of the delta so that the position is directly on the point and + # not on the border between two points. + for radius in rGrid: + if np.floor((radius - sR - dR/2) / spacR) % 2: + evenOddR = 1 + else: + evenOddR = -1 + for theta in thetaGrid: + if np.floor((theta - sTheta - dTheta/2) / spacTheta) % 2: + evenOddT = 1 + else: + evenOddT = -1 + for phi in phiGrid: + if np.floor((phi - sPhi - dPhi/2) / spacPhi) % 2: + evenOddP = 1 + else: + evenOddP = -1 + velocity = vel[count] + evenOdd = evenOddR * evenOddT * evenOddP + velocity += evenOdd * pertubation + + outfile.writelines('%10s %10s\n'%(velocity, decm)) + count += 1 + + progress = float(count) / float(nPoints) * 100 + self._update_progress(progress) + + outfile.close() + def exportAll(self, filename = 'interpolated_receivers.out'): recfile_out = open(filename, 'w') count = 0 From 4f67e3e31ac8662cf47c117a742c7ae45bad2180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 13:58:25 +0100 Subject: [PATCH 0676/1144] Debuged: Checks additionally for component 3 if component Z not available. --- pylot/core/pick/autopick.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 5a5cc4e5..4e943299 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,6 +11,7 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np +import pdb from scipy import integrate from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf @@ -28,7 +29,6 @@ def autopickevent(data, param): wdttolerance = param.getParam('wdttolerance') mdttolerance = param.getParam('mdttolerance') iplot = param.getParam('iplot') - for n in range(len(data)): station = data[n].stats.station if station not in stations: @@ -141,6 +141,8 @@ def autopickstation(wfstream, pickparam): # split components zdat = wfstream.select(component="Z") + if len(zdat) == 0: # check for other components + zdat = wfstream.select(component="3") edat = wfstream.select(component="E") if len(edat) == 0: # check for other components edat = wfstream.select(component="2") From 7ed1ad298316a7a20e15b9c95da56bf36287f393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 14:04:06 +0100 Subject: [PATCH 0677/1144] Debuged indentation error at lines 341 to 345. --- pylot/core/pick/autopick.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 4e943299..65c40e5d 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,7 +11,6 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np -import pdb from scipy import integrate from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf @@ -339,10 +338,10 @@ def autopickstation(wfstream, pickparam): else: index = min([3, len(zc) - 1]) calcwin = (zc[index] - zc[0]) * z_copy[0].stats.delta - # calculate source spectrum and get w0 and fc - specpara = DCfc(z_copy, mpickP, calcwin, iplot) - w0 = specpara.getw0() - fc = specpara.getfc() + # calculate source spectrum and get w0 and fc + specpara = DCfc(z_copy, mpickP, calcwin, iplot) + w0 = specpara.getw0() + fc = specpara.getfc() print ("autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, " "Polarity: %s" % (Pweight, SNRP, SNRPdB, FM)) From cd26d85f7cf06e10a1d8a65c65b95d3bdc0329e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 14:10:44 +0100 Subject: [PATCH 0678/1144] Debuged: Checks additionally for component 3 if component Z not available. --- pylot/core/pick/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 8319d90a..2eed988a 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -867,6 +867,8 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): # split components zdat = X.select(component="Z") + if len(zdat) == 0: # check for other components + zdat = X.select(component="3") edat = X.select(component="E") if len(edat) == 0: # check for other components edat = X.select(component="2") From c01c88b6570d3c162eae3f86b5ac89c74961aa27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 14:51:14 +0100 Subject: [PATCH 0679/1144] earllatepicker: take half wavelength for getting earliest possible pick as suggested by Diehl et al.. --- pylot/core/pick/utils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 2eed988a..916847da 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -8,7 +8,7 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ -import pdb + import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream, UTCDateTime @@ -91,9 +91,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): # determine all zero crossings in signal window (demeaned) zc = crossings_nonzero_all(x[pis] - x[pis].mean()) # calculate mean half period T0 of signal as the average of the - T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length - # T0/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - T0 / 2 + T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length! + EPick = Pick1 - T0 # half wavelength as suggested by Diehl et al. # get symmetric pick error as mean from earliest and latest possible pick From 2ea2db0791e1c6a3c1bac81b35e22cb3c749fd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 14:53:50 +0100 Subject: [PATCH 0680/1144] Debuged: Calculates real onset times for pick dictionary only, if earliest and latest possible pick are not None. --- pylot/core/pick/autopick.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 65c40e5d..2d156149 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,6 +11,7 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np +import pdb from scipy import integrate from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf @@ -760,12 +761,12 @@ def autopickstation(wfstream, pickparam): plt.close() ########################################################################## # calculate "real" onset times - if mpickP is not None: + if mpickP is not None and epickP is not None and mpickP is not None: lpickP = zdat[0].stats.starttime + lpickP epickP = zdat[0].stats.starttime + epickP mpickP = zdat[0].stats.starttime + mpickP - if mpickS is not None: + if mpickS is not None and epickS is not None and mpickS is not None: lpickS = edat[0].stats.starttime + lpickS epickS = edat[0].stats.starttime + epickS mpickS = edat[0].stats.starttime + mpickS From b5b74532141a2289881ef30ea34996a0e38ad38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 15:09:50 +0100 Subject: [PATCH 0681/1144] Debuged writephases: if first motion (fm) is None, ? is written to NLLoc-phase file. --- pylot/core/pick/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 916847da..9887c78d 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -963,6 +963,8 @@ def writephases(arrivals, fformat, filename): for key in arrivals: if arrivals[key]['P']['weight'] < 4: fm = arrivals[key]['P']['fm'] + if fm == None: + fm = '?' onset = arrivals[key]['P']['mpp'] year = onset.year month = onset.month From 54cb6d2f9a537bb1df7ee433680525ce4984b71c Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 11 Nov 2015 15:36:43 +0100 Subject: [PATCH 0682/1144] added interfaces generation for FMTOMO from SeisArray still working on propgrid generation for FMTOMO from SeisArray --- pylot/core/active/seismicArrayPreparation.py | 196 ++++++++++++++++--- 1 file changed, 167 insertions(+), 29 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 2a22dbcb..499abca6 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -303,9 +303,9 @@ class SeisArray(object): self._interpolateXY4rec() self.interpZcoords4rec() - def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, method = 'linear', filename = 'interface1.in'): + def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, elevation = 0.25, method = 'linear'): ''' - Interpolate Z values on a regular grid with cushion nodes to use it as FMTOMO topography interface. + Interpolate Z values on a regular grid with cushion nodes e.g. to use it as FMTOMO topography interface. Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). :param: nTheta, number of points in theta (NS) @@ -319,17 +319,38 @@ class SeisArray(object): :param: phiWE (W, E) extensions of the model in degree type: tuple + + :param: elevation, default: 0.25 (elevate topography so that no source lies above the surface) + type: float + ''' + return self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, elevation, method) + + def interpolateOnRegularGrid(self, nTheta, nPhi, thetaSN, phiWE, elevation, method = 'linear'): + ''' + Interpolate Z values on a regular grid with cushion nodes e.g. to use it as FMTOMO topography interface. + Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). + + :param: nTheta, number of points in theta (NS) + type: integer + + :param: nPhi, number of points in phi (WE) + type: integer + + :param: thetaSN (S, N) extensions of the model in degree + type: tuple + + :param: phiWE (W, E) extensions of the model in degree + type: tuple + + :param: elevation, default: 0.25 (elevate topography so that no source lies above the surface) + type: float ''' surface = [] - elevation = 0.25 # elevate topography so that no source lies above the surface - if filename is not None: - outfile = open(filename, 'w') - - print "Interpolating topography on regular grid with the dimensions:" + print "Interpolating interface on regular grid with the dimensions:" print "nTheta = %s, nPhi = %s, thetaSN = %s, phiWE = %s"%(nTheta, nPhi, thetaSN, phiWE) - print "method = %s, filename = %s" %(method, filename) + print "method = %s, elevation = %s" %(method, elevation) thetaS, thetaN = thetaSN phiW, phiE = phiWE @@ -352,17 +373,130 @@ class SeisArray(object): # in case the point lies outside, nan will be returned. Find nearest: if np.isnan(z) == True: z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = 'nearest') - z = float(z) + z = float(z) + elevation surface.append((xval, yval, z)) count += 1 progress = float(count) / float(nTotal) * 100 self._update_progress(progress) - if filename is not None: - outfile.writelines('%10s\n'%(z + elevation)) - return surface + def generateInterfaces(self, nTheta, nPhi, depthmax, cushionfactor = 0.1, + outfilename = 'interfaces.in', method = 'linear', + returnInterfaces = False): + ''' + Create an interfaces.in file for FMTOMO from the SeisArray boundaries. + :param: nTheta, number of points in Theta + type: int + + :param: nPhi, number of points in Phi + type: int + + :param: depthmax, maximum depth of the model (below topography) + type: float + + :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) + type: float + ''' + nInterfaces = 2 + + # generate dimensions of the grid from array + phiWE, thetaSN = self.getThetaPhiFromArray(cushionfactor) + + thetaS, thetaN = thetaSN + phiW, phiE = phiWE + R = 6371. + + outfile = open(outfilename, 'w') + + # determine the deltas + thetaDelta = abs(thetaN - thetaS) / float((nTheta - 1)) + phiDelta = abs(phiE - phiW) / float((nPhi - 1)) + + # write header for interfaces grid file (in RADIANS) + outfile.writelines('%10s\n' %(nInterfaces)) + outfile.writelines('%10s %10s\n' %(nTheta + 2, nPhi + 2)) # +2 cushion nodes + outfile.writelines('%10s %10s\n' %(np.deg2rad(thetaDelta), np.deg2rad(phiDelta))) + outfile.writelines('%10s %10s\n' %(np.deg2rad(thetaS - thetaDelta), np.deg2rad(phiW - phiDelta))) + + interface1 = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method) + interface2 = self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, -depthmax, method = method) + + for point in interface1: + z = point[2] + outfile.writelines('%10s\n'%(z + R)) + + outfile.writelines('\n') + for point in interface2: + z = point[2] + outfile.writelines('%10s\n'%(z + R)) + + outfile.close() + + if returnInterfaces == True: + return interface1, interface2 + + def getThetaPhiFromArray(self, cushionfactor = 0.1): + ''' + Determine and returns PhiWE (tuple: (West, East)) and thetaSN (tuple (South, North)) from the SeisArray boundaries. + + :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) + type: float + ''' + x, y, _ = self.getAllMeasuredPointsLists() + phi_min, phi_max = (self._getAngle(min(x)), self._getAngle(max(x))) + theta_min, theta_max = (self._getAngle(min(y)), self._getAngle(max(y))) + cushionPhi = abs(phi_max - phi_min) * cushionfactor + cushionTheta = abs(theta_max - theta_min) * cushionfactor + phiWE = (phi_min - cushionPhi, phi_max + cushionPhi) + thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) + return phiWE, thetaSN + + def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionfactor = 0.1, + refinement = (5, 5), outfilename = 'propgrid.in'): + ''' + Create a propergation grid file for FMTOMO using SeisArray boundaries + + :param: nTheta, number of points in Theta + type: int + + :param: nPhi, number of points in Phi + type: int + + :param: nR, number of points in R + type: int + + :param: Rbt (bot, top) extensions of the model in km + type: tuple + + :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) + type: float + + :param: refinement, (refinement factor, number of local cells for refinement) used by FMTOMO + type: tuple + ''' + outfile = open(outfilename, 'w') + + R = 6371. + + phiWE, thetaSN = self.getThetaPhiFromArray(cushionfactor) + + thetaS, thetaN = thetaSN + phiW, phiE = phiWE + rbot = Rbt[0] + R + rtop = Rbt[1] + R + + thetaDelta = abs(thetaN - thetaS) / float((nTheta - 1)) + phiDelta = abs(phiE - phiW) / float((nPhi - 1)) + rDelta = abs(rbot - rtop) / float((nR - 1)) + + outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) + outfile.writelines('%10s %10s %10s\n' %(rDelta, thetaDelta, phiDelta)) + outfile.writelines('%10s %10s %10s\n' %(Rbt[1], thetaS, phiW)) + outfile.writelines('%10s %10s\n' %refinement) + + outfile.close() + def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, Rbt = (-62.0, 6.0), thetaSN = None, phiWE = None, outfilename = 'vgrids.in', @@ -395,10 +529,10 @@ class SeisArray(object): type: str ''' - def getRad(angle): - PI = np.pi - rad = angle / 180 * PI - return rad + # def getRad(angle): + # PI = np.pi + # rad = angle / 180 * PI + # return rad def readMygridNlayers(filename): infile = open(filename, 'r') @@ -429,19 +563,12 @@ class SeisArray(object): R = 6371. vmin = 0.34 - cushionfactor = 0.1 # add some extra space to the model decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) outfile = open(outfilename, 'w') # generate dimensions of the grid from array if thetaSN is None and phiWE is None: - x, y, _ = self.getAllMeasuredPointsLists() - phi_min, phi_max = (self._getAngle(min(x)), self._getAngle(max(x))) - theta_min, theta_max = (self._getAngle(min(y)), self._getAngle(max(y))) - cushionPhi = abs(phi_max - phi_min) * cushionfactor - cushionTheta = abs(theta_max - theta_min) * cushionfactor - phiWE = (phi_min - cushionPhi, phi_max + cushionPhi) - thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) + phiWE, thetaSN = self.getThetaPhiFromArray() thetaS, thetaN = thetaSN phiW, phiE = phiWE @@ -464,10 +591,10 @@ class SeisArray(object): # write header for velocity grid file (in RADIANS) outfile.writelines('%10s %10s \n' %(1, 1)) outfile.writelines('%10s %10s %10s\n' %(nR + 2, nTheta + 2, nPhi + 2)) - outfile.writelines('%10s %10s %10s\n' %(rDelta, getRad(thetaDelta), getRad(phiDelta))) - outfile.writelines('%10s %10s %10s\n' %(rbot - rDelta, getRad(thetaS - thetaDelta), getRad(phiW - phiDelta))) + outfile.writelines('%10s %10s %10s\n' %(rDelta, np.deg2rad(thetaDelta), np.deg2rad(phiDelta))) + outfile.writelines('%10s %10s %10s\n' %(rbot - rDelta, np.deg2rad(thetaS - thetaDelta), np.deg2rad(phiW - phiDelta))) - surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method, filename = None) + surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method) print("\nGenerating velocity grid for FMTOMO. " "Output filename = %s, interpolation method = %s"%(outfilename, method)) @@ -511,7 +638,16 @@ class SeisArray(object): print('Wrote %d points to file %s for %d layers'%(count, outfilename, nlayers)) outfile.close() - def addCheckerboard(self, spacing = 20, pertubation = 1, inputfile = 'vgrids.in', outputfile = 'vgrids_cb.in'): + def addCheckerboard(self, spacing = 20., pertubation = 0.1, inputfile = 'vgrids.in', outputfile = 'vgrids_cb.in'): + ''' + Add a checkerboard to an existing vgrids.in velocity model. + + :param: spacing, size of the tiles + type: float + + :param: pertubation, pertubation (default: 0.1 = 10%) + type: float + ''' def readNumberOfPoints(filename): fin = open(filename, 'r') vglines = fin.readlines() @@ -631,7 +767,7 @@ class SeisArray(object): evenOddP = -1 velocity = vel[count] evenOdd = evenOddR * evenOddT * evenOddP - velocity += evenOdd * pertubation + velocity += evenOdd * pertubation * velocity outfile.writelines('%10s %10s\n'%(velocity, decm)) count += 1 @@ -639,6 +775,8 @@ class SeisArray(object): progress = float(count) / float(nPoints) * 100 self._update_progress(progress) + print('Added checkerboard to the grid in file %s with a spacing of %s and a pertubation of %s [km/s]. ' + 'Outputfile: %s.'%(inputfile, spacing, pertubation, outputfile)) outfile.close() def exportAll(self, filename = 'interpolated_receivers.out'): From dc088509d4a4066a412a62a9b18bd3c57e5cf23f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 15:45:07 +0100 Subject: [PATCH 0683/1144] For multiple event processing eventID has to be derived out of event string. Uses module string. --- autoPyLoT.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 71a8031f..ebb300b3 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -6,7 +6,7 @@ import os import argparse import glob import subprocess - +import string from obspy.core import read from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter @@ -99,7 +99,6 @@ def autoPyLoT(inputfile): data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) print('Working on event %s' % event) print(data) - wfdat = data.getWFData() # all available streams ########################################################## # !automated picking starts here! @@ -114,7 +113,8 @@ def autoPyLoT(inputfile): # For locating the event the NLLoc-control file has to be modified! # create comment line for NLLoc-control file # NLLoc-output file - nllocout = '%s/loc/%s_%s' % (nllocroot, event, nllocoutpatter) + evID = event[string.rfind(event, "/") + 1 : len(events) - 1] + nllocout = '%s/loc/%s_%s' % (nllocroot, evID, nllocoutpatter) locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) print ("Modifying NLLoc-control file %s ..." % locfile) # modification of NLLoc-control file @@ -133,7 +133,7 @@ def autoPyLoT(inputfile): ########################################################## # write phase files for various location routines # HYPO71 - hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, eventID) + hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, evID) writephases(picks, 'HYPO71', hypo71file) print '------------------------------------------' From 39a12bd1d16ba9a90d26dff330a297b0d37dcd3d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 11 Nov 2015 15:45:56 +0100 Subject: [PATCH 0684/1144] bugfix: rays2vtk last point not displayed --- pylot/core/active/fmtomo2vtk.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index 59c0968e..4e45d3ff 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -47,6 +47,9 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a return sR, sTheta, sPhi def readVelocity(filename): + ''' + Reads in velocity from vgrids file and returns a list containing all values in the same order + ''' vel = []; count = 0 fin = open(filename, 'r') vglines = fin.readlines() @@ -59,7 +62,7 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a print("Read %d points out of file: %s" %(count - 4, filename)) return vel - R = 6371 # earth radius + R = 6371. # earth radius outfile = open(outputfile, 'w') # Theta, Phi in radians, R in km @@ -167,7 +170,7 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): rays[shotnumber] = {} rays[shotnumber][raynumber] = [] for index in range(nRayPoints): - if index%nthPoint is 0 or index == nRayPoints: + if index % nthPoint is 0 or index == (nRayPoints - 1): rad, lat, lon = infile.readline().split() rays[shotnumber][raynumber].append([getDistance(np.rad2deg(float(lon))), getDistance(np.rad2deg(float(lat))), float(rad) - R]) else: From a1fbea98be323dc7550c158ff6c99bb414c0b384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 Nov 2015 16:11:25 +0100 Subject: [PATCH 0685/1144] Debuged: checks minimum number of zero crossings before calculating source spectrum from P pulse. --- pylot/core/pick/autopick.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 2d156149..0e1c3030 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,7 +11,6 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np -import pdb from scipy import integrate from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf @@ -332,7 +331,7 @@ def autopickstation(wfstream, pickparam): # calculate spectrum using only first cycles of # waveform after P onset! zc = crossings_nonzero_all(wfzc) - if np.size(zc) == 0: + if np.size(zc) == 0 or len(zc) <= 3: print ("Something is wrong with the waveform, " "no zero crossings derived!") print ("Cannot calculate source spectrum!") From 1f383579b4805e2345e4a7943b4846110ac22614 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 12 Nov 2015 09:23:28 +0100 Subject: [PATCH 0686/1144] [new] added a new utility function to find a pattern in a text file and returning the particular line as a string or None if not found --- pylot/core/util/utils.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index bbfe4d4d..483aabcb 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# -# -*- coding: utf-8 -*- import os import subprocess @@ -69,6 +67,30 @@ def getHash(time): def getOwner(fn): return pwd.getpwuid(os.stat(fn).st_uid).pw_name +def getPatternLine(fn, pattern): + """ + Takes a file name and a pattern string to search for in the file and + returns the first line which contains the pattern string otherwise None. + + :param fn: file name + :type fn: str + :param pattern: pattern string to search for + :type pattern: str + :return: the complete line containing pattern or None + + >>> getPatternLine('utils.py', 'python') + '#!/usr/bin/env python\\n' + >>> print(getPatternLine('version.py', 'palindrome')) + None + """ + fobj = open(fn, 'r') + for line in fobj.readlines(): + if pattern in line: + fobj.close() + return line + + return None + def prepTimeAxis(stime, trace): nsamp = trace.stats.npts @@ -342,3 +364,7 @@ def createAmplitude(pickID, amp, unit, category, cinfo): amplitude.type = ope.AmplitudeCategory(category) amplitude.pick_id = pickID return amplitude + +if __name__ == "__main__": + import doctest + doctest.testmod() From d933c30148b0d1439316f4965917385c5b3eb031 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 12 Nov 2015 09:25:33 +0100 Subject: [PATCH 0687/1144] [bugfix] now the whole line containing LOCFILES is replaced with a modified string using the getPatternLine utility function --- pylot/core/loc/nll.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index f411906c..3ecd1211 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -2,8 +2,13 @@ # -*- coding: utf-8 -*- import subprocess +import os from obspy.core.event import readEvents from pylot.core.pick.utils import writephases +from pylot.core.util.utils import getPatternLine +from pylot.core.util.version import get_git_version as _getVersionString + +__version__ = _getVersionString() def picksExport(picks, phasefile): ''' @@ -16,7 +21,7 @@ def picksExport(picks, phasefile): # write phases to NLLoc-phase file writephases(picks, 'NLLoc', phasefile) -def modfiyInputFile(fn, root, outpath, phasefile, tttable): +def modfiyInputFile(fn, root, outpath, phasefn, tttn): ''' :param fn: @@ -29,18 +34,21 @@ def modfiyInputFile(fn, root, outpath, phasefile, tttable): # For locating the event we have to modify the NLLoc-control file! # create comment line for NLLoc-control file NLLoc-output file print ("Modifying NLLoc-control file %s ..." % fn) - nllocout = '%s/loc/%s_%s' % (root, outpath) - locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, tttable, nllocout) + nllocout = os.path.join(root,'loc', outpath) + phasefile = os.path.join(root, 'obs', phasefn) + tttable = os.path.join(root, 'time', tttn) + locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0\n' % (phasefile, tttable, nllocout) # modification of NLLoc-control file + curlocfiles = getPatternLine(fn, 'LOCFILES') nllfile = open(fn, 'r') filedata = nllfile.read() if filedata.find(locfiles) < 0: # replace old command - filedata = filedata.replace('LOCFILES', locfiles) + filedata = filedata.replace(curlocfiles, locfiles) nllfile = open(fn, 'w') nllfile.write(filedata) - nllfile.close() + nllfile.close() def locate(call, fnin): ''' From d611b8606e62e50944381dc9a6a7e64735e32d6a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 12 Nov 2015 11:25:51 +0100 Subject: [PATCH 0688/1144] finished generatePropgrid, changed getThetaPhiFromArray output to the right order --- pylot/core/active/seismicArrayPreparation.py | 67 ++++++++++---------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 499abca6..4d9b81e6 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -358,11 +358,11 @@ class SeisArray(object): measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() # need to determine the delta to add two cushion nodes around the min/max values - thetaDelta = (thetaN - thetaS) / (nTheta - 1) - phiDelta = (phiE - phiW) / (nPhi - 1) + deltaTheta = (thetaN - thetaS) / (nTheta - 1) + deltaPhi = (phiE - phiW) / (nPhi - 1) - thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes + thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num = nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num = nPhi + 2) # +2 cushion nodes nTotal = len(thetaGrid) * len(phiGrid); count = 0 for theta in thetaGrid: @@ -401,7 +401,7 @@ class SeisArray(object): nInterfaces = 2 # generate dimensions of the grid from array - phiWE, thetaSN = self.getThetaPhiFromArray(cushionfactor) + thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) thetaS, thetaN = thetaSN phiW, phiE = phiWE @@ -410,14 +410,14 @@ class SeisArray(object): outfile = open(outfilename, 'w') # determine the deltas - thetaDelta = abs(thetaN - thetaS) / float((nTheta - 1)) - phiDelta = abs(phiE - phiW) / float((nPhi - 1)) + deltaTheta = abs(thetaN - thetaS) / float((nTheta - 1)) + deltaPhi = abs(phiE - phiW) / float((nPhi - 1)) # write header for interfaces grid file (in RADIANS) outfile.writelines('%10s\n' %(nInterfaces)) outfile.writelines('%10s %10s\n' %(nTheta + 2, nPhi + 2)) # +2 cushion nodes - outfile.writelines('%10s %10s\n' %(np.deg2rad(thetaDelta), np.deg2rad(phiDelta))) - outfile.writelines('%10s %10s\n' %(np.deg2rad(thetaS - thetaDelta), np.deg2rad(phiW - phiDelta))) + outfile.writelines('%10s %10s\n' %(np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) + outfile.writelines('%10s %10s\n' %(np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) interface1 = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method) interface2 = self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, -depthmax, method = method) @@ -450,9 +450,9 @@ class SeisArray(object): cushionTheta = abs(theta_max - theta_min) * cushionfactor phiWE = (phi_min - cushionPhi, phi_max + cushionPhi) thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) - return phiWE, thetaSN + return thetaSN, phiWE - def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionfactor = 0.1, + def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionpropgrid = 0.05, refinement = (5, 5), outfilename = 'propgrid.in'): ''' Create a propergation grid file for FMTOMO using SeisArray boundaries @@ -469,7 +469,8 @@ class SeisArray(object): :param: Rbt (bot, top) extensions of the model in km type: tuple - :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) + :param: cushionpropogrid, cushionfactor for the propagationgrid (cushion direction + opposing to vgrids cushionfactor) type: float :param: refinement, (refinement factor, number of local cells for refinement) used by FMTOMO @@ -477,22 +478,22 @@ class SeisArray(object): ''' outfile = open(outfilename, 'w') - R = 6371. + thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor = 0) - phiWE, thetaSN = self.getThetaPhiFromArray(cushionfactor) + thetaS = thetaSN[0] + cushionpropgrid + thetaN = thetaSN[1] - cushionpropgrid + phiW = phiWE[0] + cushionpropgrid + phiE = phiWE[1] - cushionpropgrid + rbot = Rbt[0] + rtop = Rbt[1] - thetaS, thetaN = thetaSN - phiW, phiE = phiWE - rbot = Rbt[0] + R - rtop = Rbt[1] + R - - thetaDelta = abs(thetaN - thetaS) / float((nTheta - 1)) - phiDelta = abs(phiE - phiW) / float((nPhi - 1)) - rDelta = abs(rbot - rtop) / float((nR - 1)) + deltaTheta = abs(thetaN - thetaS) / float(nTheta - 1) + deltaPhi = abs(phiE - phiW) / float(nPhi - 1) + deltaR = abs(rbot - rtop) / float(nR - 1) outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' %(rDelta, thetaDelta, phiDelta)) - outfile.writelines('%10s %10s %10s\n' %(Rbt[1], thetaS, phiW)) + outfile.writelines('%10s %10s %10s\n' %(deltaR, deltaTheta, deltaPhi)) + outfile.writelines('%10s %10s %10s\n' %(rtop, thetaS, phiW)) outfile.writelines('%10s %10s\n' %refinement) outfile.close() @@ -568,7 +569,7 @@ class SeisArray(object): # generate dimensions of the grid from array if thetaSN is None and phiWE is None: - phiWE, thetaSN = self.getThetaPhiFromArray() + thetaSN, phiWE = self.getThetaPhiFromArray() thetaS, thetaN = thetaSN phiW, phiE = phiWE @@ -576,14 +577,14 @@ class SeisArray(object): rtop = Rbt[1] + R # need to determine the delta to add two cushion nodes around the min/max values - thetaDelta = abs(thetaN - thetaS) / float((nTheta - 1)) - phiDelta = abs(phiE - phiW) / float((nPhi - 1)) - rDelta = abs(rbot - rtop) / float((nR - 1)) + deltaTheta = abs(thetaN - thetaS) / float((nTheta - 1)) + deltaPhi = abs(phiE - phiW) / float((nPhi - 1)) + deltaR = abs(rbot - rtop) / float((nR - 1)) # create a regular grid including +2 cushion nodes in every direction - thetaGrid = np.linspace(thetaS - thetaDelta, thetaN + thetaDelta, num = nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - phiDelta, phiE + phiDelta, num = nPhi + 2) # +2 cushion nodes - rGrid = np.linspace(rbot - rDelta, rtop + rDelta, num = nR + 2) # +2 cushion nodes + thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num = nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num = nPhi + 2) # +2 cushion nodes + rGrid = np.linspace(rbot - deltaR, rtop + deltaR, num = nR + 2) # +2 cushion nodes nTotal = len(rGrid) * len(thetaGrid) * len(phiGrid) print("Total number of grid nodes: %s"%nTotal) @@ -591,8 +592,8 @@ class SeisArray(object): # write header for velocity grid file (in RADIANS) outfile.writelines('%10s %10s \n' %(1, 1)) outfile.writelines('%10s %10s %10s\n' %(nR + 2, nTheta + 2, nPhi + 2)) - outfile.writelines('%10s %10s %10s\n' %(rDelta, np.deg2rad(thetaDelta), np.deg2rad(phiDelta))) - outfile.writelines('%10s %10s %10s\n' %(rbot - rDelta, np.deg2rad(thetaS - thetaDelta), np.deg2rad(phiW - phiDelta))) + outfile.writelines('%10s %10s %10s\n' %(deltaR, np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) + outfile.writelines('%10s %10s %10s\n' %(rbot - deltaR, np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method) From d53e4b7c0c14feeb2ecc5c9d8835f0adb73d3996 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 12 Nov 2015 13:24:48 +0100 Subject: [PATCH 0689/1144] implemented a function that generates all grids for FMTOMO --- pylot/core/active/seismicArrayPreparation.py | 77 +++++++++++++++++--- 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 4d9b81e6..0bd3a30f 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -381,6 +381,34 @@ class SeisArray(object): return surface + def generateFMTOMOinputFromArray(self, nRP, nThetaP, nPhiP, nRI, nThetaI, nPhiI, + Rbt, cushionfactor, interpolationMethod = 'linear', + customgrid = 'mygrid.in'): + print('\n------------------------------------------------------------') + print('Automatically generating input for FMTOMO from array size.') + print('Propgrid: nR = %s, nTheta = %s, nPhi = %s'%(nRP, nThetaP, nPhiP)) + print('Interpolation Grid and Interfaces Grid: nR = %s, nTheta = %s, nPhi = %s'%(nRI, nThetaI, nPhiI)) + print('Bottom and Top of model: (%s, %s)'%(Rbt[0], Rbt[1])) + print('Method: %s, customgrid = %s'%(interpolationMethod, customgrid)) + print('------------------------------------------------------------') + + def getZmin(surface): + z = [] + for point in surface: + z.append(point[2]) + return min(z) + + self.generatePropgrid(nThetaP, nPhiP, nRP, Rbt, cushionpropgrid = 0.05) + surface = self.generateVgrid(nThetaI, nPhiI, nRI, Rbt, method = interpolationMethod, + cushionfactor = cushionfactor, infilename = customgrid, + returnTopo = True) + + depthmax = abs(Rbt[0] - getZmin(surface)) - 1.0 # cushioning for the bottom interface + + self.generateInterfaces(nThetaI, nPhiI, depthmax, cushionfactor = cushionfactor, + returnInterfaces = False, method = interpolationMethod) + + def generateInterfaces(self, nTheta, nPhi, depthmax, cushionfactor = 0.1, outfilename = 'interfaces.in', method = 'linear', returnInterfaces = False): @@ -398,6 +426,9 @@ class SeisArray(object): :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) type: float ''' + + print('\n------------------------------------------------------------') + print('Generating interfaces...') nInterfaces = 2 # generate dimensions of the grid from array @@ -436,6 +467,9 @@ class SeisArray(object): if returnInterfaces == True: return interface1, interface2 + print('Finished generating interfaces.') + print('------------------------------------------------------------') + def getThetaPhiFromArray(self, cushionfactor = 0.1): ''' Determine and returns PhiWE (tuple: (West, East)) and thetaSN (tuple (South, North)) from the SeisArray boundaries. @@ -466,7 +500,7 @@ class SeisArray(object): :param: nR, number of points in R type: int - :param: Rbt (bot, top) extensions of the model in km + :param: Rbt (bot, top) extensions of the model in m type: tuple :param: cushionpropogrid, cushionfactor for the propagationgrid (cushion direction @@ -478,6 +512,13 @@ class SeisArray(object): ''' outfile = open(outfilename, 'w') + print('\n------------------------------------------------------------') + print('Generating Propagation Grid for nTheta = %s, nPhi' + ' = %s, nR = %s and a cushioning of %s' + %(nTheta, nPhi, nR, cushionpropgrid)) + print('Bottom of the grid: %s, top of the grid %s' + %(Rbt[0], Rbt[1])) + thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor = 0) thetaS = thetaSN[0] + cushionpropgrid @@ -498,10 +539,13 @@ class SeisArray(object): outfile.close() - def generateVgrid(self, nTheta = 80, nPhi = 80, nR = 120, - Rbt = (-62.0, 6.0), thetaSN = None, - phiWE = None, outfilename = 'vgrids.in', - method = 'linear', infilename = 'mygrid.in'): + print('Created Propagation Grid and saved it to %s' %outfilename) + print('------------------------------------------------------------') + + def generateVgrid(self, nTheta, nPhi, nR, Rbt, thetaSN = None, + phiWE = None, cushionfactor = 0.1, + outfilename = 'vgrids.in', method = 'linear', + infilename = 'mygrid.in', returnTopo = False): ''' Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. @@ -520,7 +564,7 @@ class SeisArray(object): :param: phiWE (W, E) extensions of the model in degree type: tuple - :param: Rbt (bot, top) extensions of the model in km + :param: Rbt (bot, top) extensions of the model in m type: tuple :param: vbot, velocity at the bottom of the model @@ -529,6 +573,8 @@ class SeisArray(object): :param: method, interpolation method for topography type: str ''' + print('\n------------------------------------------------------------') + print('generateVgrid: Starting...') # def getRad(angle): # PI = np.pi @@ -547,6 +593,7 @@ class SeisArray(object): infile = open(filename, 'r') nlayers = readMygridNlayers(filename) + print('\nreadMygrid: Reading file %s.'%filename) for index in range(nlayers): line1 = infile.readline() line2 = infile.readline() @@ -554,6 +601,10 @@ class SeisArray(object): vtop.append(float(line1.split()[1])) zbot.append(float(line2.split()[0])) vbot.append(float(line2.split()[1])) + print('Layer %s:\n[Top: v = %s [km/s], z = %s [m]]' + '\n[Bot: v = %s [km/s], z = %s [m]]' + %(index + 1, vtop[index], ztop[index], + vbot[index], zbot[index])) if not ztop[0] == 0: print('ERROR: there must be a velocity set for z = 0 in the file %s'%filename) @@ -569,7 +620,7 @@ class SeisArray(object): # generate dimensions of the grid from array if thetaSN is None and phiWE is None: - thetaSN, phiWE = self.getThetaPhiFromArray() + thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) thetaS, thetaN = thetaSN phiW, phiE = phiWE @@ -597,15 +648,15 @@ class SeisArray(object): surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method) + nlayers = readMygridNlayers(infilename) + ztop, zbot, vtop, vbot = readMygrid(infilename) + print("\nGenerating velocity grid for FMTOMO. " "Output filename = %s, interpolation method = %s"%(outfilename, method)) print("nTheta = %s, nPhi = %s, nR = %s, " "thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt)) count = 0 - nlayers = readMygridNlayers(infilename) - ztop, zbot, vtop, vbot = readMygrid(infilename) - for radius in rGrid: for theta in thetaGrid: for phi in phiGrid: @@ -636,9 +687,13 @@ class SeisArray(object): progress = float(count) / float(nTotal) * 100 self._update_progress(progress) - print('Wrote %d points to file %s for %d layers'%(count, outfilename, nlayers)) + print('\nWrote %d points to file %s for %d layers'%(count, outfilename, nlayers)) + print('------------------------------------------------------------') outfile.close() + if returnTopo == True: + return surface + def addCheckerboard(self, spacing = 20., pertubation = 0.1, inputfile = 'vgrids.in', outputfile = 'vgrids_cb.in'): ''' Add a checkerboard to an existing vgrids.in velocity model. From 6177c6292c09afc016a2f040c97a1ef36ffbc729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 12 Nov 2015 17:21:04 +0100 Subject: [PATCH 0690/1144] Cosmetics, focused on python-3 compatibility of print statements. --- autoPyLoT.py | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index ebb300b3..e3f696f5 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -88,7 +88,9 @@ def autoPyLoT(inputfile): nllocoutpatter = parameter.getParam('outpatter') else: locflag = 0 - print ("!!No location routine available, autoPyLoT just picks the events without locating them!!") + print (" !!! ") + print ("!!No location routine available, autoPyLoT is running in non-location mode!!") + print (" !!! ") # multiple event processing @@ -130,20 +132,28 @@ def autoPyLoT(inputfile): # locate the event subprocess.call([nlloccall, locfile]) + + # !iterative picking if traces remained unpicked or with bad picks! + # get theoretical onset times for picks with weights >= 4 + # in order to reprocess them using smaller time windows ########################################################## # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, evID) writephases(picks, 'HYPO71', hypo71file) - print '------------------------------------------' - print '-----Finished event %s!-----' % event - print '------------------------------------------' + endsplash = '''------------------------------------------\n' + -----Finished event %s!-----\n' + ------------------------------------------'''.format \ + (version=_getVersionString()) % evID + print(endsplash) + if locflag == 0: + print("autoPyLoT was running in non-location mode!") # single event processing else: data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) - print 'Working on event ', parameter.getParam('eventID') + print("Working on event "), parameter.getParam('eventID') print data wfdat = data.getWFData() # all available streams @@ -175,22 +185,30 @@ def autoPyLoT(inputfile): # locate the event subprocess.call([nlloccall, locfile]) + + # !iterative picking if traces remained unpicked or with bad picks! + # get theoretical onset times for picks with weights >= 4 + # in order to reprocess them using smaller time windows ########################################################## # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.getParam('eventID')) writephases(picks, 'HYPO71', hypo71file) + endsplash = '''------------------------------------------\n' + -----Finished event %s!-----\n' + ------------------------------------------'''.format \ + (version=_getVersionString()) % parameter.getParam('eventID') + print(endsplash) + if locflag == 0: + print("autoPyLoT was running in non-location mode!") - print '------------------------------------------' - print '-------Finished event %s!-------' % parameter.getParam('eventID') - print '------------------------------------------' - - print '####################################' - print '************************************' - print '*********autoPyLoT terminates*******' - print 'The Python picking and Location Tool' - print '************************************' + endsp = '''####################################\n + ************************************\n + *********autoPyLoT terminates*******\n + The Python picking and Location Tool\n + ************************************'''.format(version=_getVersionString()) + print(endsp) if __name__ == "__main__": # parse arguments From db3429e780fb38725cdf9e128fc7b1202e9fcf7a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 12 Nov 2015 19:08:33 +0100 Subject: [PATCH 0691/1144] improved text output and fixed some bugs --- pylot/core/active/seismicArrayPreparation.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 0bd3a30f..6179840b 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -398,7 +398,8 @@ class SeisArray(object): z.append(point[2]) return min(z) - self.generatePropgrid(nThetaP, nPhiP, nRP, Rbt, cushionpropgrid = 0.05) + self.generatePropgrid(nThetaP, nPhiP, nRP, Rbt, cushionfactor = cushionfactor, + cushionpropgrid = 0.05) surface = self.generateVgrid(nThetaI, nPhiI, nRI, Rbt, method = interpolationMethod, cushionfactor = cushionfactor, infilename = customgrid, returnTopo = True) @@ -486,7 +487,7 @@ class SeisArray(object): thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) return thetaSN, phiWE - def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionpropgrid = 0.05, + def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionfactor, cushionpropgrid = 0.05, refinement = (5, 5), outfilename = 'propgrid.in'): ''' Create a propergation grid file for FMTOMO using SeisArray boundaries @@ -503,6 +504,9 @@ class SeisArray(object): :param: Rbt (bot, top) extensions of the model in m type: tuple + :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) + type: float + :param: cushionpropogrid, cushionfactor for the propagationgrid (cushion direction opposing to vgrids cushionfactor) type: float @@ -519,7 +523,7 @@ class SeisArray(object): print('Bottom of the grid: %s, top of the grid %s' %(Rbt[0], Rbt[1])) - thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor = 0) + thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) thetaS = thetaSN[0] + cushionpropgrid thetaN = thetaSN[1] - cushionpropgrid From 69011f13ac3f5519f900fcdc50ec0d6dee6f9a8b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 13 Nov 2015 14:05:29 +0100 Subject: [PATCH 0692/1144] [new] started to incorporate NLLoc into manualPyLoT --- QtPyLoT.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 57bd17fe..80627ca2 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -41,6 +41,7 @@ from obspy import UTCDateTime, readEvents from pylot.core.read.data import Data from pylot.core.read.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent +from pylot.core.loc.nll import locate as locateNll from pylot.core.util.defaults import FILTERDEFAULTS from pylot.core.util.errors import FormatError, DatastructureError,\ OverwriteError @@ -54,6 +55,7 @@ from pylot.core.util.thread import AutoPickThread from pylot.core.util.version import get_git_version as _getVersionString import icons_rc +locateTool = dict(nll=locateNll) class MainWindow(QMainWindow): __version__ = _getVersionString() @@ -765,7 +767,12 @@ class MainWindow(QMainWindow): self.draw() def locateEvent(self): - pass + settings = QSettings() + loctool = settings.value("loc/tool", "nll") + extlocpath = settings.value("%s/binPath".format(loctool), None) + locroot = settings.value("%s/rootPath".format(loctool), None) + if extlocpath is None or locroot is None: + pass def check4Loc(self): return self.picksNum() > 4 From ed1727299911da3b55acc05b674ca9a8ed47008b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 14 Nov 2015 11:46:46 +0100 Subject: [PATCH 0693/1144] [bugfix] attribute was unset before using property setter --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 80627ca2..02b0dd1d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -92,7 +92,7 @@ class MainWindow(QMainWindow): self.filteroptions = {} self.pickDlgs = {} self.picks = {} - self.locflag(False) + self.loc = False # UI has to be set up before(!) children widgets are about to show up self.setupUi() From 0a0ab4bfa9b8be749761696014f2baa184bb6d18 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 16 Nov 2015 11:48:45 +0100 Subject: [PATCH 0694/1144] added checkerboard to fmtomo utils and added different checkerboard ampl. functions --- pylot/core/active/fmtomoUtils.py | 417 +++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 pylot/core/active/fmtomoUtils.py diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py new file mode 100644 index 00000000..20586b16 --- /dev/null +++ b/pylot/core/active/fmtomoUtils.py @@ -0,0 +1,417 @@ +# -*- coding: utf-8 -*- +import sys +import numpy as np + +def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'abs', inputfileref = 'vgridsref.in'): + ''' + Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in + ''' + def getDistance(angle): + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + + def readNumberOfPoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + nR = int(vglines[1].split()[0]) + nTheta = int(vglines[1].split()[1]) + nPhi = int(vglines[1].split()[2]) + + print('readNumberOf Points: Awaiting %d grid points in %s' + %(nR*nTheta*nPhi, filename)) + fin.close() + return nR, nTheta, nPhi + + def readDelta(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + dR = float(vglines[2].split()[0]) + dTheta = float(vglines[2].split()[1]) + dPhi = float(vglines[2].split()[2]) + + fin.close() + return dR, dTheta, dPhi + + def readStartpoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + sR = float(vglines[3].split()[0]) + sTheta = float(vglines[3].split()[1]) + sPhi = float(vglines[3].split()[2]) + + fin.close() + return sR, sTheta, sPhi + + def readVelocity(filename): + ''' + Reads in velocity from vgrids file and returns a list containing all values in the same order + ''' + vel = []; count = 0 + fin = open(filename, 'r') + vglines = fin.readlines() + + for line in vglines: + count += 1 + if count > 4: + vel.append(float(line.split()[0])) + + print("Read %d points out of file: %s" %(count - 4, filename)) + return vel + + R = 6371. # earth radius + outfile = open(outputfile, 'w') + + # Theta, Phi in radians, R in km + nR, nTheta, nPhi = readNumberOfPoints(inputfile) + dR, dTheta, dPhi = readDelta(inputfile) + sR, sTheta, sPhi = readStartpoints(inputfile) + vel = readVelocity(inputfile) + + nX = nPhi; nY = nTheta; nZ = nR + + sZ = sR - R + sX = getDistance(np.rad2deg(sPhi)) + sY = getDistance(np.rad2deg(sTheta)) + + dX = getDistance(np.rad2deg(dPhi)) + dY = getDistance(np.rad2deg(dTheta)) + + nPoints = nX * nY * nZ + + dZ = dR + + # write header + print("Writing header for VTK file...") + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('Velocity on FMTOMO vgrids.in points\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET STRUCTURED_POINTS\n') + + outfile.writelines('DIMENSIONS %d %d %d\n' %(nX, nY, nZ)) + outfile.writelines('ORIGIN %f %f %f\n' %(sX, sY, sZ)) + outfile.writelines('SPACING %f %f %f\n' %(dX, dY, dZ)) + + outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + if absOrRel == 'abs': + outfile.writelines('SCALARS velocity float %d\n' %(1)) + elif absOrRel == 'rel': + outfile.writelines('SCALARS velChangePercent float %d\n' %(1)) + outfile.writelines('LOOKUP_TABLE default\n') + + # write velocity + if absOrRel == 'abs': + print("Writing velocity values to VTK file...") + for velocity in vel: + outfile.writelines('%10f\n' %velocity) + elif absOrRel == 'rel': + velref = readVelocity(inputfileref) + if not len(velref) == len(vel): + print('ERROR: Number of gridpoints mismatch for %s and %s'%(inputfile, inputfileref)) + return + #velrel = [((vel - velref) / velref * 100) for vel, velref in zip(vel, velref)] + velrel = [] + for velocities in zip(vel, velref): + v, vref = velocities + if not vref == 0: + velrel.append((v - vref) / vref * 100) + else: + velrel.append(0) + + nR_ref, nTheta_ref, nPhi_ref = readNumberOfPoints(inputfileref) + if not nR_ref == nR and nTheta_ref == nTheta and nPhi_ref == nPhi: + print('ERROR: Dimension mismatch of grids %s and %s'%(inputfile, inputfileref)) + return + print("Writing velocity values to VTK file...") + for velocity in velrel: + outfile.writelines('%10f\n' %velocity) + print('Pertubations: min: %s %%, max: %s %%'%(min(velrel), max(velrel))) + + outfile.close() + print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + return + +def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): + ''' + Writes VTK file(s) for FMTOMO rays from rays.dat + + :param: nthPoint, plot every nth point of the ray + :type: integer + ''' + def getDistance(angle): + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + + infile = open(fnin, 'r') + R = 6371 + rays = {} + raynumber = 0 + nPoints = 0 + + ### NOTE: rays.dat seems to be in km and radians + + while True: + raynumber += 1 + firstline = infile.readline() + if firstline == '': break # break at EOF + raynumber = int(firstline.split()[0]) + shotnumber = int(firstline.split()[1]) + rayValid = int(firstline.split()[4]) # is zero if the ray is invalid + if rayValid == 0: + print('Invalid ray number %d for shot number %d'%(raynumber, shotnumber)) + continue + nRayPoints = int(infile.readline().split()[0]) + if not shotnumber in rays.keys(): + rays[shotnumber] = {} + rays[shotnumber][raynumber] = [] + for index in range(nRayPoints): + if index % nthPoint is 0 or index == (nRayPoints - 1): + rad, lat, lon = infile.readline().split() + rays[shotnumber][raynumber].append([getDistance(np.rad2deg(float(lon))), getDistance(np.rad2deg(float(lat))), float(rad) - R]) + else: + dummy = infile.readline() + + infile.close() + + for shotnumber in rays.keys(): + fnameout = fdirout + 'rays%03d.vtk'%(shotnumber) + outfile = open(fnameout, 'w') + + nPoints = 0 + for raynumber in rays[shotnumber]: + for ray in rays[shotnumber][raynumber]: + nPoints += 1 + + # write header + #print("Writing header for VTK file...") + print("Writing shot %d to file %s" %(shotnumber, fnameout)) + outfile.writelines('# vtk DataFile Version 3.1\n') + outfile.writelines('FMTOMO rays\n') + outfile.writelines('ASCII\n') + outfile.writelines('DATASET POLYDATA\n') + outfile.writelines('POINTS %15d float\n' %(nPoints)) + + # write coordinates + #print("Writing coordinates to VTK file...") + + for raynumber in rays[shotnumber].keys(): + for raypoint in rays[shotnumber][raynumber]: + outfile.writelines('%10f %10f %10f \n' %(raypoint[0], raypoint[1], raypoint[2])) + + outfile.writelines('LINES %15d %15d\n' %(len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) + + # write indices + #print("Writing indices to VTK file...") + + count = 0 + for raynumber in rays[shotnumber].keys(): + outfile.writelines('%d ' %(len(rays[shotnumber][raynumber]))) + for index in range(len(rays[shotnumber][raynumber])): + outfile.writelines('%d ' %(count)) + count += 1 + outfile.writelines('\n') + + # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + # outfile.writelines('SCALARS rays float %d\n' %(1)) + # outfile.writelines('LOOKUP_TABLE default\n') + + # # write velocity + # print("Writing velocity values to VTK file...") + # for velocity in vel: + # outfile.writelines('%10f\n' %velocity) + + # outfile.close() + # print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + +def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', + outputfile = 'vgrids_cb.in', ampmethod = 'linear', rect = (None, None)): + ''' + Add a checkerboard to an existing vgrids.in velocity model. + + :param: spacing, size of the tiles + type: float + + :param: pertubation, pertubation (default: 0.1 = 10%) + type: float + ''' + def readNumberOfPoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + nR = int(vglines[1].split()[0]) + nTheta = int(vglines[1].split()[1]) + nPhi = int(vglines[1].split()[2]) + + print('readNumberOf Points: Awaiting %d grid points in %s' + %(nR*nTheta*nPhi, filename)) + fin.close() + return nR, nTheta, nPhi + + def readDelta(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + dR = float(vglines[2].split()[0]) + dTheta = float(vglines[2].split()[1]) + dPhi = float(vglines[2].split()[2]) + + fin.close() + return dR, dTheta, dPhi + + def readStartpoints(filename): + fin = open(filename, 'r') + vglines = fin.readlines() + + sR = float(vglines[3].split()[0]) + sTheta = float(vglines[3].split()[1]) + sPhi = float(vglines[3].split()[2]) + + fin.close() + return sR, sTheta, sPhi + + def readVelocity(filename): + ''' + Reads in velocity from vgrids file and returns a list containing all values in the same order + ''' + vel = []; count = 0 + fin = open(filename, 'r') + vglines = fin.readlines() + + for line in vglines: + count += 1 + if count > 4: + vel.append(float(line.split()[0])) + + print("Read %d points out of file: %s" %(count - 4, filename)) + return vel + + def correctSpacing(spacing, delta, disttype = None): + if spacing > delta: + spacing_corr = round(spacing / delta) * delta + elif spacing < delta: + spacing_corr = delta + print('The spacing of the checkerboard of %s (%s) was corrected to ' + 'a value of %s to fit the grid spacing of %s.' %(spacing, disttype, spacing_corr, delta)) + return spacing_corr + + def linearAmp(InCell): + decimal = InCell - np.floor(InCell) + return (-abs(decimal - 0.5) + 0.5) * 2 + + def rectAmp(InCell, rect): + decimal = InCell - np.floor(InCell) + r1, r2 = rect + if r1 <= decimal <= r2: + return 1 + else: + return 0 + + def ampFunc(InCell, method = 'linear', rect = None): + if method == 'linear': + return linearAmp(InCell) + if method == 'rect' and rect is not None: + return rectAmp(InCell, rect) + else: + print('ampFunc: Could not amplify cb pattern') + + + R = 6371. # earth radius + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + outfile = open(outputfile, 'w') + + # Theta, Phi in radians, R in km + nR, nTheta, nPhi = readNumberOfPoints(inputfile) + dR, dThetaRad, dPhiRad = readDelta(inputfile) + sR, sThetaRad, sPhiRad = readStartpoints(inputfile) + vel = readVelocity(inputfile) + + dTheta, dPhi = np.rad2deg((dThetaRad, dPhiRad)) + sTheta, sPhi = np.rad2deg((dThetaRad, dPhiRad)) + + eR = sR + (nR - 1) * dR + ePhi = sPhi + (nPhi - 1) * dPhi + eTheta = sTheta + (nTheta - 1) * dTheta + + nPoints = nR * nTheta * nPhi + + thetaGrid = np.linspace(sTheta, eTheta, num = nTheta) + phiGrid = np.linspace(sPhi, ePhi, num = nPhi) + rGrid = np.linspace(sR, eR, num = nR) + + # write header for velocity grid file (in RADIANS) + outfile.writelines('%10s %10s \n' %(1, 1)) + outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) + outfile.writelines('%10s %10s %10s\n' %(dR, dThetaRad, dPhiRad)) + outfile.writelines('%10s %10s %10s\n' %(sR, sThetaRad, sPhiRad)) + + spacR = correctSpacing(spacing, dR, '[meter], R') + spacTheta = correctSpacing(_getAngle(spacing), dTheta, '[degree], Theta') + spacPhi = correctSpacing(_getAngle(spacing), dPhi, '[degree], Phi') + + count = 0 + evenOdd = 1 + even = 0; odd = 0 + + # In the following loop it is checked whether the positive distance from the border of the model + # for a point on the grid divided by the spacing is even or odd and then pertubated. + # The position is also shifted by half of the delta so that the position is directly on the point and + # not on the border between two points. + # "InCell" points e.g. rInCell are floats with their integer number corresponding to the cell number and + # their decimal place (0 - 1) corresponding to the position inside the cell. + # The amplification factor ampFactor comes from a linear relationship and ranges between 0 (cell border) + # and 1 (cell middle) + for radius in rGrid: + rInCell = (radius - sR - dR/2) / spacR + ampR = ampFunc(rInCell, ampmethod, rect) + if np.floor(rInCell) % 2: + evenOddR = 1 + else: + evenOddR = -1 + for theta in thetaGrid: + thetaInCell = (theta - sTheta - dTheta/2) / spacTheta + ampTheta = ampFunc(thetaInCell, ampmethod, rect) + if np.floor(thetaInCell) % 2: + evenOddT = 1 + else: + evenOddT = -1 + for phi in phiGrid: + phiInCell = (phi - sPhi - dPhi/2) / spacPhi + ampPhi = ampFunc(phiInCell, ampmethod, rect) + if np.floor(phiInCell) % 2: + evenOddP = 1 + else: + evenOddP = -1 + velocity = vel[count] + ampFactor = (ampR + ampTheta + ampPhi) / 3 + evenOdd = evenOddR * evenOddT * evenOddP * ampFactor + velocity += evenOdd * pertubation * velocity + + outfile.writelines('%10s %10s\n'%(velocity, decm)) + count += 1 + + progress = float(count) / float(nPoints) * 100 + _update_progress(progress) + + print('Added checkerboard to the grid in file %s with a spacing of %s and a pertubation of %s %%. ' + 'Outputfile: %s.'%(inputfile, spacing, pertubation, outputfile)) + outfile.close() + +def _update_progress(progress): + sys.stdout.write("%d%% done \r" % (progress) ) + sys.stdout.flush() + +def _getAngle(distance): + ''' + Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. + ''' + PI = np.pi + R = 6371. + angle = distance * 180. / (PI * R) + return angle + From 78d61a982268dee24a61a8766a8ed28bcafd3485 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 16 Nov 2015 11:49:57 +0100 Subject: [PATCH 0695/1144] added writeVTK files --- pylot/core/active/seismicArrayPreparation.py | 175 ++++--------------- 1 file changed, 31 insertions(+), 144 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 6179840b..47f50835 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -383,7 +383,7 @@ class SeisArray(object): def generateFMTOMOinputFromArray(self, nRP, nThetaP, nPhiP, nRI, nThetaI, nPhiI, Rbt, cushionfactor, interpolationMethod = 'linear', - customgrid = 'mygrid.in'): + customgrid = 'mygrid.in', writeVTK = True): print('\n------------------------------------------------------------') print('Automatically generating input for FMTOMO from array size.') print('Propgrid: nR = %s, nTheta = %s, nPhi = %s'%(nRP, nThetaP, nPhiP)) @@ -406,8 +406,36 @@ class SeisArray(object): depthmax = abs(Rbt[0] - getZmin(surface)) - 1.0 # cushioning for the bottom interface - self.generateInterfaces(nThetaI, nPhiI, depthmax, cushionfactor = cushionfactor, - returnInterfaces = False, method = interpolationMethod) + interf1, interf2 = self.generateInterfaces(nThetaI, nPhiI, depthmax, cushionfactor = cushionfactor, + returnInterfaces = True, method = interpolationMethod) + + if writeVTK == True: + from pylot.core.active import fmtomoUtils + self.surface2VTK(interf1, filename = 'interface1.vtk') + self.surface2VTK(interf2, filename = 'interface2.vtk') + self.receivers2VTK() + self.sources2VTK() + fmtomoUtils.vgrids2VTK() + + def generateReceiversIn(self, outfilename = 'receivers.in'): + outfile = open(outfilename, 'w') + + recx, recy, recz = self.getReceiverLists() + nsrc = len(self.getSourceLocations()) + outfile.writelines('%s\n'%(len(zip(recx, recy, recz)) * nsrc)) + + for index in range(nsrc): + for point in zip(recx, recy, recz): + rx, ry, rz = point + rad = - rz + lat = self._getAngle(ry) + lon = self._getAngle(rx) + outfile.writelines('%15s %15s %15s\n'%(rad, lat, lon)) + outfile.writelines('%15s\n'%(1)) + outfile.writelines('%15s\n'%(index + 1)) + outfile.writelines('%15s\n'%(1)) + + outfile.close() def generateInterfaces(self, nTheta, nPhi, depthmax, cushionfactor = 0.1, @@ -698,147 +726,6 @@ class SeisArray(object): if returnTopo == True: return surface - def addCheckerboard(self, spacing = 20., pertubation = 0.1, inputfile = 'vgrids.in', outputfile = 'vgrids_cb.in'): - ''' - Add a checkerboard to an existing vgrids.in velocity model. - - :param: spacing, size of the tiles - type: float - - :param: pertubation, pertubation (default: 0.1 = 10%) - type: float - ''' - def readNumberOfPoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - nR = int(vglines[1].split()[0]) - nTheta = int(vglines[1].split()[1]) - nPhi = int(vglines[1].split()[2]) - - print('readNumberOf Points: Awaiting %d grid points in %s' - %(nR*nTheta*nPhi, filename)) - fin.close() - return nR, nTheta, nPhi - - def readDelta(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - dR = float(vglines[2].split()[0]) - dTheta = float(vglines[2].split()[1]) - dPhi = float(vglines[2].split()[2]) - - fin.close() - return dR, dTheta, dPhi - - def readStartpoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - sR = float(vglines[3].split()[0]) - sTheta = float(vglines[3].split()[1]) - sPhi = float(vglines[3].split()[2]) - - fin.close() - return sR, sTheta, sPhi - - def readVelocity(filename): - ''' - Reads in velocity from vgrids file and returns a list containing all values in the same order - ''' - vel = []; count = 0 - fin = open(filename, 'r') - vglines = fin.readlines() - - for line in vglines: - count += 1 - if count > 4: - vel.append(float(line.split()[0])) - - print("Read %d points out of file: %s" %(count - 4, filename)) - return vel - - def correctSpacing(spacing, delta, disttype = None): - if spacing > delta: - spacing_corr = round(spacing / delta) * delta - elif spacing < delta: - spacing_corr = delta - print('The spacing of the checkerboard of %s (%s) was corrected to ' - 'a value of %s to fit the grid spacing of %s.' %(spacing, disttype, spacing_corr, delta)) - return spacing_corr - - - R = 6371. # earth radius - decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) - outfile = open(outputfile, 'w') - - # Theta, Phi in radians, R in km - nR, nTheta, nPhi = readNumberOfPoints(inputfile) - dR, dThetaRad, dPhiRad = readDelta(inputfile) - sR, sThetaRad, sPhiRad = readStartpoints(inputfile) - vel = readVelocity(inputfile) - - dTheta, dPhi = np.rad2deg((dThetaRad, dPhiRad)) - sTheta, sPhi = np.rad2deg((dThetaRad, dPhiRad)) - - eR = sR + (nR - 1) * dR - ePhi = sPhi + (nPhi - 1) * dPhi - eTheta = sTheta + (nTheta - 1) * dTheta - - nPoints = nR * nTheta * nPhi - - thetaGrid = np.linspace(sTheta, eTheta, num = nTheta) - phiGrid = np.linspace(sPhi, ePhi, num = nPhi) - rGrid = np.linspace(sR, eR, num = nR) - - # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' %(1, 1)) - outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' %(dR, dThetaRad, dPhiRad)) - outfile.writelines('%10s %10s %10s\n' %(sR, sThetaRad, sPhiRad)) - - spacR = correctSpacing(spacing, dR, '[meter], R') - spacTheta = correctSpacing(self._getAngle(spacing), dTheta, '[degree], Theta') - spacPhi = correctSpacing(self._getAngle(spacing), dPhi, '[degree], Phi') - - count = 0 - evenOdd = 1 - even = 0; odd = 0 - - # In the following loop it is checked whether the positive distance from the border of the model - # for a point on the grid divided by the spacing is even or odd and then pertubated. - # The position is also shifted by half of the delta so that the position is directly on the point and - # not on the border between two points. - for radius in rGrid: - if np.floor((radius - sR - dR/2) / spacR) % 2: - evenOddR = 1 - else: - evenOddR = -1 - for theta in thetaGrid: - if np.floor((theta - sTheta - dTheta/2) / spacTheta) % 2: - evenOddT = 1 - else: - evenOddT = -1 - for phi in phiGrid: - if np.floor((phi - sPhi - dPhi/2) / spacPhi) % 2: - evenOddP = 1 - else: - evenOddP = -1 - velocity = vel[count] - evenOdd = evenOddR * evenOddT * evenOddP - velocity += evenOdd * pertubation * velocity - - outfile.writelines('%10s %10s\n'%(velocity, decm)) - count += 1 - - progress = float(count) / float(nPoints) * 100 - self._update_progress(progress) - - print('Added checkerboard to the grid in file %s with a spacing of %s and a pertubation of %s [km/s]. ' - 'Outputfile: %s.'%(inputfile, spacing, pertubation, outputfile)) - outfile.close() - def exportAll(self, filename = 'interpolated_receivers.out'): recfile_out = open(filename, 'w') count = 0 From 0f1427a9f07ef55fe22acf9d7abf6f1d877769ff Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 16 Nov 2015 11:50:09 +0100 Subject: [PATCH 0696/1144] first try of shot.plot_traces delete button --- pylot/core/active/seismicshot.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 58267dd5..cf923ee4 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -622,16 +622,20 @@ class SeismicShot(object): fig = plt.figure() ax1 = fig.add_subplot(2,1,1) ax2 = fig.add_subplot(2,1,2, sharex = ax1) - axb = fig.add_axes([0.15, 0.91, 0.05, 0.03]) - button = Button(axb, 'repick', color = 'red', hovercolor = 'grey') - button.on_clicked(connectButton) + axb1 = fig.add_axes([0.15, 0.91, 0.05, 0.03]) + axb2 = fig.add_axes([0.22, 0.91, 0.05, 0.03]) + button1 = Button(axb1, 'repick', color = 'red', hovercolor = 'grey') + button1.on_clicked(connectButton) + button2 = Button(axb2, 'delete', color = 'green', hovercolor = 'grey') + button2.on_clicked(self.removePick(traceID)) self.traces4plot = {} if traceID not in self.traces4plot.keys(): self.traces4plot[traceID] = {'fig': fig, 'ax1': ax1, 'ax2': ax2, - 'axb': axb, + 'axb1': axb1, + 'axb2': axb2, 'button': button, 'cid': None} From 647fddb904a99c7f1ea4225145d8808d4df8ea38 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 16 Nov 2015 12:09:00 +0100 Subject: [PATCH 0697/1144] bugfix: could not highlight pick after it was deleted in plot_traces --- pylot/core/active/surveyPlotTools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index 9a63c21e..a56aa13d 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -338,6 +338,9 @@ class regions(object): if type(shot) == int: shot = self.survey.getShotDict()[shot] + if shot.getFlag(traceID) is 0: + return + self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s = 50, marker = 'o', facecolors = 'none', edgecolors = 'm', alpha = 1) if annotations == True: self.ax.annotate(s='s%s|t%s' % (shot.getShotnumber(), traceID), xy=(shot.getDistance(traceID), shot.getPick(traceID)), fontsize='xx-small') From 160ee81c445367ff04b3a3becb8596ba728e62a0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 16 Nov 2015 12:11:42 +0100 Subject: [PATCH 0698/1144] improved and fixed delete button --- pylot/core/active/seismicshot.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index cf923ee4..585ecf4d 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -615,6 +615,12 @@ class SeismicShot(object): fig.canvas.mpl_disconnect(self.traces4plot[traceID]['cid']) plt.draw() + def rmPick(event = None): + self.removePick(traceID) + self._drawStream(traceID, refresh = True) + self._drawCFs(traceID, folm, refresh = True) + plt.draw() + def connectButton(event = None): cid = fig.canvas.mpl_connect('button_press_event', onclick) self.traces4plot[traceID]['cid'] = cid @@ -627,7 +633,7 @@ class SeismicShot(object): button1 = Button(axb1, 'repick', color = 'red', hovercolor = 'grey') button1.on_clicked(connectButton) button2 = Button(axb2, 'delete', color = 'green', hovercolor = 'grey') - button2.on_clicked(self.removePick(traceID)) + button2.on_clicked(rmPick) self.traces4plot = {} if traceID not in self.traces4plot.keys(): @@ -636,7 +642,8 @@ class SeismicShot(object): 'ax2': ax2, 'axb1': axb1, 'axb2': axb2, - 'button': button, + 'button1': button1, + 'button2': button2, 'cid': None} self._drawStream(traceID) From b700940f54f83517b49225340ab5658f3f02c361 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 17 Nov 2015 10:28:18 +0100 Subject: [PATCH 0699/1144] bugfix: seismicshot dictionary for repick button was overwritten --- pylot/core/active/seismicshot.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 585ecf4d..fce06909 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -34,6 +34,7 @@ class SeismicShot(object): self.snr = {} self.snrthreshold = {} self.timeArray = {} + self.traces4plot = {} self.paras = {} self.paras['shotname'] = obsfile @@ -635,16 +636,17 @@ class SeismicShot(object): button2 = Button(axb2, 'delete', color = 'green', hovercolor = 'grey') button2.on_clicked(rmPick) - self.traces4plot = {} if traceID not in self.traces4plot.keys(): - self.traces4plot[traceID] = {'fig': fig, - 'ax1': ax1, - 'ax2': ax2, - 'axb1': axb1, - 'axb2': axb2, - 'button1': button1, - 'button2': button2, - 'cid': None} + self.traces4plot[traceID] = {} + + self.traces4plot[traceID] = {'fig': fig, + 'ax1': ax1, + 'ax2': ax2, + 'axb1': axb1, + 'axb2': axb2, + 'button1': button1, + 'button2': button2, + 'cid': None} self._drawStream(traceID) self._drawCFs(traceID, folm) From f192a72ad78d149133fb73169030671f54a438e7 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 17 Nov 2015 10:29:32 +0100 Subject: [PATCH 0700/1144] slimmed down the code --- pylot/core/active/fmtomoUtils.py | 273 +++++++++++++++++-------------- 1 file changed, 150 insertions(+), 123 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 20586b16..3ca0f5d3 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -6,83 +6,27 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a ''' Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in ''' - def getDistance(angle): - PI = np.pi - R = 6371. - distance = angle / 180 * (PI * R) - return distance - - def readNumberOfPoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - nR = int(vglines[1].split()[0]) - nTheta = int(vglines[1].split()[1]) - nPhi = int(vglines[1].split()[2]) - - print('readNumberOf Points: Awaiting %d grid points in %s' - %(nR*nTheta*nPhi, filename)) - fin.close() - return nR, nTheta, nPhi - - def readDelta(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - dR = float(vglines[2].split()[0]) - dTheta = float(vglines[2].split()[1]) - dPhi = float(vglines[2].split()[2]) - - fin.close() - return dR, dTheta, dPhi - - def readStartpoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - sR = float(vglines[3].split()[0]) - sTheta = float(vglines[3].split()[1]) - sPhi = float(vglines[3].split()[2]) - - fin.close() - return sR, sTheta, sPhi - - def readVelocity(filename): - ''' - Reads in velocity from vgrids file and returns a list containing all values in the same order - ''' - vel = []; count = 0 - fin = open(filename, 'r') - vglines = fin.readlines() - - for line in vglines: - count += 1 - if count > 4: - vel.append(float(line.split()[0])) - - print("Read %d points out of file: %s" %(count - 4, filename)) - return vel - R = 6371. # earth radius outfile = open(outputfile, 'w') - # Theta, Phi in radians, R in km - nR, nTheta, nPhi = readNumberOfPoints(inputfile) - dR, dTheta, dPhi = readDelta(inputfile) - sR, sTheta, sPhi = readStartpoints(inputfile) - vel = readVelocity(inputfile) + number, delta, start, vel = _readVgrid(inputfile) + + nR, nTheta, nPhi = number + dR, dTheta, dPhi = delta + sR, sTheta, sPhi = start + + thetaGrid, phiGrid, rGrid = _generateGrids(number, delta, start) + + nPoints = nR * nTheta * nPhi nX = nPhi; nY = nTheta; nZ = nR sZ = sR - R - sX = getDistance(np.rad2deg(sPhi)) - sY = getDistance(np.rad2deg(sTheta)) - - dX = getDistance(np.rad2deg(dPhi)) - dY = getDistance(np.rad2deg(dTheta)) - - nPoints = nX * nY * nZ + sX = _getDistance(sPhi) + sY = _getDistance(sTheta) + dX = _getDistance(dPhi) + dY = _getDistance(dTheta) dZ = dR # write header @@ -109,7 +53,8 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a for velocity in vel: outfile.writelines('%10f\n' %velocity) elif absOrRel == 'rel': - velref = readVelocity(inputfileref) + nref, dref, sref, velref = _readVgrid(inputfileref) + nR_ref, nTheta_ref, nPhi_ref = nref if not len(velref) == len(vel): print('ERROR: Number of gridpoints mismatch for %s and %s'%(inputfile, inputfileref)) return @@ -122,7 +67,6 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a else: velrel.append(0) - nR_ref, nTheta_ref, nPhi_ref = readNumberOfPoints(inputfileref) if not nR_ref == nR and nTheta_ref == nTheta and nPhi_ref == nPhi: print('ERROR: Dimension mismatch of grids %s and %s'%(inputfile, inputfileref)) return @@ -142,12 +86,6 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): :param: nthPoint, plot every nth point of the ray :type: integer ''' - def getDistance(angle): - PI = np.pi - R = 6371. - distance = angle / 180 * (PI * R) - return distance - infile = open(fnin, 'r') R = 6371 rays = {} @@ -155,7 +93,6 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): nPoints = 0 ### NOTE: rays.dat seems to be in km and radians - while True: raynumber += 1 firstline = infile.readline() @@ -173,7 +110,7 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): for index in range(nRayPoints): if index % nthPoint is 0 or index == (nRayPoints - 1): rad, lat, lon = infile.readline().split() - rays[shotnumber][raynumber].append([getDistance(np.rad2deg(float(lon))), getDistance(np.rad2deg(float(lat))), float(rad) - R]) + rays[shotnumber][raynumber].append([_getDistance(np.rad2deg(float(lon))), _getDistance(np.rad2deg(float(lat))), float(rad) - R]) else: dummy = infile.readline() @@ -199,7 +136,6 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): # write coordinates #print("Writing coordinates to VTK file...") - for raynumber in rays[shotnumber].keys(): for raypoint in rays[shotnumber][raynumber]: outfile.writelines('%10f %10f %10f \n' %(raypoint[0], raypoint[1], raypoint[2])) @@ -208,7 +144,6 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): # write indices #print("Writing indices to VTK file...") - count = 0 for raynumber in rays[shotnumber].keys(): outfile.writelines('%d ' %(len(rays[shotnumber][raynumber]))) @@ -217,29 +152,7 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): count += 1 outfile.writelines('\n') - # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - # outfile.writelines('SCALARS rays float %d\n' %(1)) - # outfile.writelines('LOOKUP_TABLE default\n') - - # # write velocity - # print("Writing velocity values to VTK file...") - # for velocity in vel: - # outfile.writelines('%10f\n' %velocity) - - # outfile.close() - # print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) - -def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', - outputfile = 'vgrids_cb.in', ampmethod = 'linear', rect = (None, None)): - ''' - Add a checkerboard to an existing vgrids.in velocity model. - - :param: spacing, size of the tiles - type: float - - :param: pertubation, pertubation (default: 0.1 = 10%) - type: float - ''' +def _readVgrid(filename): def readNumberOfPoints(filename): fin = open(filename, 'r') vglines = fin.readlines() @@ -291,6 +204,46 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', print("Read %d points out of file: %s" %(count - 4, filename)) return vel + # Theta, Phi in radians, R in km + nR, nTheta, nPhi = readNumberOfPoints(filename) + dR, dThetaRad, dPhiRad = readDelta(filename) + sR, sThetaRad, sPhiRad = readStartpoints(filename) + vel = readVelocity(filename) + + dTheta, dPhi = np.rad2deg((dThetaRad, dPhiRad)) + sTheta, sPhi = np.rad2deg((sThetaRad, sPhiRad)) + + number = (nR, nTheta, nPhi) + delta = (dR, dTheta, dPhi) + start = (sR, sTheta, sPhi) + return number, delta, start, vel + +def _generateGrids(number, delta, start): + nR, nTheta, nPhi = number + dR, dTheta, dPhi = delta + sR, sTheta, sPhi = start + + eR = sR + (nR - 1) * dR + ePhi = sPhi + (nPhi - 1) * dPhi + eTheta = sTheta + (nTheta - 1) * dTheta + + thetaGrid = np.linspace(sTheta, eTheta, num = nTheta) + phiGrid = np.linspace(sPhi, ePhi, num = nPhi) + rGrid = np.linspace(sR, eR, num = nR) + + return (thetaGrid, phiGrid, rGrid) + +def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', + outputfile = 'vgrids_cb.in', ampmethod = 'linear', rect = (None, None)): + ''' + Add a checkerboard to an existing vgrids.in velocity model. + + :param: spacing, size of the tiles + type: float + + :param: pertubation, pertubation (default: 0.1 = 10%) + type: float + ''' def correctSpacing(spacing, delta, disttype = None): if spacing > delta: spacing_corr = round(spacing / delta) * delta @@ -320,35 +273,24 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', else: print('ampFunc: Could not amplify cb pattern') - - R = 6371. # earth radius decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) outfile = open(outputfile, 'w') - # Theta, Phi in radians, R in km - nR, nTheta, nPhi = readNumberOfPoints(inputfile) - dR, dThetaRad, dPhiRad = readDelta(inputfile) - sR, sThetaRad, sPhiRad = readStartpoints(inputfile) - vel = readVelocity(inputfile) + number, delta, start, vel = _readVgrid(inputfile) - dTheta, dPhi = np.rad2deg((dThetaRad, dPhiRad)) - sTheta, sPhi = np.rad2deg((dThetaRad, dPhiRad)) - - eR = sR + (nR - 1) * dR - ePhi = sPhi + (nPhi - 1) * dPhi - eTheta = sTheta + (nTheta - 1) * dTheta + nR, nTheta, nPhi = number + dR, dTheta, dPhi = delta + sR, sTheta, sPhi = start + + thetaGrid, phiGrid, rGrid = _generateGrids(number, delta, start) nPoints = nR * nTheta * nPhi - thetaGrid = np.linspace(sTheta, eTheta, num = nTheta) - phiGrid = np.linspace(sPhi, ePhi, num = nPhi) - rGrid = np.linspace(sR, eR, num = nR) - # write header for velocity grid file (in RADIANS) outfile.writelines('%10s %10s \n' %(1, 1)) outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' %(dR, dThetaRad, dPhiRad)) - outfile.writelines('%10s %10s %10s\n' %(sR, sThetaRad, sPhiRad)) + outfile.writelines('%10s %10s %10s\n' %(dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) + outfile.writelines('%10s %10s %10s\n' %(sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) spacR = correctSpacing(spacing, dR, '[meter], R') spacTheta = correctSpacing(_getAngle(spacing), dTheta, '[degree], Theta') @@ -402,6 +344,85 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', 'Outputfile: %s.'%(inputfile, spacing, pertubation, outputfile)) outfile.close() +def addBox(x = (None, None), y = (None, None), z = (None, None), + boxvelocity = 1.0, inputfile = 'vgrids.in', + outputfile = 'vgrids_box.in'): + ''' + Add a box with constant velocity to an existing vgrids.in velocity model. + + :param: x, borders of the box (xleft, xright) + type: tuple + + :param: y, borders of the box (yleft, yright) + type: tuple + + :param: z, borders of the box (bot, top) + type: tuple + + :param: boxvelocity, default: 1.0 km/s + type: float + ''' + R = 6371. + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + outfile = open(outputfile, 'w') + + theta1 = _getAngle(y[0]) + theta2 = _getAngle(y[1]) + phi1 = _getAngle(x[0]) + phi2 = _getAngle(x[1]) + r1 = R + z[0] + r2 = R + z[1] + + print('Adding box to grid with theta = (%s, %s), phi = (%s, %s), ' + 'r = (%s, %s), velocity = %s [km/s]' + %(theta1, theta2, phi1, phi2, r1, r2, boxvelocity)) + + number, delta, start, vel = _readVgrid(inputfile) + + nR, nTheta, nPhi = number + dR, dTheta, dPhi = delta + sR, sTheta, sPhi = start + + thetaGrid, phiGrid, rGrid = _generateGrids(number, delta, start) + + nPoints = nR * nTheta * nPhi + + # write header for velocity grid file (in RADIANS) + outfile.writelines('%10s %10s \n' %(1, 1)) + outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) + outfile.writelines('%10s %10s %10s\n' %(dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) + outfile.writelines('%10s %10s %10s\n' %(sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) + + count = 0 + for radius in rGrid: + if r1 <= radius <= r2: + rFlag = 1 + else: + rFlag = 0 + for theta in thetaGrid: + if theta1 <= theta <= theta2: + thetaFlag = 1 + else: + thetaFlag = 0 + for phi in phiGrid: + if phi1 <= phi <= phi2: + phiFlag = 1 + else: + phiFlag = 0 + velocity = vel[count] + if rFlag * thetaFlag * phiFlag is not 0: + velocity = boxvelocity + + outfile.writelines('%10s %10s\n'%(velocity, decm)) + count += 1 + + progress = float(count) / float(nPoints) * 100 + _update_progress(progress) + + print('Added box to the grid in file %s. ' + 'Outputfile: %s.'%(inputfile, outputfile)) + outfile.close() + def _update_progress(progress): sys.stdout.write("%d%% done \r" % (progress) ) sys.stdout.flush() @@ -415,3 +436,9 @@ def _getAngle(distance): angle = distance * 180. / (PI * R) return angle +def _getDistance(angle): + PI = np.pi + R = 6371. + distance = angle / 180 * (PI * R) + return distance + From 19e60e5f0c2b51eeb0d985af0d1d212679b4f09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 17 Nov 2015 17:03:59 +0100 Subject: [PATCH 0701/1144] Replaced writephases with new function picskExport from module nll. --- autoPyLoT.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index e3f696f5..d0c94689 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -1,7 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - import os import argparse import glob @@ -12,7 +11,7 @@ from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter from pylot.core.util.structure import DATASTRUCTURE from pylot.core.pick.autopick import autopickevent -from pylot.core.pick.utils import writephases +from pylot.core.loc.nll import * from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -110,7 +109,7 @@ def autoPyLoT(inputfile): # locating if locflag == 1: # write phases to NLLoc-phase file - writephases(picks, 'NLLoc', phasefile) + picksExport(picks, 'NLLoc', phasefile) # For locating the event the NLLoc-control file has to be modified! # create comment line for NLLoc-control file @@ -165,7 +164,7 @@ def autoPyLoT(inputfile): # locating if locflag == 1: # write phases to NLLoc-phase file - writephases(picks, 'NLLoc', phasefile) + picksExport(picks, 'NLLoc', phasefile) # For locating the event the NLLoc-control file has to be modified! # create comment line for NLLoc-control file NLLoc-output file From 1d6d786dbbdb8c89988ceefaaf04d18a4fa3c65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 17 Nov 2015 17:05:36 +0100 Subject: [PATCH 0702/1144] Modyfied picksExport: additional parameter locrt to choose location routine, i.e. phase-file format. --- pylot/core/loc/nll.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index 3ecd1211..f45cbfe4 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -10,16 +10,18 @@ from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() -def picksExport(picks, phasefile): +def picksExport(picks, locrt, phasefile): ''' Take dictionary and exports picking data to a NLLOC-obs without creating an ObsPy event object. :param picks: picking data dictionary :type picks: dict + :param locrt: choose location routine + :type locrt: str :param phasefile: complete path to the exporting obs file :type phasefile: str ''' # write phases to NLLoc-phase file - writephases(picks, 'NLLoc', phasefile) + writephases(picks, locrt, phasefile) def modfiyInputFile(fn, root, outpath, phasefn, tttn): ''' From c51ba1bd566f03eaed89e8dd9dcc61a5428bdbf2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 Nov 2015 09:30:32 +0100 Subject: [PATCH 0703/1144] [bugfix] trying to figure out cause of segmentation fault --- QtPyLoT.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 02b0dd1d..f5d1c352 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -36,7 +36,7 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen, \ QActionGroup, QListWidget, QDockWidget import numpy as np -from obspy import UTCDateTime, readEvents +from obspy import UTCDateTime from pylot.core.read.data import Data from pylot.core.read.inputs import FilterOptions, AutoPickParameter @@ -59,7 +59,6 @@ locateTool = dict(nll=locateNll) class MainWindow(QMainWindow): __version__ = _getVersionString() - __slots__ = ['loc'] closing = Signal() def __init__(self, parent=None): @@ -651,10 +650,10 @@ class MainWindow(QMainWindow): self.drawPicks(station) else: self.updateStatus('picks discarded ({0})'.format(station)) - if not self.locflag() and self.check4Loc(): - self.locflag(True) - elif self.locflag() and not self.check4Loc(): - self.locflag(False) + if not self.getLocflag() and self.check4Loc(): + self.setLocflag(True) + elif self.getLocflag() and not self.check4Loc(): + self.setLocflag(False) def autoPick(self): list = QListWidget() @@ -783,12 +782,10 @@ class MainWindow(QMainWindow): num += len(phases) return num - @property - def locflag(self): + def getLocflag(self): return self.loc - @locflag.setter - def locflag(self, value): + def setLocflag(self, value): self.loc = value def updateStatus(self, message, duration=5000): From 8a843428838dd56460056ee5dfd88c88ec206a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 18 Nov 2015 10:27:42 +0100 Subject: [PATCH 0704/1144] Implementation of module /loc/nll finished, running for single and multiple event processing. --- autoPyLoT.py | 52 ++++++++++++++-------------------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index d0c94689..85d65a48 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -78,12 +78,11 @@ def autoPyLoT(inputfile): phasef = parameter.getParam('phasefile') phasefile = '%s/obs/%s' % (nllocroot, phasef) # get name of NLLoc-control file - locf = parameter.getParam('locfile') - locfile = '%s/run/%s' % (nllocroot, locf) - # patter of NLLoc ttimes from location grid + ctrf = parameter.getParam('ctrfile') + ctrfile = '%s/run/%s' % (nllocroot, ctrf) + # pattern of NLLoc ttimes from location grid ttpat = parameter.getParam('ttpatter') - ttpatter = '%s/time/%s' % (nllocroot, ttpat) - # patter of NLLoc-output file + # pattern of NLLoc-output file nllocoutpatter = parameter.getParam('outpatter') else: locflag = 0 @@ -112,27 +111,15 @@ def autoPyLoT(inputfile): picksExport(picks, 'NLLoc', phasefile) # For locating the event the NLLoc-control file has to be modified! - # create comment line for NLLoc-control file - # NLLoc-output file evID = event[string.rfind(event, "/") + 1 : len(events) - 1] - nllocout = '%s/loc/%s_%s' % (nllocroot, evID, nllocoutpatter) - locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) - print ("Modifying NLLoc-control file %s ..." % locfile) - # modification of NLLoc-control file - filedata = None - nllfile = open(locfile, 'r') - filedata = nllfile.read() - if filedata.find(locfiles) < 0: - # replace old command - filedata = filedata.replace('LOCFILES', locfiles) - nllfile = open(locfile, 'w') - nllfile.write(filedata) - nllfile.close() + nllocout = '%s_%s' % (evID, nllocoutpatter) + # create comment line for NLLoc-control file + modifyInputFile(ctrf, nllocroot, nllocout, phasef, ttpat) # locate the event - subprocess.call([nlloccall, locfile]) + locate(nlloccall, ctrfile) - # !iterative picking if traces remained unpicked or with bad picks! + # !iterative picking if traces remained unpicked or occupied with bad picks! # get theoretical onset times for picks with weights >= 4 # in order to reprocess them using smaller time windows ########################################################## @@ -167,25 +154,14 @@ def autoPyLoT(inputfile): picksExport(picks, 'NLLoc', phasefile) # For locating the event the NLLoc-control file has to be modified! - # create comment line for NLLoc-control file NLLoc-output file - nllocout = '%s/loc/%s_%s' % (nllocroot, parameter.getParam('eventID'), nllocoutpatter) - locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0' % (phasefile, ttpatter, nllocout) - print ("Modifying NLLoc-control file %s ..." % locfile) - # modification of NLLoc-control file - filedata = None - nllfile = open(locfile, 'r') - filedata = nllfile.read() - if filedata.find(locfiles) < 0: - # replace old command - filedata = filedata.replace('LOCFILES', locfiles) - nllfile = open(locfile, 'w') - nllfile.write(filedata) - nllfile.close() + nllocout = '%s_%s' % (parameter.getParam('eventID'), nllocoutpatter) + # create comment line for NLLoc-control file + modifyInputFile(ctrf, nllocroot, nllocout, phasef, ttpat) # locate the event - subprocess.call([nlloccall, locfile]) + locate(nlloccall, ctrfile) - # !iterative picking if traces remained unpicked or with bad picks! + # !iterative picking if traces remained unpicked or occupied with bad picks! # get theoretical onset times for picks with weights >= 4 # in order to reprocess them using smaller time windows ########################################################## From 85925576139bd26c38f6d6c43ff6a341dc42d62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 18 Nov 2015 10:28:46 +0100 Subject: [PATCH 0705/1144] Minor changes --- pylot/core/loc/nll.py | 46 +++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index f45cbfe4..00ef8b02 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -12,54 +12,70 @@ __version__ = _getVersionString() def picksExport(picks, locrt, phasefile): ''' - Take dictionary and exports picking data to a NLLOC-obs without creating an ObsPy event object. + Take dictionary and exports picking data to a NLLOC-obs + without creating an ObsPy event object. + :param picks: picking data dictionary :type picks: dict + :param locrt: choose location routine :type locrt: str + :param phasefile: complete path to the exporting obs file :type phasefile: str ''' # write phases to NLLoc-phase file writephases(picks, locrt, phasefile) -def modfiyInputFile(fn, root, outpath, phasefn, tttn): +def modifyInputFile(ctrfn, root, nllocoutn, phasefn, tttn): ''' + :param ctrfn: name of NLLoc-control file + :type: str - :param fn: - :param root: - :param outpath: - :param phasefile: - :param tttable: - :return: + :param root: root path to NLLoc working directory + :type: str + + :param nllocoutn: name of NLLoc-location output file + :type: str + + :param phasefn: name of NLLoc-input phase file + :type: str + + :param tttn: pattern of precalculated NLLoc traveltime tables + :type: str ''' - # For locating the event we have to modify the NLLoc-control file! + # For locating the event the NLLoc-control file has to be modified! # create comment line for NLLoc-control file NLLoc-output file - print ("Modifying NLLoc-control file %s ..." % fn) - nllocout = os.path.join(root,'loc', outpath) + ctrfile = os.path.join(root, 'run', ctrfn) + nllocout = os.path.join(root,'loc', nllocoutn) phasefile = os.path.join(root, 'obs', phasefn) tttable = os.path.join(root, 'time', tttn) locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0\n' % (phasefile, tttable, nllocout) # modification of NLLoc-control file - curlocfiles = getPatternLine(fn, 'LOCFILES') - nllfile = open(fn, 'r') + print ("Modifying NLLoc-control file %s ..." % ctrfile) + curlocfiles = getPatternLine(ctrfile, 'LOCFILES') + nllfile = open(ctrfile, 'r') filedata = nllfile.read() if filedata.find(locfiles) < 0: # replace old command filedata = filedata.replace(curlocfiles, locfiles) - nllfile = open(fn, 'w') + nllfile = open(ctrfile, 'w') nllfile.write(filedata) nllfile.close() def locate(call, fnin): ''' - Takes paths to NLLoc executable and input parameter file and starts the location calculation. + Takes paths to NLLoc executable and input parameter file + and starts the location calculation. + :param call: full path to NLLoc executable :type call: str + :param fnin: full path to input parameter file :type fnin: str ''' + # locate the event subprocess.call([call, fnin]) From e1c2629f97513fa0b139a4822118c6800a44a313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 18 Nov 2015 11:11:10 +0100 Subject: [PATCH 0706/1144] Modified writephases for NLLoc: writes dummy onset times (start of seismic trace) into phase file in order to derive theoretical onset times later needed for iterative picking. --- pylot/core/pick/utils.py | 92 +++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9887c78d..f4eb2282 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -961,46 +961,58 @@ def writephases(arrivals, fformat, filename): # write header fid.write('# EQEVENT: Label: EQ001 Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n') for key in arrivals: - if arrivals[key]['P']['weight'] < 4: - fm = arrivals[key]['P']['fm'] - if fm == None: - fm = '?' - onset = arrivals[key]['P']['mpp'] - year = onset.year - month = onset.month - day = onset.day - hh = onset.hour - mm = onset.minute - ss = onset.second - ms = onset.microsecond - ss_ms = ss + ms / 1000000.0 - fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 1 \n' % (key, - fm, - year, - month, - day, - hh, - mm, - ss_ms)) - if arrivals[key]['S']['weight'] < 4: - fm = '?' - onset = arrivals[key]['S']['mpp'] - year = onset.year - month = onset.month - day = onset.day - hh = onset.hour - mm = onset.minute - ss = onset.second - ms = onset.microsecond - ss_ms = ss + ms / 1000000.0 - fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 1 \n' % (key, - fm, - year, - month, - day, - hh, - mm, - ss_ms)) + # P onsets + if arrivals[key]['P']: + fm = arrivals[key]['P']['fm'] + if fm == None: + fm = '?' + onset = arrivals[key]['P']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + if arrivals[key]['P']['weight'] < 4: + pweight = 1 # use pick + else: + pweight = 0 # do not use pick + fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms, + pweight)) + # S onsets + if arrivals[key]['S']: + fm = '?' + onset = arrivals[key]['S']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + if arrivals[key]['S']['weight'] < 4: + sweight = 1 # use pick + else: + sweight = 0 # do not use pick + fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms, + sweight)) fid.close() From 96fcc2470b98fb46b77972bf6558fbfb2f729c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 18 Nov 2015 11:12:53 +0100 Subject: [PATCH 0707/1144] Modified pick dictionary: writes dummy onset times (start of seismic trace) into dictionary in order to derive theoretical onset times later needed for iterative picking. --- pylot/core/pick/autopick.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 0e1c3030..988c8e5c 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -764,11 +764,23 @@ def autopickstation(wfstream, pickparam): lpickP = zdat[0].stats.starttime + lpickP epickP = zdat[0].stats.starttime + epickP mpickP = zdat[0].stats.starttime + mpickP + else: + # dummy values (start of seismic trace) in order to derive + # theoretical onset times for iteratve picking + lpickP = zdat[0].stats.starttime + epickP = zdat[0].stats.starttime + mpickP = zdat[0].stats.starttime if mpickS is not None and epickS is not None and mpickS is not None: lpickS = edat[0].stats.starttime + lpickS epickS = edat[0].stats.starttime + epickS mpickS = edat[0].stats.starttime + mpickS + else: + # dummy values (start of seismic trace) in order to derive + # theoretical onset times for iteratve picking + lpickS = edat[0].stats.starttime + epickS = edat[0].stats.starttime + mpickS = edat[0].stats.starttime # create dictionary # for P phase From abc45a4c8fd061dcbef76a3b0af1865ef9bd1838 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 18 Nov 2015 17:20:46 +0100 Subject: [PATCH 0708/1144] bugfix: getPickError (/2) added earliest and latest to plot_traces --- pylot/core/active/seismicshot.py | 42 ++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index fce06909..d5b4acac 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -156,9 +156,9 @@ class SeismicShot(object): return pickerror def getPickError(self, traceID): - pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) + pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) / 2 if np.isnan(pickerror) == True: - print("SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID)) + print("PE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID)) return pickerror def getStreamTraceIDs(self): @@ -310,11 +310,16 @@ class SeismicShot(object): tsignal = self.getTsignal() tnoise = self.getPickIncludeRemoved(traceID) - tgap - (self.pick[traceID]['epp'], self.pick[traceID]['lpp'], + (self.pick[traceID]['epp'], + self.pick[traceID]['lpp'], self.pick[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), nfac, (tnoise, tgap, tsignal), self.getPickIncludeRemoved(traceID), stealthMode = True) + + # TEST OF 1/2 PICKERROR + # self.pick[traceID]['spe'] *= 0.5 + # TEST OF 1/2 PICKERROR def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm = 0.6): ''' @@ -674,9 +679,20 @@ class SeismicShot(object): %(self.getShotnumber(), traceID, self.getPick(traceID))) ax.plot(timeaxis, stream[0].data, 'k', label = 'trace') ax.plot([self.getPick(traceID), self.getPick(traceID)], - [min(stream[0].data), - max(stream[0].data)], + [ax.get_ylim()[0], + ax.get_ylim()[1]], 'r', label = 'most likely') + if self.getEarliest(traceID) is not None: + ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], + [ax.get_ylim()[0], + ax.get_ylim()[1]], + 'g:', label = 'earliest') + if self.getLatest(traceID) is not None: + ax.plot([self.getLatest(traceID), self.getLatest(traceID)], + [ax.get_ylim()[0], + ax.get_ylim()[1]], + 'b:', label = 'latest') + ax.legend() return ax @@ -695,9 +711,19 @@ class SeismicShot(object): ax.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label = 'HOS') ax.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label = 'AIC') ax.plot([self.getPick(traceID), self.getPick(traceID)], - [min(np.minimum(hoscf.getCF(), aiccf.getCF())), - max(np.maximum(hoscf.getCF(), aiccf.getCF()))], - 'r', label = 'most likely') + [ax.get_ylim()[0], + ax.get_ylim()[1]], + 'r', label = 'most likely') + if self.getEarliest(traceID) is not None: + ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], + [ax.get_ylim()[0], + ax.get_ylim()[1]], + 'g:', label = 'earliest') + if self.getLatest(traceID) is not None: + ax.plot([self.getLatest(traceID), self.getLatest(traceID)], + [ax.get_ylim()[0], + ax.get_ylim()[1]], + 'b:', label = 'latest') ax.plot([0, self.getPick(traceID)], [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], 'm:', label = 'folm = %s' %folm) From 0a00aab3ba07344ee481e97ee2c82c786414c87a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 18 Nov 2015 17:21:29 +0100 Subject: [PATCH 0709/1144] changed pickerror to SPE for exportFMTOMO minor additions --- pylot/core/active/activeSeismoPick.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 3dc97353..057fae16 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -142,6 +142,11 @@ class Survey(object): progress = float(count) / float(len(self.getShotDict())) * 100 self._update_progress(shot.getShotname(), tend, progress) print('\npickAllShots: Finished\n') + ntraces = self.countAllTraces() + pickedtraces = self.countAllPickedTraces() + print('Picked %s / %s traces (%d %%)\n' + %(pickedtraces, ntraces, float(pickedtraces)/float(ntraces)*100.)) + def recover(self): ''' @@ -259,7 +264,7 @@ class Survey(object): for traceID in traceIDlist: if shot.getFlag(traceID) is not 0: pick = shot.getPick(traceID) * fmtomo_factor - delta = shot.getPickError(traceID) * fmtomo_factor + delta = shot.getSymmetricPickError(traceID) * fmtomo_factor (x, y, z) = shot.getRecLoc(traceID) ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) From 657472f576a8bc93ba10fb278a3b22f4db96262d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 18 Nov 2015 17:22:18 +0100 Subject: [PATCH 0710/1144] added shifting of SNR threshold along y axis --- pylot/core/active/surveyUtils.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index d6fb1d15..d0ae2920 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -14,7 +14,7 @@ def setArtificialPick(shot_dict, traceID, pick): shot.setPick(traceID, pick) shot.setPickwindow(traceID, shot.getCut()) -def fitSNR4dist(shot_dict, shiftdist = 5): +def fitSNR4dist(shot_dict, shiftdist = 5, shiftSNR = 3): import numpy as np dists = [] picks = [] @@ -32,7 +32,7 @@ def fitSNR4dist(shot_dict, shiftdist = 5): fit_fn = np.poly1d(fit) for dist in dists: dist += shiftdist - snrthresholds.append(1/(fit_fn(dist)**2)) + snrthresholds.append((1/(fit_fn(dist)**2)) - shiftSNR) plotFittedSNR(dists, snrthresholds, snrs) return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben @@ -47,13 +47,18 @@ def plotFittedSNR(dists, snrthresholds, snrs): plt.ylabel('SNR') plt.legend() -def setFittedSNR(shot_dict, shiftdist = 5, p1 = 0.004, p2 = -0.004): +def setFittedSNR(shot_dict, shiftdist = 5, shiftSNR = 3, p1 = 0.004, p2 = -0.0007): import numpy as np #fit_fn = fitSNR4dist(shot_dict) fit_fn = np.poly1d([p1, p2]) for shot in shot_dict.values(): for traceID in shot.getTraceIDlist(): ### IMPROVE - shot.setSNRthreshold(traceID, 1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) ### s.o. + snrthreshold = (1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) - shiftSNR + if snrthreshold < 0: + print('WARNING: SNR threshold %s lower 0!!! Try to lower the shiftSNR' + %snrthreshold) + break + shot.setSNRthreshold(traceID, snrthreshold) print "setFittedSNR: Finished setting of fitted SNR-threshold" From 7f85ed99c0d9685b3e087f4ab808524e4756aa5f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 19 Nov 2015 11:38:54 +0100 Subject: [PATCH 0711/1144] bugfix textoutput --- pylot/core/active/fmtomoUtils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 3ca0f5d3..558f2fd4 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -341,7 +341,7 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', _update_progress(progress) print('Added checkerboard to the grid in file %s with a spacing of %s and a pertubation of %s %%. ' - 'Outputfile: %s.'%(inputfile, spacing, pertubation, outputfile)) + 'Outputfile: %s.'%(inputfile, spacing, pertubation*100, outputfile)) outfile.close() def addBox(x = (None, None), y = (None, None), z = (None, None), From 386131e9f3454e2c88183a29caaa7bc094d922cc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 20 Nov 2015 09:06:52 +0100 Subject: [PATCH 0712/1144] started implementation of NLLoc properties selection into manualPyLoT; new utility routine to find indices throughout QComboBox' items --- QtPyLoT.py | 2 +- pylot/core/util/defaults.py | 5 +++++ pylot/core/util/utils.py | 12 ++++++++++++ pylot/core/util/widgets.py | 28 ++++++++++++++++++++++------ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f5d1c352..b82df799 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -771,7 +771,7 @@ class MainWindow(QMainWindow): extlocpath = settings.value("%s/binPath".format(loctool), None) locroot = settings.value("%s/rootPath".format(loctool), None) if extlocpath is None or locroot is None: - pass + self.PyLoTprefs() def check4Loc(self): return self.picksNum() > 4 diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index c22ad865..cef60d1c 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -7,6 +7,9 @@ Created on Wed Feb 26 12:31:25 2014 """ import os +from pylot.core.loc import nll +from pylot.core.loc import hsat +from pylot.core.loc import velest def readFilterInformation(fname): def convert2FreqRange(*args): @@ -41,3 +44,5 @@ FILTERDEFAULTS = readFilterInformation(os.path.join(os.path.expanduser('~'), OUTPUTFORMATS = {'.xml':'QUAKEML', '.cnv':'CNV', '.obs':'NLLOC_OBS'} + +LOCTOOLS = dict(nll = nll, hsat = hsat, velest = velest) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 483aabcb..137ed24d 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -365,6 +365,18 @@ def createAmplitude(pickID, amp, unit, category, cinfo): amplitude.pick_id = pickID return amplitude +def findComboBoxIndex(combo_box, val): + """ + Function findComboBoxIndex takes a QComboBox object and a string and + returns either 0 or the index throughout all QComboBox items. + :param combo_box: Combo box object. + :type combo_box: QComboBox + :param val: Name of a combo box to search for. + :return: index value of item with name val or 0 + """ + + return combo_box.findText(val) if combo_box.findText(val) is not -1 else 0 + if __name__ == "__main__": import doctest doctest.testmod() diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 2fece9da..4e1267f3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -25,9 +25,9 @@ from obspy import Stream, UTCDateTime from pylot.core.read.inputs import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ getResolutionWindow -from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS +from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ - demeanTrace, isSorted + demeanTrace, isSorted, findComboBoxIndex def createAction(parent, text, slot=None, shortcut=None, icon=None, @@ -886,10 +886,7 @@ class OutputsTab(PropTab): eventoutputformats = OUTPUTFORMATS.keys() self.eventOutputComboBox.addItems(eventoutputformats) - if curval is None: - ind = 0 - else: - ind = self.eventOutputComboBox.findText(curval) + ind = findComboBoxIndex(self.eventOutputComboBox, curval) self.eventOutputComboBox.setCurrentIndex(ind) layout = QGridLayout() @@ -917,6 +914,25 @@ class GraphicsTab(PropTab): pass +class LocalisationTab(PropTab): + def __init__(self, parent=None): + super(LocalisationTab, self).__init__(parent) + + settings = QSettings() + curtool = settings.value("loc/tool", None) + + loctoollabel = QLabel("location tool") + self.locToolComboBox = QComboBox() + loctools = LOCTOOLS.keys() + self.locToolComboBox.addItems(loctools) + + toolind = findComboBoxIndex(self.locToolComboBox, curtool) + + self.locToolComboBox.setCurrentIndex(toolind) + + curroot = settings.value("loc/tool", None) + + class NewEventDlg(QDialog): def __init__(self, parent=None, titleString="Create a new event"): """ From 734eca30dbde13f57ef9997d6517ee039b693e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 20 Nov 2015 15:49:34 +0100 Subject: [PATCH 0713/1144] Implementation of new function iteratepicker of module autopick. --- autoPyLoT.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 85d65a48..fcfca838 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -6,11 +6,11 @@ import argparse import glob import subprocess import string -from obspy.core import read +from obspy.core import read, UTCDateTime from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter from pylot.core.util.structure import DATASTRUCTURE -from pylot.core.pick.autopick import autopickevent +from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.loc.nll import * from pylot.core.util.version import get_git_version as _getVersionString @@ -121,7 +121,30 @@ def autoPyLoT(inputfile): # !iterative picking if traces remained unpicked or occupied with bad picks! # get theoretical onset times for picks with weights >= 4 - # in order to reprocess them using smaller time windows + # in order to reprocess them using smaller time windows around theoretical onset + # get stations with bad onsets + badpicks = [] + for key in picks: + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) + + if len(badpicks) == 0: + print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") + else: + print("autoPyLoT: Found bad onsets at station(s) %s, starting re-picking them ...") \ + % badpicks + # get theoretical P-onset times from NLLoc-location file + locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) + # get latest file if several are available + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + if os.path.isfile(nllocfile): + picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) + # write phases to NLLoc-phase file + picksExport(picks, 'NLLoc', phasefile) + # locate the event + locate(nlloccall, ctrfile) + else: + print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## # write phase files for various location routines # HYPO71 @@ -160,10 +183,32 @@ def autoPyLoT(inputfile): # locate the event locate(nlloccall, ctrfile) - # !iterative picking if traces remained unpicked or occupied with bad picks! # get theoretical onset times for picks with weights >= 4 - # in order to reprocess them using smaller time windows + # in order to reprocess them using smaller time windows around theoretical onset + # get stations with bad onsets + badpicks = [] + for key in picks: + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) + + if len(badpicks) == 0: + print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") + else: + print("autoPyLoT: Found bad onsets at station(s) %s, starting re-picking them ...") \ + % badpicks + # get theoretical P-onset times from NLLoc-location file + locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) + # get latest file if several are available + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + if os.path.isfile(nllocfile): + picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) + # write phases to NLLoc-phase file + picksExport(picks, 'NLLoc', phasefile) + # locate the event + locate(nlloccall, ctrfile) + else: + print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## # write phase files for various location routines # HYPO71 From 07bbc2926e80d4b7855312fab143c5c1e14e7d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 20 Nov 2015 15:51:22 +0100 Subject: [PATCH 0714/1144] New function iteratepicker for iterative picking. --- pylot/core/pick/autopick.py | 79 +++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 988c8e5c..7685596f 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -3,7 +3,7 @@ """ Function to run automated picking algorithms using AIC, -HOS and AR prediction. Uses object CharFuns and Picker and +HOS and AR prediction. Uses objects CharFuns and Picker and function conglomerate utils. :author: MAGS2 EP3 working group / Ludger Kueperkoch @@ -16,6 +16,7 @@ from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ getSNR, fmpicker, checkPonsets, wadaticheck, crossings_nonzero_all +from pylot.core.util.utils import getPatternLine from pylot.core.read.data import Data from pylot.core.analysis.magnitude import WApp, DCfc @@ -316,12 +317,11 @@ def autopickstation(wfstream, pickparam): # from P pulse # initialize Data object data = Data() - [corzdat, restflag] = data.restituteWFData(invdir, zdat) + z_copy = zdat.copy() + [corzdat, restflag] = data.restituteWFData(invdir, z_copy) if restflag == 1: # integrate to displacement corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta) - # class needs stream object => build it - z_copy = zdat.copy() z_copy[0].data = corintzdat # largest detectable period == window length # after P pulse for calculating source spectrum @@ -799,3 +799,74 @@ def autopickstation(wfstream, pickparam): picks[phase]['Ao'] = Ao return picks + +def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): + ''' + Repicking of bad onsets. Uses theoretical onset times from NLLoc-location file. + + :param wf: waveform, obspy stream object + + :param NLLocfile: path/name of NLLoc-location file + + :param picks: dictionary of available onset times + + :param badpicks: picks to be repicked + + :param pickparameter: picking parameters from autoPyLoT-input file + ''' + + newpicks = {} + for i in range(0, len(badpicks)): + if len(badpicks[i][0]) > 4: + Ppattern = '%s ? ? ? P' % badpicks[i][0] + elif len(badpicks[i][0]) == 4: + Ppattern = '%s ? ? ? P' % badpicks[i][0] + elif len(badpicks[i][0]) < 4: + Ppattern = '%s ? ? ? P' % badpicks[i][0] + nllocline = getPatternLine(NLLocfile, Ppattern) + res = nllocline.split(None)[16] + # get theoretical P-onset time from residuum + badpicks[i][1] = picks[badpicks[i][0]]['P']['mpp'] - float(res) + + # get corresponding waveform stream + wf2pick = wf.select(station=badpicks[i][0]) + + # modify some picking parameters + pstart_old = pickparameter.getParam('pstart') + pstop_old = pickparameter.getParam('pstop') + pickwinP_old = pickparameter.getParam('pickwinP') + Precalcwin_old = pickparameter.getParam('Precalcwin') + pickparameter.setParam(pstart=badpicks[i][1] - wf2pick[0].stats.starttime \ + - pickparameter.getParam('tlta')) + pickparameter.setParam(pstop=pickparameter.getParam('pstart') + \ + (3 * pickparameter.getParam('tlta'))) + pickparameter.setParam(pickwinP=pickparameter.getParam('pickwinP') / 2) + pickparameter.setParam(Precalcwin=pickparameter.getParam('Precalcwin') / 2) + pickparameter.setParam(iplot=2) + print("iteratepicker: The following picking parameters have been modified for iterative picking:") + print("pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) + print("pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) + print("pickwinP: %fs => %fs" % (pickwinP_old, pickparameter.getParam('pickwinP'))) + print("Precalcwin: %fs => %fs" % (Precalcwin_old, pickparameter.getParam('Precalcwin'))) + + # repick station + newpicks = autopickstation(wf2pick, pickparameter) + + # replace old dictionary with new one + picks[badpicks[i][0]] = newpicks + + # reset temporary change of picking parameters + print("iteratepicker: Resetting picking parameters ...") + pickparameter.setParam(pstart=pstart_old) + pickparameter.setParam(pstop=pstop_old) + pickparameter.setParam(pickwinP=pickwinP_old) + pickparameter.setParam(Precalcwin=Precalcwin_old) + + return picks + + + + + + + From 8197d8f3d57cdff4fafb839605434c9a5f9f8510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 20 Nov 2015 15:52:14 +0100 Subject: [PATCH 0715/1144] Some input parameters changed from integers to floats. --- autoPyLoT_local.in | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index baabf0ce..93e56f16 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -17,7 +17,7 @@ PILOT #datastructure#%choose data structure /home/ludger/NLLOC/Insheim #nllocroot# %root of NLLoc-processing directory AUTOPHASES.obs #phasefile# %name of autoPyLoT-output phase file for NLLoc %(in nllocroot/obs) -Insheim_min1d2015_auto.in #locfile# %name of autoPyLoT-output control file for NLLoc +Insheim_min1d2015_auto.in #ctrfile# %name of autoPyLoT-output control file for NLLoc %(in nllocroot/run) ttime #ttpatter# %pattern of NLLoc ttimes from grid %(in nllocroot/times) @@ -41,7 +41,7 @@ AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file co #common settings picker# 15 #pstart# %start time [s] for calculating CF for P-picking 60 #pstop# %end time [s] for calculating CF for P-picking --1.0 #sstart# %start time [s] after or before(-) P-onset for calculating CF for S-picking +-1.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking 7 #sstop# %end time [s] after P-onset for calculating CF for S-picking 2 20 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] 2 30 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] @@ -51,7 +51,7 @@ AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file co %!!Be careful when editing the following!! #Z-component# HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3) -7 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s] +7.0 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s] 4 #hosorder# %for HOS-picker, order of Higher Order Statistics 2 #Parorder# %for AR-picker, order of AR process of Z-component 1.2 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick @@ -60,8 +60,8 @@ HOS #algoP# %choose algorithm for P-onset 0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick 0.001 #addnoise# %add noise to seismogram for stable AR prediction 3 0.1 0.5 0.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] -3 #pickwinP# %for initial AIC pick, length of P-pick window [s] -8 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) +3.0 #pickwinP# %for initial AIC pick, length of P-pick window [s] +8.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) 0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) 0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] 0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] @@ -74,8 +74,8 @@ ARH #algoS# %choose algorithm for S-onset 0.6 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick 0.3 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick 4 #Sarorder# %for AR-picker, order of AR process of H-components -6 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) -3 #pickwinS# %for initial AIC pick, length of S-pick window [s] +6.0 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) +3.0 #pickwinS# %for initial AIC pick, length of S-pick window [s] 2 0.2 1.5 0.5 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] 0.05 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] 0.02 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) @@ -83,7 +83,7 @@ ARH #algoS# %choose algorithm for S-onset 0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) 1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) %first-motion picker% -1 #minfmweight# %minimum required p weight for first-motion determination +1 #minfmweight# %minimum required P weight for first-motion determination 2 #minFMSNR# %miniumum required SNR for first-motion determination 0.2 #fmpickwin# %pick window around P onset for calculating zero crossings %quality assessment% @@ -92,7 +92,7 @@ ARH #algoS# %choose algorithm for S-onset 0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S 10 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected 1.2 #minAICPSNR# %below this SNR the initial P pick is rejected -6 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +3 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# 5 #minsiglength# %minimum required length of signal [s] From 67ac5807788d244e52be2efe9fa71c5b0d9c1396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 20 Nov 2015 15:53:25 +0100 Subject: [PATCH 0716/1144] Suppressed print output in setParam. --- pylot/core/read/inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index e6d609e3..cd66e845 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -145,7 +145,7 @@ class AutoPickParameter(object): def setParam(self, **kwargs): for param, value in kwargs.items(): self.__setitem__(param, value) - print(self) + #print(self) @staticmethod def _printParameterError(errmsg): From 8a16643bd81926bae6649389141403fec6fd0874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 20 Nov 2015 16:02:25 +0100 Subject: [PATCH 0717/1144] Marginal changes. --- pylot/core/pick/autopick.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 7685596f..be31c183 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -815,6 +815,10 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): :param pickparameter: picking parameters from autoPyLoT-input file ''' + print("#######################################################") + print("autoPyLoT: Found bad onsets at station(s) %s, starting re-picking them ...") \ + % badpicks + newpicks = {} for i in range(0, len(badpicks)): if len(badpicks[i][0]) > 4: @@ -842,7 +846,6 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): (3 * pickparameter.getParam('tlta'))) pickparameter.setParam(pickwinP=pickparameter.getParam('pickwinP') / 2) pickparameter.setParam(Precalcwin=pickparameter.getParam('Precalcwin') / 2) - pickparameter.setParam(iplot=2) print("iteratepicker: The following picking parameters have been modified for iterative picking:") print("pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) print("pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) From 8173e44b0497e0c96e22642394027f59ee9f5e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 20 Nov 2015 16:02:54 +0100 Subject: [PATCH 0718/1144] Matginal changes. --- autoPyLoT.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index fcfca838..6a89a660 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -131,8 +131,6 @@ def autoPyLoT(inputfile): if len(badpicks) == 0: print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") else: - print("autoPyLoT: Found bad onsets at station(s) %s, starting re-picking them ...") \ - % badpicks # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) # get latest file if several are available @@ -195,8 +193,6 @@ def autoPyLoT(inputfile): if len(badpicks) == 0: print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") else: - print("autoPyLoT: Found bad onsets at station(s) %s, starting re-picking them ...") \ - % badpicks # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) # get latest file if several are available From bd0d96c2ffd25b7ab8cfee05c84d9aea07208ba5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 Nov 2015 11:35:15 +0100 Subject: [PATCH 0719/1144] changed input for generateFMTOMOinpu --- pylot/core/active/seismicArrayPreparation.py | 32 ++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 47f50835..607d904f 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -381,14 +381,34 @@ class SeisArray(object): return surface - def generateFMTOMOinputFromArray(self, nRP, nThetaP, nPhiP, nRI, nThetaI, nPhiI, - Rbt, cushionfactor, interpolationMethod = 'linear', + def generateFMTOMOinputFromArray(self, nPointsPropgrid, nPointsInvgrid, + zBotTop, cushionfactor, interpolationMethod = 'linear', customgrid = 'mygrid.in', writeVTK = True): + ''' + Generate FMTOMO input files from the SeisArray dimensions. + Generates: vgrids.in, interfaces.in, propgrid.in + + :param: nPointsPropgrid, number of points in each direction of the propagation grid (z, y, x) + :type: tuple + + :param: nPointsInvgrid, number of points in each direction of the inversion grid (z, y, x) + :type: tuple + + :param: zBotTop, (bottom, top) dimensions of the model + :type: tuple + + :param: cushionfactor, adds cushioning around the model (0.1 = 10%) + :type: float + ''' + + nRP, nThetaP, nPhiP = nPointsPropgrid + nRI, nThetaI, nPhiI = nPointsInvgrid + print('\n------------------------------------------------------------') print('Automatically generating input for FMTOMO from array size.') print('Propgrid: nR = %s, nTheta = %s, nPhi = %s'%(nRP, nThetaP, nPhiP)) print('Interpolation Grid and Interfaces Grid: nR = %s, nTheta = %s, nPhi = %s'%(nRI, nThetaI, nPhiI)) - print('Bottom and Top of model: (%s, %s)'%(Rbt[0], Rbt[1])) + print('Bottom and Top of model: (%s, %s)'%(zBotTop[0], zBotTop[1])) print('Method: %s, customgrid = %s'%(interpolationMethod, customgrid)) print('------------------------------------------------------------') @@ -398,13 +418,13 @@ class SeisArray(object): z.append(point[2]) return min(z) - self.generatePropgrid(nThetaP, nPhiP, nRP, Rbt, cushionfactor = cushionfactor, + self.generatePropgrid(nThetaP, nPhiP, nRP, zBotTop, cushionfactor = cushionfactor, cushionpropgrid = 0.05) - surface = self.generateVgrid(nThetaI, nPhiI, nRI, Rbt, method = interpolationMethod, + surface = self.generateVgrid(nThetaI, nPhiI, nRI, zBotTop, method = interpolationMethod, cushionfactor = cushionfactor, infilename = customgrid, returnTopo = True) - depthmax = abs(Rbt[0] - getZmin(surface)) - 1.0 # cushioning for the bottom interface + depthmax = abs(zBotTop[0] - getZmin(surface)) - 1.0 # cushioning for the bottom interface interf1, interf2 = self.generateInterfaces(nThetaI, nPhiI, depthmax, cushionfactor = cushionfactor, returnInterfaces = True, method = interpolationMethod) From 851da7eb155589f7b79cdcebc54bc62ea4a45448 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Nov 2015 11:41:04 +0100 Subject: [PATCH 0720/1144] re-design of the properties tabbed window --- pylot/core/util/widgets.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 4e1267f3..307b7e39 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -930,7 +930,13 @@ class LocalisationTab(PropTab): self.locToolComboBox.setCurrentIndex(toolind) + curroot = settings.value("loc/tool", None) + if curtool is not None: + rootlabel = QLabel("{0} root dircetory".format(curtool)) + else: + rootlabel = QLabel("root dircetory") + rootlabel.setDisabled() class NewEventDlg(QDialog): From 4fdcf1cf605f2668564d1aeed3e74bfc4bc3fdc0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Nov 2015 12:11:04 +0100 Subject: [PATCH 0721/1144] [bugsearch] trying to fix UnpicklingError by clearing GUI element containing attributes --- pylot/core/active/activeSeismoPick.py | 12 +++--- pylot/core/active/seismicshot.py | 55 ++++++++++++--------------- pylot/core/active/surveyUtils.py | 5 +++ 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 057fae16..c90ce9a0 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -2,6 +2,7 @@ import sys import numpy as np from pylot.core.active import seismicshot +from pylot.core.active.surveyUtils import cleanUp class Survey(object): def __init__(self, path, sourcefile, receiverfile, useDefaultParas = False): @@ -127,11 +128,11 @@ class Survey(object): shot.setPickwindow(traceID, pickwin_used) shot.pickTraces(traceID, windowsize, folm, HosAic) # picker - shot.setSNR(traceID) + shot.setSNR(traceID) #if shot.getSNR(traceID)[0] < snrthreshold: if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): shot.removePick(traceID) - + # set epp and lpp if SNR > 1 (else earllatepicker cant set values) if shot.getSNR(traceID)[0] > 1: shot.setEarllatepick(traceID) @@ -314,10 +315,10 @@ class Survey(object): # shotnumbers.append(shot.getShotnumber()) # shotnumbers = [shotnumbers for (shotnumbers, shotnames) in sorted(zip(shotnumbers, shotnames))] - + for shotnumber in self.getShotlist(): if index <= figPerSubplot: - #ax = fig.add_subplot(3,3,i, projection = '3d', title = 'shot:' + #ax = fig.add_subplot(3,3,i, projection = '3d', title = 'shot:' #+str(shot_dict[shotnumber].getShotnumber()), xlabel = 'X', ylabel = 'Y', zlabel = 'traveltime') #shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) ax = fig.add_subplot(3, 4, index) @@ -374,7 +375,7 @@ class Survey(object): for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if plotRemoved == False: - if shot.getFlag(traceID) is not 0 or plotRemoved == True: + if shot.getFlag(traceID) is not 0 or plotRemoved == True: dist.append(shot.getDistance(traceID)) pick.append(shot.getPick(traceID)) snrlog.append(math.log10(shot.getSNR(traceID)[0])) @@ -428,6 +429,7 @@ class Survey(object): def saveSurvey(self, filename = 'survey.pickle'): import cPickle + cleanUp(self) outfile = open(filename, 'wb') cPickle.dump(self, outfile, -1) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index d5b4acac..6af61a2a 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -151,7 +151,7 @@ class SeismicShot(object): def getSymmetricPickError(self, traceID): pickerror = self.pick[traceID]['spe'] - if np.isnan(pickerror) == True: + if np.isnan(pickerror) == True: print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) return pickerror @@ -276,7 +276,7 @@ class SeismicShot(object): :param: traceID :type: int - + :param: cutwindow (equals HOScf 'cut' variable) :type: tuple @@ -313,10 +313,10 @@ class SeismicShot(object): (self.pick[traceID]['epp'], self.pick[traceID]['lpp'], self.pick[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), - nfac, (tnoise, tgap, tsignal), + nfac, (tnoise, tgap, tsignal), self.getPickIncludeRemoved(traceID), stealthMode = True) - + # TEST OF 1/2 PICKERROR # self.pick[traceID]['spe'] *= 0.5 # TEST OF 1/2 PICKERROR @@ -630,7 +630,10 @@ class SeismicShot(object): def connectButton(event = None): cid = fig.canvas.mpl_connect('button_press_event', onclick) self.traces4plot[traceID]['cid'] = cid - + + def cleanup(event): + self.traces4plot[traceID] = {} + fig = plt.figure() ax1 = fig.add_subplot(2,1,1) ax2 = fig.add_subplot(2,1,2, sharex = ax1) @@ -640,18 +643,10 @@ class SeismicShot(object): button1.on_clicked(connectButton) button2 = Button(axb2, 'delete', color = 'green', hovercolor = 'grey') button2.on_clicked(rmPick) + fig.canvas.mpl_connect('close_event', cleanup) - if traceID not in self.traces4plot.keys(): - self.traces4plot[traceID] = {} - - self.traces4plot[traceID] = {'fig': fig, - 'ax1': ax1, - 'ax2': ax2, - 'axb1': axb1, - 'axb2': axb2, - 'button1': button1, - 'button2': button2, - 'cid': None} + self.traces4plot[traceID] = dict(fig=fig, ax1=ax1, ax2=ax2, axb1=axb1, axb2=axb2, button1=button1, + button2=button2, cid=None) self._drawStream(traceID) self._drawCFs(traceID, folm) @@ -664,7 +659,7 @@ class SeismicShot(object): stime = getGlobalTimes(stream)[0] timeaxis = prepTimeAxis(stime, stream[0]) timeaxis -= stime - + if ax is None: ax = self.traces4plot[traceID]['ax1'] @@ -678,18 +673,18 @@ class SeismicShot(object): ax.set_title('Shot: %s, traceID: %s, pick: %s' %(self.getShotnumber(), traceID, self.getPick(traceID))) ax.plot(timeaxis, stream[0].data, 'k', label = 'trace') - ax.plot([self.getPick(traceID), self.getPick(traceID)], - [ax.get_ylim()[0], + ax.plot([self.getPick(traceID), self.getPick(traceID)], + [ax.get_ylim()[0], ax.get_ylim()[1]], 'r', label = 'most likely') if self.getEarliest(traceID) is not None: - ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], - [ax.get_ylim()[0], + ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], + [ax.get_ylim()[0], ax.get_ylim()[1]], 'g:', label = 'earliest') if self.getLatest(traceID) is not None: - ax.plot([self.getLatest(traceID), self.getLatest(traceID)], - [ax.get_ylim()[0], + ax.plot([self.getLatest(traceID), self.getLatest(traceID)], + [ax.get_ylim()[0], ax.get_ylim()[1]], 'b:', label = 'latest') @@ -710,18 +705,18 @@ class SeismicShot(object): ax.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label = 'HOS') ax.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label = 'AIC') - ax.plot([self.getPick(traceID), self.getPick(traceID)], - [ax.get_ylim()[0], + ax.plot([self.getPick(traceID), self.getPick(traceID)], + [ax.get_ylim()[0], ax.get_ylim()[1]], 'r', label = 'most likely') if self.getEarliest(traceID) is not None: - ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], - [ax.get_ylim()[0], + ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], + [ax.get_ylim()[0], ax.get_ylim()[1]], 'g:', label = 'earliest') if self.getLatest(traceID) is not None: - ax.plot([self.getLatest(traceID), self.getLatest(traceID)], - [ax.get_ylim()[0], + ax.plot([self.getLatest(traceID), self.getLatest(traceID)], + [ax.get_ylim()[0], ax.get_ylim()[1]], 'b:', label = 'latest') ax.plot([0, self.getPick(traceID)], @@ -784,7 +779,7 @@ class SeismicShot(object): plotmethod = {'2d': self.plot2dttc, '3d': self.plot3dttc} plotmethod[method](*args) - + def matshow(self, ax = None, step = 0.5, method = 'linear', plotRec = True, annotations = True, colorbar = True): ''' Plots a 2D matrix of the interpolated traveltimes. This needs less performance than plot3dttc diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index d0ae2920..0b72d4b8 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -86,3 +86,8 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): shots_found[shot.getShotnumber()].append(traceID) return shots_found + +def cleanUp(survey): + + for shot in survey.data.values(): + shot.traces4plot = {} From fdd7ff003b65713eb432c6affa01c27b043e821f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 Nov 2015 13:01:36 +0100 Subject: [PATCH 0722/1144] [bugfix] SNR minimum for setFittedSNR is now 1 --- pylot/core/active/surveyUtils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 0b72d4b8..cae85bcc 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -54,9 +54,10 @@ def setFittedSNR(shot_dict, shiftdist = 5, shiftSNR = 3, p1 = 0.004, p2 = -0.000 for shot in shot_dict.values(): for traceID in shot.getTraceIDlist(): ### IMPROVE snrthreshold = (1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) - shiftSNR - if snrthreshold < 0: - print('WARNING: SNR threshold %s lower 0!!! Try to lower the shiftSNR' + if snrthreshold <= 1: + print('WARNING: SNR threshold %s lower 1!!! Try to lower the shiftSNR' %snrthreshold) + shot.setSNRthreshold(traceID, 1) break shot.setSNRthreshold(traceID, snrthreshold) print "setFittedSNR: Finished setting of fitted SNR-threshold" From 8a4ac82c3a78f43764e12068c0a1787bf2303b48 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 23 Nov 2015 15:07:43 +0100 Subject: [PATCH 0723/1144] [new] added a new location tools tab to the properties widget (not working yet) --- pylot/core/util/widgets.py | 49 +++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 307b7e39..754bb3af 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -18,7 +18,7 @@ from matplotlib.widgets import MultiCursor from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout, \ QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, QPixmap, QSpinBox, \ - QTabWidget, QToolBar, QVBoxLayout, QWidget + QTabWidget, QToolBar, QVBoxLayout, QWidget, QPushButton, QFileDialog from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime @@ -789,6 +789,7 @@ class PropertiesDlg(QDialog): self.tabWidget.addTab(OutputsTab(self), "Outputs") self.tabWidget.addTab(PhasesTab(self), "Phases") self.tabWidget.addTab(GraphicsTab(self), "Graphics") + self.tabWidget.addTab(LocalisationTab(self), "Tools") self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Close) @@ -842,7 +843,7 @@ class InputsTab(PropTab): # get the full name of the actual user self.fullNameEdit = QLineEdit() - self.fullNameEdit.setText(fulluser) + self.fullNameEdit.setText(fulluser[0]) # information about data structure dataroot = settings.value("data/dataRoot") @@ -929,14 +930,46 @@ class LocalisationTab(PropTab): toolind = findComboBoxIndex(self.locToolComboBox, curtool) self.locToolComboBox.setCurrentIndex(toolind) - - curroot = settings.value("loc/tool", None) + curroot = settings.value("%s/rootPath".format(curtool), None) + curbin = settings.value("%s/binPath".format(curtool), None) + + rootlabel = QLabel("root directory") + binlabel = QLabel("bin directory") + + rootedit = QLineEdit('') + binedit = QLineEdit('') + + rootBrowse = QPushButton('...', self) + rootBrowse.clicked.connect(lambda: self.selectDirectory(rootedit)) + + binBrowse = QPushButton('...', self) + binBrowse.clicked.connect(lambda: self.selectDirectory(binedit)) + if curtool is not None: - rootlabel = QLabel("{0} root dircetory".format(curtool)) - else: - rootlabel = QLabel("root dircetory") - rootlabel.setDisabled() + rootlabel.setText("{0} root directory".format(curtool)) + binlabel.setText("{0} bin directory".format(curtool)) + if curroot is not None: + rootedit.setText(curroot) + if curbin is not None: + binedit.setText(curbin) + + layout = QGridLayout() + layout.addWidget(loctoollabel, 0, 0) + layout.addWidget(self.locToolComboBox, 0, 1) + layout.addWidget(rootlabel, 1, 0) + layout.addWidget(rootedit, 1, 1) + layout.addWidget(rootBrowse, 1, 2) + layout.addWidget(binlabel, 2, 0) + layout.addWidget(binedit, 2, 1) + layout.addWidget(binBrowse, 2, 2) + + self.setLayout(layout) + + def selectDirectory(self, edit): + selected_directory = QFileDialog.getExistingDirectory() + edit.setText(selected_directory) + class NewEventDlg(QDialog): From 574c26c3ec9301190bf0391e318109f879b44468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 23 Nov 2015 16:36:43 +0100 Subject: [PATCH 0724/1144] Modified some parameters. --- autoPyLoT_local.in | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 93e56f16..7206954d 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -6,8 +6,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2013.02_Insheim #database# %name of data base -e0019.048.13 #eventID# %event ID for single event processing +2015.10_Insheim #database# %name of data base +e0011.280.15 #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything @@ -39,10 +39,10 @@ AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file co 300 #Qp# %quality factor for P waves 100 #Qs# %quality factor for S waves #common settings picker# -15 #pstart# %start time [s] for calculating CF for P-picking -60 #pstop# %end time [s] for calculating CF for P-picking +15.0 #pstart# %start time [s] for calculating CF for P-picking +60.0 #pstop# %end time [s] for calculating CF for P-picking -1.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking -7 #sstop# %end time [s] after P-onset for calculating CF for S-picking +12.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking 2 20 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] 2 30 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] 2 15 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] @@ -95,8 +95,8 @@ ARH #algoS# %choose algorithm for S-onset 3 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# -5 #minsiglength# %minimum required length of signal [s] -1.8 #noisefactor# %noiselevel*noisefactor=threshold +4 #minsiglength# %minimum required length of signal [s] +1.5 #noisefactor# %noiselevel*noisefactor=threshold 50 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 2.0 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude From e5bddab2be7c96e1068d9c97638425f9dd364891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 23 Nov 2015 16:38:52 +0100 Subject: [PATCH 0725/1144] Finished iterative picking: If bad picks are found, autoPyLoT iteratively re-picks these traces with modified picking parameters at maximum three times, if the former runs still leave bad picks. --- autoPyLoT.py | 67 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 6a89a660..27e45841 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -133,14 +133,32 @@ def autoPyLoT(inputfile): else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) - # get latest file if several are available - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) - if os.path.isfile(nllocfile): - picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) - # write phases to NLLoc-phase file - picksExport(picks, 'NLLoc', phasefile) - # locate the event - locate(nlloccall, ctrfile) + maxnumit = 3 # maximum number of iterations + if len(glob.glob(locsearch)) > 0: + # get latest file if several are available + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nlloccounter = 0 + while len(badpicks) > 0 and nlloccounter <= maxnumit: + nlloccounter += 1 + if nlloccounter > maxnumit: + print("autoPyLoT: Number of maximum iterations reached, stop iterative picking!") + break + print("autoPyLoT: Starting with iteration No. %d ..." % nlloccounter) + picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) + # write phases to NLLoc-phase file + picksExport(picks, 'NLLoc', phasefile) + # locate the event + locate(nlloccall, ctrfile) + print("autoPyLoT: Iteration No. %d finished." % nlloccounter) + badpicks = [] + for key in picks: + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) + print("autoPyLoT: After iteration No. %d: %d bad onsets found ..." % (nlloccounter, \ + len(badpicks))) + if len(badpicks) == 0: + print("autoPyLoT: No more bad onsets found, stop iterative picking!") + break else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## @@ -195,14 +213,33 @@ def autoPyLoT(inputfile): else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) - # get latest file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) - if os.path.isfile(nllocfile): - picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) - # write phases to NLLoc-phase file - picksExport(picks, 'NLLoc', phasefile) - # locate the event - locate(nlloccall, ctrfile) + maxnumit = 3 # maximum number of iterations + if len(glob.glob(locsearch)) > 0: + # get latest file if several are available + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nlloccounter = 0 + while len(badpicks) > 0 and nlloccounter <= maxnumit: + nlloccounter += 1 + if nlloccounter > maxnumit: + print("autoPyLoT: Number of maximum iterations reached, stop iterative picking!") + break + print("autoPyLoT: Starting with iteration No. %d ..." % nlloccounter) + picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) + # write phases to NLLoc-phase file + picksExport(picks, 'NLLoc', phasefile) + # locate the event + locate(nlloccall, ctrfile) + print("autoPyLoT: Iteration No. %d finished." % nlloccounter) + badpicks = [] + for key in picks: + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) + print("autoPyLoT: After iteration No. %d: %d bad onsets found ..." % (nlloccounter, \ + len(badpicks))) + if len(badpicks) == 0: + print("autoPyLoT: No more bad onsets found, stop iterative picking!") + break else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## From 0a1d177d60477a9cca331c2f7463443374b1926c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 23 Nov 2015 16:40:24 +0100 Subject: [PATCH 0726/1144] Additional picking parameters to be temporary modified for iterative picking. --- pylot/core/pick/autopick.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index be31c183..4d693e3a 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -800,6 +800,7 @@ def autopickstation(wfstream, pickparam): return picks + def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): ''' Repicking of bad onsets. Uses theoretical onset times from NLLoc-location file. @@ -816,8 +817,8 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): ''' print("#######################################################") - print("autoPyLoT: Found bad onsets at station(s) %s, starting re-picking them ...") \ - % badpicks + print("autoPyLoT: Found %d bad onsets at station(s) %s, starting re-picking them ...") \ + % (len(badpicks), badpicks) newpicks = {} for i in range(0, len(badpicks)): @@ -838,32 +839,44 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): # modify some picking parameters pstart_old = pickparameter.getParam('pstart') pstop_old = pickparameter.getParam('pstop') + sstop_old = pickparameter.getParam('sstop') pickwinP_old = pickparameter.getParam('pickwinP') Precalcwin_old = pickparameter.getParam('Precalcwin') + noisefactor_old = pickparameter.getParam('noisefactor') + zfac_old = pickparameter.getParam('zfac') pickparameter.setParam(pstart=badpicks[i][1] - wf2pick[0].stats.starttime \ - pickparameter.getParam('tlta')) pickparameter.setParam(pstop=pickparameter.getParam('pstart') + \ (3 * pickparameter.getParam('tlta'))) + pickparameter.setParam(sstop=pickparameter.getParam('sstop') / 2) pickparameter.setParam(pickwinP=pickparameter.getParam('pickwinP') / 2) pickparameter.setParam(Precalcwin=pickparameter.getParam('Precalcwin') / 2) + pickparameter.setParam(noisefactor=1.0) + pickparameter.setParam(zfac=1.0) print("iteratepicker: The following picking parameters have been modified for iterative picking:") print("pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) print("pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) + print("sstop: %fs => %fs" % (sstop_old, pickparameter.getParam('sstop'))) print("pickwinP: %fs => %fs" % (pickwinP_old, pickparameter.getParam('pickwinP'))) print("Precalcwin: %fs => %fs" % (Precalcwin_old, pickparameter.getParam('Precalcwin'))) - + print("noisefactor: %f => %f" % (noisefactor_old, pickparameter.getParam('noisefactor'))) + print("zfac: %f => %f" % (zfac_old, pickparameter.getParam('zfac'))) + # repick station newpicks = autopickstation(wf2pick, pickparameter) # replace old dictionary with new one picks[badpicks[i][0]] = newpicks - # reset temporary change of picking parameters - print("iteratepicker: Resetting picking parameters ...") - pickparameter.setParam(pstart=pstart_old) - pickparameter.setParam(pstop=pstop_old) - pickparameter.setParam(pickwinP=pickwinP_old) - pickparameter.setParam(Precalcwin=Precalcwin_old) + # reset temporary change of picking parameters + print("iteratepicker: Resetting picking parameters ...") + pickparameter.setParam(pstart=pstart_old) + pickparameter.setParam(pstop=pstop_old) + pickparameter.setParam(sstop=sstop_old) + pickparameter.setParam(pickwinP=pickwinP_old) + pickparameter.setParam(Precalcwin=Precalcwin_old) + pickparameter.setParam(noisefactor=noisefactor_old) + pickparameter.setParam(zfac=zfac_old) return picks From 69efd4d411ff927f783d126dbcd8a5ba6d68e2a2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 24 Nov 2015 10:26:36 +0100 Subject: [PATCH 0727/1144] finished implementation of location tool tab in properties window (used to modify settings from the GUI) --- pylot/core/util/widgets.py | 50 ++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 754bb3af..26ad0114 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -789,7 +789,7 @@ class PropertiesDlg(QDialog): self.tabWidget.addTab(OutputsTab(self), "Outputs") self.tabWidget.addTab(PhasesTab(self), "Phases") self.tabWidget.addTab(GraphicsTab(self), "Graphics") - self.tabWidget.addTab(LocalisationTab(self), "Tools") + self.tabWidget.addTab(LocalisationTab(self), "Loc Tools") self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Close) @@ -934,42 +934,56 @@ class LocalisationTab(PropTab): curroot = settings.value("%s/rootPath".format(curtool), None) curbin = settings.value("%s/binPath".format(curtool), None) - rootlabel = QLabel("root directory") - binlabel = QLabel("bin directory") + self.rootlabel = QLabel("root directory") + self.binlabel = QLabel("bin directory") - rootedit = QLineEdit('') - binedit = QLineEdit('') + self.rootedit = QLineEdit('') + self.binedit = QLineEdit('') + + if curroot is not None: + self.rootedit.setText(curroot) + if curbin is not None: + self.binedit.setText(curbin) rootBrowse = QPushButton('...', self) - rootBrowse.clicked.connect(lambda: self.selectDirectory(rootedit)) + rootBrowse.clicked.connect(lambda: self.selectDirectory(self.rootedit)) binBrowse = QPushButton('...', self) - binBrowse.clicked.connect(lambda: self.selectDirectory(binedit)) + binBrowse.clicked.connect(lambda: self.selectDirectory(self.binedit)) - if curtool is not None: - rootlabel.setText("{0} root directory".format(curtool)) - binlabel.setText("{0} bin directory".format(curtool)) - if curroot is not None: - rootedit.setText(curroot) - if curbin is not None: - binedit.setText(curbin) + self.locToolComboBox.currentIndexChanged.connect(self.updateUi) + + self.updateUi() layout = QGridLayout() layout.addWidget(loctoollabel, 0, 0) layout.addWidget(self.locToolComboBox, 0, 1) - layout.addWidget(rootlabel, 1, 0) - layout.addWidget(rootedit, 1, 1) + layout.addWidget(self.rootlabel, 1, 0) + layout.addWidget(self.rootedit, 1, 1) layout.addWidget(rootBrowse, 1, 2) - layout.addWidget(binlabel, 2, 0) - layout.addWidget(binedit, 2, 1) + layout.addWidget(self.binlabel, 2, 0) + layout.addWidget(self.binedit, 2, 1) layout.addWidget(binBrowse, 2, 2) self.setLayout(layout) + def updateUi(self): + curtool = self.locToolComboBox.currentText() + if curtool is not None: + self.rootlabel.setText("{0} root directory".format(curtool)) + self.binlabel.setText("{0} bin directory".format(curtool)) + def selectDirectory(self, edit): selected_directory = QFileDialog.getExistingDirectory() edit.setText(selected_directory) + def getValues(self): + loctool = self.locToolComboBox.currentText() + values = {"%s/rootPath".format(loctool): self.rootedit.text(), + "%s/binPath".format(loctool): self.binedit.text(), + "loc/tool": loctool} + return values + class NewEventDlg(QDialog): From 7a9c44198f03019d85cc123c5a1097c3a02733b2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 24 Nov 2015 11:05:19 +0100 Subject: [PATCH 0728/1144] [bugfix] currently set data structure now selected in QComboBox widget --- pylot/core/util/widgets.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 26ad0114..8fc47268 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -843,10 +843,11 @@ class InputsTab(PropTab): # get the full name of the actual user self.fullNameEdit = QLineEdit() - self.fullNameEdit.setText(fulluser[0]) + self.fullNameEdit.setText(fulluser) # information about data structure dataroot = settings.value("data/dataRoot") + curstructure = settings.value("data/Structure") dataDirLabel = QLabel("data root directory: ") self.dataDirEdit = QLineEdit() self.dataDirEdit.setText(dataroot) @@ -858,6 +859,10 @@ class InputsTab(PropTab): self.structureSelect.addItems(DATASTRUCTURE.keys()) + dsind = findComboBoxIndex(self.structureSelect, curstructure) + + self.structureSelect.setCurrentIndex(dsind) + layout = QGridLayout() layout.addWidget(dataDirLabel, 0, 0) layout.addWidget(self.dataDirEdit, 0, 1) From f1cee0cbfd5641f8800f5ffb60ef5c6bf556760a Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 24 Nov 2015 11:30:58 +0100 Subject: [PATCH 0729/1144] [fixes #168] now any TypeError is handled by try ... except clause --- pylot/core/util/widgets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8fc47268..16731ef3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -843,7 +843,10 @@ class InputsTab(PropTab): # get the full name of the actual user self.fullNameEdit = QLineEdit() - self.fullNameEdit.setText(fulluser) + try: + self.fullNameEdit.setText(fulluser) + except TypeError as e: + self.fullNameEdit.setText(fulluser[0]) # information about data structure dataroot = settings.value("data/dataRoot") From b310062687bfb6869e6d9dac782cbddcbc1a6e14 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 24 Nov 2015 12:37:53 +0100 Subject: [PATCH 0730/1144] changed SNR threshold (scaled by exp function) --- pylot/core/active/surveyUtils.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index cae85bcc..1e8b9927 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -14,7 +14,7 @@ def setArtificialPick(shot_dict, traceID, pick): shot.setPick(traceID, pick) shot.setPickwindow(traceID, shot.getCut()) -def fitSNR4dist(shot_dict, shiftdist = 5, shiftSNR = 3): +def fitSNR4dist(shot_dict, shiftdist = 30, shiftSNR = 100): import numpy as np dists = [] picks = [] @@ -32,7 +32,7 @@ def fitSNR4dist(shot_dict, shiftdist = 5, shiftSNR = 3): fit_fn = np.poly1d(fit) for dist in dists: dist += shiftdist - snrthresholds.append((1/(fit_fn(dist)**2)) - shiftSNR) + snrthresholds.append((1/(fit_fn(dist)**2)) - shiftSNR * np.exp(-0.05 * dist)) plotFittedSNR(dists, snrthresholds, snrs) return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben @@ -47,17 +47,19 @@ def plotFittedSNR(dists, snrthresholds, snrs): plt.ylabel('SNR') plt.legend() -def setFittedSNR(shot_dict, shiftdist = 5, shiftSNR = 3, p1 = 0.004, p2 = -0.0007): +def setFittedSNR(shot_dict, shiftdist = 30, shiftSNR = 100, p1 = 0.004, p2 = -0.0007): import numpy as np + minSNR = 2.5 #fit_fn = fitSNR4dist(shot_dict) fit_fn = np.poly1d([p1, p2]) for shot in shot_dict.values(): for traceID in shot.getTraceIDlist(): ### IMPROVE - snrthreshold = (1/(fit_fn(shot.getDistance(traceID) + shiftdist)**2)) - shiftSNR + dist = shot.getDistance(traceID) + shiftdist + snrthreshold = (1/(fit_fn(dist)**2)) - shiftSNR * np.exp(-0.05 * dist) if snrthreshold <= 1: - print('WARNING: SNR threshold %s lower 1!!! Try to lower the shiftSNR' - %snrthreshold) - shot.setSNRthreshold(traceID, 1) + print('WARNING: SNR threshold %s lower %s. Set SNR threshold to %s.' + %(snrthreshold, minSNR, minSNR)) + shot.setSNRthreshold(traceID, minSNR) break shot.setSNRthreshold(traceID, snrthreshold) print "setFittedSNR: Finished setting of fitted SNR-threshold" From ab8775c58d7a65cae8415e0d4957c6ef542c095b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 24 Nov 2015 13:31:14 +0100 Subject: [PATCH 0731/1144] PragPicker: Upweight of left pick. --- pylot/core/pick/Picker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/Picker.py index 41ac2439..b7d57481 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/Picker.py @@ -364,7 +364,7 @@ class PragPicker(AutoPicking): break # now decide which pick: left or right? - if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= cfpick_r: + if flagpick_l > 0 and flagpick_r > 0 and cfpick_l <= 3 * cfpick_r: self.Pick = pick_l pickflag = 1 elif flagpick_l > 0 and flagpick_r > 0 and cfpick_l >= cfpick_r: From 7f05568f65dd39d7c66a14fd6a1fdb444e4a67c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 24 Nov 2015 13:35:23 +0100 Subject: [PATCH 0732/1144] Bugfix: Captured error, if earllatepicker returns None for lpickS1/2, epickS1/2, and Serror1/2. --- pylot/core/pick/autopick.py | 100 ++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 4d693e3a..50fdf600 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -500,58 +500,60 @@ def autopickstation(wfstream, pickparam): [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, iplot) - if algoS == 'ARH': - # get earliest pick of both earliest possible picks - epick = [epickS1, epickS2] - lpick = [lpickS1, lpickS2] - pickerr = [Serror1, Serror2] - if epickS1 == None and epickS2 is not None: - ipick = 1 - elif epickS1 is not None and epickS2 == None: - ipick = 0 - elif epickS1 is not None and epickS2 is not None: - ipick = np.argmin([epickS1, epickS2]) - elif algoS == 'AR3': - [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, - tsnrh, - mpickS, iplot) - # get earliest pick of all three picks - epick = [epickS1, epickS2, epickS3] - lpick = [lpickS1, lpickS2, lpickS3] - pickerr = [Serror1, Serror2, Serror3] - if epickS1 == None and epickS2 is not None \ - and epickS3 is not None: - ipick = np.argmin([epickS2, epickS3]) - elif epickS1 is not None and epickS2 == None \ - and epickS3 is not None: - ipick = np.argmin([epickS2, epickS3]) - elif epickS1 is not None and epickS2 is not None \ - and epickS3 == None: - ipick = np.argmin([epickS1, epickS2]) - elif epickS1 is not None and epickS2 is not None \ - and epickS3 is not None: - ipick = np.argmin([epickS1, epickS2, epickS3]) - epickS = epick[ipick] - lpickS = lpick[ipick] - Serror = pickerr[ipick] + if epickS1 is not None and epickS2 is not None: + if algoS == 'ARH': + # get earliest pick of both earliest possible picks + epick = [epickS1, epickS2] + lpick = [lpickS1, lpickS2] + pickerr = [Serror1, Serror2] + if epickS1 == None and epickS2 is not None: + ipick = 1 + elif epickS1 is not None and epickS2 == None: + ipick = 0 + elif epickS1 is not None and epickS2 is not None: + ipick = np.argmin([epickS1, epickS2]) + elif algoS == 'AR3': + [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, + tsnrh, + mpickS, iplot) + # get earliest pick of all three picks + epick = [epickS1, epickS2, epickS3] + lpick = [lpickS1, lpickS2, lpickS3] + pickerr = [Serror1, Serror2, Serror3] + if epickS1 == None and epickS2 is not None \ + and epickS3 is not None: + ipick = np.argmin([epickS2, epickS3]) + elif epickS1 is not None and epickS2 == None \ + and epickS3 is not None: + ipick = np.argmin([epickS2, epickS3]) + elif epickS1 is not None and epickS2 is not None \ + and epickS3 == None: + ipick = np.argmin([epickS1, epickS2]) + elif epickS1 is not None and epickS2 is not None \ + and epickS3 is not None: + ipick = np.argmin([epickS1, epickS2, epickS3]) - # get SNR - [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, mpickS) + epickS = epick[ipick] + lpickS = lpick[ipick] + Serror = pickerr[ipick] - # weight S-onset using symmetric error - if Serror <= timeerrorsS[0]: - Sweight = 0 - elif timeerrorsS[0] < Serror <= timeerrorsS[1]: - Sweight = 1 - elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: - Sweight = 2 - elif timeerrorsS[2] < Serror <= timeerrorsS[3]: - Sweight = 3 - elif Serror > timeerrorsS[3]: - Sweight = 4 + # get SNR + [SNRS, SNRSdB, Snoiselevel] = getSNR(h_copy, tsnrh, mpickS) - print 'autopickstation: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( - Sweight, SNRS, SNRSdB) + # weight S-onset using symmetric error + if Serror <= timeerrorsS[0]: + Sweight = 0 + elif timeerrorsS[0] < Serror <= timeerrorsS[1]: + Sweight = 1 + elif Perror > timeerrorsS[1] and Serror <= timeerrorsS[2]: + Sweight = 2 + elif timeerrorsS[2] < Serror <= timeerrorsS[3]: + Sweight = 3 + elif Serror > timeerrorsS[3]: + Sweight = 4 + + print 'autopickstation: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( + Sweight, SNRS, SNRSdB) ################################################################## # get Wood-Anderson peak-to-peak amplitude print "################################################" From 7e76b3046004e9c583136057b3cbab83ab665e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 24 Nov 2015 13:37:11 +0100 Subject: [PATCH 0733/1144] Marginal changes. --- autoPyLoT.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 27e45841..364680f7 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -84,6 +84,7 @@ def autoPyLoT(inputfile): ttpat = parameter.getParam('ttpatter') # pattern of NLLoc-output file nllocoutpatter = parameter.getParam('outpatter') + maxnumit = 3 # maximum number of iterations for re-picking else: locflag = 0 print (" !!! ") @@ -133,7 +134,6 @@ def autoPyLoT(inputfile): else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) - maxnumit = 3 # maximum number of iterations if len(glob.glob(locsearch)) > 0: # get latest file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) @@ -214,7 +214,6 @@ def autoPyLoT(inputfile): # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) nllocfile = max(glob.glob(locsearch), key=os.path.getctime) - maxnumit = 3 # maximum number of iterations if len(glob.glob(locsearch)) > 0: # get latest file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) From 021cb873aeb49ed32d632187d85f8f6ea72d6b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 24 Nov 2015 13:37:47 +0100 Subject: [PATCH 0734/1144] Optimized some parameters. --- autoPyLoT_local.in | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 7206954d..a0fb96cc 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -7,7 +7,7 @@ /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path 2015.10_Insheim #database# %name of data base -e0011.280.15 #eventID# %event ID for single event processing + #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything @@ -42,7 +42,7 @@ AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file co 15.0 #pstart# %start time [s] for calculating CF for P-picking 60.0 #pstop# %end time [s] for calculating CF for P-picking -1.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking -12.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking +10.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking 2 20 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz] 2 30 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz] 2 15 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] @@ -74,13 +74,12 @@ ARH #algoS# %choose algorithm for S-onset 0.6 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick 0.3 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick 4 #Sarorder# %for AR-picker, order of AR process of H-components -6.0 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) +5.0 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H) 3.0 #pickwinS# %for initial AIC pick, length of S-pick window [s] 2 0.2 1.5 0.5 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] -0.05 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] -0.02 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) -0.2 #pepsS# %for AR-picker, artificial uplift of samples of CF (S) -0.4 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) +0.5 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s] +0.7 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S) +0.9 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S) 1.5 #nfacS# %for AR-picker, noise factor for noise level determination (S) %first-motion picker% 1 #minfmweight# %minimum required P weight for first-motion determination @@ -90,9 +89,9 @@ ARH #algoS# %choose algorithm for S-onset #inital AIC onset# 0.01 0.02 0.04 0.08 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P 0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S -10 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected +4 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected 1.2 #minAICPSNR# %below this SNR the initial P pick is rejected -3 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected +2 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# 4 #minsiglength# %minimum required length of signal [s] From 43646e8e634c56432c697350e28ebb08c51b4af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 24 Nov 2015 14:23:18 +0100 Subject: [PATCH 0735/1144] Marginal changes. --- autoPyLoT.py | 1 - 1 file changed, 1 deletion(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 364680f7..4bf71c22 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -213,7 +213,6 @@ def autoPyLoT(inputfile): else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) if len(glob.glob(locsearch)) > 0: # get latest file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) From 50a5586355016fb1df2c93bb38f0354d6f30d21d Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 26 Nov 2015 05:17:26 +0100 Subject: [PATCH 0736/1144] [bugfix] autoPickListWidget is directly attached to the main window and autoScrolls to the latest entry --- QtPyLoT.py | 20 ++++++++++++-------- pylot/core/util/thread.py | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index b82df799..ad102c9e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -655,25 +655,28 @@ class MainWindow(QMainWindow): elif self.getLocflag() and not self.check4Loc(): self.setLocflag(False) + def addListItem(self, text): + self.listWidget.addItem(text) + self.listWidget.scrollToBottom() + def autoPick(self): - list = QListWidget() + self.listWidget = QListWidget() self.setDirty(True) logDockWidget = QDockWidget("AutoPickLog", self) logDockWidget.setObjectName("LogDockWidget") - logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea) - logDockWidget.setWidget(list) - logDockWidget.show() - logDockWidget.setFloating(False) - list.addItem('loading default values for local data ...') + logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) + logDockWidget.setWidget(self.listWidget) + self.addDockWidget(Qt.LeftDockWidgetArea, logDockWidget) + self.addListItem('loading default values for local data ...') autopick_parameter = AutoPickParameter('autoPyLoT_local.in') - list.addItem(str(autopick_parameter)) + self.addListItem(str(autopick_parameter)) # Create the worker thread and run it self.thread = AutoPickThread(parent=self, func=autopickevent, data=self.getData().getWFData(), param=autopick_parameter) - self.thread.message.connect(list.addItem) + self.thread.message.connect(self.addListItem) self.thread.start() self.drawPicks() @@ -773,6 +776,7 @@ class MainWindow(QMainWindow): if extlocpath is None or locroot is None: self.PyLoTprefs() + def check4Loc(self): return self.picksNum() > 4 diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index 7ce1513e..c0acb18d 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -21,7 +21,7 @@ class AutoPickThread(QThread): for station in picks: self.parent().addPicks(station, picks[station]) except AttributeError: - print picks + print(picks) def write(self, text): self.message.emit(text) From 3f91fddd3e867b81cfe6a7d2cc2e7e4568a8eb22 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 30 Nov 2015 09:50:47 +0100 Subject: [PATCH 0737/1144] [new] added attribute autopicks to and modified corresponding handling methods in class MainWindow --- QtPyLoT.py | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index ad102c9e..9af35be9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -91,6 +91,7 @@ class MainWindow(QMainWindow): self.filteroptions = {} self.pickDlgs = {} self.picks = {} + self.autopicks = {} self.loc = False # UI has to be set up before(!) children widgets are about to show up @@ -476,12 +477,13 @@ class MainWindow(QMainWindow): def getData(self): return self.data - def getPicks(self): - return self.picks + def getPicks(self, type='manual'): + rdict = dict(auto=self.autopicks, manual=self.picks) + return rdict[type] - def getPicksOnStation(self, station): + def getPicksOnStation(self, station, type='manual'): try: - return self.getPicks()[station] + return self.getPicks(type)[station] except KeyError: return None @@ -679,11 +681,8 @@ class MainWindow(QMainWindow): self.thread.message.connect(self.addListItem) self.thread.start() - self.drawPicks() - - - def addPicks(self, station, picks): - stat_picks = self.getPicksOnStation(station) + def addPicks(self, station, picks, type='manual'): + stat_picks = self.getPicksOnStation(station, type) rval = False if not stat_picks: stat_picks = picks @@ -707,7 +706,7 @@ class MainWindow(QMainWindow): pass else: raise Exception('FATAL: Should never occur!') - self.getPicks()[station] = stat_picks + self.getPicks(type=type)[station] = stat_picks return rval def updatePicks(self): @@ -734,24 +733,25 @@ class MainWindow(QMainWindow): picks[station] = onsets.copy() self.picks.update(picks) - def drawPicks(self, station=None): + def drawPicks(self, station=None, picktype='manual'): # if picks to draw not specified, draw all picks available if not station: - for station in self.getPicks(): - self.drawPicks(station) + for station in self.getPicks(type=picktype): + self.drawPicks(station, picktype=picktype) return # plotting picks plotID = self.getStationID(station) ax = self.getPlotWidget().axes ylims = np.array([-.5, +.5]) + plotID - phase_col = {'P': ('c', 'c--', 'b-'), - 'S': ('m', 'm--', 'r-')} + phase_col = {'P': ('c', 'c--', 'b-', 'bv', 'b^'), + 'S': ('m', 'm--', 'r-', 'rv', 'r^')} - stat_picks = self.getPicks()[station] + stat_picks = self.getPicks(type=picktype)[station] for phase in stat_picks: - picks = stat_picks[phase] + if type(stat_picks[phase]) is not dict: + return colors = phase_col[phase[0].upper()] stime = getGlobalTimes(self.getData().getWFData())[0] @@ -761,11 +761,17 @@ class MainWindow(QMainWindow): lpp = picks['lpp'] - stime spe = picks['spe'] - ax.fill_between([epp, lpp], ylims[0], ylims[1], - alpha=.5, color=colors[0]) - ax.plot([mpp - spe, mpp - spe], ylims, colors[1], - [mpp, mpp], ylims, colors[2], - [mpp + spe, mpp + spe], ylims, colors[1]) + if picktype == 'manual': + ax.fill_between([epp, lpp], ylims[0], ylims[1], + alpha=.5, color=colors[0]) + ax.plot([mpp - spe, mpp - spe], ylims, colors[1], + [mpp, mpp], ylims, colors[2], + [mpp + spe, mpp + spe], ylims, colors[1]) + elif picktype == 'auto': + ax.plot(mpp, ylims[1], colors[3], + mpp, ylims[0], colors[4]) + else: + raise TypeError('Unknow picktype {0}'.format(picktype)) self.draw() def locateEvent(self): From d67556796cc020273e55cda65db19cf07dd9d3b8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 30 Nov 2015 09:52:31 +0100 Subject: [PATCH 0738/1144] [bugfix] plotting autopicks has to be done within thread because code outside the threads scope is executed in parallel --- pylot/core/util/thread.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index c0acb18d..a131e8fe 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -17,11 +17,16 @@ class AutoPickThread(QThread): picks = self.func(self.data, self.param) + print("Autopicking finished!\n") + try: for station in picks: - self.parent().addPicks(station, picks[station]) + self.parent().addPicks(station, picks[station], type='auto') except AttributeError: print(picks) + # plot picks to section + self.parent().drawPicks(picktype='auto') + def write(self, text): self.message.emit(text) From 5f8569c7d9aa1c35fc6c90378bbbe33804b47cf4 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 30 Nov 2015 09:53:55 +0100 Subject: [PATCH 0739/1144] [bugfix] now catching all cases --- pylot/core/util/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 16731ef3..1f9a318b 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -611,13 +611,15 @@ class PickDlg(QDialog): phase_col = {'P': ('c', 'c--', 'b-'), 'S': ('m', 'm--', 'r-')} if self.getPicks(): - if phase is not None: + if phase is not None and type(self.getPicks()[phase]) is dict: picks = self.getPicks()[phase] colors = phase_col[phase[0].upper()] - else: + elif phase is None: for phase in self.getPicks(): self.drawPicks(phase) return + else: + return else: return From d29c57ab4ba74c8300abebc829f94e162a3cd1a6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 30 Nov 2015 09:55:33 +0100 Subject: [PATCH 0740/1144] [bugfix] AutoPickLog is now directly attached to the MainWindow on the LeftDockWidgetArea and autoscrolls to the bottom --- QtPyLoT.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9af35be9..441446d5 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -664,11 +664,11 @@ class MainWindow(QMainWindow): def autoPick(self): self.listWidget = QListWidget() self.setDirty(True) - logDockWidget = QDockWidget("AutoPickLog", self) - logDockWidget.setObjectName("LogDockWidget") - logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) - logDockWidget.setWidget(self.listWidget) - self.addDockWidget(Qt.LeftDockWidgetArea, logDockWidget) + self.logDockWidget = QDockWidget("AutoPickLog", self) + self.logDockWidget.setObjectName("LogDockWidget") + self.logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) + self.logDockWidget.setWidget(self.listWidget) + self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget) self.addListItem('loading default values for local data ...') autopick_parameter = AutoPickParameter('autoPyLoT_local.in') self.addListItem(str(autopick_parameter)) From 957d2ccfe70ebde6f608c31c44b8661d366ef6e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Nov 2015 13:14:23 +0100 Subject: [PATCH 0741/1144] New function to derive plateau and corner frequency of observed source spectrum. Additional to scipys implicit function curve_fit, as seismic moment is sensitive to estimated plateau of source spectrum, which in turn is sensitivec to estimated corner frequency. --- pylot/core/analysis/magnitude.py | 119 +++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 13 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index ab455762..1fd9c2d0 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -135,10 +135,10 @@ class WApp(Magnitude): plt.close(f) -class DCfc(Magnitude): +class w0fc(Magnitude): ''' Method to calculate the source spectrum and to derive from that the plateau - (so-called DC-value) and the corner frequency assuming Aki's omega-square + (usually called omega0) and the corner frequency assuming Aki's omega-square source model. Has to be derived from instrument corrected displacement traces! ''' @@ -176,20 +176,23 @@ class DCfc(Magnitude): YY = Y[fi] # get plateau (DC value) and corner frequency # initial guess of plateau - DCin = np.mean(YY[0:100]) + w0in = np.mean(YY[0:100]) # initial guess of corner frequency # where spectral level reached 50% of flat level - iin = np.where(YY >= 0.5 * DCin) + iin = np.where(YY >= 0.5 * w0in) Fcin = F[iin[0][np.size(iin) - 1]] - fit = synthsourcespec(F, DCin, Fcin) - [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [DCin, Fcin]) - self.w0 = optspecfit[0] - self.fc = optspecfit[1] - print ("DCfc: Determined DC-value: %e m/Hz, \n" - "Determined corner frequency: %f Hz" % (self.w0, self.fc)) + # use of implicit scipy function + fit = synthsourcespec(F, w0in, Fcin) + [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin]) + self.w01 = optspecfit[0] + self.fc1 = optspecfit[1] + print ("w0fc: Determined w0-value: %e m/Hz, \n" + "Determined corner frequency: %f Hz" % (self.w01, self.fc1)) + + # use of conventional fitting + [self.w02, self.fc2] = fitSourceModel(F, YY.real, Fcin, self.getiplot()) - - if self.getiplot() > 1: + if self.getiplot() > 1: f1 = plt.figure() plt.subplot(2,1,1) # show displacement in mm @@ -203,7 +206,8 @@ class DCfc(Magnitude): plt.loglog(f, Y.real, 'k') plt.loglog(F, YY.real) plt.loglog(F, fit, 'g') - plt.title('Source Spectrum from P Pulse, DC=%e m/Hz, fc=%4.1f Hz' \ + plt.loglog([self.fc, self.fc], [self.w0/100, self.w0], 'g') + plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \ % (self.w0, self.fc)) plt.xlabel('Frequency [Hz]') plt.ylabel('Amplitude [m/Hz]') @@ -233,3 +237,92 @@ def synthsourcespec(f, omega0, fcorner): return ssp + +def fitSourceModel(f, S, fc0, iplot): + ''' + Calculates synthetic source spectrum by varying corner frequency fc. + Returns best approximated plateau omega0 and corner frequency, i.e. with least + common standard deviations. + + :param: f, frequencies + :type: array + + :param: S, observed source spectrum + :type: array + + :param: fc0, initial corner frequency + :type: float + ''' + + w0 = [] + stdw0 = [] + fc = [] + stdfc = [] + STD = [] + + # get window around initial corner frequency for trials + fcstopl = fc0 - max(1, len(f) / 10) + il = np.argmin(abs(f-fcstopl)) + fcstopl = f[il] + fcstopr = fc0 + min(len(f), len(f) /10) + ir = np.argmin(abs(f-fcstopr)) + fcstopr = f[ir] + iF = np.where((f >= fcstopl) & (f <= fcstopr)) + + # vary corner frequency around initial point + for i in range(il, ir): + FC = f[i] + indexdc = np.where((f > 0 ) & (f <= FC)) + dc = np.mean(S[indexdc]) + stddc = np.std(dc - S[indexdc]) + w0.append(dc) + stdw0.append(stddc) + fc.append(FC) + # slope + indexfc = np.where((f >= FC) & (f <= fcstopr)) + yi = dc/(1+(f[indexfc]/FC)**2) + stdFC = np.std(yi - S[indexfc]) + stdfc.append(stdFC) + STD.append(stddc + stdFC) + + # get best found w0 anf fc from minimum + fc = fc[np.argmin(STD)] + w0 = w0[np.argmin(STD)] + print("fitSourceModel: best fc: %fHz, best w0: %e m/Hz" \ + % (fc, w0)) + + if iplot > 1: + plt.figure(iplot) + plt.loglog(f, S, 'k') + plt.loglog([f[0], fc], [w0, w0], 'g') + plt.loglog([fc, fc], [w0/100, w0], 'g') + plt.title('Calculated Source Spectrum, Omega0=%e m/Hz, fc=%6.2f Hz' \ + % (w0, fc)) + plt.xlabel('Frequency [Hz]') + plt.ylabel('Amplitude [m/Hz]') + plt.grid() + plt.figure(iplot+1) + plt.subplot(311) + plt.plot(f[il:ir], STD,'*') + plt.title('Common Standard Deviations') + plt.xticks([]) + plt.subplot(312) + plt.plot(f[il:ir], stdw0,'*') + plt.title('Standard Deviations of w0-Values') + plt.xticks([]) + plt.subplot(313) + plt.plot(f[il:ir],stdfc,'*') + plt.title('Standard Deviations of Corner Frequencies') + plt.xlabel('Corner Frequencies [Hz]') + plt.show() + raw_input() + plt.close() + + return w0, fc + + + + + + + From 466e0020a64de9e93857a28be7e0b2425f3d03bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Nov 2015 13:27:08 +0100 Subject: [PATCH 0742/1144] w0fc: Plateau omega0 and corner frequency are are now taken as the median of previously calculated values from different functions synthsourcespek and fitSourceModel. --- pylot/core/analysis/magnitude.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 1fd9c2d0..a16b7545 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -6,6 +6,7 @@ Created August/September 2015. :author: Ludger Küperkoch / MAGS2 EP3 working group """ +import pdb import matplotlib.pyplot as plt import numpy as np from obspy.core import Stream @@ -181,17 +182,15 @@ class w0fc(Magnitude): # where spectral level reached 50% of flat level iin = np.where(YY >= 0.5 * w0in) Fcin = F[iin[0][np.size(iin) - 1]] - # use of implicit scipy function + + # use of implicit scipy otimization function fit = synthsourcespec(F, w0in, Fcin) [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin]) - self.w01 = optspecfit[0] - self.fc1 = optspecfit[1] + w01 = optspecfit[0] + fc1 = optspecfit[1] print ("w0fc: Determined w0-value: %e m/Hz, \n" - "Determined corner frequency: %f Hz" % (self.w01, self.fc1)) + "Determined corner frequency: %f Hz" % (w01, fc1)) - # use of conventional fitting - [self.w02, self.fc2] = fitSourceModel(F, YY.real, Fcin, self.getiplot()) - if self.getiplot() > 1: f1 = plt.figure() plt.subplot(2,1,1) @@ -216,6 +215,14 @@ class w0fc(Magnitude): raw_input() plt.close(f1) + # use of conventional fitting + [w02, fc2] = fitSourceModel(F, YY.real, Fcin, self.getiplot()) + + # get w0 and fc as median + self.w0 = np.median([w01, w02]) + self.fc = np.median([fc1, fc2]) + print("w0fc: Using w0-value = %e m/Hz and fc = %f Hz" % (self.w0, self.fc)) + def synthsourcespec(f, omega0, fcorner): ''' From 9f93c25aa8f8a9f4813fdb43ec2fffda9e4098d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Nov 2015 14:41:59 +0100 Subject: [PATCH 0743/1144] Put additional parameters w0 and fc to picks dictionary. --- pylot/core/pick/autopick.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 50fdf600..15b6809a 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -18,7 +18,7 @@ from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ getSNR, fmpicker, checkPonsets, wadaticheck, crossings_nonzero_all from pylot.core.util.utils import getPatternLine from pylot.core.read.data import Data -from pylot.core.analysis.magnitude import WApp, DCfc +from pylot.core.analysis.magnitude import WApp, w0fc def autopickevent(data, param): stations = [] @@ -137,7 +137,9 @@ def autopickstation(wfstream, pickparam): Pflag = 0 Sflag = 0 Pmarker = [] - Ao = None + Ao = None # Wood-Anderson peak-to-peak amplitude + w0 = None # plateau of source spectrum + fc = None # corner frequancy of source spectrum # split components zdat = wfstream.select(component="Z") @@ -313,7 +315,7 @@ def autopickstation(wfstream, pickparam): FM = 'N' ############################################################## - # get DC value (w0) and corner frequency (fc) of source spectrum + # get DC value and corner frequency (fc) of source spectrum # from P pulse # initialize Data object data = Data() @@ -339,7 +341,7 @@ def autopickstation(wfstream, pickparam): index = min([3, len(zc) - 1]) calcwin = (zc[index] - zc[0]) * z_copy[0].stats.delta # calculate source spectrum and get w0 and fc - specpara = DCfc(z_copy, mpickP, calcwin, iplot) + specpara = w0fc(z_copy, mpickP, calcwin, iplot) w0 = specpara.getw0() fc = specpara.getfc() @@ -788,7 +790,8 @@ def autopickstation(wfstream, pickparam): # for P phase phase = 'P' phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, - 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM} + 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM, + 'w0': w0, 'fc': fc} picks = {phase: phasepick} # add P marker picks[phase]['marked'] = Pmarker @@ -800,6 +803,7 @@ def autopickstation(wfstream, pickparam): # add Wood-Anderson amplitude picks[phase]['Ao'] = Ao + return picks From 23b9fda5e448345286dcb59167db8deb97b60e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Nov 2015 16:35:58 +0100 Subject: [PATCH 0744/1144] New class M0Mw for calculating seismic moment and moment magnitude. --- pylot/core/analysis/magnitude.py | 83 ++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index a16b7545..07e69253 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -6,7 +6,6 @@ Created August/September 2015. :author: Ludger Küperkoch / MAGS2 EP3 working group """ -import pdb import matplotlib.pyplot as plt import numpy as np from obspy.core import Stream @@ -19,7 +18,7 @@ class Magnitude(object): amplitudes, local magnitudes and moment magnitudes. ''' - def __init__(self, wfstream, To, pwin, iplot): + def __init__(self, wfstream, To, pwin, iplot, w0=None, delta=None, rho=None, vp=None): ''' :param: wfstream :type: `~obspy.core.stream.Stream @@ -43,8 +42,13 @@ class Magnitude(object): self.setTo(To) self.setpwin(pwin) self.setiplot(iplot) + self.setw0(w0) + self.setrho(rho) + self.setdelta(delta) + self.setvp(vp) self.calcwapp() self.calcsourcespec() + self.calcMoMw() def getwfstream(self): @@ -71,6 +75,30 @@ class Magnitude(object): def setiplot(self, iplot): self.iplot = iplot + def setw0(self, w0): + self.w0 = w0 + + def getw0(self): + return self.w0 + + def setrho(self, rho): + self.rho = rho + + def getrho(self): + return self.rho + + def setvp(self, vp): + self.vp = vp + + def getvp(self): + return self.vp + + def setdelta(self, delta): + self.delta = delta + + def getdelta(self): + return self.delta + def getwapp(self): return self.wapp @@ -80,12 +108,22 @@ class Magnitude(object): def getfc(self): return self.fc + def getMo(self): + return self.Mo + + def getMw(self): + return self.Mw + def calcwapp(self): self.wapp = None def calcsourcespec(self): self.sourcespek = None + def calcMoMw(self): + self.Mo = None + self.Mw = None + class WApp(Magnitude): ''' Method to derive peak-to-peak amplitude as seen on a Wood-Anderson- @@ -136,6 +174,32 @@ class WApp(Magnitude): plt.close(f) +class M0Mw(Magnitude): + ''' + Method to calculate seismic moment Mo and moment magnitude Mw. + Uses class w0fc for calculating plateau wo and corner frequency + fc of source spectrum, respectively. + ''' + + def calcMoMw(self): + + stream = self.getwfstream() + tr = stream[0] + + print("Calculating seismic moment Mo and moment magnitude Mw for station %s ..." \ + % tr.stats.station) + + # additional common parameters for calculating Mo + rP = 0.52 # average radiation pattern of P waves (Aki & Richards, 1980) + freesurf = 2.0 # free surface correction, assuming vertical incidence + + self.Mo = (self.getw0() * 4 * np.pi * self.getrho() * np.power(self.getvp(), 3) * \ + self.getdelta()) / (rP * freesurf) + + self.Mw = 2/3 * np.log10(self.Mo) - 6 + print("MoMw: Calculated seismic moment Mo = %e Nm => Mw = %3.1f " % (self.Mo, self.Mw)) + + class w0fc(Magnitude): ''' Method to calculate the source spectrum and to derive from that the plateau @@ -191,6 +255,14 @@ class w0fc(Magnitude): print ("w0fc: Determined w0-value: %e m/Hz, \n" "Determined corner frequency: %f Hz" % (w01, fc1)) + # use of conventional fitting + [w02, fc2] = fitSourceModel(F, YY.real, Fcin, self.getiplot()) + + # get w0 and fc as median + self.w0 = np.median([w01, w02]) + self.fc = np.median([fc1, fc2]) + print("w0fc: Using w0-value = %e m/Hz and fc = %f Hz" % (self.w0, self.fc)) + if self.getiplot() > 1: f1 = plt.figure() plt.subplot(2,1,1) @@ -215,13 +287,6 @@ class w0fc(Magnitude): raw_input() plt.close(f1) - # use of conventional fitting - [w02, fc2] = fitSourceModel(F, YY.real, Fcin, self.getiplot()) - - # get w0 and fc as median - self.w0 = np.median([w01, w02]) - self.fc = np.median([fc1, fc2]) - print("w0fc: Using w0-value = %e m/Hz and fc = %f Hz" % (self.w0, self.fc)) def synthsourcespec(f, omega0, fcorner): From 2e8926ea5ba4fab4f3a736d77fbc8feaaf9adf31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Nov 2015 16:37:28 +0100 Subject: [PATCH 0745/1144] Implemented new class M0Mw of object magnitude. --- autoPyLoT.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index 4bf71c22..54003edb 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -13,6 +13,7 @@ from pylot.core.util.structure import DATASTRUCTURE from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.loc.nll import * from pylot.core.util.version import get_git_version as _getVersionString +from pylot.core.analysis.magnitude import M0Mw __version__ = _getVersionString() @@ -159,6 +160,15 @@ def autoPyLoT(inputfile): if len(badpicks) == 0: print("autoPyLoT: No more bad onsets found, stop iterative picking!") break + # calculating seismic moment Mo and corresponding moment + # magnitude Mw after Hanks and Kanamori (1979) from reliable + # picks/waveforms + for key in picks: + if picks[key]['P']['weight'] < 4 and picks[key]['P']['w0'] is not None: + selwf = wfdat.select(station=key) + w0 = picks[key]['P']['w0'] + sourcepara = M0Mw(selwf, None, None, None, w0, 5, \ + parameter.getParam('rho'), parameter.getParam('vp')) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## @@ -238,6 +248,17 @@ def autoPyLoT(inputfile): if len(badpicks) == 0: print("autoPyLoT: No more bad onsets found, stop iterative picking!") break + + # calculating seismic moment Mo and corresponding moment + # magnitude Mw after Hanks and Kanamori (1979) from reliable + # picks/waveforms + for key in picks: + if picks[key]['P']['weight'] < 4 and picks[key]['P']['w0'] is not None: + selwf = wfdat.select(station=key) + w0 = picks[key]['P']['w0'] + sourcepara = M0Mw(selwf, None, None, None, w0, 5, \ + parameter.getParam('rho'), parameter.getParam('vp')) + else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## From f7a84233bf423b6feba963221aea9b9cc99d64d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Nov 2015 16:38:09 +0100 Subject: [PATCH 0746/1144] Changed some parameters, esspecially for seismic moment estimation. --- autoPyLoT_local.in | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index a0fb96cc..55361e77 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -6,8 +6,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2015.10_Insheim #database# %name of data base - #eventID# %event ID for single event processing +2013.02_Insheim #database# %name of data base +e0019.048.13 #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything @@ -24,6 +24,11 @@ ttime #ttpatter# %pattern of NLLoc ttimes from AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file %(returns 'eventID_outpatter') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#parameters for seismic moment estimation# +3000 #vp# %average P-wave velocity +2600 #rho# %rock density [kg/m^3] +300 #Qp# %quality factor for P waves +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities 6 #pmin# %minimum required P picks for location 4 #p0min# %minimum required P picks for location if at least @@ -32,14 +37,9 @@ AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file co /home/ludger/bin/run_HYPOSAT4autoPILOT.csh #cshellp# %path and name of c-shell script to run location routine 7.6 8.5 #blon# %longitude bounding for location map 49 49.4 #blat# %lattitude bounding for location map -#parameters for moment magnitude estimation# -5000 #vp# %average P-wave velocity -2800 #vs# %average S-wave velocity -2200 #rho# %rock density [kg/m^3] -300 #Qp# %quality factor for P waves -100 #Qs# %quality factor for S waves +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #common settings picker# -15.0 #pstart# %start time [s] for calculating CF for P-picking +5.0 #pstart# %start time [s] for calculating CF for P-picking 60.0 #pstop# %end time [s] for calculating CF for P-picking -1.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking 10.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking @@ -94,8 +94,8 @@ ARH #algoS# %choose algorithm for S-onset 2 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# -4 #minsiglength# %minimum required length of signal [s] -1.5 #noisefactor# %noiselevel*noisefactor=threshold +3 #minsiglength# %minimum required length of signal [s] +1.2 #noisefactor# %noiselevel*noisefactor=threshold 50 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 2.0 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude From 41a495371d7fe75973e94808c303bc6567f17a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 30 Nov 2015 16:45:29 +0100 Subject: [PATCH 0747/1144] Changed average radiation pattern for P-waves to exact definition in Aki and Richards, 1980. --- pylot/core/analysis/magnitude.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 07e69253..ff1f580e 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -190,8 +190,8 @@ class M0Mw(Magnitude): % tr.stats.station) # additional common parameters for calculating Mo - rP = 0.52 # average radiation pattern of P waves (Aki & Richards, 1980) - freesurf = 2.0 # free surface correction, assuming vertical incidence + rP = 2 / np.sqrt(15) # average radiation pattern of P waves (Aki & Richards, 1980) + freesurf = 2.0 # free surface correction, assuming vertical incidence self.Mo = (self.getw0() * 4 * np.pi * self.getrho() * np.power(self.getvp(), 3) * \ self.getdelta()) / (rP * freesurf) From ed7a323c5065ba7b92ea5dc0e0fbf402f9102f94 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 1 Dec 2015 05:03:55 +0100 Subject: [PATCH 0748/1144] [new] reverting stdout redirection after auto picking is done; added method finalizeAutoPick for purposes to be done after the auto picking thread has finished --- QtPyLoT.py | 5 +++++ pylot/core/util/thread.py | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 441446d5..10984721 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -680,6 +680,11 @@ class MainWindow(QMainWindow): param=autopick_parameter) self.thread.message.connect(self.addListItem) self.thread.start() + self.thread.finished.connect(self.finalizeAutoPick) + + def finalizeAutoPick(self): + self.drawPicks(picktype='auto') + self.thread.quit() def addPicks(self, station, picks, type='manual'): stat_picks = self.getPicksOnStation(station, type) diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index a131e8fe..a4a47715 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -4,6 +4,7 @@ from PySide.QtCore import QThread, Signal class AutoPickThread(QThread): message = Signal(str) + finished = Signal() def __init__(self, parent, func, data, param): super(AutoPickThread, self).__init__() @@ -24,8 +25,8 @@ class AutoPickThread(QThread): self.parent().addPicks(station, picks[station], type='auto') except AttributeError: print(picks) - # plot picks to section - self.parent().drawPicks(picktype='auto') + sys.stdout = sys.__stdout__ + self.finished.emit() def write(self, text): From d4bb265c37526aa0b5dacca745e70636f68d78cc Mon Sep 17 00:00:00 2001 From: sebastianp Date: Tue, 1 Dec 2015 11:12:45 +0100 Subject: [PATCH 0749/1144] first time commiting, please rectify and tell me what is wrong. --- pylot/core/util/utils.py | 496 +++++++++++++++++++++------------------ 1 file changed, 266 insertions(+), 230 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 2d3b0e8b..fca9d48e 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -11,140 +11,70 @@ import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope -def runProgram(cmd, parameter=None): - """ - run an external program specified by cmd with parameters input returning the - stdout output - - :param cmd: name of the command to run - :type cmd: str - :param parameter: filename of parameter file or parameter string - :type parameter: str - :return: stdout output - :rtype: str - """ - - if parameter: - cmd.strip() - cmd += ' %s 2>&1' % parameter - - output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), - shell = True) - - - -def fnConstructor(s): - if type(s) is str: - s = s.split(':')[-1] - else: - s = getHash(UTCDateTime()) - - badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') - badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)') - - fn = badchars.sub('_', s) - - if badsuffix.match(fn): - fn = '_' + fn - return fn - - -def getLogin(): - return pwd.getpwuid(os.getuid())[0] - - -def getHash(time): +def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): ''' - :param time: time object for which a hash should be calculated - :type time: :class: `~obspy.core.utcdatetime.UTCDateTime` object - :return: str + createArrival - function to create an Obspy Arrival + :param pickresID: Resource identifier of the created pick + :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object + + # eventnum is no input for this function + :param eventnum: human-readable event identifier + :type eventnum: str + + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + + # also no input for this function + :param station: name of the station at which the seismic phase has been + picked + :type station: str + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + + :param azimuth: azimuth between source and receiver + :type azimuth: float or int, optional + :param dist: distance between source and receiver + :type dist: float or int, optional + :return: An ObsPy :class: `~obspy.core.event.Arrival` object ''' - hg = hashlib.sha1() - hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) - return hg.hexdigest() - - -def getOwner(fn): - return pwd.getpwuid(os.stat(fn).st_uid).pw_name - - -def prepTimeAxis(stime, trace): - nsamp = trace.stats.npts - srate = trace.stats.sampling_rate - tincr = trace.stats.delta - etime = stime + nsamp / srate - time_ax = np.arange(stime, etime, tincr) - if len(time_ax) < nsamp: - print 'elongate time axes by one datum' - time_ax = np.arange(stime, etime + tincr, tincr) - elif len(time_ax) > nsamp: - print 'shorten time axes by one datum' - time_ax = np.arange(stime, etime - tincr, tincr) - if len(time_ax) != nsamp: - raise ValueError('{0} samples of data \n ' - '{1} length of time vector \n' - 'delta: {2}'.format(nsamp, len(time_ax), tincr)) - return time_ax - - -def scaleWFData(data, factor=None, components='all'): - """ - produce scaled waveforms from given waveform data and a scaling factor, - waveform may be selected by their components name - :param data: waveform data to be scaled - :type data: `~obspy.core.stream.Stream` object - :param factor: scaling factor - :type factor: float - :param components: components labels for the traces in data to be scaled by - the scaling factor (optional, default: 'all') - :type components: tuple - :return: scaled waveform data - :rtype: `~obspy.core.stream.Stream` object - """ - if components is not 'all': - for comp in components: - if factor is None: - max_val = np.max(np.abs(data.select(component=comp)[0].data)) - data.select(component=comp)[0].data /= 2 * max_val - else: - data.select(component=comp)[0].data /= 2 * factor + arrival = ope.Arrival() + arrival.creation_info = cinfo + arrival.pick_id = pickresID + arrival.phase = phase + if azimuth is not None: + arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360. #what about azimuth > 180? else: - for tr in data: - if factor is None: - max_val = float(np.max(np.abs(tr.data))) - tr.data /= 2 * max_val - else: - tr.data /= 2 * factor - - return data - -def demeanTrace(trace, window): - """ - returns the DATA where each trace is demean by the average value within - WINDOW - :param trace: waveform trace object - :type trace: `~obspy.core.stream.Trace` - :param inoise: range of indices of DATA within the WINDOW - :type window: tuple - :return: trace - :rtype: `~obspy.core.stream.Trace` - """ - trace.data -= trace.data[window].mean() - return trace - - -def getGlobalTimes(stream): - min_start = UTCDateTime() - max_end = None - for trace in stream: - if trace.stats.starttime < min_start: - min_start = trace.stats.starttime - if max_end is None or trace.stats.endtime > max_end: - max_end = trace.stats.endtime - return min_start, max_end + arrival.azimuth = azimuth + arrival.distance = dist + return arrival +def createAmplitude(pickID, amp, unit, category, cinfo): + ''' + :param pickID: + :param amp: + :param unit: + :param category: + :param cinfo: + :return: + ''' + amplitude = ope.Amplitude() + amplitude.creation_info = cinfo + amplitude.generic_amplitude = amp + amplitude.unit = ope.AmplitudeUnit(unit) + amplitude.type = ope.AmplitudeCategory(category) + amplitude.pick_id = pickID + return amplitude def createCreationInfo(agency_id=None, creation_time=None, author=None): + ''' + :param agency_id: + :param creation_time: + :param author: + :return: + ''' if author is None: author = getLogin() if creation_time is None: @@ -153,56 +83,6 @@ def createCreationInfo(agency_id=None, creation_time=None, author=None): creation_time=creation_time) -def createResourceID(timetohash, restype, authority_id=None, hrstr=None): - ''' - - :param timetohash: - :param restype: type of the resource, e.g. 'orig', 'earthquake' ... - :type restype: str - :param authority_id: name of the institution carrying out the processing - :type authority_id: str, optional - :return: - ''' - assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ - "UTCDateTime object" - hid = getHash(timetohash) - if hrstr is None: - resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) - else: - resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) - if authority_id is not None: - resID.convertIDToQuakeMLURI(authority_id=authority_id) - return resID - - -def createOrigin(origintime, cinfo, latitude, longitude, depth): - ''' - createOrigin - function to create an ObsPy Origin - :param origintime: the origins time of occurence - :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object - :param latitude: latitude in decimal degree of the origins location - :type latitude: float - :param longitude: longitude in decimal degree of the origins location - :type longitude: float - :param depth: hypocentral depth of the origin - :type depth: float - :return: An ObsPy :class: `~obspy.core.event.Origin` object - ''' - - assert isinstance(origintime, UTCDateTime), "origintime has to be " \ - "a UTCDateTime object, but " \ - "actually is of type " \ - "'%s'" % type(origintime) - - origin = ope.Origin() - origin.time = origintime - origin.creation_info = cinfo - origin.latitude = latitude - origin.longitude = longitude - origin.depth = depth - return origin - - def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, authority_id=None): ''' @@ -247,14 +127,56 @@ def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, event.origins = [o] return event +def createMagnitude(originID, cinfo): + ''' + createMagnitude - function to create an ObsPy Magnitude object + :param originID: + :param cinfo: + :param authority_id: # is no input + :return: + ''' + magnitude = ope.Magnitude() + magnitude.creation_info = cinfo + magnitude.origin_id = originID + return magnitude + +def createOrigin(origintime, cinfo, latitude, longitude, depth): + ''' + createOrigin - function to create an ObsPy Origin + :param origintime: the origins time of occurence + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + # cinfo missing + :param latitude: latitude in decimal degree of the origins location + :type latitude: float + :param longitude: longitude in decimal degree of the origins location + :type longitude: float + :param depth: hypocentral depth of the origin + :type depth: float + :return: An ObsPy :class: `~obspy.core.event.Origin` object + ''' + + assert isinstance(origintime, UTCDateTime), "origintime has to be " \ + "a UTCDateTime object, but " \ + "actually is of type " \ + "'%s'" % type(origintime) + + origin = ope.Origin() + origin.time = origintime + origin.creation_info = cinfo + origin.latitude = latitude + origin.longitude = longitude + origin.depth = depth + return origin def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, wfseedstr, authority_id): ''' createPick - function to create an ObsPy Pick + # origintime missing :param picknum: number of the created pick :type picknum: int + # picktime missing :param eventnum: human-readable event identifier :type eventnum: str :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object @@ -282,61 +204,175 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') return pick - -def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): +def createResourceID(timetohash, restype, authority_id=None, hrstr=None): ''' - createArrival - function to create an Obspy Arrival - :param pickresID: Resource identifier of the created pick - :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object - :param eventnum: human-readable event identifier - :type eventnum: str - :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object - holding information on the creation of the returned object - :type cinfo: :class: `~obspy.core.event.CreationInfo` object - :param phase: name of the arrivals seismic phase - :type phase: str - :param station: name of the station at which the seismic phase has been - picked - :type station: str + + :param timetohash: + # type (timetohas) missing + :param restype: type of the resource, e.g. 'orig', 'earthquake' ... + :type restype: str :param authority_id: name of the institution carrying out the processing - :type authority_id: str - :param azimuth: azimuth between source and receiver - :type azimuth: float or int, optional - :param dist: distance between source and receiver - :type dist: float or int, optional - :return: An ObsPy :class: `~obspy.core.event.Arrival` object - ''' - arrival = ope.Arrival() - arrival.creation_info = cinfo - arrival.pick_id = pickresID - arrival.phase = phase - if azimuth is not None: - arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360. - else: - arrival.azimuth = azimuth - arrival.distance = dist - return arrival - - -def createMagnitude(originID, cinfo): - ''' - createMagnitude - function to create an ObsPy Magnitude object - :param originID: - :param cinfo: - :param authority_id: + :type authority_id: str, optional + # hrstr missing :return: ''' - magnitude = ope.Magnitude() - magnitude.creation_info = cinfo - magnitude.origin_id = originID - return magnitude + assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ + "UTCDateTime object" + hid = getHash(timetohash) + if hrstr is None: + resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) + else: + resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) + if authority_id is not None: + resID.convertIDToQuakeMLURI(authority_id=authority_id) + return resID +def demeanTrace(trace, window): + """ + returns the DATA where each trace is demean by the average value within + WINDOW + :param trace: waveform trace object + :type trace: `~obspy.core.stream.Trace` + :param inoise: range of indices of DATA within the WINDOW # not used + # param (window) mising + :type window: tuple + :return: trace + :rtype: `~obspy.core.stream.Trace` + """ + trace.data -= trace.data[window].mean() + return trace -def createAmplitude(pickID, amp, unit, category, cinfo): - amplitude = ope.Amplitude() - amplitude.creation_info = cinfo - amplitude.generic_amplitude = amp - amplitude.unit = ope.AmplitudeUnit(unit) - amplitude.type = ope.AmplitudeCategory(category) - amplitude.pick_id = pickID - return amplitude +def fnConstructor(s): + ''' + + :param s: + :return: + ''' + if type(s) is str: + s = s.split(':')[-1] + else: + s = getHash(UTCDateTime()) + + badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') + badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)') + + fn = badchars.sub('_', s) + + if badsuffix.match(fn): + fn = '_' + fn + return fn + +def getGlobalTimes(stream): + ''' + + :param stream: + :return: + ''' + min_start = UTCDateTime() + max_end = None + for trace in stream: + if trace.stats.starttime < min_start: + min_start = trace.stats.starttime + if max_end is None or trace.stats.endtime > max_end: + max_end = trace.stats.endtime + return min_start, max_end + +def getHash(time): + ''' + :param time: time object for which a hash should be calculated + :type time: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :return: str + ''' + hg = hashlib.sha1() + hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) + return hg.hexdigest() + +def getLogin(): + ''' + + :return: + ''' + return pwd.getpwuid(os.getuid())[0] + +def getOwner(fn): + ''' + + :param fn: + :return: + ''' + return pwd.getpwuid(os.stat(fn).st_uid).pw_name + +def prepTimeAxis(stime, trace): + ''' + + :param stime: + :param trace: + :return: + ''' + nsamp = trace.stats.npts + srate = trace.stats.sampling_rate + tincr = trace.stats.delta + etime = stime + nsamp / srate + time_ax = np.arange(stime, etime, tincr) + if len(time_ax) < nsamp: + print 'elongate time axes by one datum' + time_ax = np.arange(stime, etime + tincr, tincr) + elif len(time_ax) > nsamp: + print 'shorten time axes by one datum' + time_ax = np.arange(stime, etime - tincr, tincr) + if len(time_ax) != nsamp: + raise ValueError('{0} samples of data \n ' + '{1} length of time vector \n' + 'delta: {2}'.format(nsamp, len(time_ax), tincr)) + return time_ax + +def runProgram(cmd, parameter=None): + """ + run an external program specified by cmd with parameters input returning the + stdout output + + :param cmd: name of the command to run + :type cmd: str + :param parameter: filename of parameter file or parameter string + :type parameter: str + :return: stdout output + :rtype: str + """ + + if parameter: + cmd.strip() + cmd += ' %s 2>&1' % parameter + + output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), + shell = True) + # no return command? + +def scaleWFData(data, factor=None, components='all'): + """ + produce scaled waveforms from given waveform data and a scaling factor, + waveform may be selected by their components name + :param data: waveform data to be scaled + :type data: `~obspy.core.stream.Stream` object + :param factor: scaling factor + :type factor: float + :param components: components labels for the traces in data to be scaled by + the scaling factor (optional, default: 'all') + :type components: tuple + :return: scaled waveform data + :rtype: `~obspy.core.stream.Stream` object + """ + if components is not 'all': + for comp in components: + if factor is None: + max_val = np.max(np.abs(data.select(component=comp)[0].data)) + data.select(component=comp)[0].data /= 2 * max_val + else: + data.select(component=comp)[0].data /= 2 * factor + else: + for tr in data: + if factor is None: + max_val = float(np.max(np.abs(tr.data))) + tr.data /= 2 * max_val + else: + tr.data /= 2 * factor + return data \ No newline at end of file From a9d5f74a032bfa63ac167f5cc4c1d8772126a3ff Mon Sep 17 00:00:00 2001 From: sebastianp Date: Tue, 1 Dec 2015 12:30:24 +0100 Subject: [PATCH 0750/1144] minor change at createResourceID --- pylot/core/util/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index fca9d48e..6cfb86d7 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -213,7 +213,7 @@ def createResourceID(timetohash, restype, authority_id=None, hrstr=None): :type restype: str :param authority_id: name of the institution carrying out the processing :type authority_id: str, optional - # hrstr missing + :param hrstr: :return: ''' assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ From c3d7581f941fed0c9cc810c0d002c93b95a06197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 1 Dec 2015 15:39:28 +0100 Subject: [PATCH 0751/1144] Implemented modified class MoMw of object magnitude. --- autoPyLoT.py | 69 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 54003edb..7de3953d 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -88,9 +88,10 @@ def autoPyLoT(inputfile): maxnumit = 3 # maximum number of iterations for re-picking else: locflag = 0 - print (" !!! ") - print ("!!No location routine available, autoPyLoT is running in non-location mode!!") - print (" !!! ") + print(" !!! ") + print("!!No location routine available, autoPyLoT is running in non-location mode!!") + print("!!No source parameter estimation possible!!") + print(" !!! ") # multiple event processing @@ -132,6 +133,17 @@ def autoPyLoT(inputfile): if len(badpicks) == 0: print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") + # get NLLoc-location file + locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) + if len(glob.glob(locsearch)) > 0: + # get latest NLLoc-location file if several are available + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + # calculating seismic moment Mo and moment magnitude Mw + M0Mw(wfdat, None, None, None, nllocfile, picks, \ + parameter.getParam('rho'), parameter.getParam('vp')) + else: + print("autoPyLoT: No NLLoc-location file available!") + print("No source parameter estimation possible!") else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) @@ -151,6 +163,9 @@ def autoPyLoT(inputfile): # locate the event locate(nlloccall, ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) + # get updated NLLoc-location file + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + # check for bad picks badpicks = [] for key in picks: if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: @@ -159,23 +174,18 @@ def autoPyLoT(inputfile): len(badpicks))) if len(badpicks) == 0: print("autoPyLoT: No more bad onsets found, stop iterative picking!") - break - # calculating seismic moment Mo and corresponding moment - # magnitude Mw after Hanks and Kanamori (1979) from reliable - # picks/waveforms - for key in picks: - if picks[key]['P']['weight'] < 4 and picks[key]['P']['w0'] is not None: - selwf = wfdat.select(station=key) - w0 = picks[key]['P']['w0'] - sourcepara = M0Mw(selwf, None, None, None, w0, 5, \ - parameter.getParam('rho'), parameter.getParam('vp')) + nlloccounter = maxnumit + + # calculating seismic moment Mo and moment magnitude Mw + finalpicks = M0Mw(wfdat, None, None, None, nllocfile, picks, \ + parameter.getParam('rho'), parameter.getParam('vp')) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, evID) - writephases(picks, 'HYPO71', hypo71file) + writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) endsplash = '''------------------------------------------\n' -----Finished event %s!-----\n' @@ -220,6 +230,17 @@ def autoPyLoT(inputfile): if len(badpicks) == 0: print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") + # get NLLoc-location file + locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) + if len(glob.glob(locsearch)) > 0: + # get latest NLLOc-location file if several are available + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + # calculating seismic moment Mo and moment magnitude Mw + M0Mw(wfdat, None, None, None, nllocfile, picks, \ + parameter.getParam('rho'), parameter.getParam('vp')) + else: + print("autoPyLoT: No NLLoc-location file available!") + print("No source parameter estimation possible!") else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) @@ -239,6 +260,9 @@ def autoPyLoT(inputfile): # locate the event locate(nlloccall, ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) + # get updated NLLoc-location file + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + # check for bad picks badpicks = [] for key in picks: if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: @@ -247,25 +271,18 @@ def autoPyLoT(inputfile): len(badpicks))) if len(badpicks) == 0: print("autoPyLoT: No more bad onsets found, stop iterative picking!") - break - - # calculating seismic moment Mo and corresponding moment - # magnitude Mw after Hanks and Kanamori (1979) from reliable - # picks/waveforms - for key in picks: - if picks[key]['P']['weight'] < 4 and picks[key]['P']['w0'] is not None: - selwf = wfdat.select(station=key) - w0 = picks[key]['P']['w0'] - sourcepara = M0Mw(selwf, None, None, None, w0, 5, \ - parameter.getParam('rho'), parameter.getParam('vp')) + nlloccounter = maxnumit + # calculating seismic moment Mo and moment magnitude Mw + finalpicks = M0Mw(wfdat, None, None, None, nllocfile, picks, \ + parameter.getParam('rho'), parameter.getParam('vp')) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.getParam('eventID')) - writephases(picks, 'HYPO71', hypo71file) + writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) endsplash = '''------------------------------------------\n' -----Finished event %s!-----\n' From 30970b8451408985aeff03c57f464c04832eb882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 1 Dec 2015 15:41:37 +0100 Subject: [PATCH 0752/1144] Modified class MoMw: new functio run_calcMoMw using subfunction calcMoMw, gets hypocentral distances from NLLoc-location file. Returns modified pick dictionary including individual seismic moments and corresponding moment magnitudes. --- pylot/core/analysis/magnitude.py | 117 +++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 36 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index ff1f580e..074eebe9 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -10,15 +10,17 @@ import matplotlib.pyplot as plt import numpy as np from obspy.core import Stream from pylot.core.pick.utils import getsignalwin +from pylot.core.util.utils import getPatternLine from scipy.optimize import curve_fit class Magnitude(object): ''' Superclass for calculating Wood-Anderson peak-to-peak - amplitudes, local magnitudes and moment magnitudes. + amplitudes, local magnitudes, seismic moments + and moment magnitudes. ''' - def __init__(self, wfstream, To, pwin, iplot, w0=None, delta=None, rho=None, vp=None): + def __init__(self, wfstream, To, pwin, iplot, NLLocfile=None, picks=None, rho=None, vp=None): ''' :param: wfstream :type: `~obspy.core.stream.Stream @@ -32,8 +34,20 @@ class Magnitude(object): :type: float :param: iplot, no. of figure window for plotting interims results - :type: integer + :type: integer + :param: NLLocfile, name and full path to NLLoc-location file + needed when calling class MoMw + :type: string + + :param: picks, dictionary containing picking results + :type: dictionary + + :param: rho [kg/m³], rock density, parameter from autoPyLoT.in + :type: integer + + :param: vp [m/s], P-velocity + :param: integer ''' assert isinstance(wfstream, Stream), "%s is not a stream object" % str(wfstream) @@ -42,13 +56,13 @@ class Magnitude(object): self.setTo(To) self.setpwin(pwin) self.setiplot(iplot) - self.setw0(w0) + self.setNLLocfile(NLLocfile) self.setrho(rho) - self.setdelta(delta) + self.setpicks(picks) self.setvp(vp) self.calcwapp() self.calcsourcespec() - self.calcMoMw() + self.run_calcMoMw() def getwfstream(self): @@ -75,11 +89,11 @@ class Magnitude(object): def setiplot(self, iplot): self.iplot = iplot - def setw0(self, w0): - self.w0 = w0 + def setNLLocfile(self, NLLocfile): + self.NLLocfile = NLLocfile - def getw0(self): - return self.w0 + def getNLLocfile(self): + return self.NLLocfile def setrho(self, rho): self.rho = rho @@ -93,11 +107,11 @@ class Magnitude(object): def getvp(self): return self.vp - def setdelta(self, delta): - self.delta = delta + def setpicks(self, picks): + self.picks = picks - def getdelta(self): - return self.delta + def getpicks(self): + return self.picks def getwapp(self): return self.wapp @@ -108,11 +122,8 @@ class Magnitude(object): def getfc(self): return self.fc - def getMo(self): - return self.Mo - - def getMw(self): - return self.Mw + def getpicdic(self): + return self.picdic def calcwapp(self): self.wapp = None @@ -120,9 +131,8 @@ class Magnitude(object): def calcsourcespec(self): self.sourcespek = None - def calcMoMw(self): - self.Mo = None - self.Mw = None + def run_calcMoMw(self): + self.pickdic = None class WApp(Magnitude): ''' @@ -177,27 +187,62 @@ class WApp(Magnitude): class M0Mw(Magnitude): ''' Method to calculate seismic moment Mo and moment magnitude Mw. - Uses class w0fc for calculating plateau wo and corner frequency - fc of source spectrum, respectively. + Requires results of class w0fc for calculating plateau w0 + and corner frequency fc of source spectrum, respectively. Uses + subfunction calcMoMw.py. Returns modified dictionary of picks including + seismic moment Mo and corresponding moment magntiude Mw. ''' - def calcMoMw(self): + def run_calcMoMw(self): - stream = self.getwfstream() - tr = stream[0] + picks = self.getpicks() + nllocfile = self.getNLLocfile() + wfdat = self.getwfstream() + for key in picks: + if picks[key]['P']['weight'] < 4 and picks[key]['P']['w0'] is not None: + # select waveform + selwf = wfdat.select(station=key) + # get corresponding height of source spectrum plateau w0 + w0 = picks[key]['P']['w0'] + # get hypocentral distance of station + # from NLLoc-location file + if len(key) > 4: + Ppattern = '%s ? ? ? P' % key + elif len(key) == 4: + Ppattern = '%s ? ? ? P' % key + elif len(key) < 4: + Ppattern = '%s ? ? ? P' % key + nllocline = getPatternLine(nllocfile, Ppattern) + delta = float(nllocline.split(None)[21]) + # call subfunction + [Mo, Mw] = calcMoMw(selwf, w0, self.getrho(), self.getvp(), delta) + # add Mo and Mw to dictionary + picks[key]['P']['Mo'] = Mo + picks[key]['P']['Mw'] = Mw + self.picdic = picks - print("Calculating seismic moment Mo and moment magnitude Mw for station %s ..." \ - % tr.stats.station) +def calcMoMw(wfstream, w0, rho, vp, delta): + ''' + Subfunction of run_calcMoMw to calculate individual + seismic moments and corresponding moment magnitudes. + ''' - # additional common parameters for calculating Mo - rP = 2 / np.sqrt(15) # average radiation pattern of P waves (Aki & Richards, 1980) - freesurf = 2.0 # free surface correction, assuming vertical incidence + tr = wfstream[0] - self.Mo = (self.getw0() * 4 * np.pi * self.getrho() * np.power(self.getvp(), 3) * \ - self.getdelta()) / (rP * freesurf) + print("calcMoMw: Calculating seismic moment Mo and moment magnitude Mw for station %s ..." \ + % tr.stats.station) - self.Mw = 2/3 * np.log10(self.Mo) - 6 - print("MoMw: Calculated seismic moment Mo = %e Nm => Mw = %3.1f " % (self.Mo, self.Mw)) + # additional common parameters for calculating Mo + rP = 2 / np.sqrt(15) # average radiation pattern of P waves (Aki & Richards, 1980) + freesurf = 2.0 # free surface correction, assuming vertical incidence + + Mo = w0 * 4 * np.pi * rho * np.power(vp, 3) * delta / (rP * freesurf) + + Mw = np.log10(Mo * 1e07) * 2 / 3 - 10.7 #after Hanks & Kanamori (1979), defined for [dyn*cm]! + + print("calcMoMw: Calculated seismic moment Mo = %e Nm => Mw = %3.1f " % (Mo, Mw)) + + return Mo, Mw class w0fc(Magnitude): From 40f38ebf84ed08a778cc90277f6e2a7d00e1ff75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 2 Dec 2015 10:09:17 +0100 Subject: [PATCH 0753/1144] Removed calculation of source spectrum from autopick, as azimuth and angle of incidence are necessary and thus a location is needed for a reliable calculation of the source spectrum. Source spectrum is now calculated after locating the event. --- pylot/core/pick/autopick.py | 40 +++---------------------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 15b6809a..0d352279 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -15,10 +15,10 @@ from scipy import integrate from pylot.core.pick.Picker import AICPicker, PragPicker from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ - getSNR, fmpicker, checkPonsets, wadaticheck, crossings_nonzero_all + getSNR, fmpicker, checkPonsets, wadaticheck from pylot.core.util.utils import getPatternLine from pylot.core.read.data import Data -from pylot.core.analysis.magnitude import WApp, w0fc +from pylot.core.analysis.magnitude import WApp def autopickevent(data, param): stations = [] @@ -138,8 +138,6 @@ def autopickstation(wfstream, pickparam): Sflag = 0 Pmarker = [] Ao = None # Wood-Anderson peak-to-peak amplitude - w0 = None # plateau of source spectrum - fc = None # corner frequancy of source spectrum # split components zdat = wfstream.select(component="Z") @@ -314,37 +312,6 @@ def autopickstation(wfstream, pickparam): else: FM = 'N' - ############################################################## - # get DC value and corner frequency (fc) of source spectrum - # from P pulse - # initialize Data object - data = Data() - z_copy = zdat.copy() - [corzdat, restflag] = data.restituteWFData(invdir, z_copy) - if restflag == 1: - # integrate to displacement - corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta) - z_copy[0].data = corintzdat - # largest detectable period == window length - # after P pulse for calculating source spectrum - winzc = (1 / bpz2[0]) * z_copy[0].stats.sampling_rate - impickP = mpickP * z_copy[0].stats.sampling_rate - wfzc = z_copy[0].data[impickP : impickP + winzc] - # calculate spectrum using only first cycles of - # waveform after P onset! - zc = crossings_nonzero_all(wfzc) - if np.size(zc) == 0 or len(zc) <= 3: - print ("Something is wrong with the waveform, " - "no zero crossings derived!") - print ("Cannot calculate source spectrum!") - else: - index = min([3, len(zc) - 1]) - calcwin = (zc[index] - zc[0]) * z_copy[0].stats.delta - # calculate source spectrum and get w0 and fc - specpara = w0fc(z_copy, mpickP, calcwin, iplot) - w0 = specpara.getw0() - fc = specpara.getfc() - print ("autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, " "Polarity: %s" % (Pweight, SNRP, SNRPdB, FM)) Sflag = 1 @@ -790,8 +757,7 @@ def autopickstation(wfstream, pickparam): # for P phase phase = 'P' phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, - 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM, - 'w0': w0, 'fc': fc} + 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM} picks = {phase: phasepick} # add P marker picks[phase]['marked'] = Pmarker From 46cbe96a4392fd4b1af48f6d1c4fde1a79b35537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 2 Dec 2015 10:12:37 +0100 Subject: [PATCH 0754/1144] Extended class MoMw for calculating source spectrum. New functions calcsourcespec, calcMoMw and run_calcMoMw implemented. --- pylot/core/analysis/magnitude.py | 223 ++++++++++++++++++++----------- 1 file changed, 147 insertions(+), 76 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 074eebe9..d0e895c4 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -8,10 +8,12 @@ Created August/September 2015. import matplotlib.pyplot as plt import numpy as np -from obspy.core import Stream -from pylot.core.pick.utils import getsignalwin +from obspy.core import Stream, UTCDateTime +from pylot.core.pick.utils import getsignalwin, crossings_nonzero_all from pylot.core.util.utils import getPatternLine from scipy.optimize import curve_fit +from scipy import integrate +from pylot.core.read.data import Data class Magnitude(object): ''' @@ -20,7 +22,8 @@ class Magnitude(object): and moment magnitudes. ''' - def __init__(self, wfstream, To, pwin, iplot, NLLocfile=None, picks=None, rho=None, vp=None): + def __init__(self, wfstream, To, pwin, iplot, NLLocfile=None, \ + picks=None, rho=None, vp=None, invdir=None): ''' :param: wfstream :type: `~obspy.core.stream.Stream @@ -30,7 +33,7 @@ class Magnitude(object): :param: pwin, pick window [To To+pwin] to get maximum peak-to-peak amplitude (WApp) or to calculate - source spectrum (DCfc) + source spectrum (DCfc) around P onset :type: float :param: iplot, no. of figure window for plotting interims results @@ -48,6 +51,9 @@ class Magnitude(object): :param: vp [m/s], P-velocity :param: integer + + :param: invdir, path to inventory or dataless-SEED file + :type: string ''' assert isinstance(wfstream, Stream), "%s is not a stream object" % str(wfstream) @@ -60,6 +66,7 @@ class Magnitude(object): self.setrho(rho) self.setpicks(picks) self.setvp(vp) + self.setinvdir(invdir) self.calcwapp() self.calcsourcespec() self.run_calcMoMw() @@ -122,6 +129,12 @@ class Magnitude(object): def getfc(self): return self.fc + def setinvdir(self, invdir): + self.invdir = invdir + + def getinvdir(self): + return self.invdir + def getpicdic(self): return self.picdic @@ -190,7 +203,8 @@ class M0Mw(Magnitude): Requires results of class w0fc for calculating plateau w0 and corner frequency fc of source spectrum, respectively. Uses subfunction calcMoMw.py. Returns modified dictionary of picks including - seismic moment Mo and corresponding moment magntiude Mw. + Dc-value, corner frequency fc, seismic moment Mo and + corresponding moment magntiude Mw. ''' def run_calcMoMw(self): @@ -198,12 +212,15 @@ class M0Mw(Magnitude): picks = self.getpicks() nllocfile = self.getNLLocfile() wfdat = self.getwfstream() + # get vertical component data only + zdat = wfdat.select(component="Z") + if len(zdat) == 0: # check for other components + zdat = wfdat.select(component="3") + for key in picks: - if picks[key]['P']['weight'] < 4 and picks[key]['P']['w0'] is not None: + if picks[key]['P']['weight'] < 4: # select waveform - selwf = wfdat.select(station=key) - # get corresponding height of source spectrum plateau w0 - w0 = picks[key]['P']['w0'] + selwf = zdat.select(station=key) # get hypocentral distance of station # from NLLoc-location file if len(key) > 4: @@ -214,14 +231,27 @@ class M0Mw(Magnitude): Ppattern = '%s ? ? ? P' % key nllocline = getPatternLine(nllocfile, Ppattern) delta = float(nllocline.split(None)[21]) - # call subfunction - [Mo, Mw] = calcMoMw(selwf, w0, self.getrho(), self.getvp(), delta) - # add Mo and Mw to dictionary + # call subfunction to estimate source spectrum + # and to derive w0 and fc + [w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \ + self.getiplot(), self.getinvdir()) + + if w0 is not None: + # call subfunction to calculate Mo and Mw + [Mo, Mw] = calcMoMw(selwf, w0, self.getrho(), self.getvp(), \ + delta, self.getinvdir()) + else: + Mo = None + Mw = None + + # add w0, fc, Mo and Mw to dictionary + picks[key]['P']['w0'] = w0 + picks[key]['P']['fc'] = fc picks[key]['P']['Mo'] = Mo picks[key]['P']['Mw'] = Mw self.picdic = picks -def calcMoMw(wfstream, w0, rho, vp, delta): +def calcMoMw(wfstream, w0, rho, vp, delta, inv): ''' Subfunction of run_calcMoMw to calculate individual seismic moments and corresponding moment magnitudes. @@ -245,94 +275,135 @@ def calcMoMw(wfstream, w0, rho, vp, delta): return Mo, Mw -class w0fc(Magnitude): + +def calcsourcespec(wfstream, onset, iplot, inventory): ''' - Method to calculate the source spectrum and to derive from that the plateau + Subfunction to calculate the source spectrum and to derive from that the plateau (usually called omega0) and the corner frequency assuming Aki's omega-square - source model. Has to be derived from instrument corrected displacement traces! + source model. Has to be derived from instrument corrected displacement traces, + thus restitution and integration necessary! ''' + print ("Calculating source spectrum ....") - def calcsourcespec(self): - print ("Calculating source spectrum ....") + fc = None + w0 = None + data = Data() + z_copy = wfstream.copy() - self.w0 = None # DC-value - self.fc = None # corner frequency - - stream = self.getwfstream() - tr = stream[0] + [corzdat, restflag] = data.restituteWFData(inventory, z_copy) + if restflag == 1: + # integrate to displacment + corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta) + z_copy[0].data = corintzdat + tr = z_copy[0] + # get window after P pulse for + # calculating source spectrum + if tr.stats.sampling_rate <= 100: + winzc = tr.stats.sampling_rate + elif tr.stats.sampling_rate > 100 and \ + tr.stats.sampling_rate <= 200: + winzc = 0.5 * tr.stats.sampling_rate + elif tr.stats.sampling_rate > 200 and \ + tr.stats.sampling_rate <= 400: + winzc = 0.2 * tr.stats.sampling_rate + elif tr.stats.sampling_rate > 400: + winzc = tr.stats.sampling_rate + tstart = UTCDateTime(tr.stats.starttime) + tonset = onset.timestamp -tstart.timestamp + impickP = tonset * tr.stats.sampling_rate + wfzc = tr.data[impickP : impickP + winzc] # get time array t = np.arange(0, len(tr) * tr.stats.delta, tr.stats.delta) - iwin = getsignalwin(t, self.getTo(), self.getpwin()) - xdat = tr.data[iwin] + # calculate spectrum using only first cycles of + # waveform after P onset! + zc = crossings_nonzero_all(wfzc) + if np.size(zc) == 0 or len(zc) <= 3: + print ("Something is wrong with the waveform, " + "no zero crossings derived!") + print ("No calculation of source spectrum possible!") + plotflag = 0 + else: + plotflag = 1 + index = min([3, len(zc) - 1]) + calcwin = (zc[index] - zc[0]) * z_copy[0].stats.delta + iwin = getsignalwin(t, tonset, calcwin) + xdat = tr.data[iwin] - # fft - fny = tr.stats.sampling_rate / 2 - l = len(xdat) / tr.stats.sampling_rate - n = tr.stats.sampling_rate * l # number of fft bins after Bath - # find next power of 2 of data length - m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2))) - N = int(np.power(m, 2)) - y = tr.stats.delta * np.fft.fft(xdat, N) - Y = abs(y[: N/2]) - L = (N - 1) / tr.stats.sampling_rate - f = np.arange(0, fny, 1/L) + # fft + fny = tr.stats.sampling_rate / 2 + l = len(xdat) / tr.stats.sampling_rate + n = tr.stats.sampling_rate * l # number of fft bins after Bath + # find next power of 2 of data length + m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2))) + N = int(np.power(m, 2)) + y = tr.stats.delta * np.fft.fft(xdat, N) + Y = abs(y[: N/2]) + L = (N - 1) / tr.stats.sampling_rate + f = np.arange(0, fny, 1/L) - # remove zero-frequency and frequencies above - # corner frequency of seismometer (assumed - # to be 100 Hz) - fi = np.where((f >= 1) & (f < 100)) - F = f[fi] - YY = Y[fi] - # get plateau (DC value) and corner frequency - # initial guess of plateau - w0in = np.mean(YY[0:100]) - # initial guess of corner frequency - # where spectral level reached 50% of flat level - iin = np.where(YY >= 0.5 * w0in) - Fcin = F[iin[0][np.size(iin) - 1]] + # remove zero-frequency and frequencies above + # corner frequency of seismometer (assumed + # to be 100 Hz) + fi = np.where((f >= 1) & (f < 100)) + F = f[fi] + YY = Y[fi] + # get plateau (DC value) and corner frequency + # initial guess of plateau + w0in = np.mean(YY[0:100]) + # initial guess of corner frequency + # where spectral level reached 50% of flat level + iin = np.where(YY >= 0.5 * w0in) + Fcin = F[iin[0][np.size(iin) - 1]] - # use of implicit scipy otimization function - fit = synthsourcespec(F, w0in, Fcin) - [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin]) - w01 = optspecfit[0] - fc1 = optspecfit[1] - print ("w0fc: Determined w0-value: %e m/Hz, \n" - "Determined corner frequency: %f Hz" % (w01, fc1)) + # use of implicit scipy otimization function + fit = synthsourcespec(F, w0in, Fcin) + [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin]) + w01 = optspecfit[0] + fc1 = optspecfit[1] + print ("w0fc: Determined w0-value: %e m/Hz, \n" + "Determined corner frequency: %f Hz" % (w01, fc1)) - # use of conventional fitting - [w02, fc2] = fitSourceModel(F, YY.real, Fcin, self.getiplot()) + # use of conventional fitting + [w02, fc2] = fitSourceModel(F, YY.real, Fcin, iplot) - # get w0 and fc as median - self.w0 = np.median([w01, w02]) - self.fc = np.median([fc1, fc2]) - print("w0fc: Using w0-value = %e m/Hz and fc = %f Hz" % (self.w0, self.fc)) + # get w0 and fc as median + w0 = np.median([w01, w02]) + fc = np.median([fc1, fc2]) + print("w0fc: Using w0-value = %e m/Hz and fc = %f Hz" % (w0, fc)) - if self.getiplot() > 1: - f1 = plt.figure() - plt.subplot(2,1,1) - # show displacement in mm - plt.plot(t, np.multiply(tr, 1000), 'k') + if iplot > 1: + f1 = plt.figure() + plt.subplot(2,1,1) + # show displacement in mm + plt.plot(t, np.multiply(tr, 1000), 'k') + if plotflag == 1: plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') - plt.title('Seismogram and P pulse, station %s' % tr.stats.station) - plt.xlabel('Time since %s' % tr.stats.starttime) - plt.ylabel('Displacement [mm]') + plt.title('Seismogram and P Pulse, Station %s-%s' \ + % (tr.stats.station, tr.stats.channel)) + else: + plt.title('Seismogram, Station %s-%s' \ + % (tr.stats.station, tr.stats.channel)) + plt.xlabel('Time since %s' % tr.stats.starttime) + plt.ylabel('Displacement [mm]') + if plotflag == 1: plt.subplot(2,1,2) plt.loglog(f, Y.real, 'k') plt.loglog(F, YY.real) plt.loglog(F, fit, 'g') - plt.loglog([self.fc, self.fc], [self.w0/100, self.w0], 'g') + plt.loglog([fc, fc], [w0/100, w0], 'g') plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \ - % (self.w0, self.fc)) + % (w0, fc)) plt.xlabel('Frequency [Hz]') plt.ylabel('Amplitude [m/Hz]') plt.grid() - plt.show() - raw_input() - plt.close(f1) - + plt.show() + raw_input() + plt.close(f1) + return w0, fc + def synthsourcespec(f, omega0, fcorner): ''' From c227b6499e7f7d5df71ddbe1a1765e982323bc51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 2 Dec 2015 10:15:05 +0100 Subject: [PATCH 0755/1144] Modified for extended class MoMw of object magntitude. MoMw returns pick dictionary containing individual corner frequencies, w0-values, seismic moments and moment magnitudes. --- autoPyLoT.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 7de3953d..eb205dbd 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -139,8 +139,9 @@ def autoPyLoT(inputfile): # get latest NLLoc-location file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # calculating seismic moment Mo and moment magnitude Mw - M0Mw(wfdat, None, None, None, nllocfile, picks, \ - parameter.getParam('rho'), parameter.getParam('vp')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -177,8 +178,9 @@ def autoPyLoT(inputfile): nlloccounter = maxnumit # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, None, nllocfile, picks, \ - parameter.getParam('rho'), parameter.getParam('vp')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## @@ -236,8 +238,9 @@ def autoPyLoT(inputfile): # get latest NLLOc-location file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # calculating seismic moment Mo and moment magnitude Mw - M0Mw(wfdat, None, None, None, nllocfile, picks, \ - parameter.getParam('rho'), parameter.getParam('vp')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -274,8 +277,9 @@ def autoPyLoT(inputfile): nlloccounter = maxnumit # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, None, nllocfile, picks, \ - parameter.getParam('rho'), parameter.getParam('vp')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## From f3ba22b235fe821fe59d7ffa20742fbe4def1169 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 2 Dec 2015 19:17:04 +0100 Subject: [PATCH 0756/1144] [new] introduced verbose flag to suppress to much output during auto picking (slows down especially to GUI triggered auto picking process) --- pylot/core/pick/autopick.py | 135 ++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 58 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 15b6809a..366a36e3 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -46,7 +46,7 @@ def autopickevent(data, param): # check S-P times (Wadati) return wadaticheck(jk_checked_onsets, wdttolerance, iplot) -def autopickstation(wfstream, pickparam): +def autopickstation(wfstream, pickparam, verbose=False): """ :param: wfstream :type: `~obspy.core.stream.Stream` @@ -153,11 +153,11 @@ def autopickstation(wfstream, pickparam): ndat = wfstream.select(component="1") if algoP == 'HOS' or algoP == 'ARZ' and zdat is not None: - print '##########################################' - print 'autopickstation: Working on P onset of station %s' % zdat[ - 0].stats.station - print 'Filtering vertical trace ...' - print zdat + msg = '##########################################\nautopickstation:' \ + ' Working on P onset of station {station}\nFiltering vertical ' \ + 'trace ...\n{data}'.format(station=zdat[0].stats.station, + data=str(zdat)) + if verbose: print(msg) z_copy = zdat.copy() # filter and taper data tr_filt = zdat[0].copy() @@ -171,9 +171,9 @@ def autopickstation(wfstream, pickparam): Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime Ldiff = Lwf - Lc if Ldiff < 0: - print 'autopickstation: Cutting times are too large for actual ' \ - 'waveform!' - print 'Using entire waveform instead!' + msg = 'autopickstation: Cutting times are too large for actual ' \ + 'waveform!\nUsing entire waveform instead!' + if verbose: print(msg) pstart = 0 pstop = len(zdat[0].data) * zdat[0].stats.delta cuttimes = [pstart, pstop] @@ -206,10 +206,11 @@ def autopickstation(wfstream, pickparam): z_copy[0].data = tr_filt.data zne = z_copy if len(ndat) == 0 or len(edat) == 0: - print ("One or more horizontal components missing!") - print ("Signal length only checked on vertical component!") - print ("Decreasing minsiglengh from %f to %f" - % (minsiglength, minsiglength / 2)) + msg = 'One or more horizontal components missing!\nSignal ' \ + 'length only checked on vertical component!\n' \ + 'Decreasing minsiglengh from {0} to ' \ + '{1}'.format(minsiglength, minsiglength / 2) + if verbose: print(msg) Pflag = checksignallength(zne, aicpick.getpick(), tsnrz, minsiglength / 2, nfacsl, minpercent, iplot) @@ -235,8 +236,9 @@ def autopickstation(wfstream, pickparam): # check for spuriously picked S onset # both horizontal traces needed if len(ndat) == 0 or len(edat) == 0: - print 'One or more horizontal components missing!' - print 'Skipping control function checkZ4S.' + msg = 'One or more horizontal components missing!\n' \ + 'Skipping control function checkZ4S.' + if verbose: print(msg) else: Pflag = checkZ4S(zne, aicpick.getpick(), zfac, tsnrz[3], iplot) @@ -252,11 +254,12 @@ def autopickstation(wfstream, pickparam): aicpick.getSNR() >= minAICPSNR and Pflag == 1): aicPflag = 1 - print 'AIC P-pick passes quality control: Slope: %f counts/s, SNR: %f' % \ - (aicpick.getSlope(), aicpick.getSNR()) - print 'Go on with refined picking ...' + msg = 'AIC P-pick passes quality control: Slope: {0} counts/s, ' \ + 'SNR: {1}\nGo on with refined picking ...\n' \ + 'autopickstation: re-filtering vertical trace ' \ + '...'.format(aicpick.getSlope(), aicpick.getSNR()) + if verbose: print(msg) # re-filter waveform with larger bandpass - print 'autopickstation: re-filtering vertical trace ...' z_copy = zdat.copy() tr_filt = zdat[0].copy() tr_filt.filter('bandpass', freqmin=bpz2[0], freqmax=bpz2[1], @@ -334,9 +337,10 @@ def autopickstation(wfstream, pickparam): # waveform after P onset! zc = crossings_nonzero_all(wfzc) if np.size(zc) == 0 or len(zc) <= 3: - print ("Something is wrong with the waveform, " - "no zero crossings derived!") - print ("Cannot calculate source spectrum!") + msg = "Something is wrong with the waveform, " \ + "no zero crossings derived!\nCannot " \ + "calculate source spectrum!" + if verbose: print(msg) else: index = min([3, len(zc) - 1]) calcwin = (zc[index] - zc[0]) * z_copy[0].stats.delta @@ -345,33 +349,42 @@ def autopickstation(wfstream, pickparam): w0 = specpara.getw0() fc = specpara.getfc() - print ("autopickstation: P-weight: %d, SNR: %f, SNR[dB]: %f, " - "Polarity: %s" % (Pweight, SNRP, SNRPdB, FM)) + msg = "autopickstation: P-weight: {0}, " \ + "SNR: {1}, SNR[dB]: {2}, Polarity: {3}".format(Pweight, + SNRP, + SNRPdB, + FM) + print(msg) Sflag = 1 else: - print ("Bad initial (AIC) P-pick, skipping this onset!") - print 'AIC-SNR=', aicpick.getSNR(), 'AIC-Slope=', aicpick.getSlope(), 'counts/s' - print '(min. AIC-SNR=', minAICPSNR, ', min. AIC-Slope=', minAICPslope, 'counts/s)' + msg = 'Bad initial (AIC) P-pick, skipping this onset!\n' \ + 'AIC-SNR={0}, AIC-Slope={1}counts/s\n' \ + '(min. AIC-SNR={2}, ' \ + 'min. AIC-Slope={3}counts/s)'.format(aicpick.getSNR(), + aicpick.getSlope(), + minAICPSNR, + minAICPslope) + if verbose: print(msg) Sflag = 0 else: - print ("autopickstation: No vertical component data available!, " - "Skipping station!") + print("autopickstation: No vertical component data available!, " + "Skipping station!") if edat is not None and ndat is not None and len(edat) > 0 and len( ndat) > 0 and Pweight < 4: - print ("Go on picking S onset ...") - print ("##################################################") - print ("Working on S onset of station %s" % edat[0].stats.station) - print ("Filtering horizontal traces ...") - + msg = "Go on picking S onset ...\n" \ + "##################################################\n" \ + "Working on S onset of station {0}\nFiltering horizontal " \ + "traces ...".format(edat[0].stats.station) + if verbose: print(msg) # determine time window for calculating CF after P onset cuttimesh = [round(max([mpickP + sstart, 0])), round(min([mpickP + sstop, Lwf]))] if algoS == 'ARH': - print edat, ndat + if verbose: print(edat, ndat) # re-create stream object including both horizontal components hdat = edat.copy() hdat += ndat @@ -388,7 +401,7 @@ def autopickstation(wfstream, pickparam): h_copy[0].data = trH1_filt.data h_copy[1].data = trH2_filt.data elif algoS == 'AR3': - print zdat, edat, ndat + if verbose: print(zdat, edat, ndat) # re-create stream object including all components hdat = zdat.copy() hdat += edat @@ -441,15 +454,15 @@ def autopickstation(wfstream, pickparam): aicarhpick.getSNR() >= minAICSSNR and aicarhpick.getpick() is not None): aicSflag = 1 - print 'AIC S-pick passes quality control: Slope: %f counts/s, SNR: %f' \ - % (aicarhpick.getSlope(), aicarhpick.getSNR()) - print 'Go on with refined picking ...' + msg = 'AIC S-pick passes quality control: Slope: {0} counts/s, ' \ + 'SNR: {1}\nGo on with refined picking ...\n' \ + 'autopickstation: re-filtering horizontal traces ' \ + '...'.format(aicarhpick.getSlope(), aicarhpick.getSNR()) # re-calculate CF from re-filtered trace in vicinity of initial # onset cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), round(aicarhpick.getpick() + Srecalcwin)] # re-filter waveform with larger bandpass - print 'autopickstation: re-filtering horizontal traces...' h_copy = hdat.copy() # filter and taper data if algoS == 'ARH': @@ -554,11 +567,12 @@ def autopickstation(wfstream, pickparam): elif Serror > timeerrorsS[3]: Sweight = 4 - print 'autopickstation: S-weight: %d, SNR: %f, SNR[dB]: %f' % ( - Sweight, SNRS, SNRSdB) + print('autopickstation: S-weight: {0}, SNR: {1}, ' + 'SNR[dB]: {2}\n' + '################################################' + ''.format(Sweight, SNRS, SNRSdB)) ################################################################## # get Wood-Anderson peak-to-peak amplitude - print "################################################" # initialize Data object data = Data() # re-create stream object including both horizontal components @@ -580,15 +594,19 @@ def autopickstation(wfstream, pickparam): Ao = wapp.getwapp() else: - print 'Bad initial (AIC) S-pick, skipping this onset!' - print 'AIC-SNR=', aicarhpick.getSNR(), \ - 'AIC-Slope=', aicarhpick.getSlope(), 'counts/s' - print '(min. AIC-SNR=', minAICSSNR, ', min. AIC-Slope=', \ - minAICSslope, 'counts/s)' + msg = 'Bad initial (AIC) S-pick, skipping this onset!\n' \ + 'AIC-SNR={0}, AIC-Slope={1}counts/s\n' \ + '(min. AIC-SNR={2}, ' \ + 'min. AIC-Slope={3}counts/s)\n' \ + '################################################' \ + ''.format(aicarhpick.getSNR(), + aicarhpick.getSlope(), + minAICSSNR, + minAICSslope) + if verbose: print(msg) ############################################################ # get Wood-Anderson peak-to-peak amplitude - print "################################################" # initialize Data object data = Data() # re-create stream object including both horizontal components @@ -604,8 +622,8 @@ def autopickstation(wfstream, pickparam): Ao = wapp.getwapp() else: - print 'autopickstation: No horizontal component data available or ' \ - 'bad P onset, skipping S picking!' + print('autopickstation: No horizontal component data available or ' \ + 'bad P onset, skipping S picking!') ############################################################## if iplot > 0: @@ -819,12 +837,13 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): :param badpicks: picks to be repicked - :param pickparameter: picking parameters from autoPyLoT-input file + :param pickparameter: picking parameters from autoPyLoT-input file ''' - print("#######################################################") - print("autoPyLoT: Found %d bad onsets at station(s) %s, starting re-picking them ...") \ - % (len(badpicks), badpicks) + msg = '#######################################################\n' \ + 'autoPyLoT: Found {0} bad onsets at station(s) {1}, ' \ + 'starting re-picking them ...'.format(len(badpicks), badpicks) + print(msg) newpicks = {} for i in range(0, len(badpicks)): @@ -867,13 +886,13 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): print("Precalcwin: %fs => %fs" % (Precalcwin_old, pickparameter.getParam('Precalcwin'))) print("noisefactor: %f => %f" % (noisefactor_old, pickparameter.getParam('noisefactor'))) print("zfac: %f => %f" % (zfac_old, pickparameter.getParam('zfac'))) - + # repick station newpicks = autopickstation(wf2pick, pickparameter) - + # replace old dictionary with new one picks[badpicks[i][0]] = newpicks - + # reset temporary change of picking parameters print("iteratepicker: Resetting picking parameters ...") pickparameter.setParam(pstart=pstart_old) From 69d3f43db948dcc9248c11116b18d053ea0a690a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 3 Dec 2015 12:19:34 +0100 Subject: [PATCH 0757/1144] code commenting --- pylot/core/active/fmtomo2vtk.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index 4e45d3ff..cf503e22 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -4,6 +4,9 @@ import numpy as np def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'abs', inputfileref = 'vgridsref.in'): ''' Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in + + :param: absOrRel, can be "abs" or "rel" for absolute or relative velocities. if "rel" inputfileref must be given + :type: str ''' def getDistance(angle): PI = np.pi @@ -136,7 +139,10 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): ''' - Writes VTK file(s) for FMTOMO rays from rays.dat + Writes VTK file(s) for FMTOMO rays from rays.dat. There is one file created for each ray. + + :param: fdirout, output directory, must exist before + :type: str :param: nthPoint, plot every nth point of the ray :type: integer From df5f0f41b465c601f68a4096690274b89afe29ec Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 3 Dec 2015 12:21:18 +0100 Subject: [PATCH 0758/1144] comment --- pylot/core/active/surveyPlotTools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index a56aa13d..e913cb9c 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -106,6 +106,9 @@ class regions(object): self.printOutput('Disconnected polygon selection') def addTextfield(self, xpos = 0, ypos = 0.95, width = 1, height = 0.03): + ''' + Adds an ax for text output to the plot. + ''' self.axtext = self.ax.figure.add_axes([xpos, ypos, width, From 77ad274f8fdd1e6bafdb5b94a11689a25be68e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 3 Dec 2015 14:55:07 +0100 Subject: [PATCH 0759/1144] Some bugs fixed, implemented calculation of network moment magnitude. --- autoPyLoT.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index eb205dbd..387090cd 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -6,6 +6,7 @@ import argparse import glob import subprocess import string +import numpy as np from obspy.core import read, UTCDateTime from pylot.core.read.data import Data from pylot.core.read.inputs import AutoPickParameter @@ -161,6 +162,8 @@ def autoPyLoT(inputfile): picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) # write phases to NLLoc-phase file picksExport(picks, 'NLLoc', phasefile) + # remove actual NLLoc-location file to keep only the last + os.remove(nllocfile) # locate the event locate(nlloccall, ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) @@ -181,13 +184,23 @@ def autoPyLoT(inputfile): finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ nllocfile, picks, parameter.getParam('rho'), \ parameter.getParam('vp'), parameter.getParam('invdir')) + # get network moment magntiude + netMw = [] + for key in finalpicks.getpicdic(): + if finalpicks.getpicdic()[key]['P']['Mw'] is not None: + netMw.append(finalpicks.getpicdic()[key]['P']['Mw']) + netMw = np.median(netMw) + print("Network moment magnitude: %4.1f" % netMw) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## # write phase files for various location routines # HYPO71 - hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, evID) - writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + hypo71file = '%s/autoPyLoT_HYPO71.pha' % event + if finalpicks.getpicdic() is not None: + writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + else: + writephases(picks, 'HYPO71', hypo71file) endsplash = '''------------------------------------------\n' -----Finished event %s!-----\n' @@ -260,6 +273,8 @@ def autoPyLoT(inputfile): picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) # write phases to NLLoc-phase file picksExport(picks, 'NLLoc', phasefile) + # remove actual NLLoc-location file to keep only the last + os.remove(nllocfile) # locate the event locate(nlloccall, ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) @@ -280,13 +295,23 @@ def autoPyLoT(inputfile): finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ nllocfile, picks, parameter.getParam('rho'), \ parameter.getParam('vp'), parameter.getParam('invdir')) + # get network moment magntiude + netMw = [] + for key in finalpicks.getpicdic(): + if finalpicks.getpicdic()[key]['P']['Mw'] is not None: + netMw.append(finalpicks.getpicdic()[key]['P']['Mw']) + netMw = np.median(netMw) + print("Network moment magnitude: %4.1f" % netMw) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") ########################################################## # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.getParam('eventID')) - writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + if finalpicks.getpicdic() is not None: + writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + else: + writephases(picks, 'HYPO71', hypo71file) endsplash = '''------------------------------------------\n' -----Finished event %s!-----\n' From d6ae82e070e6bc5d71c692e342b9dcb607117759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 3 Dec 2015 14:57:44 +0100 Subject: [PATCH 0760/1144] Included rotation of seismograms using Obspys stream.rotation for a more reliable estimation of source spectra. --- pylot/core/analysis/magnitude.py | 294 ++++++++++++++++++------------- 1 file changed, 175 insertions(+), 119 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index d0e895c4..b2c06ca2 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -12,7 +12,7 @@ from obspy.core import Stream, UTCDateTime from pylot.core.pick.utils import getsignalwin, crossings_nonzero_all from pylot.core.util.utils import getPatternLine from scipy.optimize import curve_fit -from scipy import integrate +from scipy import integrate, signal from pylot.core.read.data import Data class Magnitude(object): @@ -200,7 +200,7 @@ class WApp(Magnitude): class M0Mw(Magnitude): ''' Method to calculate seismic moment Mo and moment magnitude Mw. - Requires results of class w0fc for calculating plateau w0 + Requires results of class calcsourcespec for calculating plateau w0 and corner frequency fc of source spectrum, respectively. Uses subfunction calcMoMw.py. Returns modified dictionary of picks including Dc-value, corner frequency fc, seismic moment Mo and @@ -212,17 +212,12 @@ class M0Mw(Magnitude): picks = self.getpicks() nllocfile = self.getNLLocfile() wfdat = self.getwfstream() - # get vertical component data only - zdat = wfdat.select(component="Z") - if len(zdat) == 0: # check for other components - zdat = wfdat.select(component="3") + self.picdic = None for key in picks: if picks[key]['P']['weight'] < 4: # select waveform - selwf = zdat.select(station=key) - # get hypocentral distance of station - # from NLLoc-location file + selwf = wfdat.select(station=key) if len(key) > 4: Ppattern = '%s ? ? ? P' % key elif len(key) == 4: @@ -230,15 +225,22 @@ class M0Mw(Magnitude): elif len(key) < 4: Ppattern = '%s ? ? ? P' % key nllocline = getPatternLine(nllocfile, Ppattern) + # get hypocentral distance, station azimuth and + # angle of incidence from NLLoc-location file delta = float(nllocline.split(None)[21]) + az = float(nllocline.split(None)[22]) + inc = float(nllocline.split(None)[24]) # call subfunction to estimate source spectrum # and to derive w0 and fc [w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \ - self.getiplot(), self.getinvdir()) + self.getinvdir(), az, inc, self.getiplot()) if w0 is not None: # call subfunction to calculate Mo and Mw - [Mo, Mw] = calcMoMw(selwf, w0, self.getrho(), self.getvp(), \ + zdat = selwf.select(component="Z") + if len(zdat) == 0: # check for other components + zdat = selwf.select(component="3") + [Mo, Mw] = calcMoMw(zdat, w0, self.getrho(), self.getvp(), \ delta, self.getinvdir()) else: Mo = None @@ -276,131 +278,180 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): -def calcsourcespec(wfstream, onset, iplot, inventory): +def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, iplot): ''' Subfunction to calculate the source spectrum and to derive from that the plateau (usually called omega0) and the corner frequency assuming Aki's omega-square source model. Has to be derived from instrument corrected displacement traces, - thus restitution and integration necessary! + thus restitution and integration necessary! Integrated traces have to be rotated + into ray-coordinate system ZNE => LQT! ''' print ("Calculating source spectrum ....") fc = None w0 = None data = Data() - z_copy = wfstream.copy() - - [corzdat, restflag] = data.restituteWFData(inventory, z_copy) + wf_copy = wfstream.copy() + [cordat, restflag] = data.restituteWFData(inventory, wf_copy) if restflag == 1: - # integrate to displacment - corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta) - z_copy[0].data = corintzdat - tr = z_copy[0] - # get window after P pulse for - # calculating source spectrum - if tr.stats.sampling_rate <= 100: - winzc = tr.stats.sampling_rate - elif tr.stats.sampling_rate > 100 and \ - tr.stats.sampling_rate <= 200: - winzc = 0.5 * tr.stats.sampling_rate - elif tr.stats.sampling_rate > 200 and \ - tr.stats.sampling_rate <= 400: - winzc = 0.2 * tr.stats.sampling_rate - elif tr.stats.sampling_rate > 400: - winzc = tr.stats.sampling_rate - tstart = UTCDateTime(tr.stats.starttime) - tonset = onset.timestamp -tstart.timestamp - impickP = tonset * tr.stats.sampling_rate - wfzc = tr.data[impickP : impickP + winzc] - # get time array - t = np.arange(0, len(tr) * tr.stats.delta, tr.stats.delta) - # calculate spectrum using only first cycles of - # waveform after P onset! - zc = crossings_nonzero_all(wfzc) - if np.size(zc) == 0 or len(zc) <= 3: - print ("Something is wrong with the waveform, " - "no zero crossings derived!") - print ("No calculation of source spectrum possible!") - plotflag = 0 - else: - plotflag = 1 - index = min([3, len(zc) - 1]) - calcwin = (zc[index] - zc[0]) * z_copy[0].stats.delta - iwin = getsignalwin(t, tonset, calcwin) - xdat = tr.data[iwin] + zdat = cordat.select(component="Z") + if len(zdat) == 0: + zdat = cordat.select(component="3") + cordat_copy = cordat.copy() + # get equal time stamps and lengths of traces + # necessary for rotation of traces + tr0start = cordat_copy[0].stats.starttime + tr0start = tr0start.timestamp + tr0end = cordat_copy[0].stats.endtime + tr0end = tr0end.timestamp + tr1start = cordat_copy[1].stats.starttime + tr1start = tr1start.timestamp + tr1end = cordat_copy[1].stats.endtime + tr1end = tr1end.timestamp + tr2start = cordat_copy[2].stats.starttime + tr2start = tr2start.timestamp + tr2end = cordat_copy[0].stats.endtime + tr2end = tr2end.timestamp + trstart = UTCDateTime(max([tr0start, tr1start, tr2start])) + trend = UTCDateTime(min([tr0end, tr1end, tr2end])) + cordat_copy.trim(trstart, trend) + minlen = min([len(cordat_copy[0]), len(cordat_copy[1]), len(cordat_copy[2])]) + cordat_copy[0].data = cordat_copy[0].data[0:minlen] + cordat_copy[1].data = cordat_copy[1].data[0:minlen] + cordat_copy[2].data = cordat_copy[2].data[0:minlen] + try: + # rotate into LQT (ray-coordindate-) system using Obspy's rotate + # L: P-wave direction + # Q: SV-wave direction + # T: SH-wave direction + LQT=cordat_copy.rotate('ZNE->LQT',azimuth, incidence) + ldat = LQT.select(component="L") + if len(ldat) == 0: + # yet Obspy's rotate can not handle channels 3/2/1 + ldat = LQT.select(component="Z") - # fft - fny = tr.stats.sampling_rate / 2 - l = len(xdat) / tr.stats.sampling_rate - n = tr.stats.sampling_rate * l # number of fft bins after Bath - # find next power of 2 of data length - m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2))) - N = int(np.power(m, 2)) - y = tr.stats.delta * np.fft.fft(xdat, N) - Y = abs(y[: N/2]) - L = (N - 1) / tr.stats.sampling_rate - f = np.arange(0, fny, 1/L) + # integrate to displacement + # unrotated vertical component (for copmarison) + inttrz = signal.detrend(integrate.cumtrapz(zdat[0].data, None, \ + zdat[0].stats.delta)) + # rotated component Z => L + Ldat = signal.detrend(integrate.cumtrapz(ldat[0].data, None, \ + ldat[0].stats.delta)) - # remove zero-frequency and frequencies above - # corner frequency of seismometer (assumed - # to be 100 Hz) - fi = np.where((f >= 1) & (f < 100)) - F = f[fi] - YY = Y[fi] - # get plateau (DC value) and corner frequency - # initial guess of plateau - w0in = np.mean(YY[0:100]) - # initial guess of corner frequency - # where spectral level reached 50% of flat level - iin = np.where(YY >= 0.5 * w0in) - Fcin = F[iin[0][np.size(iin) - 1]] + # get window after P pulse for + # calculating source spectrum + if zdat[0].stats.sampling_rate <= 100: + winzc = zdat[0].stats.sampling_rate + elif zdat[0].stats.sampling_rate > 100 and \ + zdat[0].stats.sampling_rate <= 200: + winzc = 0.5 * zdat[0].stats.sampling_rate + elif zdat[0].stats.sampling_rate > 200 and \ + zdat[0].stats.sampling_rate <= 400: + winzc = 0.2 * zdat[0].stats.sampling_rate + elif zdat[0].stats.sampling_rate > 400: + winzc = zdat[0].stats.sampling_rate + tstart = UTCDateTime(zdat[0].stats.starttime) + tonset = onset.timestamp -tstart.timestamp + impickP = tonset * zdat[0].stats.sampling_rate + wfzc = Ldat[impickP : impickP + winzc] + # get time array + t = np.arange(0, len(inttrz) * zdat[0].stats.delta, \ + zdat[0].stats.delta) + # calculate spectrum using only first cycles of + # waveform after P onset! + zc = crossings_nonzero_all(wfzc) + if np.size(zc) == 0 or len(zc) <= 3: + print ("calcsourcespec: Something is wrong with the waveform, " + "no zero crossings derived!") + print ("No calculation of source spectrum possible!") + plotflag = 0 + else: + plotflag = 1 + index = min([3, len(zc) - 1]) + calcwin = (zc[index] - zc[0]) * zdat[0].stats.delta + iwin = getsignalwin(t, tonset, calcwin) + xdat = Ldat[iwin] - # use of implicit scipy otimization function - fit = synthsourcespec(F, w0in, Fcin) - [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin]) - w01 = optspecfit[0] - fc1 = optspecfit[1] - print ("w0fc: Determined w0-value: %e m/Hz, \n" - "Determined corner frequency: %f Hz" % (w01, fc1)) + # fft + fny = zdat[0].stats.sampling_rate / 2 + l = len(xdat) / zdat[0].stats.sampling_rate + # number of fft bins after Bath + n = zdat[0].stats.sampling_rate * l + # find next power of 2 of data length + m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2))) + N = int(np.power(m, 2)) + y = zdat[0].stats.delta * np.fft.fft(xdat, N) + Y = abs(y[: N/2]) + L = (N - 1) / zdat[0].stats.sampling_rate + f = np.arange(0, fny, 1/L) + + # remove zero-frequency and frequencies above + # corner frequency of seismometer (assumed + # to be 100 Hz) + fi = np.where((f >= 1) & (f < 100)) + F = f[fi] + YY = Y[fi] + # get plateau (DC value) and corner frequency + # initial guess of plateau + w0in = np.mean(YY[0:100]) + # initial guess of corner frequency + # where spectral level reached 50% of flat level + iin = np.where(YY >= 0.5 * w0in) + Fcin = F[iin[0][np.size(iin) - 1]] + + # use of implicit scipy otimization function + fit = synthsourcespec(F, w0in, Fcin) + [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin]) + w01 = optspecfit[0] + fc1 = optspecfit[1] + print ("calcsourcespec: Determined w0-value: %e m/Hz, \n" + "Determined corner frequency: %f Hz" % (w01, fc1)) - # use of conventional fitting - [w02, fc2] = fitSourceModel(F, YY.real, Fcin, iplot) + # use of conventional fitting + [w02, fc2] = fitSourceModel(F, YY.real, Fcin, iplot) - # get w0 and fc as median - w0 = np.median([w01, w02]) - fc = np.median([fc1, fc2]) - print("w0fc: Using w0-value = %e m/Hz and fc = %f Hz" % (w0, fc)) + # get w0 and fc as median + w0 = np.median([w01, w02]) + fc = np.median([fc1, fc2]) + print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % (w0, fc)) + + except TypeError as er: + raise TypeError('''{0}'''.format(er)) - if iplot > 1: - f1 = plt.figure() - plt.subplot(2,1,1) - # show displacement in mm - plt.plot(t, np.multiply(tr, 1000), 'k') - if plotflag == 1: - plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') - plt.title('Seismogram and P Pulse, Station %s-%s' \ - % (tr.stats.station, tr.stats.channel)) - else: - plt.title('Seismogram, Station %s-%s' \ - % (tr.stats.station, tr.stats.channel)) - plt.xlabel('Time since %s' % tr.stats.starttime) - plt.ylabel('Displacement [mm]') + if iplot > 1: + f1 = plt.figure() + tLdat = np.arange(0, len(Ldat) * zdat[0].stats.delta, \ + zdat[0].stats.delta) + plt.subplot(2,1,1) + # show displacement in mm + p1, = plt.plot(t, np.multiply(inttrz, 1000), 'k') + p2, = plt.plot(tLdat, np.multiply(Ldat, 1000)) + plt.legend([p1, p2], ['Displacement', 'Rotated Displacement']) + if plotflag == 1: + plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') + plt.title('Seismogram and P Pulse, Station %s-%s' \ + % (zdat[0].stats.station, zdat[0].stats.channel)) + else: + plt.title('Seismogram, Station %s-%s' \ + % (zdat[0].stats.station, zdat[0].stats.channel)) + plt.xlabel('Time since %s' % zdat[0].stats.starttime) + plt.ylabel('Displacement [mm]') - if plotflag == 1: - plt.subplot(2,1,2) - plt.loglog(f, Y.real, 'k') - plt.loglog(F, YY.real) - plt.loglog(F, fit, 'g') - plt.loglog([fc, fc], [w0/100, w0], 'g') - plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \ - % (w0, fc)) - plt.xlabel('Frequency [Hz]') - plt.ylabel('Amplitude [m/Hz]') - plt.grid() - plt.show() - raw_input() - plt.close(f1) + if plotflag == 1: + plt.subplot(2,1,2) + plt.loglog(f, Y.real, 'k') + plt.loglog(F, YY.real) + plt.loglog(F, fit, 'g') + plt.loglog([fc, fc], [w0/100, w0], 'g') + plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \ + % (w0, fc)) + plt.xlabel('Frequency [Hz]') + plt.ylabel('Amplitude [m/Hz]') + plt.grid() + plt.show() + raw_input() + plt.close(f1) return w0, fc @@ -474,8 +525,13 @@ def fitSourceModel(f, S, fc0, iplot): STD.append(stddc + stdFC) # get best found w0 anf fc from minimum - fc = fc[np.argmin(STD)] - w0 = w0[np.argmin(STD)] + if len(STD) > 0: + fc = fc[np.argmin(STD)] + w0 = w0[np.argmin(STD)] + elif len(STD) == 0: + fc = fc0 + w0 = max(S) + print("fitSourceModel: best fc: %fHz, best w0: %e m/Hz" \ % (fc, w0)) From 67e37fe53007bb2d505bf01fe827d1907e5e0896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 3 Dec 2015 14:59:39 +0100 Subject: [PATCH 0761/1144] Initialization of picks dictionary including Mo, Mw, w0 and fc. --- pylot/core/pick/autopick.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index b6c1a11b..934e35b0 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -204,7 +204,7 @@ def autopickstation(wfstream, pickparam, verbose=False): z_copy[0].data = tr_filt.data zne = z_copy if len(ndat) == 0 or len(edat) == 0: - msg = 'One or more horizontal components missing!\nSignal ' \ + msg = 'One or more horizontal component(s) missing!\nSignal ' \ 'length only checked on vertical component!\n' \ 'Decreasing minsiglengh from {0} to ' \ '{1}'.format(minsiglength, minsiglength / 2) @@ -424,6 +424,7 @@ def autopickstation(wfstream, pickparam, verbose=False): 'SNR: {1}\nGo on with refined picking ...\n' \ 'autopickstation: re-filtering horizontal traces ' \ '...'.format(aicarhpick.getSlope(), aicarhpick.getSNR()) + if verbose: print(msg) # re-calculate CF from re-filtered trace in vicinity of initial # onset cuttimesh2 = [round(aicarhpick.getpick() - Srecalcwin), @@ -774,7 +775,8 @@ def autopickstation(wfstream, pickparam, verbose=False): # for P phase phase = 'P' phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, - 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM} + 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM, + 'w0': None, 'fc': None, 'Mo': None, 'Mw': None} picks = {phase: phasepick} # add P marker picks[phase]['marked'] = Pmarker From 2b90c73f9f5f46bc0e882daa312df6f0f1f62a73 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 3 Dec 2015 15:25:04 +0100 Subject: [PATCH 0762/1144] changed quotes for consistency --- pylot/core/pick/autopick.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index b6c1a11b..77ac893a 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -335,15 +335,15 @@ def autopickstation(wfstream, pickparam, verbose=False): Sflag = 0 else: - print("autopickstation: No vertical component data available!, " - "Skipping station!") + print('autopickstation: No vertical component data available!, ' + 'Skipping station!') if edat is not None and ndat is not None and len(edat) > 0 and len( ndat) > 0 and Pweight < 4: - msg = "Go on picking S onset ...\n" \ - "##################################################\n" \ - "Working on S onset of station {0}\nFiltering horizontal " \ - "traces ...".format(edat[0].stats.station) + msg = 'Go on picking S onset ...\n' \ + '##################################################\n' \ + 'Working on S onset of station {0}\nFiltering horizontal ' \ + 'traces ...'.format(edat[0].stats.station) if verbose: print(msg) # determine time window for calculating CF after P onset cuttimesh = [round(max([mpickP + sstart, 0])), From 5f8018a8dc15c6a9526bc5687c5bc34cab2a184c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 3 Dec 2015 16:02:41 +0100 Subject: [PATCH 0763/1144] Modified some parameters. --- autoPyLoT_local.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 55361e77..afb5d49f 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -6,8 +6,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2013.02_Insheim #database# %name of data base -e0019.048.13 #eventID# %event ID for single event processing +2015.09_Insheim #database# %name of data base + #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything @@ -25,8 +25,8 @@ AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file %(returns 'eventID_outpatter') %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #parameters for seismic moment estimation# -3000 #vp# %average P-wave velocity -2600 #rho# %rock density [kg/m^3] +3530 #vp# %average P-wave velocity +2500 #rho# %average rock density [kg/m^3] 300 #Qp# %quality factor for P waves %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities From 7c490d5e1f22f8acab8c6a72d29719866a9a1613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 3 Dec 2015 16:15:26 +0100 Subject: [PATCH 0764/1144] Little changes. --- autoPyLoT_local.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index afb5d49f..0e379a32 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -6,8 +6,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2015.09_Insheim #database# %name of data base - #eventID# %event ID for single event processing +2013.02_Insheim #database# %name of data base +e0019.048.13 #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything From 74794061f69fb226c5c428a403b39e6885f7672b Mon Sep 17 00:00:00 2001 From: sebastianp Date: Thu, 3 Dec 2015 17:20:05 +0100 Subject: [PATCH 0765/1144] alphabetical sorting of functions and editing docstring --- pylot/core/util/utils.py | 556 +++++++++++++++++++++------------------ 1 file changed, 299 insertions(+), 257 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 137ed24d..e4e856f9 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,165 +10,60 @@ import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope -def runProgram(cmd, parameter=None): - """ - run an external program specified by cmd with parameters input returning the - stdout output - - :param cmd: name of the command to run - :type cmd: str - :param parameter: filename of parameter file or parameter string - :type parameter: str - :return: stdout output - :rtype: str - """ - - if parameter: - cmd.strip() - cmd += ' %s 2>&1' % parameter - - output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), - shell = True) - -def isSorted(iterable): - return sorted(iterable) == iterable - -def fnConstructor(s): - if type(s) is str: - s = s.split(':')[-1] - else: - s = getHash(UTCDateTime()) - - badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') - badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)') - - fn = badchars.sub('_', s) - - if badsuffix.match(fn): - fn = '_' + fn - return fn - - -def getLogin(): - return pwd.getpwuid(os.getuid())[0] - - -def getHash(time): +def createAmplitude(pickID, amp, unit, category, cinfo): ''' - :param time: time object for which a hash should be calculated - :type time: :class: `~obspy.core.utcdatetime.UTCDateTime` object - :return: str + + :param pickID: + :param amp: + :param unit: + :param category: + :param cinfo: + :return: ''' - hg = hashlib.sha1() - hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) - return hg.hexdigest() + amplitude = ope.Amplitude() + amplitude.creation_info = cinfo + amplitude.generic_amplitude = amp + amplitude.unit = ope.AmplitudeUnit(unit) + amplitude.type = ope.AmplitudeCategory(category) + amplitude.pick_id = pickID + return amplitude +def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): + ''' + createArrival - function to create an Obspy Arrival -def getOwner(fn): - return pwd.getpwuid(os.stat(fn).st_uid).pw_name - -def getPatternLine(fn, pattern): - """ - Takes a file name and a pattern string to search for in the file and - returns the first line which contains the pattern string otherwise None. - - :param fn: file name - :type fn: str - :param pattern: pattern string to search for - :type pattern: str - :return: the complete line containing pattern or None - - >>> getPatternLine('utils.py', 'python') - '#!/usr/bin/env python\\n' - >>> print(getPatternLine('version.py', 'palindrome')) - None - """ - fobj = open(fn, 'r') - for line in fobj.readlines(): - if pattern in line: - fobj.close() - return line - - return None - - -def prepTimeAxis(stime, trace): - nsamp = trace.stats.npts - srate = trace.stats.sampling_rate - tincr = trace.stats.delta - etime = stime + nsamp / srate - time_ax = np.arange(stime, etime, tincr) - if len(time_ax) < nsamp: - print 'elongate time axes by one datum' - time_ax = np.arange(stime, etime + tincr, tincr) - elif len(time_ax) > nsamp: - print 'shorten time axes by one datum' - time_ax = np.arange(stime, etime - tincr, tincr) - if len(time_ax) != nsamp: - raise ValueError('{0} samples of data \n ' - '{1} length of time vector \n' - 'delta: {2}'.format(nsamp, len(time_ax), tincr)) - return time_ax - - -def scaleWFData(data, factor=None, components='all'): - """ - produce scaled waveforms from given waveform data and a scaling factor, - waveform may be selected by their components name - :param data: waveform data to be scaled - :type data: `~obspy.core.stream.Stream` object - :param factor: scaling factor - :type factor: float - :param components: components labels for the traces in data to be scaled by - the scaling factor (optional, default: 'all') - :type components: tuple - :return: scaled waveform data - :rtype: `~obspy.core.stream.Stream` object - """ - if components is not 'all': - for comp in components: - if factor is None: - max_val = np.max(np.abs(data.select(component=comp)[0].data)) - data.select(component=comp)[0].data /= 2 * max_val - else: - data.select(component=comp)[0].data /= 2 * factor + :param pickresID: Resource identifier of the created pick + :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + :param azimuth: azimuth between source and receiver + :type azimuth: float or int, optional + :param dist: distance between source and receiver + :type dist: float or int, optional + :return: An ObsPy :class: `~obspy.core.event.Arrival` object + ''' + arrival = ope.Arrival() + arrival.creation_info = cinfo + arrival.pick_id = pickresID + arrival.phase = phase + if azimuth is not None: + arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360. else: - for tr in data: - if factor is None: - max_val = float(np.max(np.abs(tr.data))) - tr.data /= 2 * max_val - else: - tr.data /= 2 * factor - - return data - -def demeanTrace(trace, window): - """ - returns the DATA where each trace is demean by the average value within - WINDOW - :param trace: waveform trace object - :type trace: `~obspy.core.stream.Trace` - :param inoise: range of indices of DATA within the WINDOW - :type window: tuple - :return: trace - :rtype: `~obspy.core.stream.Trace` - """ - trace.data -= trace.data[window].mean() - return trace - - -def getGlobalTimes(stream): - min_start = UTCDateTime() - max_end = None - for trace in stream: - if trace.stats.starttime < min_start: - min_start = trace.stats.starttime - if max_end is None or trace.stats.endtime > max_end: - max_end = trace.stats.endtime - return min_start, max_end - + arrival.azimuth = azimuth + arrival.distance = dist + return arrival def createCreationInfo(agency_id=None, creation_time=None, author=None): + ''' + + :param agency_id: + :param creation_time: + :param author: + :return: + ''' if author is None: author = getLogin() if creation_time is None: @@ -176,57 +71,6 @@ def createCreationInfo(agency_id=None, creation_time=None, author=None): return ope.CreationInfo(agency_id=agency_id, author=author, creation_time=creation_time) - -def createResourceID(timetohash, restype, authority_id=None, hrstr=None): - ''' - - :param timetohash: - :param restype: type of the resource, e.g. 'orig', 'earthquake' ... - :type restype: str - :param authority_id: name of the institution carrying out the processing - :type authority_id: str, optional - :return: - ''' - assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ - "UTCDateTime object" - hid = getHash(timetohash) - if hrstr is None: - resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) - else: - resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) - if authority_id is not None: - resID.convertIDToQuakeMLURI(authority_id=authority_id) - return resID - - -def createOrigin(origintime, cinfo, latitude, longitude, depth): - ''' - createOrigin - function to create an ObsPy Origin - :param origintime: the origins time of occurence - :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object - :param latitude: latitude in decimal degree of the origins location - :type latitude: float - :param longitude: longitude in decimal degree of the origins location - :type longitude: float - :param depth: hypocentral depth of the origin - :type depth: float - :return: An ObsPy :class: `~obspy.core.event.Origin` object - ''' - - assert isinstance(origintime, UTCDateTime), "origintime has to be " \ - "a UTCDateTime object, but " \ - "actually is of type " \ - "'%s'" % type(origintime) - - origin = ope.Origin() - origin.time = origintime - origin.creation_info = cinfo - origin.latitude = latitude - origin.longitude = longitude - origin.depth = depth - return origin - - def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, authority_id=None): ''' @@ -271,14 +115,60 @@ def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, event.origins = [o] return event +def createMagnitude(originID, cinfo): + ''' + createMagnitude - function to create an ObsPy Magnitude object + :param originID: + :type originID: + :param cinfo: + :type cinfo: + :return: + ''' + magnitude = ope.Magnitude() + magnitude.creation_info = cinfo + magnitude.origin_id = originID + return magnitude + +def createOrigin(origintime, cinfo, latitude, longitude, depth): + ''' + createOrigin - function to create an ObsPy Origin + :param origintime: the origins time of occurence + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :param cinfo: + :type cinfo: + :param latitude: latitude in decimal degree of the origins location + :type latitude: float + :param longitude: longitude in decimal degree of the origins location + :type longitude: float + :param depth: hypocentral depth of the origin + :type depth: float + :return: An ObsPy :class: `~obspy.core.event.Origin` object + ''' + + assert isinstance(origintime, UTCDateTime), "origintime has to be " \ + "a UTCDateTime object, but " \ + "actually is of type " \ + "'%s'" % type(origintime) + + origin = ope.Origin() + origin.time = origintime + origin.creation_info = cinfo + origin.latitude = latitude + origin.longitude = longitude + origin.depth = depth + return origin def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, wfseedstr, authority_id): ''' createPick - function to create an ObsPy Pick + :param origintime: + :type origintime: :param picknum: number of the created pick :type picknum: int + :param picktime: + :type picktime: :param eventnum: human-readable event identifier :type eventnum: str :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object @@ -306,64 +196,43 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') return pick - -def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): +def createResourceID(timetohash, restype, authority_id=None, hrstr=None): ''' - createArrival - function to create an Obspy Arrival - :param pickresID: Resource identifier of the created pick - :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object - :param eventnum: human-readable event identifier - :type eventnum: str - :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object - holding information on the creation of the returned object - :type cinfo: :class: `~obspy.core.event.CreationInfo` object - :param phase: name of the arrivals seismic phase - :type phase: str - :param station: name of the station at which the seismic phase has been - picked - :type station: str + + :param timetohash: + :type timetohash + :param restype: type of the resource, e.g. 'orig', 'earthquake' ... + :type restype: str :param authority_id: name of the institution carrying out the processing - :type authority_id: str - :param azimuth: azimuth between source and receiver - :type azimuth: float or int, optional - :param dist: distance between source and receiver - :type dist: float or int, optional - :return: An ObsPy :class: `~obspy.core.event.Arrival` object - ''' - arrival = ope.Arrival() - arrival.creation_info = cinfo - arrival.pick_id = pickresID - arrival.phase = phase - if azimuth is not None: - arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360. - else: - arrival.azimuth = azimuth - arrival.distance = dist - return arrival - - -def createMagnitude(originID, cinfo): - ''' - createMagnitude - function to create an ObsPy Magnitude object - :param originID: - :param cinfo: - :param authority_id: + :type authority_id: str, optional + :param hrstr: + :type hrstr: :return: ''' - magnitude = ope.Magnitude() - magnitude.creation_info = cinfo - magnitude.origin_id = originID - return magnitude + assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ + "UTCDateTime object" + hid = getHash(timetohash) + if hrstr is None: + resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) + else: + resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) + if authority_id is not None: + resID.convertIDToQuakeMLURI(authority_id=authority_id) + return resID - -def createAmplitude(pickID, amp, unit, category, cinfo): - amplitude = ope.Amplitude() - amplitude.creation_info = cinfo - amplitude.generic_amplitude = amp - amplitude.unit = ope.AmplitudeUnit(unit) - amplitude.type = ope.AmplitudeCategory(category) - amplitude.pick_id = pickID - return amplitude +def demeanTrace(trace, window): + """ + returns the DATA where each trace is demean by the average value within + WINDOW + :param trace: waveform trace object + :type trace: `~obspy.core.stream.Trace` + :param window: + :type window: tuple + :return: trace + :rtype: `~obspy.core.stream.Trace` + """ + trace.data -= trace.data[window].mean() + return trace def findComboBoxIndex(combo_box, val): """ @@ -372,11 +241,184 @@ def findComboBoxIndex(combo_box, val): :param combo_box: Combo box object. :type combo_box: QComboBox :param val: Name of a combo box to search for. + :type val: :return: index value of item with name val or 0 """ - return combo_box.findText(val) if combo_box.findText(val) is not -1 else 0 +def fnConstructor(s): + ''' + + :param s: + :type s: + :return: + ''' + if type(s) is str: + s = s.split(':')[-1] + else: + s = getHash(UTCDateTime()) + + badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$') + badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)') + + fn = badchars.sub('_', s) + + if badsuffix.match(fn): + fn = '_' + fn + return fn + +def getGlobalTimes(stream): + ''' + + :param stream: + :type stream + :return: + ''' + min_start = UTCDateTime() + max_end = None + for trace in stream: + if trace.stats.starttime < min_start: + min_start = trace.stats.starttime + if max_end is None or trace.stats.endtime > max_end: + max_end = trace.stats.endtime + return min_start, max_end + +def getHash(time): + ''' + :param time: time object for which a hash should be calculated + :type time: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :return: str + ''' + hg = hashlib.sha1() + hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) + return hg.hexdigest() + +def getLogin(): + ''' + + :return: + ''' + return pwd.getpwuid(os.getuid())[0] + +def getOwner(fn): + ''' + + :param fn: + :type fn: + :return: + ''' + return pwd.getpwuid(os.stat(fn).st_uid).pw_name + +def getPatternLine(fn, pattern): + """ + Takes a file name and a pattern string to search for in the file and + returns the first line which contains the pattern string otherwise None. + + :param fn: file name + :type fn: str + :param pattern: pattern string to search for + :type pattern: str + :return: the complete line containing pattern or None + + >>> getPatternLine('utils.py', 'python') + '#!/usr/bin/env python\\n' + >>> print(getPatternLine('version.py', 'palindrome')) + None + """ + fobj = open(fn, 'r') + for line in fobj.readlines(): + if pattern in line: + fobj.close() + return line + + return None + +def isSorted(iterable): + ''' + + :param iterable: + :type iterable: + :return: + ''' + return sorted(iterable) == iterable + +def prepTimeAxis(stime, trace): + ''' + + :param stime: + :type stime: + :param trace: + :type trace: + :return: + ''' + nsamp = trace.stats.npts + srate = trace.stats.sampling_rate + tincr = trace.stats.delta + etime = stime + nsamp / srate + time_ax = np.arange(stime, etime, tincr) + if len(time_ax) < nsamp: + print 'elongate time axes by one datum' + time_ax = np.arange(stime, etime + tincr, tincr) + elif len(time_ax) > nsamp: + print 'shorten time axes by one datum' + time_ax = np.arange(stime, etime - tincr, tincr) + if len(time_ax) != nsamp: + raise ValueError('{0} samples of data \n ' + '{1} length of time vector \n' + 'delta: {2}'.format(nsamp, len(time_ax), tincr)) + return time_ax + +def scaleWFData(data, factor=None, components='all'): + """ + produce scaled waveforms from given waveform data and a scaling factor, + waveform may be selected by their components name + :param data: waveform data to be scaled + :type data: `~obspy.core.stream.Stream` object + :param factor: scaling factor + :type factor: float + :param components: components labels for the traces in data to be scaled by + the scaling factor (optional, default: 'all') + :type components: tuple + :return: scaled waveform data + :rtype: `~obspy.core.stream.Stream` object + """ + if components is not 'all': + for comp in components: + if factor is None: + max_val = np.max(np.abs(data.select(component=comp)[0].data)) + data.select(component=comp)[0].data /= 2 * max_val + else: + data.select(component=comp)[0].data /= 2 * factor + else: + for tr in data: + if factor is None: + max_val = float(np.max(np.abs(tr.data))) + tr.data /= 2 * max_val + else: + tr.data /= 2 * factor + + return data + +def runProgram(cmd, parameter=None): + """ + run an external program specified by cmd with parameters input returning the + stdout output + + :param cmd: name of the command to run + :type cmd: str + :param parameter: filename of parameter file or parameter string + :type parameter: str + :return: stdout output + :rtype: str + """ + + if parameter: + cmd.strip() + cmd += ' %s 2>&1' % parameter + + output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), + shell = True) + if __name__ == "__main__": import doctest doctest.testmod() From 28276d1f8c5b9fd88e8c041af891029595f55e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Dec 2015 14:39:17 +0100 Subject: [PATCH 0766/1144] Set default path for autoPyLoT_local.in to /home/user/.pylot using os.expanduser("~"). --- QtPyLoT.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 10984721..e8f4e6f9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -24,6 +24,7 @@ https://www.iconfinder.com/iconsets/flavour """ import os, sys +from os.path import expanduser import matplotlib matplotlib.use('Qt4Agg') @@ -670,7 +671,8 @@ class MainWindow(QMainWindow): self.logDockWidget.setWidget(self.listWidget) self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget) self.addListItem('loading default values for local data ...') - autopick_parameter = AutoPickParameter('autoPyLoT_local.in') + home = expanduser("~") + autopick_parameter = AutoPickParameter('%s/.pylot/autoPyLoT_local.in' % home) self.addListItem(str(autopick_parameter)) # Create the worker thread and run it From 0c7c5645b6ce4db408605ddb85bca41f278f49dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Dec 2015 16:05:53 +0100 Subject: [PATCH 0767/1144] Implemented correction for attenuation in calcsourcespek. --- pylot/core/analysis/magnitude.py | 42 ++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index b2c06ca2..e6233b25 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -23,7 +23,7 @@ class Magnitude(object): ''' def __init__(self, wfstream, To, pwin, iplot, NLLocfile=None, \ - picks=None, rho=None, vp=None, invdir=None): + picks=None, rho=None, vp=None, Qp=None, invdir=None): ''' :param: wfstream :type: `~obspy.core.stream.Stream @@ -66,6 +66,7 @@ class Magnitude(object): self.setrho(rho) self.setpicks(picks) self.setvp(vp) + self.setQp(Qp) self.setinvdir(invdir) self.calcwapp() self.calcsourcespec() @@ -114,6 +115,12 @@ class Magnitude(object): def getvp(self): return self.vp + def setQp(self, Qp): + self.Qp = Qp + + def getQp(self): + return self.Qp + def setpicks(self, picks): self.picks = picks @@ -233,7 +240,8 @@ class M0Mw(Magnitude): # call subfunction to estimate source spectrum # and to derive w0 and fc [w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \ - self.getinvdir(), az, inc, self.getiplot()) + self.getinvdir(), az, inc, self.getQp(), \ + self.getiplot()) if w0 is not None: # call subfunction to calculate Mo and Mw @@ -278,7 +286,7 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): -def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, iplot): +def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, Qp, iplot): ''' Subfunction to calculate the source spectrum and to derive from that the plateau (usually called omega0) and the corner frequency assuming Aki's omega-square @@ -288,6 +296,13 @@ def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, iplot): ''' print ("Calculating source spectrum ....") + # get Q value + qu = Qp.split('f**') + # constant Q + Q = int(qu[0]) + # A, i.e. power of frequency + A = float(qu[1]) + fc = None w0 = None data = Data() @@ -392,24 +407,26 @@ def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, iplot): fi = np.where((f >= 1) & (f < 100)) F = f[fi] YY = Y[fi] + # correction for attenuation + YYcor = YY.real*Q**A # get plateau (DC value) and corner frequency # initial guess of plateau - w0in = np.mean(YY[0:100]) + w0in = np.mean(YYcor[0:100]) # initial guess of corner frequency # where spectral level reached 50% of flat level - iin = np.where(YY >= 0.5 * w0in) + iin = np.where(YYcor >= 0.5 * w0in) Fcin = F[iin[0][np.size(iin) - 1]] # use of implicit scipy otimization function fit = synthsourcespec(F, w0in, Fcin) - [optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin]) + [optspecfit, pcov] = curve_fit(synthsourcespec, F, YYcor, [w0in, Fcin]) w01 = optspecfit[0] fc1 = optspecfit[1] print ("calcsourcespec: Determined w0-value: %e m/Hz, \n" "Determined corner frequency: %f Hz" % (w01, fc1)) # use of conventional fitting - [w02, fc2] = fitSourceModel(F, YY.real, Fcin, iplot) + [w02, fc2] = fitSourceModel(F, YYcor, Fcin, iplot) # get w0 and fc as median w0 = np.median([w01, w02]) @@ -440,10 +457,15 @@ def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, iplot): if plotflag == 1: plt.subplot(2,1,2) - plt.loglog(f, Y.real, 'k') - plt.loglog(F, YY.real) - plt.loglog(F, fit, 'g') + p1, = plt.loglog(f, Y.real, 'k') + p2, = plt.loglog(F, YY.real) + p3, = plt.loglog(F, YYcor, 'r') + p4, = plt.loglog(F, fit, 'g') plt.loglog([fc, fc], [w0/100, w0], 'g') + plt.legend([p1, p2, p3, p4], ['Raw Spectrum', \ + 'Used Raw Spectrum', \ + 'Q-Corrected Spectrum', \ + 'Fit to Spectrum']) plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \ % (w0, fc)) plt.xlabel('Frequency [Hz]') From 1312d06ccf4a054545a7f12246fbe1a0ed6002e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Dec 2015 16:07:36 +0100 Subject: [PATCH 0768/1144] Corrected for modified class MoMw, which needs additional parameter Qp. --- autoPyLoT.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 387090cd..658739fb 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -142,7 +142,8 @@ def autoPyLoT(inputfile): # calculating seismic moment Mo and moment magnitude Mw finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('invdir')) + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -183,7 +184,8 @@ def autoPyLoT(inputfile): # calculating seismic moment Mo and moment magnitude Mw finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('invdir')) + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) # get network moment magntiude netMw = [] for key in finalpicks.getpicdic(): @@ -253,7 +255,8 @@ def autoPyLoT(inputfile): # calculating seismic moment Mo and moment magnitude Mw finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('invdir')) + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -294,7 +297,8 @@ def autoPyLoT(inputfile): # calculating seismic moment Mo and moment magnitude Mw finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('invdir')) + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) # get network moment magntiude netMw = [] for key in finalpicks.getpicdic(): From 0d18e35f6a1abf3ff8ff25a694bdfd0b9c35fbc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 4 Dec 2015 16:08:49 +0100 Subject: [PATCH 0769/1144] New parameter Qp to correct for attenuation, i.e. frequency dependent Q value. --- autoPyLoT_local.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 0e379a32..1b845cbc 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -27,14 +27,13 @@ AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file #parameters for seismic moment estimation# 3530 #vp# %average P-wave velocity 2500 #rho# %average rock density [kg/m^3] -300 #Qp# %quality factor for P waves +300f**0.8 #Qp# %quality factor for P waves (Qp*f^a) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities 6 #pmin# %minimum required P picks for location 4 #p0min# %minimum required P picks for location if at least %3 excellent P picks are found 2 #smin# %minimum required S picks for location -/home/ludger/bin/run_HYPOSAT4autoPILOT.csh #cshellp# %path and name of c-shell script to run location routine 7.6 8.5 #blon# %longitude bounding for location map 49 49.4 #blat# %lattitude bounding for location map %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From d5b277db99369998f8cd6d568ef1c9c6a33a0f57 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 7 Dec 2015 11:20:30 +0100 Subject: [PATCH 0770/1144] implemented cleanBySPE and plotSPE --- pylot/core/active/activeSeismoPick.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index c90ce9a0..630e510d 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -9,6 +9,11 @@ class Survey(object): ''' The Survey Class contains all shots [type: seismicshot] of a survey as well as the aquisition geometry and the topography. + + It contains methods to pick all traces of all shots. + + It contains several methods e.g. for plotting of all picks (and postprocessing), + creating plots for all shots. ''' self.data = {} self._topography = None @@ -82,6 +87,9 @@ class Survey(object): outfile.close() def _updateShots(self): + ''' + Removes traces that do not exist in the dataset for any reason. + ''' filename = 'updateShots.out' count = 0; countTraces = 0 for shot in self.data.values(): @@ -148,6 +156,24 @@ class Survey(object): print('Picked %s / %s traces (%d %%)\n' %(pickedtraces, ntraces, float(pickedtraces)/float(ntraces)*100.)) + def cleanBySPE(self, maxSPE): + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if shot.getFlag(traceID) == 1: + if shot.getSymmetricPickError(traceID) > maxSPE: + shot.setFlag(traceID, 0) + + def plotSPE(self): + import matplotlib.pyplot as plt + spe = [] + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if shot.getFlag(traceID) == 1: + spe.append(shot.getSymmetricPickError(traceID)) + spe.sort() + plt.plot(spe, label = 'SPE') + plt.ylabel('Symmetric Pickerror') + plt.legend() def recover(self): ''' From 1a9f68f7413fe5b3e51bae86d86bce2eaa54d491 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 7 Dec 2015 11:21:11 +0100 Subject: [PATCH 0771/1144] bugfixes (minSNR was 1, problem in for loop with break?) --- pylot/core/active/surveyUtils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 1e8b9927..12f5cc39 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -41,7 +41,7 @@ def plotFittedSNR(dists, snrthresholds, snrs): import matplotlib.pyplot as plt plt.interactive(True) fig = plt.figure() - plt.plot(dists, snrs, '.', markersize = 0.5, label = 'SNR values') + plt.plot(dists, snrs, '.', markersize = 1.0, label = 'SNR values') plt.plot(dists, snrthresholds, 'r.', markersize = 1, label = 'Fitted threshold') plt.xlabel('Distance[m]') plt.ylabel('SNR') @@ -56,12 +56,12 @@ def setFittedSNR(shot_dict, shiftdist = 30, shiftSNR = 100, p1 = 0.004, p2 = -0. for traceID in shot.getTraceIDlist(): ### IMPROVE dist = shot.getDistance(traceID) + shiftdist snrthreshold = (1/(fit_fn(dist)**2)) - shiftSNR * np.exp(-0.05 * dist) - if snrthreshold <= 1: + if snrthreshold < minSNR: print('WARNING: SNR threshold %s lower %s. Set SNR threshold to %s.' %(snrthreshold, minSNR, minSNR)) shot.setSNRthreshold(traceID, minSNR) - break - shot.setSNRthreshold(traceID, snrthreshold) + else: + shot.setSNRthreshold(traceID, snrthreshold) print "setFittedSNR: Finished setting of fitted SNR-threshold" From cdf924e8c2647c0efa0ebeaddd838f0b9e3a5137 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 7 Dec 2015 11:47:57 +0100 Subject: [PATCH 0772/1144] bugfix: earliest and latest pp not changed when repicking manually --- pylot/core/active/seismicshot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 6af61a2a..7306392a 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -616,6 +616,8 @@ class SeismicShot(object): def onclick(event): self.setPick(traceID, event.xdata) + if self.getSNR(traceID)[0] > 1: + self.setEarllatepick(traceID) self._drawStream(traceID, refresh = True) self._drawCFs(traceID, folm, refresh = True) fig.canvas.mpl_disconnect(self.traces4plot[traceID]['cid']) From cb3f9804f95f6b41ccfba2de8b34a511370b0192 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 8 Dec 2015 11:34:55 +0100 Subject: [PATCH 0773/1144] implemented setting manual picks from file --- pylot/core/active/seismicshot.py | 53 +++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 7306392a..cc8c3cd1 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -317,6 +317,10 @@ class SeismicShot(object): self.getPickIncludeRemoved(traceID), stealthMode = True) + if self.pick[traceID]['epp'] < 0: + self.pick[traceID]['epp'] + #print('setEarllatepick: Set epp to 0 because it was < 0') + # TEST OF 1/2 PICKERROR # self.pick[traceID]['spe'] *= 0.5 # TEST OF 1/2 PICKERROR @@ -448,26 +452,47 @@ class SeismicShot(object): if len(traceID_list) > 0: return traceID_list - def setManualPicks(self, traceID, picklist): ########## picklist momentan nicht allgemein, nur testweise benutzt ########## - ''' - Sets the manual picks for a receiver with the ID == traceID for comparison. + # def setManualPicks(self, traceID, picklist): ########## picklist momentan nicht allgemein, nur testweise benutzt ########## + # ''' + # Sets the manual picks for a receiver with the ID == traceID for comparison. - :param: traceID - :type: int + # :param: traceID + # :type: int - :param: picklist, list containing the manual picks (mostlikely, earliest, latest). - :type: list - ''' - picks = picklist[traceID - 1].split() - mostlikely = float(picks[1]) - earliest = float(picks[2]) - latest = float(picks[3]) + # :param: picklist, list containing the manual picks (mostlikely, earliest, latest). + # :type: list + # ''' + # picks = picklist[traceID - 1].split() + # mostlikely = float(picks[1]) + # earliest = float(picks[2]) + # latest = float(picks[3]) - if not self.manualpicks.has_key(traceID): - self.manualpicks[traceID] = (mostlikely, earliest, latest) + # if not self.manualpicks.has_key(traceID): + # self.manualpicks[traceID] = (mostlikely, earliest, latest) #else: # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) + def setManualPicksFromFile(self, directory = 'picks'): + ''' + Read manual picks from *.pck file. + The * must be identical with the shotnumber. + ''' + if directory[-1] == '/': + filename = directory + str(self.getShotnumber()) + '.pck' + else: + filename = directory + '/' + str(self.getShotnumber()) + '.pck' + infile = open(filename, 'r') + mpicks = infile.readlines() + for line in mpicks: + if line.split()[0] == []: + continue + traceID, mpp, epp, lpp = line.split() + traceID = int(traceID) + if traceID in self.pick.keys(): + self.manualpicks[traceID] = {'mpp': float(mpp), + 'epp': float(epp), + 'lpp': float(lpp)} + def setPick(self, traceID, pick): ########## siehe Kommentar ########## if not traceID in self.pick.keys(): self.pick[traceID] = {} From feab50af28386526d5ab2ac3ea8de159b51e4656 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 8 Dec 2015 13:49:47 +0100 Subject: [PATCH 0774/1144] bugfixes + renaming --- pylot/core/active/seismicshot.py | 100 +++++++++++++++++++------------ 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index cc8c3cd1..bb86839d 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -28,8 +28,8 @@ class SeismicShot(object): self.recCoordlist = None self.srcCoordlist = None self.traceIDs = None - self.pick = {} - self.pickwindow= {} + self.picks = {} + self.pwindow= {} self.manualpicks= {} self.snr = {} self.snrthreshold = {} @@ -133,24 +133,34 @@ class SeismicShot(object): def getSourcefile(self): return self.paras['sourcefile'] + def getManualPick(self, traceID): + if not self.getManualPickFlag(traceID) == 0: + return self.manualpicks[traceID]['mpp'] + + def getManualEarliest(self, traceID): + return self.manualpicks[traceID]['epp'] + + def getManualLatest(self, traceID): + return self.manualpicks[traceID]['lpp'] + def getPick(self, traceID, returnRemoved = False): - if not self.getFlag(traceID) == 0: - return self.pick[traceID]['mpp'] + if not self.getPickFlag(traceID) == 0: + return self.picks[traceID]['mpp'] if returnRemoved == True: #print('getPick: Returned removed pick for shot %d, traceID %d' %(self.getShotnumber(), traceID)) - return self.pick[traceID]['mpp'] + return self.picks[traceID]['mpp'] def getPickIncludeRemoved(self, traceID): return self.getPick(traceID, returnRemoved = True) def getEarliest(self, traceID): - return self.pick[traceID]['epp'] + return self.picks[traceID]['epp'] def getLatest(self, traceID): - return self.pick[traceID]['lpp'] + return self.picks[traceID]['lpp'] def getSymmetricPickError(self, traceID): - pickerror = self.pick[traceID]['spe'] + pickerror = self.picks[traceID]['spe'] if np.isnan(pickerror) == True: print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) return pickerror @@ -182,11 +192,11 @@ class SeismicShot(object): def getPickwindow(self, traceID): try: - self.pickwindow[traceID] + self.pwindow[traceID] except KeyError as e: print('no pickwindow for trace %s, set to %s' % (traceID, self.getCut())) self.setPickwindow(traceID, self.getCut()) - return self.pickwindow[traceID] + return self.pwindow[traceID] def getSNR(self, traceID): return self.snr[traceID] @@ -310,19 +320,19 @@ class SeismicShot(object): tsignal = self.getTsignal() tnoise = self.getPickIncludeRemoved(traceID) - tgap - (self.pick[traceID]['epp'], - self.pick[traceID]['lpp'], - self.pick[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), + (self.picks[traceID]['epp'], + self.picks[traceID]['lpp'], + self.picks[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), nfac, (tnoise, tgap, tsignal), self.getPickIncludeRemoved(traceID), stealthMode = True) - if self.pick[traceID]['epp'] < 0: - self.pick[traceID]['epp'] + if self.picks[traceID]['epp'] < 0: + self.picks[traceID]['epp'] #print('setEarllatepick: Set epp to 0 because it was < 0') # TEST OF 1/2 PICKERROR - # self.pick[traceID]['spe'] *= 0.5 + # self.picks[traceID]['spe'] *= 0.5 # TEST OF 1/2 PICKERROR def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm = 0.6): @@ -488,18 +498,23 @@ class SeismicShot(object): continue traceID, mpp, epp, lpp = line.split() traceID = int(traceID) - if traceID in self.pick.keys(): + if traceID in self.picks.keys(): self.manualpicks[traceID] = {'mpp': float(mpp), 'epp': float(epp), 'lpp': float(lpp)} + if float(mpp) <= 0: + self.setManualPickFlag(traceID, 0) + else: + self.setManualPickFlag(traceID, 1) + def setPick(self, traceID, pick): ########## siehe Kommentar ########## - if not traceID in self.pick.keys(): - self.pick[traceID] = {} - self.pick[traceID]['mpp'] = pick - self.pick[traceID]['flag'] = 1 + if not traceID in self.picks.keys(): + self.picks[traceID] = {} + self.picks[traceID]['mpp'] = pick + self.picks[traceID]['flag'] = 1 # ++++++++++++++ Block raus genommen, da Error beim 2ten Mal picken! (Ueberschreiben von erstem Pick!) - # if not self.pick.has_key(traceID): + # if not self.picks.has_key(traceID): # self.getPick(traceID) = picks # else: # raise KeyError('pick to be set more than once for traceID %s' % traceID) @@ -508,17 +523,24 @@ class SeismicShot(object): # parlist = open(parfile,'r').readlines() def removePick(self, traceID): - self.setFlag(traceID, 0) + self.setPickFlag(traceID, 0) - def setFlag(self, traceID, flag): + def setPickFlag(self, traceID, flag): 'Set flag = 0 if pick is invalid, else flag = 1' - self.pick[traceID]['flag'] = flag + self.picks[traceID]['flag'] = flag - def getFlag(self, traceID): - return self.pick[traceID]['flag'] + def getPickFlag(self, traceID): + return self.picks[traceID]['flag'] + + def setManualPickFlag(self, traceID, flag): + 'Set flag = 0 if pick is invalid, else flag = 1' + self.manualpicks[traceID]['flag'] = flag + + def getManualPickFlag(self, traceID): + return self.manualpicks[traceID]['flag'] def setPickwindow(self, traceID, pickwindow): - self.pickwindow[traceID] = pickwindow + self.pwindow[traceID] = pickwindow def setSNR(self, traceID): ########## FORCED HOS PICK ########## ''' @@ -547,7 +569,7 @@ class SeismicShot(object): ''' distancearray = [] - for traceID in self.pick.keys(): + for traceID in self.picks.keys(): if self.getRecLoc(traceID) > self.getSrcLoc(traceID): distancearray.append(self.getDistance(traceID)) elif self.getRecLoc(traceID) < self.getSrcLoc(traceID): @@ -571,7 +593,7 @@ class SeismicShot(object): # dist_medarray = [] # i = 1 - # for traceID in self.pick.keys(): + # for traceID in self.picks.keys(): # aictimearray.append(self.getPick(traceID)) ###### HIER NICHT MEHR aic = [0] oder hos = [1] # hostimearray.append(self.getPick(traceID)) # if dist_med is not 0: dist_medarray.append(dist_med[self.getDistance(traceID)]) @@ -611,9 +633,9 @@ class SeismicShot(object): # pickwindowarray_upperb = [] # i = 1 - # for traceID in self.pickwindow.keys(): - # pickwindowarray_lowerb.append(self.pickwindow[traceID][0]) - # pickwindowarray_upperb.append(self.pickwindow[traceID][1]) + # for traceID in self.pwindow.keys(): + # pickwindowarray_lowerb.append(self.pwindow[traceID][0]) + # pickwindowarray_upperb.append(self.pwindow[traceID][1]) # i += 1 # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_lowerb, ':k') @@ -775,8 +797,8 @@ class SeismicShot(object): x = [] y = [] z = [] - for traceID in self.pick.keys(): - if self.getFlag(traceID) != 0: + for traceID in self.picks.keys(): + if self.getPickFlag(traceID) != 0: x.append(self.getRecLoc(traceID)[0]) y.append(self.getRecLoc(traceID)[1]) z.append(self.getPick(traceID)) @@ -832,12 +854,12 @@ class SeismicShot(object): y = []; ycut = [] z = []; zcut = [] - for traceID in self.pick.keys(): - if self.getFlag(traceID) != 0: + for traceID in self.picks.keys(): + if self.getPickFlag(traceID) != 0: x.append(self.getRecLoc(traceID)[0]) y.append(self.getRecLoc(traceID)[1]) z.append(self.getPick(traceID)) - if self.getFlag(traceID) == 0 and self.getPickIncludeRemoved(traceID) is not None: + if self.getPickFlag(traceID) == 0 and self.getPickIncludeRemoved(traceID) is not None: xcut.append(self.getRecLoc(traceID)[0]) ycut.append(self.getRecLoc(traceID)[1]) zcut.append(self.getPickIncludeRemoved(traceID)) @@ -878,7 +900,7 @@ class SeismicShot(object): if annotations == True: for traceID in self.getTraceIDlist(): - if self.getFlag(traceID) is not 0: + if self.getPickFlag(traceID) is not 0: ax.annotate(' %s' %traceID , xy = (self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), fontsize = 'x-small', color = 'k') else: From c9a75ca2d04981d794a28e19dc88e999d89d37ab Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 8 Dec 2015 13:50:08 +0100 Subject: [PATCH 0775/1144] renaming --- pylot/core/active/surveyPlotTools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index e913cb9c..dae7a9ea 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -60,7 +60,7 @@ class regions(object): shot.getPickIncludeRemoved(traceID), shot.getShotnumber(), traceID, - shot.getFlag(traceID))) + shot.getPickFlag(traceID))) allpicks.sort() self._allpicks = allpicks @@ -297,7 +297,7 @@ class regions(object): for shotnumber in shots.keys(): shot = self.shot_dict[shotnumber] for traceID in shots[shotnumber]: - if shot.getFlag(traceID) is not 0: + if shot.getPickFlag(traceID) is not 0: pickX = shot.getDistance(traceID) pickY = shot.getPick(traceID) if insidePoly(x, y, pickX, pickY): @@ -341,7 +341,7 @@ class regions(object): if type(shot) == int: shot = self.survey.getShotDict()[shot] - if shot.getFlag(traceID) is 0: + if shot.getPickFlag(traceID) is 0: return self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s = 50, marker = 'o', facecolors = 'none', edgecolors = 'm', alpha = 1) @@ -474,7 +474,7 @@ class regions(object): shot = self.survey.getShotDict()[shotnumber] for traceID in shot.getTraceIDlist(): - if shot.getFlag(traceID) is not 0: + if shot.getPickFlag(traceID) is not 0: self.highlightPick(shot, traceID, annotations) self.drawFigure() From ee16cef90199d9c4be4aaad845f87acec4160c9a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 8 Dec 2015 13:50:19 +0100 Subject: [PATCH 0776/1144] renaming --- pylot/core/active/activeSeismoPick.py | 28 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 630e510d..6c791ea8 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -70,6 +70,14 @@ class Survey(object): "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" %(cutwindow, tmovwind, tsignal, tgap)) + def setManualPicksFromFiles(self, directory = 'picks'): + ''' + Read manual picks from *.pck files in a directory. + The * must be identical with the shotnumber. + ''' + for shot in self.data.values(): + shot.setManualPicksFromFile(directory) + def _removeAllEmptyTraces(self): filename = 'removeEmptyTraces.out' count = 0 @@ -159,16 +167,16 @@ class Survey(object): def cleanBySPE(self, maxSPE): for shot in self.data.values(): for traceID in shot.getTraceIDlist(): - if shot.getFlag(traceID) == 1: + if shot.getPickFlag(traceID) == 1: if shot.getSymmetricPickError(traceID) > maxSPE: - shot.setFlag(traceID, 0) + shot.setPickFlag(traceID, 0) def plotSPE(self): import matplotlib.pyplot as plt spe = [] for shot in self.data.values(): for traceID in shot.getTraceIDlist(): - if shot.getFlag(traceID) == 1: + if shot.getPickFlag(traceID) == 1: spe.append(shot.getSymmetricPickError(traceID)) spe.sort() plt.plot(spe, label = 'SPE') @@ -183,8 +191,8 @@ class Survey(object): numpicks = 0 for shot in self.data.values(): for traceID in shot.getTraceIDlist(): - if shot.getFlag(traceID) == 0: - shot.setFlag(traceID, 1) + if shot.getPickFlag(traceID) == 0: + shot.setPickFlag(traceID, 1) if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): shot.removePick(traceID) else: @@ -248,7 +256,7 @@ class Survey(object): for traceID in shot.getTraceIDlist(): snrlist.append(shot.getSNR(traceID)[0]) dist.append(shot.getDistance(traceID)) - if shot.getFlag(traceID) is not 0: + if shot.getPickFlag(traceID) is not 0: pickedTraces += 1 info_dict[shot.getShotnumber()] = {'numtraces': numtraces, 'picked traces': [pickedTraces, @@ -289,7 +297,7 @@ class Survey(object): traceIDlist.sort() ttfile.writelines(str(self.countPickedTraces(shot)) + '\n') for traceID in traceIDlist: - if shot.getFlag(traceID) is not 0: + if shot.getPickFlag(traceID) is not 0: pick = shot.getPick(traceID) * fmtomo_factor delta = shot.getSymmetricPickError(traceID) * fmtomo_factor (x, y, z) = shot.getRecLoc(traceID) @@ -306,7 +314,7 @@ class Survey(object): def countPickedTraces(self, shot): count = 0 for traceID in shot.getTraceIDlist(): - if shot.getFlag(traceID) is not 0: + if shot.getPickFlag(traceID) is not 0: count += 1 return count @@ -314,7 +322,7 @@ class Survey(object): count = 0 for shot in self.data.values(): for traceID in shot.getTraceIDlist(): - if shot.getFlag(traceID) is not 0: + if shot.getPickFlag(traceID) is not 0: count += 1 return count @@ -401,7 +409,7 @@ class Survey(object): for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if plotRemoved == False: - if shot.getFlag(traceID) is not 0 or plotRemoved == True: + if shot.getPickFlag(traceID) is not 0 or plotRemoved == True: dist.append(shot.getDistance(traceID)) pick.append(shot.getPick(traceID)) snrlog.append(math.log10(shot.getSNR(traceID)[0])) From 490ad0f0c2f39664b0aa2570d317f4c1856af3e6 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 9 Dec 2015 11:18:49 +0100 Subject: [PATCH 0777/1144] bugfix: setParameters for shot not working, because survey needed parameters on init --- pylot/core/active/activeSeismoPick.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 6c791ea8..8fab57e0 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -21,11 +21,16 @@ class Survey(object): self._sourcefile = sourcefile self._obsdir = path self._generateSurvey() + self._initiateFilenames() if useDefaultParas == True: self.setParametersForShots() self._removeAllEmptyTraces() self._updateShots() - self.setArtificialPick(0, 0) + + def _initiateFilenames(self): + for shot in self.data.values(): + shot.setRecfile(self.getPath() + self.getReceiverfile()) + shot.setSourcefile(self.getPath() + self.getSourcefile()) def _generateSurvey(self): from obspy.core import read @@ -63,8 +68,6 @@ class Survey(object): shot.setTmovwind(tmovwind) shot.setTsignal(tsignal) shot.setTgap(tgap) - shot.setRecfile(self.getPath() + self.getReceiverfile()) - shot.setSourcefile(self.getPath() + self.getSourcefile()) shot.setOrder(order = 4) print ("setParametersForShots: Parameters set to:\n" "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" From a866bf52280a56fe3953ebb884b712a7d9c213bb Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 9 Dec 2015 12:01:41 +0100 Subject: [PATCH 0778/1144] added methods for auto to manual comparisons --- pylot/core/active/activeSeismoPick.py | 100 +++++++++++++++++--------- 1 file changed, 68 insertions(+), 32 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 8fab57e0..4529a3a0 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -49,38 +49,6 @@ class Survey(object): print ("Generated Survey object for %d shots" % len(shotlist)) print ("Total number of traces: %d \n" %self.countAllTraces()) - def setArtificialPick(self, traceID, pick): - ''' - Sets an artificial pick for a traceID of all shots in the survey object. - (This can be used to create a pick with t = 0 at the source origin) - ''' - for shot in self.data.values(): - shot.setPick(traceID, pick) - - def setParametersForShots(self, cutwindow = (0, 0.2), tmovwind = 0.3, tsignal = 0.03, tgap = 0.0007): - if (cutwindow == (0, 0.2) and tmovwind == 0.3 and - tsignal == 0.03 and tgap == 0.0007): - print ("Warning: Standard values used for " - "setParamters. This might not be clever.") - # CHANGE this later. Parameters only needed for survey, not for each shot. - for shot in self.data.values(): - shot.setCut(cutwindow) - shot.setTmovwind(tmovwind) - shot.setTsignal(tsignal) - shot.setTgap(tgap) - shot.setOrder(order = 4) - print ("setParametersForShots: Parameters set to:\n" - "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" - %(cutwindow, tmovwind, tsignal, tgap)) - - def setManualPicksFromFiles(self, directory = 'picks'): - ''' - Read manual picks from *.pck files in a directory. - The * must be identical with the shotnumber. - ''' - for shot in self.data.values(): - shot.setManualPicksFromFile(directory) - def _removeAllEmptyTraces(self): filename = 'removeEmptyTraces.out' count = 0 @@ -120,6 +88,74 @@ class Survey(object): "on removed traces."%(filename)) outfile.close() + def setArtificialPick(self, traceID, pick): + ''' + Sets an artificial pick for a traceID of all shots in the survey object. + (This can be used to create a pick with t = 0 at the source origin) + ''' + for shot in self.data.values(): + shot.setPick(traceID, pick) + + def setParametersForShots(self, cutwindow = (0, 0.2), tmovwind = 0.3, tsignal = 0.03, tgap = 0.0007): + if (cutwindow == (0, 0.2) and tmovwind == 0.3 and + tsignal == 0.03 and tgap == 0.0007): + print ("Warning: Standard values used for " + "setParamters. This might not be clever.") + # CHANGE this later. Parameters only needed for survey, not for each shot. + for shot in self.data.values(): + shot.setCut(cutwindow) + shot.setTmovwind(tmovwind) + shot.setTsignal(tsignal) + shot.setTgap(tgap) + shot.setOrder(order = 4) + print ("setParametersForShots: Parameters set to:\n" + "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" + %(cutwindow, tmovwind, tsignal, tgap)) + + def setManualPicksFromFiles(self, directory = 'picks'): + ''' + Read manual picks from *.pck files in a directory. + The * must be identical with the shotnumber. + ''' + for shot in self.data.values(): + shot.setManualPicksFromFile(directory) + + def getDiffsFromManual(self): + ''' + Returns a dictionary with the differences between manual and automatic pick for all shots. + ''' + diffs = {} + for shot in self.data.values(): + if not shot in diffs.keys(): + diffs[shot] = {} + for traceID in shot.getTraceIDlist(): + if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: + diffs[shot][traceID] = shot.getPick(traceID) - shot.getManualPick(traceID) + return diffs + + def plotDiffs(self): + import matplotlib.pyplot as plt + diffs = []; dists = []; picks = [] + diffsDic = self.getDiffsFromManual() + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: + dists.append(shot.getDistance(traceID)) + picks.append(shot.getManualPick(traceID)) + diffs.append(diffsDic[shot][traceID]) + + label = 'Difference to automatic picks [s]' + fig = plt.figure() + ax = fig.add_subplot(111) + + sc = ax.scatter(dists, picks, c = diffs, s=5, edgecolors='none', label = label) + cbar = plt.colorbar(sc, fraction=0.05) + cbar.set_label(label) + ax.set_xlabel('Distance [m]') + ax.set_ylabel('Time [s]') + ax.text(0.5, 0.95, 'Plot of all MANUAL picks', transform=ax.transAxes, horizontalalignment='center') + + def pickAllShots(self, windowsize, HosAic = 'hos', vmin = 333, vmax = 5500, folm = 0.6): ''' Automatically pick all traces of all shots of the survey. From 943432dfb3ebc100d4b7a2d12ea70fceb49567e8 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 9 Dec 2015 13:39:58 +0100 Subject: [PATCH 0779/1144] updated plot of 2d ttcs --- pylot/core/active/seismicshot.py | 86 +++++++++++++++++--------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index bb86839d..639e9879 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -565,63 +565,67 @@ class SeismicShot(object): def getDistArray4ttcPlot(self): ########## nur fuer 2D benoetigt ########## ''' - Function to create a distance array for the plots. 2D only! + Function to create a distance array for the plots. 2D only! X DIRECTION!! ''' distancearray = [] for traceID in self.picks.keys(): - if self.getRecLoc(traceID) > self.getSrcLoc(traceID): + if self.getRecLoc(traceID)[0] > self.getSrcLoc()[0]: distancearray.append(self.getDistance(traceID)) - elif self.getRecLoc(traceID) < self.getSrcLoc(traceID): + elif self.getRecLoc(traceID)[0] <= self.getSrcLoc()[0]: distancearray.append((-1)*self.getDistance(traceID)) return distancearray - # def plot2dttc(self, dist_med = 0): ########## 2D ########## - # ''' - # Function to plot the traveltime curve for automated picks (AIC & HOS) of a shot. 2d only! + def plot2dttc(self, ax = None): ########## 2D ########## + ''' + Function to plot the traveltime curve for automated picks of a shot. 2d only! ATM: X DIRECTION!! + ''' + import matplotlib.pyplot as plt + plt.interactive('True') + picks = [] - # :param: dist_med (optional) - # :type: 'dictionary' - # ''' - # import matplotlib.pyplot as plt - # plt.interactive('True') - # aictimearray = [] - # hostimearray = [] - # if dist_med is not 0: - # dist_medarray = [] + for traceID in self.picks.keys(): + picks.append(self.getPick(traceID)) - # i = 1 - # for traceID in self.picks.keys(): - # aictimearray.append(self.getPick(traceID)) ###### HIER NICHT MEHR aic = [0] oder hos = [1] - # hostimearray.append(self.getPick(traceID)) - # if dist_med is not 0: dist_medarray.append(dist_med[self.getDistance(traceID)]) - # i += 1 + if ax is None: + fig = plt.figure() + ax = fig.add_subplot(111) - # plt.plot(self.getDistArray4ttcPlot(), aictimearray, 'r', label = "AIC") - # plt.plot(self.getDistArray4ttcPlot(), hostimearray, 'y', label = "HOS") - # if dist_med is not 0: plt.plot(self.getDistArray4ttcPlot(), dist_medarray, ':b') - # plt.title(self.shotname) + # shotnumbers = [shotnumbers for (shotnumbers, shotnames) in sorted(zip(shotnumbers, shotnames))] + plotarray = sorted(zip(self.getDistArray4ttcPlot(), picks)) + x = []; y = [] + for point in plotarray: + x.append(point[0]) + y.append(point[1]) + ax.plot(x, y,'r', label = "Automatic Picks") + plt.title('Shot: %s' %self.getShotnumber()) - # def plotmanualttc(self): ########## 2D ########## - # ''' - # Function to plot the traveltime curve for manual picks of a shot. 2D only! - # ''' - # import matplotlib.pyplot as plt - # plt.interactive('True') - # manualpicktimesarray = [] - # dist_medarray = [] + def plotmanual2dttc(self, ax = None): ########## 2D ########## + ''' + Function to plot the traveltime curve for manual picks of a shot. 2D only! + ''' + import matplotlib.pyplot as plt + plt.interactive('True') + manualpicktimesarray = [] - # i = 1 - # for traceID in self.manualpicks.keys(): - # if self.manualpicks[traceID][0] <= 0: - # manualpicktimesarray.append(None) - # else: - # manualpicktimesarray.append(self.manualpicks[traceID][0]) - # i += 1 + for traceID in self.picks.keys(): + if not traceID in self.manualpicks.keys() or self.getManualPickFlag(traceID) == 0: + manualpicktimesarray.append(None) + else: + manualpicktimesarray.append(self.getManualPick(traceID)) - # plt.plot(self.getDistArray4ttcPlot(), manualpicktimesarray, 'b', label = "manual") + if ax is None: + fig = plt.figure() + ax = fig.add_subplot(111) + + plotarray = sorted(zip(self.getDistArray4ttcPlot(), manualpicktimesarray)) + x = []; y = [] + for point in plotarray: + x.append(point[0]) + y.append(point[1]) + ax.plot(x, y, 'b', label = "Manual Picks") # def plotpickwindow(self): ########## 2D ########## # ''' From 29c14fb512b691113872e5e2a37e7c2cf4d50bb6 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 9 Dec 2015 14:02:58 +0100 Subject: [PATCH 0780/1144] added plot2dttc mode to plotAllShots --- pylot/core/active/activeSeismoPick.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 4529a3a0..3cf59e80 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -365,7 +365,7 @@ class Survey(object): count += 1 return count - def plotAllShots(self, rows = 3, columns = 4): + def plotAllShots(self, rows = 3, columns = 4, mode = '3d'): ''' Plots all shots as Matrices with the color corresponding to the traveltime for each receiver. IMPORTANT NOTE: Topography (z - coordinate) is not considered in the diagrams! @@ -395,7 +395,11 @@ class Survey(object): #+str(shot_dict[shotnumber].getShotnumber()), xlabel = 'X', ylabel = 'Y', zlabel = 'traveltime') #shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) ax = fig.add_subplot(3, 4, index) - self.getShot(shotnumber).matshow(ax = ax, colorbar = False, annotations = True) + if mode == '3d': + self.getShot(shotnumber).matshow(ax = ax, colorbar = False, annotations = True) + elif mode == '2d': + self.getShot(shotnumber).plot2dttc(ax) + self.getShot(shotnumber).plotmanual2dttc(ax) index += 1 if index > figPerSubplot: fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1, wspace = 0, hspace = 0) From 17c3dd4f693981e37422531e522f7a4c15a8ae92 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 9 Dec 2015 14:03:20 +0100 Subject: [PATCH 0781/1144] plot2dttc textposition changes etc --- pylot/core/active/seismicshot.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 639e9879..2f6595f9 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -600,7 +600,8 @@ class SeismicShot(object): x.append(point[0]) y.append(point[1]) ax.plot(x, y,'r', label = "Automatic Picks") - plt.title('Shot: %s' %self.getShotnumber()) + ax.text(0.5, 0.9, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes + , horizontalalignment = 'center') def plotmanual2dttc(self, ax = None): ########## 2D ########## ''' @@ -660,7 +661,7 @@ class SeismicShot(object): ax.plot([tnoise, pick], [noiselevel, noiselevel], 'g:', linewidth = lw, label = 'gap') ax.plot([tnoise + tgap, pick + tsignal], [noiselevel * snr, noiselevel * snr], 'b', linewidth = lw, label = 'signal level') ax.legend() - ax.text(0.05, 0.95, 'SNR: %s' %snr, transform = ax.transAxes) + ax.text(0.05, 0.9, 'SNR: %s' %snr, transform = ax.transAxes) def plot_traces(self, traceID, folm = 0.6): ########## 2D, muss noch mehr verbessert werden ########## from matplotlib.widgets import Button @@ -882,7 +883,8 @@ class SeismicShot(object): count = 0 ax.imshow(zgrid, extent = [min(x), max(x), min(y), max(y)], vmin = tmin, vmax = tmax, cmap = cmap, origin = 'lower', alpha = 0.85) - plt.text(0.45, 0.9, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes) + ax.text(0.5, 0.95, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes + , horizontalalignment = 'center') sc = ax.scatter(x, y, c = z, s = 30, label = 'picked shots', vmin = tmin, vmax = tmax, cmap = cmap, linewidths = 1.5) for xyz in zip(xcut, ycut, zcut): x, y, z = xyz From 2bb99a6f2ed66858d04676ef5d3f38f7c20c4f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 10 Dec 2015 12:02:32 +0100 Subject: [PATCH 0782/1144] bugfix: Even if no location is possible, autoPyLoT can handle finalpicks. --- autoPyLoT.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 658739fb..00068c73 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -107,7 +107,7 @@ def autoPyLoT(inputfile): ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) - + finalpicks = picks ########################################################## # locating if locflag == 1: @@ -199,8 +199,11 @@ def autoPyLoT(inputfile): # write phase files for various location routines # HYPO71 hypo71file = '%s/autoPyLoT_HYPO71.pha' % event - if finalpicks.getpicdic() is not None: - writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + if hasattr(finalpicks, 'getpicdic'): + if finalpicks.getpicdic() is not None: + writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + else: + writephases(picks, 'HYPO71', hypo71file) else: writephases(picks, 'HYPO71', hypo71file) @@ -222,7 +225,7 @@ def autoPyLoT(inputfile): ########################################################## # !automated picking starts here! picks = autopickevent(wfdat, parameter) - + finalpicks = picks ########################################################## # locating if locflag == 1: @@ -312,8 +315,11 @@ def autoPyLoT(inputfile): # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.getParam('eventID')) - if finalpicks.getpicdic() is not None: - writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + if hasattr(finalpicks, 'getpicdic'): + if finalpicks.getpicdic() is not None: + writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + else: + writephases(picks, 'HYPO71', hypo71file) else: writephases(picks, 'HYPO71', hypo71file) From 537b18774acd321b5be6a2b23edcba6af1f02173 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 10 Dec 2015 13:16:44 +0100 Subject: [PATCH 0783/1144] [fix] fixed multiple calls to print by print one formatted message --- pylot/core/active/activeSeismoPick.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 3cf59e80..0be9d5b4 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -143,7 +143,7 @@ class Survey(object): dists.append(shot.getDistance(traceID)) picks.append(shot.getManualPick(traceID)) diffs.append(diffsDic[shot][traceID]) - + label = 'Difference to automatic picks [s]' fig = plt.figure() ax = fig.add_subplot(111) @@ -345,10 +345,19 @@ class Survey(object): count += 1 ttfile.close() srcfile.close() - print 'Wrote output for %s traces' %count - print 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems to take Lat, Lon, Depth and creates output for FMTOMO as Depth, Lat, Lon' - print 'Dimensions of the seismic Array, transformed for FMTOMO, are Depth(%s, %s), Lat(%s, %s), Lon(%s, %s)'%( - min(DepthAll), max(DepthAll), min(LatAll), max(LatAll), min(LonAll), max(LonAll)) + msg = 'Wrote output for {0} traces\n' \ + 'WARNING: output generated for FMTOMO-obsdata. Obsdata seems ' \ + 'to take Lat, Lon, Depth and creates output for FMTOMO as ' \ + 'Depth, Lat, Lon\nDimensions of the seismic Array, ' \ + 'transformed for FMTOMO, are Depth({1}, {2}), Lat({3}, {4}), ' \ + 'Lon({5}, {6})'.format(count, + min(DepthAll), + max(DepthAll), + min(LatAll), + max(LatAll), + min(LonAll), + max(LonAll)) + print(msg) def countPickedTraces(self, shot): count = 0 From d2b39ff0785a4e402b7cb07daff0f5f47a5f4340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 10 Dec 2015 14:30:38 +0100 Subject: [PATCH 0784/1144] Improved some print output, bugfix: avoids getting negative value for pstart when iterative re-picking. --- pylot/core/pick/autopick.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index a54a94c2..7f829fd3 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -826,6 +826,9 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): badpicks[i][1] = picks[badpicks[i][0]]['P']['mpp'] - float(res) # get corresponding waveform stream + msg = '#######################################################\n' \ + 'iteratepicker: Re-picking station {0}'.format(badpicks[i][0]) + print(msg) wf2pick = wf.select(station=badpicks[i][0]) # modify some picking parameters @@ -836,8 +839,8 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): Precalcwin_old = pickparameter.getParam('Precalcwin') noisefactor_old = pickparameter.getParam('noisefactor') zfac_old = pickparameter.getParam('zfac') - pickparameter.setParam(pstart=badpicks[i][1] - wf2pick[0].stats.starttime \ - - pickparameter.getParam('tlta')) + pickparameter.setParam(pstart=max([0, badpicks[i][1] - wf2pick[0].stats.starttime \ + - pickparameter.getParam('tlta')])) pickparameter.setParam(pstop=pickparameter.getParam('pstart') + \ (3 * pickparameter.getParam('tlta'))) pickparameter.setParam(sstop=pickparameter.getParam('sstop') / 2) From 534718aadff5d0c5a9c192c00daca91554b6e75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 10 Dec 2015 15:50:36 +0100 Subject: [PATCH 0785/1144] Improved figure caption, shows vertical trace including station ID and channel even if no or only bad pick is available. --- pylot/core/pick/autopick.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 7f829fd3..fd86f600 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -638,6 +638,11 @@ def autopickstation(wfstream, pickparam, verbose=False): plt.legend([p1, p2], ['Data', 'CF1']) plt.title('%s, P Weight=%d, SNR=None, ' 'SNRdB=None' % (tr_filt.stats.channel, Pweight)) + else: + plt.title('%s, %s, P Weight=%d' % (tr_filt.stats.station, + tr_filt.stats.channel, + Pweight)) + plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') From f29d28591043b3ffb7b7f816b2337e76b511db3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 10 Dec 2015 15:52:30 +0100 Subject: [PATCH 0786/1144] Relaxed some quality-control parameters in order to increase quantity of picks. --- autoPyLoT_local.in | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/autoPyLoT_local.in b/autoPyLoT_local.in index 1b845cbc..329a0fe2 100644 --- a/autoPyLoT_local.in +++ b/autoPyLoT_local.in @@ -6,8 +6,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2013.02_Insheim #database# %name of data base -e0019.048.13 #eventID# %event ID for single event processing +2015.11_Insheim #database# %name of data base + #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything @@ -38,7 +38,7 @@ AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file co 49 49.4 #blat# %lattitude bounding for location map %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #common settings picker# -5.0 #pstart# %start time [s] for calculating CF for P-picking +15.0 #pstart# %start time [s] for calculating CF for P-picking 60.0 #pstop# %end time [s] for calculating CF for P-picking -1.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking 10.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking @@ -47,7 +47,7 @@ AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file co 2 15 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz] 2 20 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz] #special settings for calculating CF# -%!!Be careful when editing the following!! +%!!Edit the following only if you know what you are doing!!% #Z-component# HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3) 7.0 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s] @@ -60,7 +60,7 @@ HOS #algoP# %choose algorithm for P-onset 0.001 #addnoise# %add noise to seismogram for stable AR prediction 3 0.1 0.5 0.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] 3.0 #pickwinP# %for initial AIC pick, length of P-pick window [s] -8.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) +6.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) 0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) 0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] 0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] @@ -94,8 +94,8 @@ ARH #algoS# %choose algorithm for S-onset 1.5 #minAICSSNR# %below this SNR the initial S pick is rejected #check duration of signal using envelope function# 3 #minsiglength# %minimum required length of signal [s] -1.2 #noisefactor# %noiselevel*noisefactor=threshold -50 #minpercent# %required percentage of samples higher than threshold +1.0 #noisefactor# %noiselevel*noisefactor=threshold +40 #minpercent# %required percentage of samples higher than threshold #check for spuriously picked S-onsets# 2.0 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude #check statistics of P onsets# From 0b1f16866bb45859bebb651343a1f448009bd621 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 11 Dec 2015 11:08:06 +0100 Subject: [PATCH 0787/1144] added plotHist --- pylot/core/active/activeSeismoPick.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 3cf59e80..ce8ea46d 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -155,6 +155,17 @@ class Survey(object): ax.set_ylabel('Time [s]') ax.text(0.5, 0.95, 'Plot of all MANUAL picks', transform=ax.transAxes, horizontalalignment='center') + def plotHist(self): + import matplotlib.pyplot as plt + plt.interactive(True) + diffs = [] + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: + diffs.append(self.getDiffsFromManual()[shot][traceID]) + plt.hist(diffs, 20) + plt.title('Histogram of the differences between automatic and manual pick') + plt.xlabel('Difference in time (auto - manual) [s]') def pickAllShots(self, windowsize, HosAic = 'hos', vmin = 333, vmax = 5500, folm = 0.6): ''' From f6930618f2828d9a6bf9d5830fa3290f2e696f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 11 Dec 2015 15:53:35 +0100 Subject: [PATCH 0788/1144] calcMoMw: modified for calculating Mo and Mw using metric units. calcsourcespec: modified correction for attenutation using an exponential including Q(f). --- pylot/core/analysis/magnitude.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index e6233b25..4353b1e5 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -240,8 +240,8 @@ class M0Mw(Magnitude): # call subfunction to estimate source spectrum # and to derive w0 and fc [w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \ - self.getinvdir(), az, inc, self.getQp(), \ - self.getiplot()) + self.getinvdir(), self.getvp(), delta, az, \ + inc, self.getQp(), self.getiplot()) if w0 is not None: # call subfunction to calculate Mo and Mw @@ -268,6 +268,7 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): ''' tr = wfstream[0] + delta = delta * 1000 # hypocentral distance in [m] print("calcMoMw: Calculating seismic moment Mo and moment magnitude Mw for station %s ..." \ % tr.stats.station) @@ -278,7 +279,8 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): Mo = w0 * 4 * np.pi * rho * np.power(vp, 3) * delta / (rP * freesurf) - Mw = np.log10(Mo * 1e07) * 2 / 3 - 10.7 #after Hanks & Kanamori (1979), defined for [dyn*cm]! + #Mw = np.log10(Mo * 1e07) * 2 / 3 - 10.7 # after Hanks & Kanamori (1979), defined for [dyn*cm]! + Mw = np.log10(Mo) * 2 / 3 - 6.7 # for metric units print("calcMoMw: Calculated seismic moment Mo = %e Nm => Mw = %3.1f " % (Mo, Mw)) @@ -286,7 +288,7 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): -def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, Qp, iplot): +def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp, iplot): ''' Subfunction to calculate the source spectrum and to derive from that the plateau (usually called omega0) and the corner frequency assuming Aki's omega-square @@ -302,6 +304,7 @@ def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, Qp, iplot): Q = int(qu[0]) # A, i.e. power of frequency A = float(qu[1]) + delta = delta * 1000 # hypocentral distance in [m] fc = None w0 = None @@ -408,7 +411,9 @@ def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, Qp, iplot): F = f[fi] YY = Y[fi] # correction for attenuation - YYcor = YY.real*Q**A + wa = 2 * np.pi * F #angular frequency + D = np.exp((wa * delta) / (2 * vp * Q*F**A)) + YYcor = YY.real*D # get plateau (DC value) and corner frequency # initial guess of plateau w0in = np.mean(YYcor[0:100]) From 5f0e59d95a1a590dc8719487b069ee7b6082c8d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 14 Dec 2015 09:34:56 +0100 Subject: [PATCH 0789/1144] Additional comments to make the code clearer. --- pylot/core/analysis/magnitude.py | 63 ++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 4353b1e5..827d6055 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ -Created August/September 2015. +Created autumn/winter 2015. :author: Ludger Küperkoch / MAGS2 EP3 working group """ @@ -18,7 +18,7 @@ from pylot.core.read.data import Data class Magnitude(object): ''' Superclass for calculating Wood-Anderson peak-to-peak - amplitudes, local magnitudes, seismic moments + amplitudes, local magnitudes, source spectra, seismic moments and moment magnitudes. ''' @@ -52,7 +52,7 @@ class Magnitude(object): :param: vp [m/s], P-velocity :param: integer - :param: invdir, path to inventory or dataless-SEED file + :param: invdir, name and path to inventory or dataless-SEED file :type: string ''' @@ -265,6 +265,21 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): ''' Subfunction of run_calcMoMw to calculate individual seismic moments and corresponding moment magnitudes. + + :param: wfstream + :type: `~obspy.core.stream.Stream` + + :param: w0, height of plateau of source spectrum + :type: float + + :param: rho, rock density [kg/m³] + :type: integer + + :param: delta, hypocentral distance [km] + :type: integer + + :param: inv, name/path of inventory or dataless-SEED file + :type: string ''' tr = wfstream[0] @@ -293,8 +308,35 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp Subfunction to calculate the source spectrum and to derive from that the plateau (usually called omega0) and the corner frequency assuming Aki's omega-square source model. Has to be derived from instrument corrected displacement traces, - thus restitution and integration necessary! Integrated traces have to be rotated - into ray-coordinate system ZNE => LQT! + thus restitution and integration necessary! Integrated traces are rotated + into ray-coordinate system ZNE => LQT using Obspy's rotate modul! + + :param: wfstream + :type: `~obspy.core.stream.Stream` + + :param: onset, P-phase onset time + :type: float + + :param: inventory, path/name of inventory or dataless-SEED file + :type: string + + :param: vp, Vp-wave velocity + :type: float + + :param: delta, hypocentral distance [km] + :type: integer + + :param: azimuth + :type: integer + + :param: incidence + :type: integer + + :param: Qp, quality factor for P-waves + :type: integer + + :param: iplot, show results (iplot>1) or not (iplot(<2) + :type: integer ''' print ("Calculating source spectrum ....") @@ -346,7 +388,11 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp LQT=cordat_copy.rotate('ZNE->LQT',azimuth, incidence) ldat = LQT.select(component="L") if len(ldat) == 0: - # yet Obspy's rotate can not handle channels 3/2/1 + # if horizontal channels are 2 and 3 + # no azimuth information is available and thus no + # rotation is possible! + print("calcsourcespec: Azimuth information is missing, " + "no rotation of components possible!") ldat = LQT.select(component="Z") # integrate to displacement @@ -410,10 +456,12 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp fi = np.where((f >= 1) & (f < 100)) F = f[fi] YY = Y[fi] + # correction for attenuation wa = 2 * np.pi * F #angular frequency D = np.exp((wa * delta) / (2 * vp * Q*F**A)) YYcor = YY.real*D + # get plateau (DC value) and corner frequency # initial guess of plateau w0in = np.mean(YYcor[0:100]) @@ -433,7 +481,8 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp # use of conventional fitting [w02, fc2] = fitSourceModel(F, YYcor, Fcin, iplot) - # get w0 and fc as median + # get w0 and fc as median of both + # source spectrum fits w0 = np.median([w01, w02]) fc = np.median([fc1, fc2]) print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % (w0, fc)) From 3b4e1dcd1ea4c6ef2eb97b3d1428d7eb9516e8ce Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 4 Jan 2016 15:46:24 +0100 Subject: [PATCH 0790/1144] added changes for manual picks --- pylot/core/active/activeSeismoPick.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index ce8ea46d..5ae1cfa9 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -135,35 +135,41 @@ class Survey(object): def plotDiffs(self): import matplotlib.pyplot as plt - diffs = []; dists = []; picks = [] + diffs = []; dists = []; mpicks = []; picks = [] diffsDic = self.getDiffsFromManual() for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: dists.append(shot.getDistance(traceID)) - picks.append(shot.getManualPick(traceID)) + mpicks.append(shot.getManualPick(traceID)) + picks.append(shot.getPick(traceID)) diffs.append(diffsDic[shot][traceID]) - label = 'Difference to automatic picks [s]' + labelm = 'manual picks' + labela = 'automatic picks' fig = plt.figure() ax = fig.add_subplot(111) - sc = ax.scatter(dists, picks, c = diffs, s=5, edgecolors='none', label = label) + sc_a = ax.scatter(dists, picks, c = '0.5', s=10, edgecolors='none', label = labela, alpha = 0.3) + sc = ax.scatter(dists, mpicks, c = diffs, s=5, edgecolors='none', label = labelm) cbar = plt.colorbar(sc, fraction=0.05) - cbar.set_label(label) + cbar.set_label(labelm) ax.set_xlabel('Distance [m]') ax.set_ylabel('Time [s]') ax.text(0.5, 0.95, 'Plot of all MANUAL picks', transform=ax.transAxes, horizontalalignment='center') - def plotHist(self): + def plotHist(self, nbins = 20, ax = None): import matplotlib.pyplot as plt plt.interactive(True) diffs = [] + if ax == None: + fig = plt.figure() + ax = fig.add_subplot(111) for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: diffs.append(self.getDiffsFromManual()[shot][traceID]) - plt.hist(diffs, 20) + plt.hist(diffs, nbins) plt.title('Histogram of the differences between automatic and manual pick') plt.xlabel('Difference in time (auto - manual) [s]') @@ -407,7 +413,7 @@ class Survey(object): #shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) ax = fig.add_subplot(3, 4, index) if mode == '3d': - self.getShot(shotnumber).matshow(ax = ax, colorbar = False, annotations = True) + self.getShot(shotnumber).matshow(ax = ax, colorbar = False, annotations = True, legend = False) elif mode == '2d': self.getShot(shotnumber).plot2dttc(ax) self.getShot(shotnumber).plotmanual2dttc(ax) From 3c1be950b94b63858f5e80dd991ad00d8d1ac0a8 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 4 Jan 2016 15:47:05 +0100 Subject: [PATCH 0791/1144] removed most of the folm = 0.6 default values --- pylot/core/active/seismicshot.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 2f6595f9..0bed77d4 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -37,6 +37,7 @@ class SeismicShot(object): self.traces4plot = {} self.paras = {} self.paras['shotname'] = obsfile + self.folm = None def removeEmptyTraces(self): traceIDs = [] @@ -279,7 +280,7 @@ class SeismicShot(object): #raise ValueError('ambigious or empty traceID: %s' % traceID) - def pickTraces(self, traceID, windowsize, folm = 0.6, HosAic = 'hos'): ########## input variables ########## + def pickTraces(self, traceID, windowsize, folm, HosAic = 'hos'): ########## input variables ########## # LOCALMAX NOT IMPLEMENTED! ''' Intitiate picking for a trace. @@ -299,7 +300,7 @@ class SeismicShot(object): :param: windowsize, window around the returned HOS picktime, to search for the AIC minumum :type: 'tuple' - :param: folm, fraction of local maximumm (default = 0.6) + :param: folm, fraction of local maximumm :type: 'real' :param: HosAic, get hos or aic pick (can be 'hos'(default) or 'aic') @@ -308,6 +309,8 @@ class SeismicShot(object): hoscf = self.getHOScf(traceID) ### determination of both, HOS and AIC (need to change threshold-picker) ### aiccf = self.getAICcf(traceID) + self.folm = folm + self.timeArray[traceID] = hoscf.getTimeArray() aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) setHosAic = {'hos': hoscftime, @@ -335,7 +338,7 @@ class SeismicShot(object): # self.picks[traceID]['spe'] *= 0.5 # TEST OF 1/2 PICKERROR - def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm = 0.6): + def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm): ''' Threshold picker, using the local maximum in a pickwindow to find the time at which a fraction of the local maximum is reached for the first time. @@ -355,7 +358,7 @@ class SeismicShot(object): :param: cutwindow [seconds], cut a part of the trace as in Characteristic Function :type: 'tuple' - :param: folm, fraction of local maximum (default = 0.6) + :param: folm, fraction of local maximum :type: 'real' ''' hoscflist = list(hoscf.getCF()) @@ -663,7 +666,7 @@ class SeismicShot(object): ax.legend() ax.text(0.05, 0.9, 'SNR: %s' %snr, transform = ax.transAxes) - def plot_traces(self, traceID, folm = 0.6): ########## 2D, muss noch mehr verbessert werden ########## + def plot_traces(self, traceID): ########## 2D, muss noch mehr verbessert werden ########## from matplotlib.widgets import Button def onclick(event): @@ -688,6 +691,8 @@ class SeismicShot(object): def cleanup(event): self.traces4plot[traceID] = {} + folm = self.folm + fig = plt.figure() ax1 = fig.add_subplot(2,1,1) ax2 = fig.add_subplot(2,1,2, sharex = ax1) @@ -745,7 +750,7 @@ class SeismicShot(object): ax.legend() return ax - def _drawCFs(self, traceID, folm, refresh = False): + def _drawCFs(self, traceID, folm = None, refresh = False): hoscf = self.getHOScf(traceID) aiccf = self.getAICcf(traceID) ax = self.traces4plot[traceID]['ax2'] @@ -773,9 +778,10 @@ class SeismicShot(object): [ax.get_ylim()[0], ax.get_ylim()[1]], 'b:', label = 'latest') - ax.plot([0, self.getPick(traceID)], - [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], - 'm:', label = 'folm = %s' %folm) + if folm is not None: + ax.plot([0, self.getPick(traceID)], + [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], + 'm:', label = 'folm = %s' %folm) ax.set_xlabel('Time [s]') ax.legend() @@ -834,7 +840,7 @@ class SeismicShot(object): plotmethod[method](*args) - def matshow(self, ax = None, step = 0.5, method = 'linear', plotRec = True, annotations = True, colorbar = True): + def matshow(self, ax = None, step = 0.5, method = 'linear', plotRec = True, annotations = True, colorbar = True, legend = True): ''' Plots a 2D matrix of the interpolated traveltimes. This needs less performance than plot3dttc @@ -899,7 +905,8 @@ class SeismicShot(object): cbar = plt.colorbar(sc) cbar.set_label('Time [s]') - ax.legend() + if legend == True: + ax.legend() ax.set_xlabel('X') ax.set_ylabel('Y') ax.plot(self.getSrcLoc()[0], self.getSrcLoc()[1],'*k', markersize = 15) # plot source location From acd9e942f97cbd3618fab9f5c03bbbc37f2acd3a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 4 Jan 2016 15:47:46 +0100 Subject: [PATCH 0792/1144] improved SNR plots --- pylot/core/active/surveyUtils.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 12f5cc39..a3081517 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -16,11 +16,13 @@ def setArtificialPick(shot_dict, traceID, pick): def fitSNR4dist(shot_dict, shiftdist = 30, shiftSNR = 100): import numpy as np + import matplotlib.pyplot as plt dists = [] picks = [] snrs = [] snr_sqrt_inv = [] snrthresholds = [] + snrBestFit = [] for shot in shot_dict.values(): for traceID in shot.getTraceIDlist(): if shot.getSNR(traceID)[0] >= 1: @@ -31,18 +33,23 @@ def fitSNR4dist(shot_dict, shiftdist = 30, shiftSNR = 100): fit = np.polyfit(dists, snr_sqrt_inv, 1) fit_fn = np.poly1d(fit) for dist in dists: + snrBestFit.append((1/(fit_fn(dist)**2))) dist += shiftdist snrthresholds.append((1/(fit_fn(dist)**2)) - shiftSNR * np.exp(-0.05 * dist)) - plotFittedSNR(dists, snrthresholds, snrs) + plotFittedSNR(dists, snrthresholds, snrs, snrBestFit) return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben -def plotFittedSNR(dists, snrthresholds, snrs): +def plotFittedSNR(dists, snrthresholds, snrs, snrBestFit): import matplotlib.pyplot as plt plt.interactive(True) fig = plt.figure() - plt.plot(dists, snrs, '.', markersize = 1.0, label = 'SNR values') - plt.plot(dists, snrthresholds, 'r.', markersize = 1, label = 'Fitted threshold') + plt.plot(dists, snrs, 'b.', markersize = 2.0, label = 'SNR values') + dists.sort() + snrthresholds.sort(reverse = True) + snrBestFit.sort(reverse = True) + plt.plot(dists, snrthresholds, 'r', markersize = 1, label = 'Fitted threshold') + plt.plot(dists, snrBestFit, 'k', markersize = 1, label = 'Best fitted curve') plt.xlabel('Distance[m]') plt.ylabel('SNR') plt.legend() From ce705888f198e41c4fb8e6501a8146a3c3b55f6c Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 19 Jan 2016 10:31:06 +0100 Subject: [PATCH 0793/1144] bugfixes and other not further specified changes --- pylot/core/active/activeSeismoPick.py | 5 +++-- pylot/core/active/seismicArrayPreparation.py | 13 +++++++++---- pylot/core/active/seismicshot.py | 17 ++++++++++++----- pylot/core/active/surveyUtils.py | 10 ++++++++-- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 5632664f..2e8c4058 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -170,9 +170,10 @@ class Survey(object): for traceID in shot.getTraceIDlist(): if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: diffs.append(self.getDiffsFromManual()[shot][traceID]) - plt.hist(diffs, nbins) + hist = plt.hist(diffs, nbins, histtype = 'step', normed = True, stacked = True) plt.title('Histogram of the differences between automatic and manual pick') plt.xlabel('Difference in time (auto - manual) [s]') + return diffs def pickAllShots(self, windowsize, HosAic = 'hos', vmin = 333, vmax = 5500, folm = 0.6): ''' @@ -421,7 +422,7 @@ class Survey(object): #ax = fig.add_subplot(3,3,i, projection = '3d', title = 'shot:' #+str(shot_dict[shotnumber].getShotnumber()), xlabel = 'X', ylabel = 'Y', zlabel = 'traveltime') #shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) - ax = fig.add_subplot(3, 4, index) + ax = fig.add_subplot(rows, columns, index) if mode == '3d': self.getShot(shotnumber).matshow(ax = ax, colorbar = False, annotations = True, legend = False) elif mode == '2d': diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 607d904f..22cd8486 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -747,6 +747,9 @@ class SeisArray(object): return surface def exportAll(self, filename = 'interpolated_receivers.out'): + ''' + Exports all receivers to an input file for ActiveSeismoPick3D. + ''' recfile_out = open(filename, 'w') count = 0 for traceID in self.getReceiverCoordinates().keys(): @@ -803,7 +806,7 @@ class SeisArray(object): xrc, yrc, zrc = self.getReceiverLists() xsc, ysc, zsc = self.getSourceLocsLists() - plt.title('3D plot of seismic array %s'%self.recfile) + plt.title('3D plot of seismic array.') if len(xmt) > 0: ax.plot(xmt, ymt, zmt, 'b.', markersize = 10, label = 'measured topo points') if len(xrc) > 0: @@ -812,7 +815,7 @@ class SeisArray(object): ax.plot(xmr, ymr, zmr, 'ro', label = 'measured receivers') if len(xsc) > 0: ax.plot(xsc, ysc, zsc, 'b*', label = 'shot locations') - ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') + ax.set_xlabel('X [m]'); ax.set_ylabel('Y [m]'); ax.set_zlabel('Z [m]') ax.legend() return ax @@ -842,13 +845,15 @@ class SeisArray(object): zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) - ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) + surf = ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) + cbar = plt.colorbar(surf) + cbar.set_label('Elevation [m]') if exag == False: ax.set_zlim(-(max(x) - min(x)/2),(max(x) - min(x)/2)) ax.set_aspect('equal') - ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('elevation') + ax.set_xlabel('X [m]'); ax.set_ylabel('Y [m]'); ax.set_zlabel('Z [m]') ax.legend() return ax diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 0bed77d4..8fbdbfb8 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -365,7 +365,11 @@ class SeismicShot(object): leftb = int(pickwindow[0] / self.getCut()[1] * len(hoscflist)) rightb = int(pickwindow[1] / self.getCut()[1] * len(hoscflist)) - threshold = folm * max(hoscflist[leftb : rightb]) # combination of local maximum and threshold + #threshold = folm * max(hoscflist[leftb : rightb]) # combination of local maximum and threshold + + ### TEST TEST + threshold = folm * (max(hoscflist[leftb : rightb]) - min(hoscflist[leftb : rightb])) + min(hoscflist[leftb : rightb]) # combination of local maximum and threshold + ### TEST TEST m = leftb @@ -376,7 +380,10 @@ class SeismicShot(object): lb = max(0, m - windowsize[0]) # if window exceeds t = 0 aiccfcut = list(aiccf.getCF())[lb : m + windowsize[1]] - n = aiccfcut.index(min(aiccfcut)) + if len(aiccfcut) > 0: + n = aiccfcut.index(min(aiccfcut)) + else: + n = 0 m = lb + n @@ -814,8 +821,8 @@ class SeismicShot(object): y.append(self.getRecLoc(traceID)[1]) z.append(self.getPick(traceID)) - xaxis = np.arange(min(x), max(x), step) - yaxis = np.arange(min(y), max(y), step) + xaxis = np.arange(min(x) + step, max(x), step) + yaxis = np.arange(min(y) + step, max(y), step) xgrid, ygrid = np.meshgrid(xaxis, yaxis) zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) @@ -892,9 +899,9 @@ class SeismicShot(object): ax.text(0.5, 0.95, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes , horizontalalignment = 'center') sc = ax.scatter(x, y, c = z, s = 30, label = 'picked shots', vmin = tmin, vmax = tmax, cmap = cmap, linewidths = 1.5) + label = None for xyz in zip(xcut, ycut, zcut): x, y, z = xyz - label = None if z > tmax: count += 1 z = 'w' diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index a3081517..c4180e10 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -54,7 +54,7 @@ def plotFittedSNR(dists, snrthresholds, snrs, snrBestFit): plt.ylabel('SNR') plt.legend() -def setFittedSNR(shot_dict, shiftdist = 30, shiftSNR = 100, p1 = 0.004, p2 = -0.0007): +def setDynamicFittedSNR(shot_dict, shiftdist = 30, shiftSNR = 100, p1 = 0.004, p2 = -0.0007): import numpy as np minSNR = 2.5 #fit_fn = fitSNR4dist(shot_dict) @@ -69,8 +69,14 @@ def setFittedSNR(shot_dict, shiftdist = 30, shiftSNR = 100, p1 = 0.004, p2 = -0. shot.setSNRthreshold(traceID, minSNR) else: shot.setSNRthreshold(traceID, snrthreshold) - print "setFittedSNR: Finished setting of fitted SNR-threshold" + print "setDynamicFittedSNR: Finished setting of fitted SNR-threshold" +def setConstantSNR(shot_dict, snrthreshold = 2.5): + import numpy as np + for shot in shot_dict.values(): + for traceID in shot.getTraceIDlist(): + shot.setSNRthreshold(traceID, snrthreshold) + print "setConstantSNR: Finished setting of SNR threshold to a constant value of %s"%snrthreshold def findTracesInRanges(shot_dict, distancebin, pickbin): ''' From f01c6109a8dde55f70764928d832382ffecf297b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 29 Jan 2016 07:21:17 +0100 Subject: [PATCH 0794/1144] [reformat] reformatted imports and parentheses indentation --- QtPyLoT.py | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e8f4e6f9..174ed447 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -23,8 +23,9 @@ https://www.iconfinder.com/iconsets/flavour (http://www.gnu.org/copyleft/lesser.html) """ -import os, sys -from os.path import expanduser +import os +import sys + import matplotlib matplotlib.use('Qt4Agg') @@ -44,12 +45,12 @@ from pylot.core.read.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent from pylot.core.loc.nll import locate as locateNll from pylot.core.util.defaults import FILTERDEFAULTS -from pylot.core.util.errors import FormatError, DatastructureError,\ +from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError from pylot.core.util.connection import checkurl -from pylot.core.util.utils import fnConstructor, createEvent, getLogin,\ +from pylot.core.util.utils import fnConstructor, createEvent, getLogin, \ createCreationInfo, getGlobalTimes -from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg,\ +from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import AutoPickThread @@ -58,6 +59,7 @@ import icons_rc locateTool = dict(nll=locateNll) + class MainWindow(QMainWindow): __version__ = _getVersionString() closing = Signal() @@ -85,7 +87,7 @@ class MainWindow(QMainWindow): self.dispComponent = str(settings.value("plotting/dispComponent", "Z")) if settings.value("data/dataRoot", None) is None: dirname = QFileDialog().getExistingDirectory( - caption='Choose data root ...') + caption='Choose data root ...') settings.setValue("data/dataRoot", dirname) settings.sync() @@ -115,7 +117,7 @@ class MainWindow(QMainWindow): try: self.startTime = min( - [tr.stats.starttime for tr in self.data.wfdata]) + [tr.stats.starttime for tr in self.data.wfdata]) except: self.startTime = UTCDateTime() @@ -613,8 +615,8 @@ class MainWindow(QMainWindow): else: self.updateStatus('Filter loaded ... ' '[{0}: {1} Hz]'.format( - self.getFilterOptions().getFilterType(), - self.getFilterOptions().getFreq())) + self.getFilterOptions().getFilterType(), + self.getFilterOptions().getFreq())) if self.filterAction.isChecked(): self.filterWaveformData() @@ -671,15 +673,15 @@ class MainWindow(QMainWindow): self.logDockWidget.setWidget(self.listWidget) self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget) self.addListItem('loading default values for local data ...') - home = expanduser("~") + home = os.path.expanduser("~") autopick_parameter = AutoPickParameter('%s/.pylot/autoPyLoT_local.in' % home) self.addListItem(str(autopick_parameter)) # Create the worker thread and run it self.thread = AutoPickThread(parent=self, - func=autopickevent, - data=self.getData().getWFData(), - param=autopick_parameter) + func=autopickevent, + data=self.getData().getWFData(), + param=autopick_parameter) self.thread.message.connect(self.addListItem) self.thread.start() self.thread.finished.connect(self.finalizeAutoPick) @@ -698,10 +700,10 @@ class MainWindow(QMainWindow): msgBox.setText("The picks for station {0} have been " "changed.".format(station)) msgBox.setDetailedText("Old picks:\n" - "{old_picks}\n\n" - "New picks:\n" - "{new_picks}".format(old_picks=stat_picks, - new_picks=picks)) + "{old_picks}\n\n" + "New picks:\n" + "{new_picks}".format(old_picks=stat_picks, + new_picks=picks)) msgBox.setInformativeText("Do you want to save your changes?") msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Save) @@ -789,7 +791,6 @@ class MainWindow(QMainWindow): if extlocpath is None or locroot is None: self.PyLoTprefs() - def check4Loc(self): return self.picksNum() > 4 @@ -810,12 +811,12 @@ class MainWindow(QMainWindow): if self.getData() is not None: if not self.getData().isNew(): self.setWindowTitle( - "PyLoT - processing event %s[*]" % self.getData().getID()) + "PyLoT - processing event %s[*]" % self.getData().getID()) elif self.getData().isNew(): self.setWindowTitle("PyLoT - New event [*]") else: self.setWindowTitle( - "PyLoT - seismic processing the python way[*]") + "PyLoT - seismic processing the python way[*]") self.setWindowModified(self.dirty) def tutorUser(self): @@ -853,7 +854,7 @@ class MainWindow(QMainWindow): def helpHelp(self): if checkurl(): form = HelpForm( - 'https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') + 'https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') else: form = HelpForm(':/help.html') form.show() From 70f276100393810257cdc20eab9e96eab585fab7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Feb 2016 06:11:25 +0100 Subject: [PATCH 0795/1144] [reformat] reformatted indentation --- pylot/core/pick/utils.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index f4eb2282..d7584bff 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -942,12 +942,12 @@ def writephases(arrivals, fformat, filename): :param: arrivals :type: dictionary containing all phase information including - station ID, phase, first motion, weight (uncertainty), + station ID, phase, first motion, weight (uncertainty), .... :param: fformat :type: string, chosen file format (location routine), - choose between NLLoc, HYPO71, HYPOSAT, VELEST, + choose between NLLoc, HYPO71, HYPOSAT, VELEST, HYPOINVERSE, and hypoDD :param: filename, full path and name of phase file @@ -956,7 +956,7 @@ def writephases(arrivals, fformat, filename): if fformat == 'NLLoc': - print ("Writing phases to %s for NLLoc" % filename) + print ("Writing phases to %s for NLLoc" % filename) fid = open("%s" % filename, 'w') # write header fid.write('# EQEVENT: Label: EQ001 Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n') @@ -1017,7 +1017,7 @@ def writephases(arrivals, fformat, filename): fid.close() elif fformat == 'HYPO71': - print ("Writing phases to %s for HYPO71" % filename) + print ("Writing phases to %s for HYPO71" % filename) fid = open("%s" % filename, 'w') # write header fid.write(' EQ001\n') @@ -1025,8 +1025,8 @@ def writephases(arrivals, fformat, filename): if arrivals[key]['P']['weight'] < 4: Ponset = arrivals[key]['P']['mpp'] Sonset = arrivals[key]['S']['mpp'] - pweight = arrivals[key]['P']['weight'] - sweight = arrivals[key]['S']['weight'] + pweight = arrivals[key]['P']['weight'] + sweight = arrivals[key]['S']['weight'] fm = arrivals[key]['P']['fm'] if fm is None: fm = '-' @@ -1088,7 +1088,7 @@ def writephases(arrivals, fformat, filename): Ao)) fid.close() - + if __name__ == '__main__': import doctest From 5170a0b1c6a9fa7fd9b8d5a9ead80edfcf733dca Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 1 Feb 2016 06:13:17 +0100 Subject: [PATCH 0796/1144] [new] implementation of a probability density function representation of the pick (untested) --- pylot/core/pick/utils.py | 111 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index d7584bff..5fa19b4c 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -133,6 +133,117 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): return EPick, LPick, PickError +def gauss_parameter(te, tm, tl, eta): + ''' + takes three onset times and returns the parameters sig1, sig2, a1 and a2 + to represent the pick as a probability density funtion (PDF) with two + Gauss branches + :param te: + :param tm: + :param tl: + :param eta: + :return: + ''' + + sig1 = (tm - te) / np.sqrt(2 * np.log(1 / eta)) + sig2 = (tl - tm) / np.sqrt(2 * np.log(1 / eta)) + + a1 = 2 / (1 + sig2 / sig1) + a2 = 2 / (1 + sig1 / sig2) + + return sig1, sig2, a1, a2 + + +def exp_parameter(te, tm, tl, eta): + ''' + takes three onset times te, tm and tl and returns the parameters sig1, + sig2 and a to represent the pick as a probability density function (PDF) + with two exponential decay branches + :param te: + :param tm: + :param tl: + :param eta: + :return: + ''' + + sig1 = np.log(eta) / (te - tm) + sig2 = np.log(eta) / (tm - tl) + a = 1 / (1 / sig1 + 1 / sig2) + + return sig1, sig2, a + + +def gauss_branches(x, mu, sig1, sig2, a1, a2): + ''' + function gauss_branches takes an axes x, a center value mu, two sigma + values sig1 and sig2 and two scaling factors a1 and a2 and return a + list containing the values of a probability density function (PDF) + consisting of gauss branches + :param x: + :type x: + :param mu: + :type mu: + :param sig1: + :type sig1: + :param sig2: + :type sig2: + :param a1: + :type a1: + :param a2: + :returns fun_vals: list with function values along axes x + ''' + fun_vals = [] + for k in x: + if k < mu: + fun_vals.append(a1 * 1 / (np.sqrt(2 * np.pi) * sig1) * np.exp(-((k - mu) / sig1)**2 / 2 )) + else: + fun_vals.append(a2 * 1 / (np.sqrt(2 * np.pi) * sig2) * np.exp(-((k - mu) / sig2)**2 / 2)) + return fun_vals + + +def exp_branches(x, mu, sig1, sig2, a): + ''' + function exp_branches takes an axes x, a center value mu, two sigma + values sig1 and sig2 and a scaling factor a and return a + list containing the values of a probability density function (PDF) + consisting of exponential decay branches + :param x: + :param mu: + :param sig1: + :param sig2: + :param a: + :returns fun_vals: list with function values along axes x: + ''' + fun_vals = [] + for k in x: + if k < mu: + fun_vals.append(a * np.exp(sig1 * (k - mu))) + else: + fun_vals.append(a * np.exp(-sig2 * (k - mu))) + return fun_vals + + +def pick_pdf(t, te, tm, tl, type='gauss', eta=0.01): + ''' + + :param t: + :param te: + :param tm: + :param tl: + :param type: + :param eta: + :param args: + :return: + ''' + + parameter = dict(gauss=gauss_parameter, exp=exp_parameter) + branches = dict(gauss=gauss_branches, exp=exp_branches) + + params = parameter[type](te, tm, tl, eta) + + return branches[type](t, tm, *params) + + def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): ''' Function to derive first motion (polarity) of given phase onset Pick. From 8893a8ec6b362cec0b1d44ac014331a79e3d41c1 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 1 Feb 2016 11:18:42 +0100 Subject: [PATCH 0797/1144] statistic plots added --- pylot/core/active/surveyUtils.py | 128 +++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index c4180e10..e513d876 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -107,3 +107,131 @@ def cleanUp(survey): for shot in survey.data.values(): shot.traces4plot = {} + +# def plotScatterStats(survey, key, ax = None): +# import matplotlib.pyplot as plt +# x = []; y = []; value = [] +# stats = survey.getStats() +# for shotnumber in stats.keys(): +# if type(value) == list: +# value.append(stats[shotnumber][key][0]) +# else: +# value.append(stats[shotnumber][key]) +# x.append(survey.data[shotnumber].getSrcLoc()[0]) +# y.append(survey.data[shotnumber].getSrcLoc()[1]) + +# if ax == None: +# fig = plt.figure() +# ax = fig.add_subplot(111) + +# sc = ax.scatter(x, y, s = value, c = value) +# plt.xlabel('X') +# plt.ylabel('Y') +# cbar = plt.colorbar(sc) +# cbar.set_label(key) + +def plotScatterStats4Shots(survey, key): + ''' + Statistics, scatter plot. + key can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' + ''' + import matplotlib.pyplot as plt + import numpy as np + statsShot = {} + x = []; y = []; value = [] + for shot in survey.data.values(): + for traceID in shot.getTraceIDlist(): + if not shot in statsShot.keys(): + statsShot[shot] = {'x': shot.getSrcLoc()[0], + 'y': shot.getSrcLoc()[1], + 'SNR': [], + 'SPE': [], + 'picked traces': 0} + + statsShot[shot]['SNR'].append(shot.getSNR(traceID)[0]) + if shot.getPickFlag(traceID) == 1: + statsShot[shot]['picked traces'] += 1 + statsShot[shot]['SPE'].append(shot.getSymmetricPickError(traceID)) + + for shot in statsShot.keys(): + statsShot[shot]['mean SNR'] = np.mean(statsShot[shot]['SNR']) + statsShot[shot]['median SNR'] = np.median(statsShot[shot]['SNR']) + statsShot[shot]['mean SPE'] = np.mean(statsShot[shot]['SPE']) + statsShot[shot]['median SPE'] = np.median(statsShot[shot]['SPE']) + + for shot in statsShot.keys(): + x.append(statsShot[shot]['x']) + y.append(statsShot[shot]['y']) + value.append(statsShot[shot][key]) + + fig = plt.figure() + ax = fig.add_subplot(111) + + size = [] + for val in value: + size.append(100 * val / max(value)) + + sc = ax.scatter(x, y, s = size, c = value) + plt.title('Plot of all shots') + plt.xlabel('X') + plt.ylabel('Y') + cbar = plt.colorbar(sc) + cbar.set_label(key) + + for shot in statsShot.keys(): + ax.annotate(' %s' %shot.getShotnumber() , xy = (shot.getSrcLoc()[0], shot.getSrcLoc()[1]), + fontsize = 'x-small', color = 'k') + +def plotScatterStats4Receivers(survey, key): + ''' + Statistics, scatter plot. + key can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' + ''' + import matplotlib.pyplot as plt + import numpy as np + statsRec = {} + x = []; y = []; value = [] + for shot in survey.data.values(): + for traceID in shot.getTraceIDlist(): + if not traceID in statsRec.keys(): + statsRec[traceID] = {'x': shot.getRecLoc(traceID)[0], + 'y': shot.getRecLoc(traceID)[1], + 'SNR': [], + 'SPE': [], + 'picked traces': 0} + + statsRec[traceID]['SNR'].append(shot.getSNR(traceID)[0]) + if shot.getPickFlag(traceID) == 1: + statsRec[traceID]['picked traces'] += 1 + statsRec[traceID]['SPE'].append(shot.getSymmetricPickError(traceID)) + + + for traceID in statsRec.keys(): + statsRec[traceID]['mean SNR'] = np.mean(statsRec[traceID]['SNR']) + statsRec[traceID]['median SNR'] = np.median(statsRec[traceID]['SNR']) + statsRec[traceID]['mean SPE'] = np.mean(statsRec[traceID]['SPE']) + statsRec[traceID]['median SPE'] = np.median(statsRec[traceID]['SPE']) + + for traceID in statsRec.keys(): + x.append(statsRec[traceID]['x']) + y.append(statsRec[traceID]['y']) + value.append(statsRec[traceID][key]) + + fig = plt.figure() + ax = fig.add_subplot(111) + + size = [] + for val in value: + size.append(100 * val / max(value)) + + sc = ax.scatter(x, y, s = size, c = value) + plt.title('Plot of all receivers') + plt.xlabel('X') + plt.ylabel('Y') + cbar = plt.colorbar(sc) + cbar.set_label(key) + + shot = survey.data.values()[0] + for traceID in shot.getTraceIDlist(): + ax.annotate(' %s' %traceID , xy = (shot.getRecLoc(traceID)[0], shot.getRecLoc(traceID)[1]), + fontsize = 'x-small', color = 'k') From 8f6f6cf8b6878bc10ffb7487d7eaa41ad3aef0a7 Mon Sep 17 00:00:00 2001 From: sebastianp Date: Wed, 3 Feb 2016 14:35:07 +0100 Subject: [PATCH 0798/1144] completition of Docstring --- pylot/core/analysis/correlation.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/pylot/core/analysis/correlation.py b/pylot/core/analysis/correlation.py index ef0a6e9e..2cf97231 100644 --- a/pylot/core/analysis/correlation.py +++ b/pylot/core/analysis/correlation.py @@ -6,11 +6,15 @@ import numpy as np def crosscorrsingle(wf1, wf2, taumax): ''' - - :param Wx: - :param Wy: - :param taumax: - :return: + Calculates the crosscorrelation between two waveforms with a defined maximum timedifference. + :param wf1: first waveformdata + :type wf1: list + :param wf2: second waveformdata + :type wf2: list + :param taumax: maximum time difference between waveforms + :type taumax: positive integer + :return: returns the crosscorrelation funktion 'c' and the lagvector 'l' + :rtype: c and l are lists ''' N = len(wf1) c = np.zeros(2 * taumax - 1) @@ -83,10 +87,13 @@ def wfscrosscorr(weights, wfs, taumax): SWB 26.01.2010 as arranged with Thomas Meier and Monika Bischoff - :param weights: - :param wfs: - :param taumax: - :return: + :param weights: weighting factors for the single components + :type weights: tuple + :param wfs: tuple of `~numpy.array` object containing waveform data + :type wfs: tuple + :param taumax: maximum time difference + :type taumax: positive integer + :return: returns cross correlation function normalized by the waveform energy ''' ccnorm = 0. From 0d0b43103b8774e547f2dc2fa29af12a1b51edbc Mon Sep 17 00:00:00 2001 From: sebastianp Date: Wed, 3 Feb 2016 14:49:16 +0100 Subject: [PATCH 0799/1144] editing of Docstring --- pylot/core/analysis/trigger.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pylot/core/analysis/trigger.py b/pylot/core/analysis/trigger.py index baa4ccd8..aa900cef 100644 --- a/pylot/core/analysis/trigger.py +++ b/pylot/core/analysis/trigger.py @@ -8,12 +8,18 @@ def createSingleTriggerlist(st, station='ZV01', trigcomp='Z', stalta=(1, 10), trigonoff=(6, 1)): ''' uses a single-station trigger to create a triggerlist for this station - :param st: - :param station: - :param trigcomp: - :param stalta: - :param trigonoff: - :return: + :param st: obspy stream + :type st: + :param station: station name to get triggers for (optional, default = ZV01) + :type station: str + :param trigcomp: (optional, default = Z) + :type trigcomp: str + :param stalta: (optional, default = (1,10)) + :type stalta: tuple + :param trigonoff: (optional, default = (6,1)) + :type trigonoff: tuple + :return: list of triggtimes + :rtype: list ''' tr = st.copy().select(component=trigcomp, station=station)[0] df = tr.stats.sampling_rate @@ -32,7 +38,7 @@ def createSubCoincTriggerlist(trig, station='ZV01'): coincidence trigger and are seen at the demanded station :param trig: list containing triggers from coincidence trigger :type trig: list - :param station: station name to get triggers for + :param station: station name to get triggers for (optional, default = ZV01) :type station: str :return: list of triggertimes :rtype: list From ada9f4e7803928bbfa822deaaefc42ca61cf8d1c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 5 Feb 2016 13:41:22 +0100 Subject: [PATCH 0800/1144] [new] started implementation of probability density representation routines --- pylot/core/pick/utils.py | 111 ---------------------------- pylot/core/util/pdf.py | 151 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 111 deletions(-) create mode 100644 pylot/core/util/pdf.py diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 5fa19b4c..d7584bff 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -133,117 +133,6 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): return EPick, LPick, PickError -def gauss_parameter(te, tm, tl, eta): - ''' - takes three onset times and returns the parameters sig1, sig2, a1 and a2 - to represent the pick as a probability density funtion (PDF) with two - Gauss branches - :param te: - :param tm: - :param tl: - :param eta: - :return: - ''' - - sig1 = (tm - te) / np.sqrt(2 * np.log(1 / eta)) - sig2 = (tl - tm) / np.sqrt(2 * np.log(1 / eta)) - - a1 = 2 / (1 + sig2 / sig1) - a2 = 2 / (1 + sig1 / sig2) - - return sig1, sig2, a1, a2 - - -def exp_parameter(te, tm, tl, eta): - ''' - takes three onset times te, tm and tl and returns the parameters sig1, - sig2 and a to represent the pick as a probability density function (PDF) - with two exponential decay branches - :param te: - :param tm: - :param tl: - :param eta: - :return: - ''' - - sig1 = np.log(eta) / (te - tm) - sig2 = np.log(eta) / (tm - tl) - a = 1 / (1 / sig1 + 1 / sig2) - - return sig1, sig2, a - - -def gauss_branches(x, mu, sig1, sig2, a1, a2): - ''' - function gauss_branches takes an axes x, a center value mu, two sigma - values sig1 and sig2 and two scaling factors a1 and a2 and return a - list containing the values of a probability density function (PDF) - consisting of gauss branches - :param x: - :type x: - :param mu: - :type mu: - :param sig1: - :type sig1: - :param sig2: - :type sig2: - :param a1: - :type a1: - :param a2: - :returns fun_vals: list with function values along axes x - ''' - fun_vals = [] - for k in x: - if k < mu: - fun_vals.append(a1 * 1 / (np.sqrt(2 * np.pi) * sig1) * np.exp(-((k - mu) / sig1)**2 / 2 )) - else: - fun_vals.append(a2 * 1 / (np.sqrt(2 * np.pi) * sig2) * np.exp(-((k - mu) / sig2)**2 / 2)) - return fun_vals - - -def exp_branches(x, mu, sig1, sig2, a): - ''' - function exp_branches takes an axes x, a center value mu, two sigma - values sig1 and sig2 and a scaling factor a and return a - list containing the values of a probability density function (PDF) - consisting of exponential decay branches - :param x: - :param mu: - :param sig1: - :param sig2: - :param a: - :returns fun_vals: list with function values along axes x: - ''' - fun_vals = [] - for k in x: - if k < mu: - fun_vals.append(a * np.exp(sig1 * (k - mu))) - else: - fun_vals.append(a * np.exp(-sig2 * (k - mu))) - return fun_vals - - -def pick_pdf(t, te, tm, tl, type='gauss', eta=0.01): - ''' - - :param t: - :param te: - :param tm: - :param tl: - :param type: - :param eta: - :param args: - :return: - ''' - - parameter = dict(gauss=gauss_parameter, exp=exp_parameter) - branches = dict(gauss=gauss_branches, exp=exp_branches) - - params = parameter[type](te, tm, tl, eta) - - return branches[type](t, tm, *params) - - def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): ''' Function to derive first motion (polarity) of given phase onset Pick. diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py new file mode 100644 index 00000000..c130b617 --- /dev/null +++ b/pylot/core/util/pdf.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import numpy as np +from pylot.core.util.version import get_git_version as _getVersionString + +__version__ = _getVersionString() +__author__ = 'sebastianw' + + +def gauss_parameter(te, tm, tl, eta): + ''' + takes three onset times and returns the parameters sig1, sig2, a1 and a2 + to represent the pick as a probability density funtion (PDF) with two + Gauss branches + :param te: + :param tm: + :param tl: + :param eta: + :return: + ''' + + sig1 = (tm - te) / np.sqrt(2 * np.log(1 / eta)) + sig2 = (tl - tm) / np.sqrt(2 * np.log(1 / eta)) + + a1 = 2 / (1 + sig2 / sig1) + a2 = 2 / (1 + sig1 / sig2) + + return sig1, sig2, a1, a2 + + +def exp_parameter(te, tm, tl, eta): + ''' + takes three onset times te, tm and tl and returns the parameters sig1, + sig2 and a to represent the pick as a probability density function (PDF) + with two exponential decay branches + :param te: + :param tm: + :param tl: + :param eta: + :return: + ''' + + sig1 = np.log(eta) / (te - tm) + sig2 = np.log(eta) / (tm - tl) + a = 1 / (1 / sig1 + 1 / sig2) + + return sig1, sig2, a + + +def gauss_branches(x, mu, sig1, sig2, a1, a2): + ''' + function gauss_branches takes an axes x, a center value mu, two sigma + values sig1 and sig2 and two scaling factors a1 and a2 and return a + list containing the values of a probability density function (PDF) + consisting of gauss branches + :param x: + :type x: + :param mu: + :type mu: + :param sig1: + :type sig1: + :param sig2: + :type sig2: + :param a1: + :type a1: + :param a2: + :returns fun_vals: list with function values along axes x + ''' + fun_vals = [] + for k in x: + if k < mu: + fun_vals.append(a1 * 1 / (np.sqrt(2 * np.pi) * sig1) * np.exp(-((k - mu) / sig1)**2 / 2 )) + else: + fun_vals.append(a2 * 1 / (np.sqrt(2 * np.pi) * sig2) * np.exp(-((k - mu) / sig2)**2 / 2)) + return np.array(fun_vals) + + +def exp_branches(x, mu, sig1, sig2, a): + ''' + function exp_branches takes an axes x, a center value mu, two sigma + values sig1 and sig2 and a scaling factor a and return a + list containing the values of a probability density function (PDF) + consisting of exponential decay branches + :param x: + :param mu: + :param sig1: + :param sig2: + :param a: + :returns fun_vals: list with function values along axes x: + ''' + fun_vals = [] + for k in x: + if k < mu: + fun_vals.append(a * np.exp(sig1 * (k - mu))) + else: + fun_vals.append(a * np.exp(-sig2 * (k - mu))) + return np.array(fun_vals) + +# define container dictionaries for different types of pdfs +parameter = dict(gauss=gauss_parameter, exp=exp_parameter) +branches = dict(gauss=gauss_branches, exp=exp_branches) + + +class ProbabilityDensityFunction(object): + ''' + A probability density function toolkit. + ''' + + version = __version__ + + def __init__(self, x, lbound, midpoint, rbound, decfact=0.01, type='gauss'): + ''' + Initialize a new ProbabilityDensityFunction object. Takes arguments x, + lbound, midpoint and rbound to define a probability density function + defined on the interval of x. Maximum density is given at the midpoint + and on the boundaries the function has declined to decfact times the + maximum value. Integration of the function over a particular interval + gives the probability for the variable value to lie in that interval. + :param x: interval on which the pdf is defined + :param lbound: left boundary + :param midpoint: point of maximum probability density + :param rbound: right boundary + :param decfact: boundary decline factor + :param type: determines the type of the probability density function's + branches + ''' + + self.axis = np.array(x) + self.nodes = dict(lbound=lbound, midpoint=midpoint, rbound=rbound, eta=decfact) + self.type = type + + def __add__(self, other): + pass + + def __sub__(self, other): + pass + + @property + def type(self): + return self.type + + @type.setter + def type(self, type): + self.type = type + + def params(self): + return parameter[self.type](**self.nodes) + + def data(self): + return branches[self.type](self.axis, self.nodes['midpoint'], *self.params()) From 303a5f9cf049bade0fdeb9c7889f235bbda9fcbc Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 6 Feb 2016 09:04:50 +0100 Subject: [PATCH 0801/1144] [edit] probability density function superclass implemented due to the different character of these functions --- pylot/core/util/pdf.py | 61 +++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index c130b617..74c07360 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -109,6 +109,50 @@ class ProbabilityDensityFunction(object): version = __version__ + def __init__(self, x, pdf): + self.axis = x + self.data = pdf + + def __add__(self, other): + assert isinstance(other, ProbabilityDensityFunction), \ + 'both operands must be of type ProbabilityDensityFunction' + if self.sampling_rate() == other.sampling_rate(): + max = np.maximum(self.axis, other.axis) + x = np.arange(-max, max + self.sampling_rate(), self.sampling_rate()) + else: + raise ValueError('Sampling rates do not match!') + + pdf1 = self.data + + return ProbabilityDensityFunction(x, pdf) + + def __sub__(self, other): + pass + + def __nonzero__(self): + return True + + @property + def data(self): + return self.data + + @data.setter + def data(self, pdf): + self.data = np.array(pdf) + + @property + def axis(self): + return self.axis + + @axis.setter + def axis(self, x): + self.axis = np.array(x) + + def sampling_rate(self): + return self.axis[1] - self.axis[0] + +class PickPDF(ProbabilityDensityFunction): + def __init__(self, x, lbound, midpoint, rbound, decfact=0.01, type='gauss'): ''' Initialize a new ProbabilityDensityFunction object. Takes arguments x, @@ -126,15 +170,9 @@ class ProbabilityDensityFunction(object): branches ''' - self.axis = np.array(x) - self.nodes = dict(lbound=lbound, midpoint=midpoint, rbound=rbound, eta=decfact) + self.nodes = dict(te=lbound, tm=midpoint, tl=rbound, eta=decfact) self.type = type - - def __add__(self, other): - pass - - def __sub__(self, other): - pass + super(PickPDF, self).__init__(x, self.pdf()) @property def type(self): @@ -147,5 +185,8 @@ class ProbabilityDensityFunction(object): def params(self): return parameter[self.type](**self.nodes) - def data(self): - return branches[self.type](self.axis, self.nodes['midpoint'], *self.params()) + def get(self, key): + return self.nodes[key] + + def pdf(self): + return branches[self.type](self.axis, self.get('tm'), *self.params()) From e7a5e388f65d7d4087fa15a96c98e66344598c25 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 9 Feb 2016 11:15:04 +0100 Subject: [PATCH 0802/1144] [edit] implementation of probability density function interface ready for testing --- pylot/core/util/pdf.py | 67 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 74c07360..7498f174 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -116,18 +116,22 @@ class ProbabilityDensityFunction(object): def __add__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - if self.sampling_rate() == other.sampling_rate(): - max = np.maximum(self.axis, other.axis) - x = np.arange(-max, max + self.sampling_rate(), self.sampling_rate()) - else: - raise ValueError('Sampling rates do not match!') - pdf1 = self.data + x, pdf_self, pdf_other = self.rearrange(other) + + pdf = np.convolve(pdf_self, pdf_other, 'same') * self.delta() return ProbabilityDensityFunction(x, pdf) def __sub__(self, other): - pass + assert isinstance(other, ProbabilityDensityFunction), \ + 'both operands must be of type ProbabilityDensityFunction' + + x, pdf_self, pdf_other = self.rearrange(other, plus=False) + + pdf = np.convolve(pdf_self, pdf_other[::-1], 'same') * self.delta() + + return ProbabilityDensityFunction(x, pdf) def __nonzero__(self): return True @@ -148,9 +152,56 @@ class ProbabilityDensityFunction(object): def axis(self, x): self.axis = np.array(x) - def sampling_rate(self): + def delta(self): return self.axis[1] - self.axis[0] + def rearrange(self, other, plus=True): + ''' + Method rearrange takes another Probability Density Function and returns + a new axis with mid-point 0 and covering positive and negative range + of axis values, either containing the maximum value of both axis or + the sum of the maxima + :param other: + :param plus: + :return: + ''' + + assert isinstance(other, ProbabilityDensityFunction), \ + 'both operands must be of type ProbabilityDensityFunction' + + sd = self.delta() + od = other.delta() + + samin = np.min(self.axis) + oamin = np.min(other.axis) + + # test if 0 is a sampling node + nodes_test = (not samin % sd and not oamin % od) + + # test if sampling rates match and if 0 is a sampling node + if sd == od and nodes_test: + if plus: + max = np.max(self.axis) + np.max(other.axis) + else: + max = np.max(np.max(self.axis), np.max(other.axis)) + x = np.arange(-max, max + sd, sd) + else: + raise ValueError('Sampling rates do not match or nodes shifted!') + + pdf_self = np.zeros(x.size) + pdf_other = np.zeros(x.size) + + sstart = np.where(x == samin) + s_end = sstart + self.data.size + ostart = np.where(x == oamin) + o_end = ostart + other.data.size + + pdf_self[sstart:s_end] = self.data + pdf_other[ostart:o_end] = other.data + + return x, pdf_self, pdf_other + + class PickPDF(ProbabilityDensityFunction): def __init__(self, x, lbound, midpoint, rbound, decfact=0.01, type='gauss'): From 2956f3b7336d5daa1a3dfa6e819ebdeafeccc2de Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 11 Feb 2016 11:27:18 +0100 Subject: [PATCH 0803/1144] [edit] subclassing has been substituted by classmethod fromPick opportunity for creating a PDF object --- pylot/core/util/pdf.py | 87 +++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 7498f174..0fa1753a 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -8,6 +8,13 @@ __version__ = _getVersionString() __author__ = 'sebastianw' +def create_axis(x0, incr, npts): + ax = np.zeros(npts) + for i in range(npts): + ax[i] = x0 + incr * i + return ax + + def gauss_parameter(te, tm, tl, eta): ''' takes three onset times and returns the parameters sig1, sig2, a1 and a2 @@ -109,19 +116,22 @@ class ProbabilityDensityFunction(object): version = __version__ - def __init__(self, x, pdf): - self.axis = x + def __init__(self, x0, incr, npts, pdf): + self.x0 = x0 + self.incr = incr + self.npts = npts + self.axis = create_axis(x0, incr, npts) self.data = pdf def __add__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - x, pdf_self, pdf_other = self.rearrange(other) + incr, limits, pdf_self, pdf_other = self.rearrange(other) pdf = np.convolve(pdf_self, pdf_other, 'same') * self.delta() - return ProbabilityDensityFunction(x, pdf) + return ProbabilityDensityFunction(incr, limits, pdf) def __sub__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ @@ -138,22 +148,38 @@ class ProbabilityDensityFunction(object): @property def data(self): - return self.data + return self._pdf @data.setter def data(self, pdf): - self.data = np.array(pdf) + self._pdf = np.array(pdf) @property def axis(self): - return self.axis + return self._x @axis.setter def axis(self, x): - self.axis = np.array(x) + self._x = np.array(x) - def delta(self): - return self.axis[1] - self.axis[0] + @classmethod + def fromPick(self, incr, lbound, midpoint, rbound, decfact=0.01, type='gauss'): + ''' + Initialize a new ProbabilityDensityFunction object. + Takes incr, lbound, midpoint and rbound to derive x0 and the number + of points npts for the axis vector. + Maximum density + is given at the midpoint and on the boundaries the function has + declined to decfact times the maximum value. Integration of the + function over a particular interval gives the probability for the + variable value to be in that interval. + ''' + margin = 1.5 * np.max(midpoint - lbound, rbound - midpoint) + x0 = midpoint - margin + npts = 2 * int(margin // incr) + params = parameter[type](lbound, midpoint, rbound, decfact) + pdf = branches[type](create_axis(x0, incr, npts), midpoint, *params) + return ProbabilityDensityFunction(x0, incr, npts, pdf) def rearrange(self, other, plus=True): ''' @@ -200,44 +226,3 @@ class ProbabilityDensityFunction(object): pdf_other[ostart:o_end] = other.data return x, pdf_self, pdf_other - - -class PickPDF(ProbabilityDensityFunction): - - def __init__(self, x, lbound, midpoint, rbound, decfact=0.01, type='gauss'): - ''' - Initialize a new ProbabilityDensityFunction object. Takes arguments x, - lbound, midpoint and rbound to define a probability density function - defined on the interval of x. Maximum density is given at the midpoint - and on the boundaries the function has declined to decfact times the - maximum value. Integration of the function over a particular interval - gives the probability for the variable value to lie in that interval. - :param x: interval on which the pdf is defined - :param lbound: left boundary - :param midpoint: point of maximum probability density - :param rbound: right boundary - :param decfact: boundary decline factor - :param type: determines the type of the probability density function's - branches - ''' - - self.nodes = dict(te=lbound, tm=midpoint, tl=rbound, eta=decfact) - self.type = type - super(PickPDF, self).__init__(x, self.pdf()) - - @property - def type(self): - return self.type - - @type.setter - def type(self, type): - self.type = type - - def params(self): - return parameter[self.type](**self.nodes) - - def get(self, key): - return self.nodes[key] - - def pdf(self): - return branches[self.type](self.axis, self.get('tm'), *self.params()) From d5e16d64da1efcb3bd3aec016592ec0ea0d2b213 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 12 Feb 2016 09:50:59 +0100 Subject: [PATCH 0804/1144] [edit] implemented method to derive limits for the special methods for addition and subtraction --- pylot/core/util/pdf.py | 103 +++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 0fa1753a..09472d47 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import numpy as np +from obspy import UTCDateTime from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -127,21 +128,21 @@ class ProbabilityDensityFunction(object): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - incr, limits, pdf_self, pdf_other = self.rearrange(other) + x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) - pdf = np.convolve(pdf_self, pdf_other, 'same') * self.delta() + pdf = np.convolve(pdf_self, pdf_other, 'same') * incr - return ProbabilityDensityFunction(incr, limits, pdf) + return ProbabilityDensityFunction(x0, incr, npts, pdf) def __sub__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - x, pdf_self, pdf_other = self.rearrange(other, plus=False) + x0, incr, npts, pdf_self, pdf_other = self.rearrange(other, plus=False) - pdf = np.convolve(pdf_self, pdf_other[::-1], 'same') * self.delta() + pdf = np.correlate(pdf_self, pdf_other, 'same') * incr - return ProbabilityDensityFunction(x, pdf) + return ProbabilityDensityFunction(x0, incr, npts, pdf) def __nonzero__(self): return True @@ -176,11 +177,59 @@ class ProbabilityDensityFunction(object): ''' margin = 1.5 * np.max(midpoint - lbound, rbound - midpoint) x0 = midpoint - margin - npts = 2 * int(margin // incr) + npts = int(2 * margin // incr) params = parameter[type](lbound, midpoint, rbound, decfact) - pdf = branches[type](create_axis(x0, incr, npts), midpoint, *params) + try: + pdf = branches[type](create_axis(x0, incr, npts), midpoint, *params) + except TypeError as e: + print('Warning:\n' + e.message + '\n' + 'trying timestamp instead') + assert isinstance(midpoint, UTCDateTime), 'object not capable of' \ + ' timestamp representation' + pdf = branches[type](create_axis(x0, incr, npts), + midpoint.timestamp, *params) return ProbabilityDensityFunction(x0, incr, npts, pdf) + def findlimits(self, incr, l1, l2, r1, r2, max_npts=1e5): + ''' + Takes an increment incr and two left and two right limits and returns + the left most limit and the minimum number of points needed to cover + the whole given interval. + :param incr: + :param l1: + :param l2: + :param r1: + :param r2: + :param max_npts: + :return: + ''' + + if l1 >= l2 and r1 >= r2: + x0 = l2 + npts = int(r1 - x0 // incr) + elif l1 < l2 and r1 >= r2: + x0 = l1 + npts = int(r1 - x0 // incr) + elif l1 >= l2 and r1 < r2: + x0 = l2 + npts = int(r2 - x0 // incr) + elif l1 >= r2: + x0 = l2 + npts = int(r1 - x0 // incr) + elif l2 >= r1: + x0 = l1 + npts = int(r2 - x0 // incr) + else: + x0 = None + npts = None + + if npts > max_npts: + raise ValueError('Maximum number of points exceeded:\n' + 'max_npts - %d\n' + 'npts - %d\n' % (max_npts, npts)) + + return x0, npts + + def rearrange(self, other, plus=True): ''' Method rearrange takes another Probability Density Function and returns @@ -195,34 +244,30 @@ class ProbabilityDensityFunction(object): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - sd = self.delta() - od = other.delta() + smin = np.min(self.axis) + smax = np.max(self.axis) + omin = np.min(other.axis) + omax = np.max(other.axis) - samin = np.min(self.axis) - oamin = np.min(other.axis) - - # test if 0 is a sampling node - nodes_test = (not samin % sd and not oamin % od) - - # test if sampling rates match and if 0 is a sampling node - if sd == od and nodes_test: - if plus: - max = np.max(self.axis) + np.max(other.axis) - else: - max = np.max(np.max(self.axis), np.max(other.axis)) - x = np.arange(-max, max + sd, sd) + if not self.incr == other.incr: + raise NotImplementedError('Upsampling of the lower sampled PDF not implemented yet!') else: - raise ValueError('Sampling rates do not match or nodes shifted!') + incr = self.incr - pdf_self = np.zeros(x.size) - pdf_other = np.zeros(x.size) + x0, npts = self.findlimits(incr, smin, smax, omin, omax) - sstart = np.where(x == samin) + pdf_self = np.zeros(npts) + pdf_other = np.zeros(npts) + + x = create_axis(x0, incr, npts) + + sstart = np.where(x == smin) s_end = sstart + self.data.size - ostart = np.where(x == oamin) + ostart = np.where(x == omin) o_end = ostart + other.data.size pdf_self[sstart:s_end] = self.data pdf_other[ostart:o_end] = other.data - return x, pdf_self, pdf_other + return x0, incr, npts, pdf_self, pdf_other + From f2cad2e151861cf93b6c4cf3b35b5d0f032ded15 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 12 Feb 2016 10:09:12 +0100 Subject: [PATCH 0805/1144] [edit] changed implementation name from findlimits to commonlimits which is probably more intuitive for the user along with a new signature using other instead of giving the limits in advance --- pylot/core/util/pdf.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 09472d47..4a37dfd3 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -132,16 +132,24 @@ class ProbabilityDensityFunction(object): pdf = np.convolve(pdf_self, pdf_other, 'same') * incr + # shift axis values for correct plotting + midpoint = int(npts // 2) + 1 + x0 += incr * midpoint + return ProbabilityDensityFunction(x0, incr, npts, pdf) def __sub__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - x0, incr, npts, pdf_self, pdf_other = self.rearrange(other, plus=False) + x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) pdf = np.correlate(pdf_self, pdf_other, 'same') * incr + # shift axis values for correct plotting + midpoint = int(npts // 2) + 1 + x0 -= incr * midpoint + return ProbabilityDensityFunction(x0, incr, npts, pdf) def __nonzero__(self): @@ -189,7 +197,7 @@ class ProbabilityDensityFunction(object): midpoint.timestamp, *params) return ProbabilityDensityFunction(x0, incr, npts, pdf) - def findlimits(self, incr, l1, l2, r1, r2, max_npts=1e5): + def commonlimits(self, incr, other, max_npts=1e5): ''' Takes an increment incr and two left and two right limits and returns the left most limit and the minimum number of points needed to cover @@ -203,6 +211,11 @@ class ProbabilityDensityFunction(object): :return: ''' + l1 = self.x0 + r1 = np.max(self.axis) + l2 = other.x0 + r2 = np.max(other.axis) + if l1 >= l2 and r1 >= r2: x0 = l2 npts = int(r1 - x0 // incr) @@ -230,40 +243,34 @@ class ProbabilityDensityFunction(object): return x0, npts - def rearrange(self, other, plus=True): + def rearrange(self, other): ''' Method rearrange takes another Probability Density Function and returns a new axis with mid-point 0 and covering positive and negative range of axis values, either containing the maximum value of both axis or the sum of the maxima :param other: - :param plus: :return: ''' assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - smin = np.min(self.axis) - smax = np.max(self.axis) - omin = np.min(other.axis) - omax = np.max(other.axis) - if not self.incr == other.incr: raise NotImplementedError('Upsampling of the lower sampled PDF not implemented yet!') else: incr = self.incr - x0, npts = self.findlimits(incr, smin, smax, omin, omax) + x0, npts = self.commonlimits(incr, other) pdf_self = np.zeros(npts) pdf_other = np.zeros(npts) x = create_axis(x0, incr, npts) - sstart = np.where(x == smin) + sstart = np.where(x == self.x0) s_end = sstart + self.data.size - ostart = np.where(x == omin) + ostart = np.where(x == other.x0) o_end = ostart + other.data.size pdf_self[sstart:s_end] = self.data From 3ee221b8ebb58463d4cdc76d52061bf5878c39da Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 15 Feb 2016 20:13:02 +0100 Subject: [PATCH 0806/1144] [edit] implementation of difference of two independent random variable as the correlation of the PDFs completed; summation pending due to unclear axis determination of the resultant PDF --- pylot/core/util/pdf.py | 110 ++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 41 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 4a37dfd3..660d1601 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -15,6 +15,9 @@ def create_axis(x0, incr, npts): ax[i] = x0 + incr * i return ax +def find_nearest_index(array, value): + return (np.abs(array-value)).argmin() + def gauss_parameter(te, tm, tl, eta): ''' @@ -128,15 +131,16 @@ class ProbabilityDensityFunction(object): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) - - pdf = np.convolve(pdf_self, pdf_other, 'same') * incr - - # shift axis values for correct plotting - midpoint = int(npts // 2) + 1 - x0 += incr * midpoint - - return ProbabilityDensityFunction(x0, incr, npts, pdf) + raise NotImplementedError('implementation of resulting axis unclear - feature pending!') + # x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) + # + # pdf = np.convolve(pdf_self, pdf_other, 'same') * incr + # + # # shift axis values for correct plotting + # midpoint = npts / 2 + # x0 = incr * midpoint + # + # return ProbabilityDensityFunction(x0, incr, npts, pdf) def __sub__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ @@ -147,8 +151,8 @@ class ProbabilityDensityFunction(object): pdf = np.correlate(pdf_self, pdf_other, 'same') * incr # shift axis values for correct plotting - midpoint = int(npts // 2) + 1 - x0 -= incr * midpoint + midpoint = npts / 2 + x0 = -incr * midpoint return ProbabilityDensityFunction(x0, incr, npts, pdf) @@ -172,29 +176,49 @@ class ProbabilityDensityFunction(object): self._x = np.array(x) @classmethod - def fromPick(self, incr, lbound, midpoint, rbound, decfact=0.01, type='gauss'): + def fromPick(self, incr, lbound, barycentre, rbound, decfact=0.01, type='gauss'): ''' Initialize a new ProbabilityDensityFunction object. - Takes incr, lbound, midpoint and rbound to derive x0 and the number + Takes incr, lbound, barycentre and rbound to derive x0 and the number of points npts for the axis vector. Maximum density - is given at the midpoint and on the boundaries the function has + is given at the barycentre and on the boundaries the function has declined to decfact times the maximum value. Integration of the function over a particular interval gives the probability for the variable value to be in that interval. ''' - margin = 1.5 * np.max(midpoint - lbound, rbound - midpoint) - x0 = midpoint - margin - npts = int(2 * margin // incr) - params = parameter[type](lbound, midpoint, rbound, decfact) + + # derive adequate window of definition + margin = 1.5 * np.max([barycentre - lbound, rbound - barycentre]) + + # find midpoint accounting also for `~obspy.UTCDateTime` object usage try: - pdf = branches[type](create_axis(x0, incr, npts), midpoint, *params) + midpoint = (rbound + lbound) / 2 + except TypeError: + midpoint = (rbound + float(lbound)) / 2 + + # find x0 on a grid point and sufficient npts + n = int(np.ceil((barycentre - midpoint) / incr)) + m = int(np.ceil((margin / incr))) + midpoint = barycentre - n * incr + margin = m * incr + x0 = midpoint - margin + npts = 2 * m + + # calculate parameter for pdf representing function + params = parameter[type](lbound, barycentre, rbound, decfact) + + # calculate pdf values + try: + pdf = branches[type](create_axis(x0, incr, npts), barycentre, *params) except TypeError as e: print('Warning:\n' + e.message + '\n' + 'trying timestamp instead') - assert isinstance(midpoint, UTCDateTime), 'object not capable of' \ + assert isinstance(barycentre, UTCDateTime), 'object not capable of' \ ' timestamp representation' pdf = branches[type](create_axis(x0, incr, npts), - midpoint.timestamp, *params) + barycentre.timestamp, *params) + + # return the object return ProbabilityDensityFunction(x0, incr, npts, pdf) def commonlimits(self, incr, other, max_npts=1e5): @@ -209,37 +233,41 @@ class ProbabilityDensityFunction(object): :param r2: :param max_npts: :return: + ''' + # >>> manu = ProbabilityDensityFunction.fromPick(0.01, 0.3, 0.5, 0.54) + # >>> auto = ProbabilityDensityFunction.fromPick(0.01, 0.3, 0.34, 0.54) + # >>> manu.commonlimits(0.01, auto) + # ( l1 = self.x0 - r1 = np.max(self.axis) + r1 = l1 + self.incr * self.npts l2 = other.x0 - r2 = np.max(other.axis) + r2 = l2 + other.incr * other.npts - if l1 >= l2 and r1 >= r2: - x0 = l2 - npts = int(r1 - x0 // incr) - elif l1 < l2 and r1 >= r2: + if l1 < l2: x0 = l1 - npts = int(r1 - x0 // incr) - elif l1 >= l2 and r1 < r2: - x0 = l2 - npts = int(r2 - x0 // incr) - elif l1 >= r2: - x0 = l2 - npts = int(r1 - x0 // incr) - elif l2 >= r1: - x0 = l1 - npts = int(r2 - x0 // incr) else: - x0 = None - npts = None + x0 = l2 + + # calculate index for rounding + ri = int(np.ceil(np.abs(np.log10(incr)))) + + if r1 < r2: + npts = int(round(r2 - x0, ri) // incr) + else: + npts = int(round(r1 - x0, ri) // incr) if npts > max_npts: raise ValueError('Maximum number of points exceeded:\n' 'max_npts - %d\n' 'npts - %d\n' % (max_npts, npts)) + npts = np.max([npts, self.npts, other.npts]) + + if npts < self.npts or npts < other.npts: + raise ValueError('new npts is to small') + return x0, npts @@ -268,9 +296,9 @@ class ProbabilityDensityFunction(object): x = create_axis(x0, incr, npts) - sstart = np.where(x == self.x0) + sstart = find_nearest_index(x, self.x0) s_end = sstart + self.data.size - ostart = np.where(x == other.x0) + ostart = find_nearest_index(x, other.x0) o_end = ostart + other.data.size pdf_self[sstart:s_end] = self.data From e7b454bae8a140427acbb79e627b5250a3a88a37 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 16 Feb 2016 08:47:42 +0100 Subject: [PATCH 0807/1144] [edit] just style related changes --- pylot/core/util/pdf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 660d1601..2b354a23 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -15,6 +15,7 @@ def create_axis(x0, incr, npts): ax[i] = x0 + incr * i return ax + def find_nearest_index(array, value): return (np.abs(array-value)).argmin() @@ -135,7 +136,7 @@ class ProbabilityDensityFunction(object): # x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) # # pdf = np.convolve(pdf_self, pdf_other, 'same') * incr - # + # # # shift axis values for correct plotting # midpoint = npts / 2 # x0 = incr * midpoint From 96ea0d7ac367c0bff74885a1180ccac48c81e0ae Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 1 Mar 2016 12:26:56 +0100 Subject: [PATCH 0808/1144] improved readability of formula --- pylot/core/util/pdf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 2b354a23..e070ccc3 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -82,9 +82,9 @@ def gauss_branches(x, mu, sig1, sig2, a1, a2): fun_vals = [] for k in x: if k < mu: - fun_vals.append(a1 * 1 / (np.sqrt(2 * np.pi) * sig1) * np.exp(-((k - mu) / sig1)**2 / 2 )) + fun_vals.append(a1 * 1 / (np.sqrt(2 * np.pi) * sig1) * np.exp(-((k - mu) / sig1) ** 2 / 2)) else: - fun_vals.append(a2 * 1 / (np.sqrt(2 * np.pi) * sig2) * np.exp(-((k - mu) / sig2)**2 / 2)) + fun_vals.append(a2 * 1 / (np.sqrt(2 * np.pi) * sig2) * np.exp(-((k - mu) / sig2) ** 2 / 2)) return np.array(fun_vals) From ddc682de95ea09c57aa8940dcc1494b2a42ccf60 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 2 Mar 2016 11:04:11 +0100 Subject: [PATCH 0809/1144] [bugfix] selected file filter should be evaluated --- QtPyLoT.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 174ed447..f9c5b313 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -431,13 +431,14 @@ class MainWindow(QMainWindow): print('warning: {0}'.format(e)) directory = os.path.join(self.getRoot(), self.getEventFileName()) file_filter = "QuakeML file (*.xml);;VELEST observation file format (*.cnv);;NonLinLoc observation file (*.obs)" - fname = QFileDialog.getSaveFileName(self, 'Save event data ...', + fname, selected_filter = QFileDialog.getSaveFileName(self, 'Save event data ...', directory, file_filter) - fbasename, exform = os.path.splitext(fname[0]) + fbasename, exform = os.path.splitext(fname) + + if not exform and selected_filter: + exform = selected_filter.split('*')[1][:-1] - if not exform: - exform = file_filter[0].split('*')[1][:-1] return fbasename, exform settings = QSettings() From 5e26519d662cfd29d9c0de5f4a160ca287a4edd1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 2 Mar 2016 11:06:31 +0100 Subject: [PATCH 0810/1144] [fix] avoid usage of hardly programmed indices --- pylot/core/pick/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index d7584bff..20e6cb14 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -321,7 +321,7 @@ def crossings_nonzero_all(data): return ((pos[:-1] & npos[1:]) | (npos[:-1] & pos[1:])).nonzero()[0] -def getSNR(X, TSNR, t1): +def getSNR(X, TSNR, t1, tracenum=0): ''' Function to calculate SNR of certain part of seismogram relative to given time (onset) out of given noise and signal windows. A safety gap @@ -340,9 +340,11 @@ def getSNR(X, TSNR, t1): assert isinstance(X, Stream), "%s is not a stream object" % str(X) - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, - X[0].stats.delta) + x = X[tracenum].data + npts = X[tracenum].stats.npts + sr = X[tracenum].stats.sampling_rate + dt = X[tracenum].stats.delta + t = np.arange(0, npts / sr, dt) # get noise window inoise = getnoisewin(t, t1, TSNR[0], TSNR[1]) From 8c7a2af2db1fdea6675ac5d1f7e7c9a737312d21 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 2 Mar 2016 11:07:32 +0100 Subject: [PATCH 0811/1144] [fix] do not try to proceed if no picks are available --- pylot/core/read/data.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 3cef4d44..6cc47acf 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -432,11 +432,13 @@ class Data(object): if firstonset is None or firstonset > onset: firstonset = onset - if 'smi:local' in self.getID(): + if 'smi:local' in self.getID() and firstonset: fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') ID = ResourceIdentifier('event/' + fonset_str) ID.convertIDToQuakeMLURI(authority_id=authority_id) self.getEvtData().resource_id = ID + else: + print('No picks to apply!') def applyArrivals(arrivals): """ From c09a3271f907a6a2c40924f237a8f45a40968bc2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 2 Mar 2016 11:08:14 +0100 Subject: [PATCH 0812/1144] make use of new option to getSNR --- pylot/core/util/widgets.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1f9a318b..f13a09fd 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -470,8 +470,12 @@ class PickDlg(QDialog): noise_win = settings.value('picking/noise_win_P', 5.) gap_win = settings.value('picking/gap_win_P', .2) signal_win = settings.value('picking/signal_win_P', 3.) + itrace = int(trace_number) - result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick) + while itrace > len(wfdata) - 1: + itrace -= 1 + + result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick, itrace) snr = result[0] noiselevel = result[2] * nfac From c14650d33cde2134d1c32a221bd4831d224a45a6 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 2 Mar 2016 13:34:24 +0100 Subject: [PATCH 0813/1144] [change] changed filter settings for local seismic data --- filter.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filter.in b/filter.in index 16aed0b7..8303b88a 100644 --- a/filter.in +++ b/filter.in @@ -1,2 +1,2 @@ -P -S bandpass 4 0.5 5.0 \ No newline at end of file +P bandpass 4 2.0 20.0 +S bandpass 4 2.0 15.0 \ No newline at end of file From 827a0da21005c96e641303ada99a4a813f65d5a4 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 2 Mar 2016 13:36:18 +0100 Subject: [PATCH 0814/1144] [fix] imports to obspy's read_events changed due to deprecated naming of function readEvents --- pylot/core/loc/nll.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index 00ef8b02..36de5625 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -3,7 +3,7 @@ import subprocess import os -from obspy.core.event import readEvents +from obspy import readEvents from pylot.core.pick.utils import writephases from pylot.core.util.utils import getPatternLine from pylot.core.util.version import get_git_version as _getVersionString From 848d11270b66587a51398459ff0c144d6cb144a2 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 2 Mar 2016 13:37:40 +0100 Subject: [PATCH 0815/1144] [fixes #181] now picking on horizontal components 1, 2 and N,E is possible --- pylot/core/util/widgets.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index f13a09fd..f782a147 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -422,6 +422,11 @@ class PickDlg(QDialog): trace = selectTrace(trace, 'NE') if trace: wfdata.append(trace) + elif component == '1' or component == '2': + for trace in self.getWFData(): + trace = selectTrace(trace, '12') + if trace: + wfdata.append(trace) elif component == 'Z': wfdata = self.getWFData().select(component=component) return wfdata @@ -527,10 +532,16 @@ class PickDlg(QDialog): inoise = getnoisewin(t, ini_pick, noise_win, gap_win) trace = demeanTrace(trace, inoise) - horiz_comp = ('n', 'e') + try: + horiz_comp = ('n', 'e') + data = scaleWFData(data, noiselevel * 2.5, horiz_comp) + except IndexError as e: + print('warning: {0}'.format(e)) + horiz_comp = ('1', '2') data = scaleWFData(data, noiselevel * 2.5, horiz_comp) + x_res = getResolutionWindow(snr) self.setXLims(tuple([ini_pick - x_res, ini_pick + x_res])) From 6108519acb908ddc51274337f95383cfdd53507d Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 3 Mar 2016 10:37:35 +0100 Subject: [PATCH 0816/1144] [closes #182] noiselevel now determined as the maximum of the absolute values of the noisewindow (holds also for signallevel); now the SNR and noiselevel is determined from the filtered waveform --- pylot/RELEASE-VERSION | 2 +- pylot/core/pick/utils.py | 8 ++++++-- pylot/core/util/widgets.py | 19 +++++++++++-------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 070ed7aa..bfb55486 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -a31e-dirty +848d-dirty diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 20e6cb14..2044b388 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -362,8 +362,12 @@ def getSNR(X, TSNR, t1, tracenum=0): x = x - np.mean(x[inoise]) # calculate ratios - noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) - signallevel = np.sqrt(np.mean(np.square(x[isignal]))) + # noiselevel = np.sqrt(np.mean(np.square(x[inoise]))) + # signallevel = np.sqrt(np.mean(np.square(x[isignal]))) + + noiselevel = np.abs(x[inoise]).max() + signallevel = np.abs(x[isignal]).max() + SNR = signallevel / noiselevel SNRdB = 10 * np.log10(SNR) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index f782a147..fe3141b4 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -516,31 +516,34 @@ class PickDlg(QDialog): gap_win = settings.value('picking/gap_win_P', .2) signal_win = settings.value('picking/signal_win_P', 3.) - result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick) - - snr = result[0] - noiselevel = result[2] * nfac - + # copy data for plotting data = self.getWFData().copy() + # filter data and trace on which is picked prior to determination of SNR phase = self.selectPhase.currentText() filteroptions = self.getFilterOptions(phase).parseFilterOptions() data.filter(**filteroptions) + wfdata.filter(**filteroptions) + # determine SNR and noiselevel + result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick) + snr = result[0] + noiselevel = result[2] * nfac + + # prepare plotting of data for trace in data: t = prepTimeAxis(trace.stats.starttime - self.getStartTime(), trace) inoise = getnoisewin(t, ini_pick, noise_win, gap_win) trace = demeanTrace(trace, inoise) + # account for non-oriented horizontal waveforms try: horiz_comp = ('n', 'e') data = scaleWFData(data, noiselevel * 2.5, horiz_comp) except IndexError as e: print('warning: {0}'.format(e)) horiz_comp = ('1', '2') - - data = scaleWFData(data, noiselevel * 2.5, horiz_comp) - + data = scaleWFData(data, noiselevel * 2.5, horiz_comp) x_res = getResolutionWindow(snr) From c2168dbef3a3b692f0e5f486917c6bebca8632f8 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 3 Mar 2016 11:12:27 +0100 Subject: [PATCH 0817/1144] [fixes #185] filtering of waveforms now conserved for picking --- pylot/core/util/widgets.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index fe3141b4..bd8d85a8 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -480,6 +480,15 @@ class PickDlg(QDialog): while itrace > len(wfdata) - 1: itrace -= 1 + # copy data for plotting + data = self.getWFData().copy() + + # filter data and trace on which is picked prior to determination of SNR + phase = self.selectPhase.currentText() + filteroptions = self.getFilterOptions(phase).parseFilterOptions() + data.filter(**filteroptions) + wfdata.filter(**filteroptions) + result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick, itrace) snr = result[0] @@ -488,16 +497,16 @@ class PickDlg(QDialog): x_res = getResolutionWindow(snr) # remove mean noise level from waveforms - wfdata = self.getWFData().copy() - for trace in wfdata: + for trace in data: t = prepTimeAxis(trace.stats.starttime - self.getStartTime(), trace) inoise = getnoisewin(t, ini_pick, noise_win, gap_win) trace = demeanTrace(trace=trace, window=inoise) + self.setXLims([ini_pick - x_res, ini_pick + x_res]) self.setYLims(np.array([-noiselevel * 2.5, noiselevel * 2.5]) + trace_number) - self.getPlotWidget().plotWFData(wfdata=wfdata, + self.getPlotWidget().plotWFData(wfdata=data, title=self.getStation() + ' picking mode', zoomx=self.getXLims(), From 6f26ab07b73c9c2218f82e2a35c40c75d78fc169 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 3 Mar 2016 13:17:10 +0100 Subject: [PATCH 0818/1144] [closes #184] saving picks works saving now works without openening the filename prompt twice or more often --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f9c5b313..47619cc4 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -456,7 +456,7 @@ class MainWindow(QMainWindow): ret = msgBox.exec_() if ret == QMessageBox.Save: self.getData().resetPicks() - self.saveData() + return self.saveData() elif ret == QMessageBox.Cancel: return False try: From 18785edf6800134678e08f906928d504e0ace0b1 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 3 Mar 2016 14:02:49 +0100 Subject: [PATCH 0819/1144] [fixes #186] filteroptions are only parsed if they are not NoneType --- pylot/core/util/widgets.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index bd8d85a8..d9b4cd22 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -486,8 +486,9 @@ class PickDlg(QDialog): # filter data and trace on which is picked prior to determination of SNR phase = self.selectPhase.currentText() filteroptions = self.getFilterOptions(phase).parseFilterOptions() - data.filter(**filteroptions) - wfdata.filter(**filteroptions) + if filteroptions: + data.filter(**filteroptions) + wfdata.filter(**filteroptions) result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick, itrace) @@ -531,8 +532,9 @@ class PickDlg(QDialog): # filter data and trace on which is picked prior to determination of SNR phase = self.selectPhase.currentText() filteroptions = self.getFilterOptions(phase).parseFilterOptions() - data.filter(**filteroptions) - wfdata.filter(**filteroptions) + if filteroptions: + data.filter(**filteroptions) + wfdata.filter(**filteroptions) # determine SNR and noiselevel result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick) From 4606f8480990e0660b019449317d5356f68340d3 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 3 Mar 2016 15:14:17 +0100 Subject: [PATCH 0820/1144] [new] implementing comparison methods into pdf class --- pylot/core/util/pdf.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index e070ccc3..aaa70194 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -133,15 +133,13 @@ class ProbabilityDensityFunction(object): 'both operands must be of type ProbabilityDensityFunction' raise NotImplementedError('implementation of resulting axis unclear - feature pending!') - # x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) - # - # pdf = np.convolve(pdf_self, pdf_other, 'same') * incr - # - # # shift axis values for correct plotting - # midpoint = npts / 2 - # x0 = incr * midpoint - # - # return ProbabilityDensityFunction(x0, incr, npts, pdf) + x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) + pdf = np.convolve(pdf_self, pdf_other, 'same') * incr + + # shift axis values for correct plotting + npts *= 2 + x0 *= 2 + return ProbabilityDensityFunction(x0, incr, npts, pdf) def __sub__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ @@ -222,6 +220,25 @@ class ProbabilityDensityFunction(object): # return the object return ProbabilityDensityFunction(x0, incr, npts, pdf) + def expectation(self): + ''' + returns the expectation value of the actual pdf object + + ..formula:: + mu_{\Delta t} = \int\limits_{-\infty}^\infty x \cdot f(x)dx + + :return float: rval + ''' + + rval = 0 + for n, x in enumerate(self.axis): + rval += x * self.data[n] + return rval * self.incr + + def standard_deviation(self): + pass + + def commonlimits(self, incr, other, max_npts=1e5): ''' Takes an increment incr and two left and two right limits and returns From 78a5a5117af76b0a7ba09c7f7448ab2c650dd023 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 4 Mar 2016 06:25:17 +0100 Subject: [PATCH 0821/1144] [add] new function added which return the index of the value of an array which is nearest to a desired value --- pylot/core/util/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index e4e856f9..0f72fdf6 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -246,6 +246,16 @@ def findComboBoxIndex(combo_box, val): """ return combo_box.findText(val) if combo_box.findText(val) is not -1 else 0 +def find_nearest(array, value): + ''' + Function find_nearest takes an array and a value and returns the + index of the nearest value found in the array. + :param array: + :param value: + :return: + ''' + return (np.abs(array-value)).argmin() + def fnConstructor(s): ''' From e6b5848f36f88e6beac3faaa6dfd8e3e5dbcc223 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 4 Mar 2016 06:27:11 +0100 Subject: [PATCH 0822/1144] [add] untested implementations of standard deviation and probability value determination methods to class ProbabilityDensityFunction --- pylot/core/util/pdf.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index aaa70194..3149fc0a 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -3,6 +3,7 @@ import numpy as np from obspy import UTCDateTime +from pylot.core.util.utils import find_nearest from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -133,13 +134,13 @@ class ProbabilityDensityFunction(object): 'both operands must be of type ProbabilityDensityFunction' raise NotImplementedError('implementation of resulting axis unclear - feature pending!') - x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) - pdf = np.convolve(pdf_self, pdf_other, 'same') * incr - - # shift axis values for correct plotting - npts *= 2 - x0 *= 2 - return ProbabilityDensityFunction(x0, incr, npts, pdf) + # x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) + # pdf = np.convolve(pdf_self, pdf_other, 'same') * incr + # + # # shift axis values for correct plotting + # npts *= 2 + # x0 *= 2 + # return ProbabilityDensityFunction(x0, incr, npts, pdf) def __sub__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ @@ -236,8 +237,27 @@ class ProbabilityDensityFunction(object): return rval * self.incr def standard_deviation(self): - pass + mu = self.expectation() + rval = 0 + for n, x in enumerate(self.axis): + rval += (x - mu) ** 2 * self.data[n] + return rval * self.incr + def prob_lt_val(self, value): + if value <= self.axis[0] or value > self.axis[-1]: + raise ValueError('value out of bounds: {0}'.format(value)) + return self.data[self.axis <= value].sum() * self.incr + + def prob_gt_val(self, value): + if value < self.axis[0] or value >= self.axis[-1]: + raise ValueError('value out of bounds: {0}'.format(value)) + return self.data[self.axis >= value].sum() * self.incr + + def prob_val(self, value): + if not (self.axis[0] <= value <= self.axis[-1]): + Warning('{0} not on axis'.format(value)) + return None + return self.data[find_nearest(self.axis, value)] * self.incr def commonlimits(self, incr, other, max_npts=1e5): ''' From 9f7f00314ad19706e19c191513f125d1d2eed67e Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 4 Mar 2016 14:13:44 +0100 Subject: [PATCH 0823/1144] [closes #187] pdf methods for some values like expectation and plotting ready --- pylot/core/util/pdf.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 3149fc0a..c3af13e0 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -157,7 +157,7 @@ class ProbabilityDensityFunction(object): return ProbabilityDensityFunction(x0, incr, npts, pdf) def __nonzero__(self): - return True + return bool(np.round(self.prob_gt_val(self.axis[0]), 4) == 1.) @property def data(self): @@ -259,6 +259,18 @@ class ProbabilityDensityFunction(object): return None return self.data[find_nearest(self.axis, value)] * self.incr + def plot(self): + import matplotlib.pyplot as plt + + plt.plot(self.axis, self.data) + plt.xlabel('x') + plt.ylabel('f(x)') + if self: + plt.title('Probability density function') + else: + plt.title('Function not suitable as probability density function') + plt.show() + def commonlimits(self, incr, other, max_npts=1e5): ''' Takes an increment incr and two left and two right limits and returns From e6f404a219bde435463bbed1c10ac22aa21fdb2e Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 7 Mar 2016 10:15:42 +0100 Subject: [PATCH 0824/1144] [closes #188] picks are set correctly now earliest and latest possible picks are now derived from the actually displayed (in some cases filtered) waveforms --- pylot/core/util/widgets.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d9b4cd22..0a2b1c7b 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -521,10 +521,10 @@ class PickDlg(QDialog): settings = QSettings() - nfac = settings.value('picking/nfac_P', 1.5) - noise_win = settings.value('picking/noise_win_P', 5.) - gap_win = settings.value('picking/gap_win_P', .2) - signal_win = settings.value('picking/signal_win_P', 3.) + nfac = settings.value('picking/nfac_S', 1.5) + noise_win = settings.value('picking/noise_win_S', 5.) + gap_win = settings.value('picking/gap_win_S', .2) + signal_win = settings.value('picking/signal_win_S', 3.) # copy data for plotting data = self.getWFData().copy() @@ -583,15 +583,21 @@ class PickDlg(QDialog): pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global channel = self.getChannelID(round(gui_event.ydata)) - wfdata = self.getWFData().copy().select(channel=channel) - stime = self.getStartTime() - # get earliest and latest possible pick and symmetric pick error - [epp, lpp, spe] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) - # get name of phase actually picked phase = self.selectPhase.currentText() + # get filter parameter for the phase to be picked + filteroptions = self.getFilterOptions(phase).parseFilterOptions() + + # copy and filter data for earliest and latest possible picks + wfdata = self.getWFData().copy().select(channel=channel) + wfdata.filter(**filteroptions) + + # get earliest and latest possible pick and symmetric pick error + [epp, lpp, spe] = earllatepicker(wfdata, 1.5, (5., .5, 2.), pick) + # return absolute time values for phases + stime = self.getStartTime() epp = stime + epp mpp = stime + pick lpp = stime + lpp From db4dd47daa14b7e4c6fef141a19867623765d304 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 8 Mar 2016 20:35:56 +0100 Subject: [PATCH 0825/1144] [bugfixes] made some fixes to the subtraction method; in some cases ValueErrors are raised which are now handled but raised as Warning --- pylot/core/util/pdf.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index c3af13e0..f4063149 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import warnings import numpy as np from obspy import UTCDateTime from pylot.core.util.utils import find_nearest @@ -9,6 +10,13 @@ from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() __author__ = 'sebastianw' +def broadcast(pdf, si, ei, data): + try: + pdf[si:ei] = data + except ValueError as e: + warnings.warn(str(e), Warning) + return broadcast(pdf, si, ei, data[:-1]) + return pdf def create_axis(x0, incr, npts): ax = np.zeros(npts) @@ -16,11 +24,6 @@ def create_axis(x0, incr, npts): ax[i] = x0 + incr * i return ax - -def find_nearest_index(array, value): - return (np.abs(array-value)).argmin() - - def gauss_parameter(te, tm, tl, eta): ''' takes three onset times and returns the parameters sig1, sig2, a1 and a2 @@ -265,6 +268,7 @@ class ProbabilityDensityFunction(object): plt.plot(self.axis, self.data) plt.xlabel('x') plt.ylabel('f(x)') + plt.autoscale(axis='x', tight=True) if self: plt.title('Probability density function') else: @@ -346,13 +350,13 @@ class ProbabilityDensityFunction(object): x = create_axis(x0, incr, npts) - sstart = find_nearest_index(x, self.x0) + sstart = find_nearest(x, self.x0) s_end = sstart + self.data.size - ostart = find_nearest_index(x, other.x0) + ostart = find_nearest(x, other.x0) o_end = ostart + other.data.size - pdf_self[sstart:s_end] = self.data - pdf_other[ostart:o_end] = other.data + broadcast(pdf_self, sstart, s_end, self.data) + broadcast(pdf_other, ostart, o_end, other.data) return x0, incr, npts, pdf_self, pdf_other From 2de79eac7788d5529fce014b90cf609bb36e7292 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 9 Mar 2016 06:19:41 +0100 Subject: [PATCH 0826/1144] [change] moved recursive function for broadcasting pdf in a new shape into the class' body --- pylot/core/util/pdf.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index f4063149..c6d75ba4 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -10,14 +10,6 @@ from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() __author__ = 'sebastianw' -def broadcast(pdf, si, ei, data): - try: - pdf[si:ei] = data - except ValueError as e: - warnings.warn(str(e), Warning) - return broadcast(pdf, si, ei, data[:-1]) - return pdf - def create_axis(x0, incr, npts): ax = np.zeros(npts) for i in range(npts): @@ -224,6 +216,14 @@ class ProbabilityDensityFunction(object): # return the object return ProbabilityDensityFunction(x0, incr, npts, pdf) + def broadcast(self, pdf, si, ei, data): + try: + pdf[si:ei] = data + except ValueError as e: + warnings.warn(str(e), Warning) + return self.broadcast(pdf, si, ei, data[:-1]) + return pdf + def expectation(self): ''' returns the expectation value of the actual pdf object @@ -355,8 +355,7 @@ class ProbabilityDensityFunction(object): ostart = find_nearest(x, other.x0) o_end = ostart + other.data.size - broadcast(pdf_self, sstart, s_end, self.data) - broadcast(pdf_other, ostart, o_end, other.data) + pdf_self = self.broadcast(pdf_self, sstart, s_end, self.data) + pdf_other = self.broadcast(pdf_other, ostart, o_end, other.data) return x0, incr, npts, pdf_self, pdf_other - From f3467221cc2b06000e706bde36715f6cb6316aa7 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 9 Mar 2016 06:21:48 +0100 Subject: [PATCH 0827/1144] finished implementation of summation of two independent variables represented by a pdf --- pylot/core/util/pdf.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index c6d75ba4..dbcffc74 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -128,14 +128,13 @@ class ProbabilityDensityFunction(object): assert isinstance(other, ProbabilityDensityFunction), \ 'both operands must be of type ProbabilityDensityFunction' - raise NotImplementedError('implementation of resulting axis unclear - feature pending!') - # x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) - # pdf = np.convolve(pdf_self, pdf_other, 'same') * incr - # - # # shift axis values for correct plotting - # npts *= 2 - # x0 *= 2 - # return ProbabilityDensityFunction(x0, incr, npts, pdf) + x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) + pdf = np.convolve(pdf_self, pdf_other, 'full') * incr + + # shift axis values for correct plotting + npts = pdf.size + x0 *= 2 + return ProbabilityDensityFunction(x0, incr, npts, pdf) def __sub__(self, other): assert isinstance(other, ProbabilityDensityFunction), \ From a5fe83851422517dc7b558cce17bccd5106763d9 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 9 Mar 2016 06:22:26 +0100 Subject: [PATCH 0828/1144] added a str representation for the pdf class --- pylot/core/util/pdf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index dbcffc74..0aad8ad7 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -153,6 +153,9 @@ class ProbabilityDensityFunction(object): def __nonzero__(self): return bool(np.round(self.prob_gt_val(self.axis[0]), 4) == 1.) + def __str__(self): + return str(self.data) + @property def data(self): return self._pdf From 15080f1699e4dc83dc27e305b613113ffa50ac3c Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 9 Mar 2016 11:21:33 +0100 Subject: [PATCH 0829/1144] [fix] fixed the nonzero test for pdf definition that all values have to be greater than zero and the integration over the whole interval has to evaluate to 1 with given precision --- pylot/core/util/pdf.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 0aad8ad7..556f36ca 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -151,11 +151,18 @@ class ProbabilityDensityFunction(object): return ProbabilityDensityFunction(x0, incr, npts, pdf) def __nonzero__(self): - return bool(np.round(self.prob_gt_val(self.axis[0]), 4) == 1.) + prec = self.precision(self.incr) + gtzero = np.any(self.data >= 0) + probone = bool(np.round(self.prob_gt_val(self.axis[0]), prec) == 1.) + return (gtzero and probone) def __str__(self): return str(self.data) + @staticmethod + def precision(incr): + return int(np.ceil(np.abs(np.log10(incr)))) + @property def data(self): return self._pdf @@ -307,7 +314,7 @@ class ProbabilityDensityFunction(object): x0 = l2 # calculate index for rounding - ri = int(np.ceil(np.abs(np.log10(incr)))) + ri = self.precision(incr) if r1 < r2: npts = int(round(r2 - x0, ri) // incr) From b8d680f54f79b606431f63bb1f98f177ccc77bcb Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 18 Mar 2016 15:35:12 +0100 Subject: [PATCH 0830/1144] 189 Channels shown in wrong order. --- QtPyLoT.py | 2 +- pylot/RELEASE-VERSION | 2 +- pylot/core/util/defaults.py | 4 ++++ pylot/core/util/widgets.py | 12 +++++++++--- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 47619cc4..45b6345f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -535,7 +535,7 @@ class MainWindow(QMainWindow): comp = self.getComponent() title = 'section: {0} components'.format(zne_text[comp]) wfst = self.getData().getWFData().select(component=comp) - self.getPlotWidget().plotWFData(wfdata=wfst, title=title) + self.getPlotWidget().plotWFData(wfdata=wfst, title=title, mapping=False) self.draw() plotDict = self.getPlotWidget().getPlotDict() pos = plotDict.keys() diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index bfb55486..c35d452c 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -848d-dirty +1508-dirty diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index cef60d1c..25847da9 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -46,3 +46,7 @@ OUTPUTFORMATS = {'.xml':'QUAKEML', '.obs':'NLLOC_OBS'} LOCTOOLS = dict(nll = nll, hsat = hsat, velest = velest) + +COMPONENTS_MAPPING = dict(Z = 2, N = 1, E = 0) +COMPONENTS_MAPPING['1'] = 1 +COMPONENTS_MAPPING['2'] = 0 diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0a2b1c7b..52d23d03 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -25,7 +25,8 @@ from obspy import Stream, UTCDateTime from pylot.core.read.inputs import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ getResolutionWindow -from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS +from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS,\ + COMPONENTS_MAPPING from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ demeanTrace, isSorted, findComboBoxIndex @@ -87,13 +88,18 @@ class MPLWidget(FigureCanvas): self._parent = parent def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, - noiselevel=None, scaleddata=False): + noiselevel=None, scaleddata=False, mapping=True): self.getAxes().cla() self.clearPlotDict() wfstart, wfend = getGlobalTimes(wfdata) + nmax = 0 for n, trace in enumerate(wfdata): channel = trace.stats.channel station = trace.stats.station + if mapping: + comp = channel[-1] + n = COMPONENTS_MAPPING[comp] + nmax = n if n > nmax else nmax msg = 'plotting %s channel of station %s' % (channel, station) print(msg) stime = trace.stats.starttime - wfstart @@ -110,7 +116,7 @@ class MPLWidget(FigureCanvas): ylabel = '' self.updateWidget(xlabel, ylabel, title) self.setXLims([0, wfend - wfstart]) - self.setYLims([-0.5, n + 0.5]) + self.setYLims([-0.5, nmax + 0.5]) if zoomx is not None: self.setXLims(zoomx) if zoomy is not None: From d954c3bbe8010eba3069f0264aba048712d4fc2d Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 21 Mar 2016 11:14:16 +0100 Subject: [PATCH 0831/1144] [edit] restructuring autopicking module renamed several function and classes, moved script files to scripts --- pylot/core/active/seismicshot.py | 4 ++-- pylot/core/pick/autopick.py | 4 ++-- pylot/core/pick/{CharFuns.py => charfuns.py} | 0 pylot/core/pick/{Picker.py => picker.py} | 8 ++++---- pylot/core/pick/run_makeCF.py | 4 ++-- .../{pick/getnoisewin.py => scripts/pylot-noisewindow.py} | 2 +- .../pylot-pick-earliest-latest.py} | 0 .../fmpicker.py => scripts/pylot-pick-firstmotion.py} | 4 ++-- .../getsignalwin.py => scripts/pylot-signalwindow.py} | 2 +- pylot/core/{pick/getSNR.py => scripts/pylot-snr.py} | 2 +- test_autopick.py | 4 ++-- 11 files changed, 17 insertions(+), 17 deletions(-) rename pylot/core/pick/{CharFuns.py => charfuns.py} (100%) rename pylot/core/pick/{Picker.py => picker.py} (99%) rename pylot/core/{pick/getnoisewin.py => scripts/pylot-noisewindow.py} (87%) rename pylot/core/{pick/earllatepicker.py => scripts/pylot-pick-earliest-latest.py} (100%) rename pylot/core/{pick/fmpicker.py => scripts/pylot-pick-firstmotion.py} (74%) rename pylot/core/{pick/getsignalwin.py => scripts/pylot-signalwindow.py} (85%) rename pylot/core/{pick/getSNR.py => scripts/pylot-snr.py} (93%) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 8fbdbfb8..23fd6022 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -6,8 +6,8 @@ import numpy as np from obspy.core import read from obspy import Stream from obspy import Trace -from pylot.core.pick.CharFuns import HOScf -from pylot.core.pick.CharFuns import AICcf +from pylot.core.pick.charfuns import HOScf +from pylot.core.pick.charfuns import AICcf from pylot.core.pick.utils import getSNR from pylot.core.pick.utils import earllatepicker import matplotlib.pyplot as plt diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index fd86f600..8d81f144 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -12,8 +12,8 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np from scipy import integrate -from pylot.core.pick.Picker import AICPicker, PragPicker -from pylot.core.pick.CharFuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf +from pylot.core.pick.picker import AICPicker, PragPicker +from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ getSNR, fmpicker, checkPonsets, wadaticheck from pylot.core.util.utils import getPatternLine diff --git a/pylot/core/pick/CharFuns.py b/pylot/core/pick/charfuns.py similarity index 100% rename from pylot/core/pick/CharFuns.py rename to pylot/core/pick/charfuns.py diff --git a/pylot/core/pick/Picker.py b/pylot/core/pick/picker.py similarity index 99% rename from pylot/core/pick/Picker.py rename to pylot/core/pick/picker.py index b7d57481..da91d833 100644 --- a/pylot/core/pick/Picker.py +++ b/pylot/core/pick/picker.py @@ -22,10 +22,10 @@ calculated after Diehl & Kissling (2009). import numpy as np import matplotlib.pyplot as plt from pylot.core.pick.utils import getnoisewin, getsignalwin -from pylot.core.pick.CharFuns import CharacteristicFunction +from pylot.core.pick.charfuns import CharacteristicFunction import warnings -class AutoPicking(object): +class AutoPicker(object): ''' Superclass of different, automated picking algorithms applied on a CF determined using AIC, HOS, or AR prediction. @@ -137,7 +137,7 @@ class AutoPicking(object): self.Pick = None -class AICPicker(AutoPicking): +class AICPicker(AutoPicker): ''' Method to derive the onset time of an arriving phase based on CF derived from AIC. In order to get an impression of the quality of this inital pick, @@ -289,7 +289,7 @@ class AICPicker(AutoPicking): print('AICPicker: Could not find minimum, picking window too short?') -class PragPicker(AutoPicking): +class PragPicker(AutoPicker): ''' Method of pragmatic picking exploiting information given by CF. ''' diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/pick/run_makeCF.py index 2ecd636f..b57172a6 100755 --- a/pylot/core/pick/run_makeCF.py +++ b/pylot/core/pick/run_makeCF.py @@ -9,8 +9,8 @@ from obspy.core import read import matplotlib.pyplot as plt import numpy as np -from pylot.core.pick.CharFuns import CharacteristicFunction -from pylot.core.pick.Picker import AutoPicking +from pylot.core.pick.charfuns import CharacteristicFunction +from pylot.core.pick.picker import AutoPicker from pylot.core.pick.utils import * import glob import argparse diff --git a/pylot/core/pick/getnoisewin.py b/pylot/core/scripts/pylot-noisewindow.py similarity index 87% rename from pylot/core/pick/getnoisewin.py rename to pylot/core/scripts/pylot-noisewindow.py index 8c21913f..9a4cb00c 100644 --- a/pylot/core/pick/getnoisewin.py +++ b/pylot/core/scripts/pylot-noisewindow.py @@ -7,7 +7,7 @@ from pylot.core.pick.utils import getnoisewin if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--t', type=~numpy.array, help='numpy array of time stamps') + parser.add_argument('--t', type=numpy.array, help='numpy array of time stamps') parser.add_argument('--t1', type=float, help='time from which relativ to it noise window is extracted') parser.add_argument('--tnoise', type=float, help='length of time window [s] for noise part extraction') parser.add_argument('--tgap', type=float, help='safety gap between signal (t1=onset) and noise') diff --git a/pylot/core/pick/earllatepicker.py b/pylot/core/scripts/pylot-pick-earliest-latest.py similarity index 100% rename from pylot/core/pick/earllatepicker.py rename to pylot/core/scripts/pylot-pick-earliest-latest.py diff --git a/pylot/core/pick/fmpicker.py b/pylot/core/scripts/pylot-pick-firstmotion.py similarity index 74% rename from pylot/core/pick/fmpicker.py rename to pylot/core/scripts/pylot-pick-firstmotion.py index 0ccbc1d1..2952480e 100755 --- a/pylot/core/pick/fmpicker.py +++ b/pylot/core/scripts/pylot-pick-firstmotion.py @@ -13,8 +13,8 @@ from pylot.core.pick.utils import fmpicker if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') - parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') + parser.add_argument('--Xraw', type=obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') + parser.add_argument('--Xfilt', type=obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') diff --git a/pylot/core/pick/getsignalwin.py b/pylot/core/scripts/pylot-signalwindow.py similarity index 85% rename from pylot/core/pick/getsignalwin.py rename to pylot/core/scripts/pylot-signalwindow.py index 4b3013b8..2655c6ea 100644 --- a/pylot/core/pick/getsignalwin.py +++ b/pylot/core/scripts/pylot-signalwindow.py @@ -7,7 +7,7 @@ from pylot.core.pick.utils import getsignalwin if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--t', type=~numpy.array, help='numpy array of time stamps') + parser.add_argument('--t', type=numpy.array, help='numpy array of time stamps') parser.add_argument('--t1', type=float, help='time from which relativ to it signal window is extracted') parser.add_argument('--tsignal', type=float, help='length of time window [s] for signal part extraction') args = parser.parse_args() diff --git a/pylot/core/pick/getSNR.py b/pylot/core/scripts/pylot-snr.py similarity index 93% rename from pylot/core/pick/getSNR.py rename to pylot/core/scripts/pylot-snr.py index bbfbdba5..5c932e87 100644 --- a/pylot/core/pick/getSNR.py +++ b/pylot/core/scripts/pylot-snr.py @@ -14,7 +14,7 @@ from pylot.core.pick.utils import getSNR if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--data', '-d', type=~obspy.core.stream.Stream, + parser.add_argument('--data', '-d', type=obspy.core.stream.Stream, help='time series (seismogram) read with obspy module ' 'read', dest='data') diff --git a/test_autopick.py b/test_autopick.py index bc942eab..1b668cf7 100755 --- a/test_autopick.py +++ b/test_autopick.py @@ -9,8 +9,8 @@ from obspy.core import read import matplotlib.pyplot as plt import numpy as np -from pylot.core.pick.CharFuns import * -from pylot.core.pick.Picker import * +from pylot.core.pick.charfuns import * +from pylot.core.pick.picker import * import glob import argparse From fa6626d62aa4bafd433777ee29afdee32547f3d2 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 21 Mar 2016 12:06:11 +0100 Subject: [PATCH 0832/1144] [edit] catch reference before assignment charactersitic function variable might be referenced before they are assigned; now they are assigned as None and calls to assert help finding wrong definitions if not assigned correctly within if clause --- pylot/core/pick/autopick.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 8d81f144..bd8c1a31 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,8 +11,8 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np -from scipy import integrate from pylot.core.pick.picker import AICPicker, PragPicker +from pylot.core.pick.charfuns import CharacteristicFunction from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ getSNR, fmpicker, checkPonsets, wadaticheck @@ -175,6 +175,7 @@ def autopickstation(wfstream, pickparam, verbose=False): pstart = 0 pstop = len(zdat[0].data) * zdat[0].stats.delta cuttimes = [pstart, pstop] + cf1 = None if algoP == 'HOS': # calculate HOS-CF using subclass HOScf of class # CharacteristicFunction @@ -188,6 +189,9 @@ def autopickstation(wfstream, pickparam, verbose=False): # calculate AIC-HOS-CF using subclass AICcf of class # CharacteristicFunction # class needs stream object => build it + assert isinstance(cf1, CharacteristicFunction), 'cf2 is not set ' \ + 'correctly: maybe the algorithm name ({algoP}) is ' \ + 'corrupted'.format(algoP=algoP) tr_aic = tr_filt.copy() tr_aic.data = cf1.getCF() z_copy[0].data = tr_aic.data @@ -270,6 +274,7 @@ def autopickstation(wfstream, pickparam, verbose=False): cuttimes2 = [round(max([aicpick.getpick() - Precalcwin, 0])), round(min([len(zdat[0].data) * zdat[0].stats.delta, aicpick.getpick() + Precalcwin]))] + cf2 = None if algoP == 'HOS': # calculate HOS-CF using subclass HOScf of class # CharacteristicFunction @@ -282,6 +287,9 @@ def autopickstation(wfstream, pickparam, verbose=False): addnoise) # instance of ARZcf ############################################################## # get refined onset time from CF2 using class Picker + assert isinstance(cf2, CharacteristicFunction), 'cf2 is not set ' \ + 'correctly: maybe the algorithm name ({algoP}) is ' \ + 'corrupted'.format(algoP=algoP) refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, aicpick.getpick()) mpickP = refPpick.getpick() From 7225da87db41c15ba3809353910a0664907b2375 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 22 Mar 2016 08:42:09 +0100 Subject: [PATCH 0833/1144] [edit] reformatted code Code indentation and PEP 8 violations automatically reduced with IDE tool --- pylot/core/pick/autopick.py | 201 +++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 95 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index bd8c1a31..396e4232 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,15 +11,17 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np +from pylot.core.read.inputs import AutoPickParameter from pylot.core.pick.picker import AICPicker, PragPicker from pylot.core.pick.charfuns import CharacteristicFunction from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf -from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker,\ +from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \ getSNR, fmpicker, checkPonsets, wadaticheck from pylot.core.util.utils import getPatternLine from pylot.core.read.data import Data from pylot.core.analysis.magnitude import WApp + def autopickevent(data, param): stations = [] all_onsets = {} @@ -46,14 +48,18 @@ def autopickevent(data, param): # check S-P times (Wadati) return wadaticheck(jk_checked_onsets, wdttolerance, iplot) + def autopickstation(wfstream, pickparam, verbose=False): """ - :param: wfstream - :type: `~obspy.core.stream.Stream` + :param wfstream: `~obspy.core.stream.Stream` containing waveform + :type wfstream: obspy.core.stream.Stream - :param: pickparam - :type: container of picking parameters from input file, + :param pickparam: container of picking parameters from input file, usually autoPyLoT.in + :type pickparam: AutoPickParameter + :param verbose: + :type verbose: bool + """ # declaring pickparam variables (only for convenience) @@ -137,7 +143,7 @@ def autopickstation(wfstream, pickparam, verbose=False): Pflag = 0 Sflag = 0 Pmarker = [] - Ao = None # Wood-Anderson peak-to-peak amplitude + Ao = None # Wood-Anderson peak-to-peak amplitude # split components zdat = wfstream.select(component="Z") @@ -152,9 +158,9 @@ def autopickstation(wfstream, pickparam, verbose=False): if algoP == 'HOS' or algoP == 'ARZ' and zdat is not None: msg = '##########################################\nautopickstation:' \ - ' Working on P onset of station {station}\nFiltering vertical ' \ - 'trace ...\n{data}'.format(station=zdat[0].stats.station, - data=str(zdat)) + ' Working on P onset of station {station}\nFiltering vertical ' \ + 'trace ...\n{data}'.format(station=zdat[0].stats.station, + data=str(zdat)) if verbose: print(msg) z_copy = zdat.copy() # filter and taper data @@ -169,8 +175,8 @@ def autopickstation(wfstream, pickparam, verbose=False): Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime Ldiff = Lwf - Lc if Ldiff < 0: - msg = 'autopickstation: Cutting times are too large for actual ' \ - 'waveform!\nUsing entire waveform instead!' + msg = 'autopickstation: Cutting times are too large for actual ' \ + 'waveform!\nUsing entire waveform instead!' if verbose: print(msg) pstart = 0 pstop = len(zdat[0].data) * zdat[0].stats.delta @@ -190,8 +196,9 @@ def autopickstation(wfstream, pickparam, verbose=False): # CharacteristicFunction # class needs stream object => build it assert isinstance(cf1, CharacteristicFunction), 'cf2 is not set ' \ - 'correctly: maybe the algorithm name ({algoP}) is ' \ - 'corrupted'.format(algoP=algoP) + 'correctly: maybe the algorithm name ({algoP}) is ' \ + 'corrupted'.format( + algoP=algoP) tr_aic = tr_filt.copy() tr_aic.data = cf1.getCF() z_copy[0].data = tr_aic.data @@ -221,10 +228,10 @@ def autopickstation(wfstream, pickparam, verbose=False): trH1_filt = edat.copy() trH2_filt = ndat.copy() trH1_filt.filter('bandpass', freqmin=bph1[0], - freqmax=bph1[1], + freqmax=bph1[1], zerophase=False) trH2_filt.filter('bandpass', freqmin=bph1[0], - freqmax=bph1[1], + freqmax=bph1[1], zerophase=False) trH1_filt.taper(max_percentage=0.05, type='hann') trH2_filt.taper(max_percentage=0.05, type='hann') @@ -253,8 +260,7 @@ def autopickstation(wfstream, pickparam, verbose=False): ############################################################## # go on with processing if AIC onset passes quality control if (aicpick.getSlope() >= minAICPslope and - aicpick.getSNR() >= minAICPSNR and - Pflag == 1): + aicpick.getSNR() >= minAICPSNR and Pflag == 1): aicPflag = 1 msg = 'AIC P-pick passes quality control: Slope: {0} counts/s, ' \ 'SNR: {1}\nGo on with refined picking ...\n' \ @@ -288,15 +294,16 @@ def autopickstation(wfstream, pickparam, verbose=False): ############################################################## # get refined onset time from CF2 using class Picker assert isinstance(cf2, CharacteristicFunction), 'cf2 is not set ' \ - 'correctly: maybe the algorithm name ({algoP}) is ' \ - 'corrupted'.format(algoP=algoP) + 'correctly: maybe the algorithm name ({algoP}) is ' \ + 'corrupted'.format( + algoP=algoP) refPpick = PragPicker(cf2, tsnrz, pickwinP, iplot, ausP, tsmoothP, aicpick.getpick()) mpickP = refPpick.getpick() ############################################################# if mpickP is not None: # quality assessment - # get earliest and latest possible pick and symmetrized uncertainty + # get earliest/latest possible pick and symmetrized uncertainty [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, mpickP, iplot) @@ -481,7 +488,7 @@ def autopickstation(wfstream, pickparam, verbose=False): ############################################################# if mpickS is not None: # quality assessment - # get earliest and latest possible pick and symmetrized uncertainty + # get earliest/latest possible pick and symmetrized uncertainty h_copy[0].data = trH1_filt.data [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, @@ -496,28 +503,30 @@ def autopickstation(wfstream, pickparam, verbose=False): epick = [epickS1, epickS2] lpick = [lpickS1, lpickS2] pickerr = [Serror1, Serror2] - if epickS1 == None and epickS2 is not None: + if epickS1 is None and epickS2 is not None: ipick = 1 - elif epickS1 is not None and epickS2 == None: + elif epickS1 is not None and epickS2 is None: ipick = 0 elif epickS1 is not None and epickS2 is not None: ipick = np.argmin([epickS1, epickS2]) elif algoS == 'AR3': - [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, nfacS, + [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, + nfacS, tsnrh, - mpickS, iplot) + mpickS, + iplot) # get earliest pick of all three picks epick = [epickS1, epickS2, epickS3] lpick = [lpickS1, lpickS2, lpickS3] pickerr = [Serror1, Serror2, Serror3] - if epickS1 == None and epickS2 is not None \ + if epickS1 is None and epickS2 is not None \ and epickS3 is not None: ipick = np.argmin([epickS2, epickS3]) - elif epickS1 is not None and epickS2 == None \ + elif epickS1 is not None and epickS2 is None \ and epickS3 is not None: ipick = np.argmin([epickS2, epickS3]) elif epickS1 is not None and epickS2 is not None \ - and epickS3 == None: + and epickS3 is None: ipick = np.argmin([epickS1, epickS2]) elif epickS1 is not None and epickS2 is not None \ and epickS3 is not None: @@ -546,7 +555,7 @@ def autopickstation(wfstream, pickparam, verbose=False): 'SNR[dB]: {2}\n' '################################################' ''.format(Sweight, SNRS, SNRSdB)) - ################################################################## + ################################################################ # get Wood-Anderson peak-to-peak amplitude # initialize Data object data = Data() @@ -563,8 +572,8 @@ def autopickstation(wfstream, pickparam, verbose=False): else: # use larger window for getting peak-to-peak amplitude # as the S pick is quite unsure - wapp = WApp(cordat, mpickP, mpickP + sstop + \ - (0.5 * (mpickP + sstop)), iplot) + wapp = WApp(cordat, mpickP, mpickP + sstop + + (0.5 * (mpickP + sstop)), iplot) Ao = wapp.getwapp() @@ -593,7 +602,8 @@ def autopickstation(wfstream, pickparam, verbose=False): # calculate WA-peak-to-peak amplitude # using subclass WApp of superclass Magnitude wapp = WApp(cordat, mpickP, mpickP + sstop + (0.5 * (mpickP - + sstop)), iplot) + + sstop)), + iplot) Ao = wapp.getwapp() else: @@ -650,7 +660,7 @@ def autopickstation(wfstream, pickparam, verbose=False): plt.title('%s, %s, P Weight=%d' % (tr_filt.stats.station, tr_filt.stats.channel, Pweight)) - + plt.yticks([]) plt.ylim([-1.5, 1.5]) plt.ylabel('Normalized Counts') @@ -789,7 +799,7 @@ def autopickstation(wfstream, pickparam, verbose=False): phase = 'P' phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM, - 'w0': None, 'fc': None, 'Mo': None, 'Mw': None} + 'w0': None, 'fc': None, 'Mo': None, 'Mw': None} picks = {phase: phasepick} # add P marker picks[phase]['marked'] = Pmarker @@ -801,7 +811,6 @@ def autopickstation(wfstream, pickparam, verbose=False): # add Wood-Anderson amplitude picks[phase]['Ao'] = Ao - return picks @@ -827,70 +836,72 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): newpicks = {} for i in range(0, len(badpicks)): - if len(badpicks[i][0]) > 4: - Ppattern = '%s ? ? ? P' % badpicks[i][0] - elif len(badpicks[i][0]) == 4: - Ppattern = '%s ? ? ? P' % badpicks[i][0] - elif len(badpicks[i][0]) < 4: - Ppattern = '%s ? ? ? P' % badpicks[i][0] - nllocline = getPatternLine(NLLocfile, Ppattern) - res = nllocline.split(None)[16] - # get theoretical P-onset time from residuum - badpicks[i][1] = picks[badpicks[i][0]]['P']['mpp'] - float(res) + if len(badpicks[i][0]) > 4: + Ppattern = '%s ? ? ? P' % badpicks[i][0] + elif len(badpicks[i][0]) == 4: + Ppattern = '%s ? ? ? P' % badpicks[i][0] + elif len(badpicks[i][0]) < 4: + Ppattern = '%s ? ? ? P' % badpicks[i][0] + nllocline = getPatternLine(NLLocfile, Ppattern) + res = nllocline.split(None)[16] + # get theoretical P-onset time from residuum + badpicks[i][1] = picks[badpicks[i][0]]['P']['mpp'] - float(res) - # get corresponding waveform stream - msg = '#######################################################\n' \ - 'iteratepicker: Re-picking station {0}'.format(badpicks[i][0]) - print(msg) - wf2pick = wf.select(station=badpicks[i][0]) + # get corresponding waveform stream + msg = '#######################################################\n' \ + 'iteratepicker: Re-picking station {0}'.format(badpicks[i][0]) + print(msg) + wf2pick = wf.select(station=badpicks[i][0]) - # modify some picking parameters - pstart_old = pickparameter.getParam('pstart') - pstop_old = pickparameter.getParam('pstop') - sstop_old = pickparameter.getParam('sstop') - pickwinP_old = pickparameter.getParam('pickwinP') - Precalcwin_old = pickparameter.getParam('Precalcwin') - noisefactor_old = pickparameter.getParam('noisefactor') - zfac_old = pickparameter.getParam('zfac') - pickparameter.setParam(pstart=max([0, badpicks[i][1] - wf2pick[0].stats.starttime \ - - pickparameter.getParam('tlta')])) - pickparameter.setParam(pstop=pickparameter.getParam('pstart') + \ - (3 * pickparameter.getParam('tlta'))) - pickparameter.setParam(sstop=pickparameter.getParam('sstop') / 2) - pickparameter.setParam(pickwinP=pickparameter.getParam('pickwinP') / 2) - pickparameter.setParam(Precalcwin=pickparameter.getParam('Precalcwin') / 2) - pickparameter.setParam(noisefactor=1.0) - pickparameter.setParam(zfac=1.0) - print("iteratepicker: The following picking parameters have been modified for iterative picking:") - print("pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) - print("pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) - print("sstop: %fs => %fs" % (sstop_old, pickparameter.getParam('sstop'))) - print("pickwinP: %fs => %fs" % (pickwinP_old, pickparameter.getParam('pickwinP'))) - print("Precalcwin: %fs => %fs" % (Precalcwin_old, pickparameter.getParam('Precalcwin'))) - print("noisefactor: %f => %f" % (noisefactor_old, pickparameter.getParam('noisefactor'))) - print("zfac: %f => %f" % (zfac_old, pickparameter.getParam('zfac'))) + # modify some picking parameters + pstart_old = pickparameter.getParam('pstart') + pstop_old = pickparameter.getParam('pstop') + sstop_old = pickparameter.getParam('sstop') + pickwinP_old = pickparameter.getParam('pickwinP') + Precalcwin_old = pickparameter.getParam('Precalcwin') + noisefactor_old = pickparameter.getParam('noisefactor') + zfac_old = pickparameter.getParam('zfac') + pickparameter.setParam( + pstart=max([0, badpicks[i][1] - wf2pick[0].stats.starttime \ + - pickparameter.getParam('tlta')])) + pickparameter.setParam(pstop=pickparameter.getParam('pstart') + \ + (3 * pickparameter.getParam('tlta'))) + pickparameter.setParam(sstop=pickparameter.getParam('sstop') / 2) + pickparameter.setParam(pickwinP=pickparameter.getParam('pickwinP') / 2) + pickparameter.setParam( + Precalcwin=pickparameter.getParam('Precalcwin') / 2) + pickparameter.setParam(noisefactor=1.0) + pickparameter.setParam(zfac=1.0) + print( + "iteratepicker: The following picking parameters have been modified for iterative picking:") + print( + "pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) + print( + "pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) + print( + "sstop: %fs => %fs" % (sstop_old, pickparameter.getParam('sstop'))) + print("pickwinP: %fs => %fs" % ( + pickwinP_old, pickparameter.getParam('pickwinP'))) + print("Precalcwin: %fs => %fs" % ( + Precalcwin_old, pickparameter.getParam('Precalcwin'))) + print("noisefactor: %f => %f" % ( + noisefactor_old, pickparameter.getParam('noisefactor'))) + print("zfac: %f => %f" % (zfac_old, pickparameter.getParam('zfac'))) - # repick station - newpicks = autopickstation(wf2pick, pickparameter) + # repick station + newpicks = autopickstation(wf2pick, pickparameter) - # replace old dictionary with new one - picks[badpicks[i][0]] = newpicks + # replace old dictionary with new one + picks[badpicks[i][0]] = newpicks - # reset temporary change of picking parameters - print("iteratepicker: Resetting picking parameters ...") - pickparameter.setParam(pstart=pstart_old) - pickparameter.setParam(pstop=pstop_old) - pickparameter.setParam(sstop=sstop_old) - pickparameter.setParam(pickwinP=pickwinP_old) - pickparameter.setParam(Precalcwin=Precalcwin_old) - pickparameter.setParam(noisefactor=noisefactor_old) - pickparameter.setParam(zfac=zfac_old) + # reset temporary change of picking parameters + print("iteratepicker: Resetting picking parameters ...") + pickparameter.setParam(pstart=pstart_old) + pickparameter.setParam(pstop=pstop_old) + pickparameter.setParam(sstop=sstop_old) + pickparameter.setParam(pickwinP=pickwinP_old) + pickparameter.setParam(Precalcwin=Precalcwin_old) + pickparameter.setParam(noisefactor=noisefactor_old) + pickparameter.setParam(zfac=zfac_old) return picks - - - - - - - From cad6ed22745345af37993b8a2447d8a3dfd65c45 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 22 Mar 2016 10:49:06 +0100 Subject: [PATCH 0834/1144] [edit] exit if no waveform files are selected PyLoT will only start if waveformfiles are selected --- QtPyLoT.py | 17 ++++++++++++----- pylot/core/read/data.py | 3 +++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 45b6345f..755a8191 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -110,8 +110,10 @@ class MainWindow(QMainWindow): # load and display waveform data self.dirty = False self.loadData() - self.loadWaveformData() - self.updateFilterOptions() + if self.loadWaveformData(): + self.updateFilterOptions() + else: + sys.exit(0) def setupUi(self): @@ -403,6 +405,8 @@ class MainWindow(QMainWindow): else: raise DatastructureError('not specified') + if not self.fnames: + return None return self.fnames except DatastructureError as e: print(e) @@ -525,10 +529,13 @@ class MainWindow(QMainWindow): def loadWaveformData(self): if self.fnames and self.okToContinue(): self.setDirty(True) - self.data.setWFData(self.fnames) + ans = self.data.setWFData(self.fnames) elif self.fnames is None and self.okToContinue(): - self.data.setWFData(self.getWFFnames()) - self.plotWaveformData() + ans = self.data.setWFData(self.getWFFnames()) + if ans: + self.plotWaveformData() + else: + return ans def plotWaveformData(self): zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'} diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 6cc47acf..459d56f0 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -186,8 +186,11 @@ class Data(object): self.wforiginal = None if fnames is not None: self.appendWFData(fnames) + else: + return False self.wforiginal = self.getWFData().copy() self.dirty = False + return True def appendWFData(self, fnames): """ From 438b2706b7eae61e0071e95f29659588cbcb42b2 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 22 Mar 2016 10:52:20 +0100 Subject: [PATCH 0835/1144] [edit] removed calls to names marked as deprecated with the last update of obspy some modules and functions names will be removed in future releases and consequently have been marked as deprecated; PyLoT now uses the desired names --- pylot/core/loc/nll.py | 1 - pylot/core/read/data.py | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index 36de5625..6c27ac43 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -3,7 +3,6 @@ import subprocess import os -from obspy import readEvents from pylot.core.pick.utils import writephases from pylot.core.util.utils import getPatternLine from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 459d56f0..f48edf0d 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -3,9 +3,9 @@ import os import glob -from obspy.xseed import Parser +from obspy.io.xseed import Parser from obspy.core import read, Stream, UTCDateTime -from obspy import readEvents, read_inventory +from obspy import read_events, read_inventory from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID from pylot.core.read.io import readPILOTEvent @@ -37,9 +37,10 @@ class Data(object): if isinstance(evtdata, Event): self.evtdata = evtdata elif isinstance(evtdata, dict): - cat = readPILOTEvent(**evtdata) + evt = readPILOTEvent(**evtdata) + self.evtdata = evt elif evtdata: - cat = readEvents(evtdata) + cat = read_events(evtdata) self.evtdata = cat[0] else: # create an empty Event object self.setNew() From 31d56cb28773506758c03efb69a18671a9565f7f Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 23 Mar 2016 11:48:58 +0100 Subject: [PATCH 0836/1144] [edit] introducing picker tag New tag introduced to distinguish between pickers (either person's or programs name) --- pylot/core/pick/autopick.py | 22 ++++++++-------------- pylot/core/read/data.py | 2 ++ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 396e4232..a1f9fd28 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -144,6 +144,7 @@ def autopickstation(wfstream, pickparam, verbose=False): Sflag = 0 Pmarker = [] Ao = None # Wood-Anderson peak-to-peak amplitude + picker = 'autoPyLoT' # name of the picking programm # split components zdat = wfstream.select(component="Z") @@ -796,21 +797,14 @@ def autopickstation(wfstream, pickparam, verbose=False): # create dictionary # for P phase - phase = 'P' - phasepick = {'lpp': lpickP, 'epp': epickP, 'mpp': mpickP, 'spe': Perror, - 'snr': SNRP, 'snrdb': SNRPdB, 'weight': Pweight, 'fm': FM, - 'w0': None, 'fc': None, 'Mo': None, 'Mw': None} - picks = {phase: phasepick} - # add P marker - picks[phase]['marked'] = Pmarker + ppick = dict(lpp=lpickP, epp=epickP, mpp=mpickP, spe=Perror, snr=SNRP, + snrdb=SNRPdB, weight=Pweight, fm=FM, w0=None, fc=None, Mo=None, + Mw=None, picker=picker, marked=Pmarker) # add S phase - phase = 'S' - phasepick = {'lpp': lpickS, 'epp': epickS, 'mpp': mpickS, 'spe': Serror, - 'snr': SNRS, 'snrdb': SNRSdB, 'weight': Sweight, 'fm': None} - picks[phase] = phasepick - # add Wood-Anderson amplitude - picks[phase]['Ao'] = Ao - + spick = dict(lpp=lpickS, epp=epickS, mpp=mpickS, spe=Serror, snr=SNRS, + snrdb=SNRSdB, weight=Sweight, fm=None, picker=picker, Ao=Ao) + # merge picks into returning dictionary + picks = dict(P=ppick, S=spick) return picks diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index f48edf0d..fcbc3565 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -421,12 +421,14 @@ class Data(object): epp = phase['epp'] lpp = phase['lpp'] error = phase['spe'] + picker = phase['picker'] pick = Pick() pick.time = onset pick.time_errors.lower_uncertainty = onset - epp pick.time_errors.upper_uncertainty = lpp - onset pick.time_errors.uncertainty = error pick.phase_hint = label + pick.method_id = ResourceIdentifier(id=picker) pick.waveform_id = WaveformStreamID(station_code=station) self.getEvtData().picks.append(pick) try: From 722e21f58248f3260787a0a7c82065634d478eb7 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 23 Mar 2016 11:56:25 +0100 Subject: [PATCH 0837/1144] [edit] avoid catching unspecified Exceptions specific Exception catchment is better than general; additionally errors are raised for better debugging control --- pylot/core/read/inputs.py | 5 ++++- pylot/core/util/errors.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index cd66e845..d4b30a49 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from pylot.core.util.errors import ParameterError class AutoPickParameter(object): ''' @@ -57,7 +58,7 @@ class AutoPickParameter(object): for line in lines: parspl = line.split('\t')[:2] parFileCont[parspl[0].strip()] = parspl[1] - except Exception as e: + except IndexError as e: self._printParameterError(e) inputFile.seek(0) lines = inputFile.readlines() @@ -136,11 +137,13 @@ class AutoPickParameter(object): return self.__getitem__(param) except KeyError as e: self._printParameterError(e) + raise ParameterError(e) except TypeError: try: return self.__getitem__(args) except KeyError as e: self._printParameterError(e) + raise ParameterError(e) def setParam(self, **kwargs): for param, value in kwargs.items(): diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py index daf21d46..8b28a348 100644 --- a/pylot/core/util/errors.py +++ b/pylot/core/util/errors.py @@ -20,3 +20,6 @@ class DatastructureError(Exception): class OverwriteError(IOError): pass + +class ParameterError(Exception): + pass \ No newline at end of file From 06576586c6aac90360f9e9eb675e6d6f9dbf6d1d Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 23 Mar 2016 12:00:54 +0100 Subject: [PATCH 0838/1144] [edit] make use of new pick tag find out the person's name for tagging the picks set within PyLoT --- pylot/core/util/widgets.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 52d23d03..385843f3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -175,6 +175,8 @@ class PickDlg(QDialog): self.station = station self.rotate = rotate self.components = 'ZNE' + settings = QSettings() + self._user = settings.value('user/Login', 'anonymous') if picks: self.picks = picks else: @@ -389,6 +391,9 @@ class PickDlg(QDialog): traceIDs.append(traceID) return traceIDs + def getUser(self): + return self._user + def getFilterOptions(self, phase): options = self.filteroptions[phase] return FilterOptions(**options) @@ -609,7 +614,8 @@ class PickDlg(QDialog): lpp = stime + lpp # save pick times for actual phase - phasepicks = {'epp': epp, 'lpp': lpp, 'mpp': mpp, 'spe': spe} + phasepicks = dict(epp=epp, lpp=lpp, mpp=mpp, spe=spe, + picker=self.getUser()) try: oldphasepick = self.picks[phase] From c1c2aa4b7c19e95a8c89f999572e241bded99b74 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 23 Mar 2016 12:01:46 +0100 Subject: [PATCH 0839/1144] [bugfix] value has to be returned in either case --- QtPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 755a8191..3203d1c6 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -534,6 +534,7 @@ class MainWindow(QMainWindow): ans = self.data.setWFData(self.getWFFnames()) if ans: self.plotWaveformData() + return ans else: return ans From 65df2c7ddeca7b40f61ef6782b662bc41bf9cf8e Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 25 Mar 2016 05:07:18 +0100 Subject: [PATCH 0840/1144] [closes #174] now the filter button in the picking window is no toggle button anymore and has no effect when in picking mode --- pylot/core/util/widgets.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 52d23d03..62e76cc9 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -180,6 +180,7 @@ class PickDlg(QDialog): else: self.picks = {} self.filteroptions = FILTERDEFAULTS + self.filt_block = False # initialize panning attributes self.press = None @@ -253,8 +254,7 @@ class PickDlg(QDialog): slot=self.filterWFData, icon=filter_icon, tip='Toggle filtered/original' - ' waveforms', - checkable=True) + ' waveforms') self.zoomAction = createAction(parent=self, text='Zoom', slot=self.zoom, icon=zoom_icon, tip='Zoom into waveform', @@ -354,6 +354,7 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.setIniPick) self.filterWFData() + self.filt_block = self.toggleFilterBlocker() else: self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.panPress) @@ -701,7 +702,12 @@ class PickDlg(QDialog): ax.figure.canvas.draw() + def toggleFilterBlocker(self): + return not self.filt_block + def filterWFData(self): + if self.filt_block: + return self.updateCurrentLimits() data = self.getWFData().copy() old_title = self.getPlotWidget().getAxes().get_title() From cee48146d05dc0fc2b0cb40a9694a389900fe6ef Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 29 Mar 2016 07:58:20 +0200 Subject: [PATCH 0841/1144] [closes #178] makes use of the new status variable pick_block introduced as filter_block to solve #174 --- pylot/core/util/widgets.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9d76be58..a4f42063 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -64,7 +64,7 @@ class MPLWidget(FigureCanvas): # clear axes each time plot is called self.axes.hold(True) # initialize super class - FigureCanvas.__init__(self, self.figure) + super(MPLWidget, self).__init__(self.figure) # add an cursor for station selection self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True, @@ -182,7 +182,7 @@ class PickDlg(QDialog): else: self.picks = {} self.filteroptions = FILTERDEFAULTS - self.filt_block = False + self.pick_block = False # initialize panning attributes self.press = None @@ -356,7 +356,7 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.setIniPick) self.filterWFData() - self.filt_block = self.toggleFilterBlocker() + self.pick_block = self.togglePickBlocker() else: self.disconnectPressEvent() self.cidpress = self.connectPressEvent(self.panPress) @@ -708,11 +708,11 @@ class PickDlg(QDialog): ax.figure.canvas.draw() - def toggleFilterBlocker(self): - return not self.filt_block + def togglePickBlocker(self): + return not self.pick_block def filterWFData(self): - if self.filt_block: + if self.pick_block: return self.updateCurrentLimits() data = self.getWFData().copy() @@ -768,7 +768,9 @@ class PickDlg(QDialog): self.getPlotWidget().setYLims(self.getYLims()) def zoom(self): - if self.zoomAction.isChecked(): + if self.zoomAction.isChecked() and self.pick_block: + self.zoomAction.setChecked(False) + elif self.zoomAction.isChecked(): self.disconnectPressEvent() self.disconnectMotionEvent() self.disconnectReleaseEvent() From 4c5b58dbf1198aae661239da70565ee8875db9f9 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 08:24:32 +0200 Subject: [PATCH 0842/1144] [addresses #190, closes #192] handles unknown picker information pick tag is set to 'Unknown' for loaded data that do not provide picker information when saved with PyLoT prior to revision 06576586c6aac90360f9e9eb675e6d6f9dbf6d1d --- pylot/core/read/data.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index fcbc3565..e59e69f1 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -3,6 +3,7 @@ import os import glob +import warnings from obspy.io.xseed import Parser from obspy.core import read, Stream, UTCDateTime from obspy import read_events, read_inventory @@ -421,7 +422,11 @@ class Data(object): epp = phase['epp'] lpp = phase['lpp'] error = phase['spe'] - picker = phase['picker'] + try: + picker = phase['picker'] + except KeyError as e: + warnings.warn(str(e), Warning) + picker = 'Unknown' pick = Pick() pick.time = onset pick.time_errors.lower_uncertainty = onset - epp From 040c21dc130cb6a8e6e6f48f47dbd67eb689f952 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 08:47:03 +0200 Subject: [PATCH 0843/1144] [adresses #190] read information on the picker if available --- QtPyLoT.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 3203d1c6..2a4f336b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -746,6 +746,10 @@ class MainWindow(QMainWindow): phase['epp'] = epp phase['lpp'] = lpp phase['spe'] = spe + try: + phase['picker'] = str(pick.method_id).split('/')[1] + except IndexError: + pass onsets[pick.phase_hint] = phase.copy() picks[station] = onsets.copy() From f075970f5e613e1df079fa412b9b7a23012850f3 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 10:30:45 +0200 Subject: [PATCH 0844/1144] [adresses #190] autopicks in QtPyLoT can now also be updated this change is necessary to be able to load autopickdata from storage independently --- QtPyLoT.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 2a4f336b..25ba561f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -727,7 +727,7 @@ class MainWindow(QMainWindow): self.getPicks(type=type)[station] = stat_picks return rval - def updatePicks(self): + def updatePicks(self, type='manual'): evt = self.getData().getEvtData() picks = {} for pick in evt.picks: @@ -753,7 +753,10 @@ class MainWindow(QMainWindow): onsets[pick.phase_hint] = phase.copy() picks[station] = onsets.copy() - self.picks.update(picks) + if type == 'manual': + self.picks.update(picks) + elif type == 'auto': + self.autopicks.update(picks) def drawPicks(self, station=None, picktype='manual'): # if picks to draw not specified, draw all picks available From 14048d6784e1aae532e6a367db86fc5b61600958 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 11:00:31 +0200 Subject: [PATCH 0845/1144] [edit] reformat string only if certain prefix is found --- QtPyLoT.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 25ba561f..f0096cc2 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -747,7 +747,10 @@ class MainWindow(QMainWindow): phase['lpp'] = lpp phase['spe'] = spe try: - phase['picker'] = str(pick.method_id).split('/')[1] + picker = str(pick.method_id) + if picker.startswith('smi:local/'): + picker = picker.split('smi:local/')[1] + phase['picker'] = picker except IndexError: pass From 7b22bce2b06507b7c0879d4864d0fa51b4c4da35 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 11:26:08 +0200 Subject: [PATCH 0846/1144] [close #190] pick handling is now consistent for manual and automatic picks manual and automatic picks are now treated in the same way but separately; implementation of comparison routines should be much more easy now --- QtPyLoT.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f0096cc2..0df9666a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -356,7 +356,10 @@ class MainWindow(QMainWindow): settings = QSettings() return settings.value("data/dataRoot") - def loadData(self, fname=None): + def loadAutoPicks(self): + self.loadData(type='auto') + + def loadData(self, fname=None, type='manual'): if not self.okToContinue(): return if fname is None: @@ -373,7 +376,7 @@ class MainWindow(QMainWindow): fname = unicode(action.data().toString()) self.setFileName(fname) self.data += Data(self, evtdata=self.getFileName()) - self.updatePicks() + self.updatePicks(type=type) self.drawPicks() def getLastEvent(self): From fef1a953912b984bdb26dd6d6753316c29379c97 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 11:40:16 +0200 Subject: [PATCH 0847/1144] [fixes #193] fixed bug introduced by component mapping fixes a bug which was introduced in revision b8d680f54f79b606431f63bb1f98f177ccc77bcb --- pylot/core/util/widgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a4f42063..73dcdca0 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -99,7 +99,8 @@ class MPLWidget(FigureCanvas): if mapping: comp = channel[-1] n = COMPONENTS_MAPPING[comp] - nmax = n if n > nmax else nmax + if n > nmax: + nmax = n msg = 'plotting %s channel of station %s' % (channel, station) print(msg) stime = trace.stats.starttime - wfstart From 4409a9c3ce718d3bfad40e38af613efc39613f97 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 12:09:26 +0200 Subject: [PATCH 0848/1144] [fixes #179] plotting of picks preserved after filtering --- QtPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 0df9666a..041e7c5c 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -581,6 +581,7 @@ class MainWindow(QMainWindow): else: self.getData().resetWFData() self.plotWaveformData() + self.drawPicks() def adjustFilterOptions(self): fstring = "Filter Options ({0})".format(self.getSeismicPhase()) From 2d66248cf9832877d35167cb922af32903ec644b Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 14:06:57 +0200 Subject: [PATCH 0849/1144] [fixes #172] now also in overview all horizontals are plotted --- QtPyLoT.py | 4 +++- pylot/core/util/defaults.py | 9 ++++++--- pylot/core/util/widgets.py | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 041e7c5c..3fa2d4c8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -44,7 +44,7 @@ from pylot.core.read.data import Data from pylot.core.read.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent from pylot.core.loc.nll import locate as locateNll -from pylot.core.util.defaults import FILTERDEFAULTS +from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError from pylot.core.util.connection import checkurl @@ -545,7 +545,9 @@ class MainWindow(QMainWindow): zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'} comp = self.getComponent() title = 'section: {0} components'.format(zne_text[comp]) + alter_comp = COMPNAME_MAP[comp] wfst = self.getData().getWFData().select(component=comp) + wfst += self.getData().getWFData().select(component=alter_comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title, mapping=False) self.draw() plotDict = self.getPlotWidget().getPlotDict() diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index 25847da9..523ff579 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -47,6 +47,9 @@ OUTPUTFORMATS = {'.xml':'QUAKEML', LOCTOOLS = dict(nll = nll, hsat = hsat, velest = velest) -COMPONENTS_MAPPING = dict(Z = 2, N = 1, E = 0) -COMPONENTS_MAPPING['1'] = 1 -COMPONENTS_MAPPING['2'] = 0 +COMPPOSITION_MAP = dict(Z = 2, N = 1, E = 0) +COMPPOSITION_MAP['1'] = 1 +COMPPOSITION_MAP['2'] = 0 +COMPPOSITION_MAP['3'] = 2 + +COMPNAME_MAP = dict(Z = '3', N = '1', E = '2') diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 73dcdca0..632afe8c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -26,7 +26,7 @@ from pylot.core.read.inputs import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ getResolutionWindow from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS,\ - COMPONENTS_MAPPING + COMPPOSITION_MAP from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ demeanTrace, isSorted, findComboBoxIndex @@ -98,7 +98,7 @@ class MPLWidget(FigureCanvas): station = trace.stats.station if mapping: comp = channel[-1] - n = COMPONENTS_MAPPING[comp] + n = COMPPOSITION_MAP[comp] if n > nmax: nmax = n msg = 'plotting %s channel of station %s' % (channel, station) From 320b7b7219e621141409e848f974f59fefd278db Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 29 Mar 2016 14:42:51 +0200 Subject: [PATCH 0850/1144] [bugfix] pick blocker was not reset after phase was picked --- pylot/core/util/widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 632afe8c..99236cc3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -650,6 +650,7 @@ class PickDlg(QDialog): self.drawPicks() self.disconnectPressEvent() self.zoomAction.setEnabled(True) + self.pick_block = self.togglePickBlocker() self.selectPhase.setCurrentIndex(-1) self.setPlotLabels() From a2640e3126eaa7e97556bb5625f2fe138858efc0 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 30 Mar 2016 07:00:31 +0200 Subject: [PATCH 0851/1144] reformatting code --- QtPyLoT.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 3fa2d4c8..a2476e9c 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -87,7 +87,7 @@ class MainWindow(QMainWindow): self.dispComponent = str(settings.value("plotting/dispComponent", "Z")) if settings.value("data/dataRoot", None) is None: dirname = QFileDialog().getExistingDirectory( - caption='Choose data root ...') + caption='Choose data root ...') settings.setValue("data/dataRoot", dirname) settings.sync() @@ -119,7 +119,7 @@ class MainWindow(QMainWindow): try: self.startTime = min( - [tr.stats.starttime for tr in self.data.wfdata]) + [tr.stats.starttime for tr in self.data.wfdata]) except: self.startTime = UTCDateTime() @@ -373,7 +373,7 @@ class MainWindow(QMainWindow): filter=filt) fname = fname[0] else: - fname = unicode(action.data().toString()) + fname = str(action.data().toString()) self.setFileName(fname) self.data += Data(self, evtdata=self.getFileName()) self.updatePicks(type=type) @@ -439,7 +439,7 @@ class MainWindow(QMainWindow): directory = os.path.join(self.getRoot(), self.getEventFileName()) file_filter = "QuakeML file (*.xml);;VELEST observation file format (*.cnv);;NonLinLoc observation file (*.obs)" fname, selected_filter = QFileDialog.getSaveFileName(self, 'Save event data ...', - directory, file_filter) + directory, file_filter) fbasename, exform = os.path.splitext(fname) @@ -630,8 +630,8 @@ class MainWindow(QMainWindow): else: self.updateStatus('Filter loaded ... ' '[{0}: {1} Hz]'.format( - self.getFilterOptions().getFilterType(), - self.getFilterOptions().getFreq())) + self.getFilterOptions().getFilterType(), + self.getFilterOptions().getFreq())) if self.filterAction.isChecked(): self.filterWaveformData() @@ -836,12 +836,12 @@ class MainWindow(QMainWindow): if self.getData() is not None: if not self.getData().isNew(): self.setWindowTitle( - "PyLoT - processing event %s[*]" % self.getData().getID()) + "PyLoT - processing event %s[*]" % self.getData().getID()) elif self.getData().isNew(): self.setWindowTitle("PyLoT - New event [*]") else: self.setWindowTitle( - "PyLoT - seismic processing the python way[*]") + "PyLoT - seismic processing the python way[*]") self.setWindowModified(self.dirty) def tutorUser(self): @@ -879,7 +879,7 @@ class MainWindow(QMainWindow): def helpHelp(self): if checkurl(): form = HelpForm( - 'https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') + 'https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/wiki') else: form = HelpForm(':/help.html') form.show() From d7cfd0d1765076876119423989b5c729a2d44e77 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 30 Mar 2016 08:14:58 +0200 Subject: [PATCH 0852/1144] =?UTF-8?q?WALL-E:=20Einmal=20aufr=C3=A4umen=20u?= =?UTF-8?q?nd=20zur=C3=BCck!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- autoPyLoT.py | 111 +++--- makePyLoT.py | 6 +- pylot/core/active/activeSeismoPick.py | 152 ++++---- pylot/core/active/fmtomo2vtk.py | 97 ++--- pylot/core/active/fmtomoUtils.py | 160 ++++---- pylot/core/active/seismicArrayPreparation.py | 354 ++++++++++-------- pylot/core/active/seismicshot.py | 286 +++++++------- pylot/core/active/surveyPlotTools.py | 157 ++++---- pylot/core/active/surveyUtils.py | 152 +++++--- pylot/core/analysis/coinctimes.py | 2 - pylot/core/analysis/magnitude.py | 220 ++++++----- pylot/core/analysis/trigger.py | 6 +- pylot/core/loc/nll.py | 15 +- pylot/core/pick/autopick.py | 16 +- pylot/core/pick/charfuns.py | 255 ++++++------- pylot/core/pick/picker.py | 59 +-- pylot/core/pick/utils.py | 228 ++++++----- pylot/core/read/data.py | 1 - pylot/core/read/inputs.py | 6 +- pylot/core/read/io.py | 5 +- .../scripts/pylot-pick-earliest-latest.py | 7 +- pylot/core/scripts/pylot-pick-firstmotion.py | 7 +- pylot/core/{pick => scripts}/run_makeCF.py | 0 pylot/core/util/defaults.py | 26 +- pylot/core/util/errors.py | 3 +- pylot/core/util/thread.py | 2 +- pylot/core/util/utils.py | 27 +- pylot/core/util/version.py | 7 +- pylot/core/util/widgets.py | 23 +- 29 files changed, 1285 insertions(+), 1105 deletions(-) rename pylot/core/{pick => scripts}/run_makeCF.py (100%) diff --git a/autoPyLoT.py b/autoPyLoT.py index 00068c73..e67ea8f3 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -1,6 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +from __future__ import print_function import os import argparse import glob @@ -55,9 +56,9 @@ def autoPyLoT(inputfile): if parameter.hasParam('datastructure'): datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() - dsfields = {'root' :parameter.getParam('rootpath'), - 'dpath' :parameter.getParam('datapath'), - 'dbase' :parameter.getParam('database')} + dsfields = {'root': parameter.getParam('rootpath'), + 'dpath': parameter.getParam('datapath'), + 'dbase': parameter.getParam('database')} exf = ['root', 'dpath', 'dbase'] @@ -86,7 +87,7 @@ def autoPyLoT(inputfile): ttpat = parameter.getParam('ttpatter') # pattern of NLLoc-output file nllocoutpatter = parameter.getParam('outpatter') - maxnumit = 3 # maximum number of iterations for re-picking + maxnumit = 3 # maximum number of iterations for re-picking else: locflag = 0 print(" !!! ") @@ -94,7 +95,6 @@ def autoPyLoT(inputfile): print("!!No source parameter estimation possible!!") print(" !!! ") - # multiple event processing # read each event in database datapath = datastructure.expandDataPath() @@ -115,7 +115,7 @@ def autoPyLoT(inputfile): picksExport(picks, 'NLLoc', phasefile) # For locating the event the NLLoc-control file has to be modified! - evID = event[string.rfind(event, "/") + 1 : len(events) - 1] + evID = event[string.rfind(event, "/") + 1: len(events) - 1] nllocout = '%s_%s' % (evID, nllocoutpatter) # create comment line for NLLoc-control file modifyInputFile(ctrf, nllocroot, nllocout, phasef, ttpat) @@ -129,21 +129,21 @@ def autoPyLoT(inputfile): # get stations with bad onsets badpicks = [] for key in picks: - if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: - badpicks.append([key, picks[key]['P']['mpp']]) + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) if len(badpicks) == 0: - print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") + print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") # get NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) if len(glob.glob(locsearch)) > 0: # get latest NLLoc-location file if several are available - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -152,9 +152,9 @@ def autoPyLoT(inputfile): locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) if len(glob.glob(locsearch)) > 0: # get latest file if several are available - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) nlloccounter = 0 - while len(badpicks) > 0 and nlloccounter <= maxnumit: + while len(badpicks) > 0 and nlloccounter <= maxnumit: nlloccounter += 1 if nlloccounter > maxnumit: print("autoPyLoT: Number of maximum iterations reached, stop iterative picking!") @@ -169,28 +169,28 @@ def autoPyLoT(inputfile): locate(nlloccall, ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) # get updated NLLoc-location file - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # check for bad picks badpicks = [] for key in picks: - if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: - badpicks.append([key, picks[key]['P']['mpp']]) + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) print("autoPyLoT: After iteration No. %d: %d bad onsets found ..." % (nlloccounter, \ - len(badpicks))) + len(badpicks))) if len(badpicks) == 0: print("autoPyLoT: No more bad onsets found, stop iterative picking!") nlloccounter = maxnumit # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) # get network moment magntiude netMw = [] - for key in finalpicks.getpicdic(): - if finalpicks.getpicdic()[key]['P']['Mw'] is not None: - netMw.append(finalpicks.getpicdic()[key]['P']['Mw']) + for key in finalpicks.getpicdic(): + if finalpicks.getpicdic()[key]['P']['Mw'] is not None: + netMw.append(finalpicks.getpicdic()[key]['P']['Mw']) netMw = np.median(netMw) print("Network moment magnitude: %4.1f" % netMw) else: @@ -208,7 +208,7 @@ def autoPyLoT(inputfile): writephases(picks, 'HYPO71', hypo71file) endsplash = '''------------------------------------------\n' - -----Finished event %s!-----\n' + -----Finished event %s!-----\n' ------------------------------------------'''.format \ (version=_getVersionString()) % evID print(endsplash) @@ -218,8 +218,8 @@ def autoPyLoT(inputfile): # single event processing else: data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) - print("Working on event "), parameter.getParam('eventID') - print data + print("Working on event {0}".format(parameter.getParam('eventID'))) + print(data) wfdat = data.getWFData() # all available streams ########################################################## @@ -245,21 +245,21 @@ def autoPyLoT(inputfile): # get stations with bad onsets badpicks = [] for key in picks: - if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: - badpicks.append([key, picks[key]['P']['mpp']]) + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) if len(badpicks) == 0: - print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") + print("autoPyLoT: No bad onsets found, thus no iterative picking necessary!") # get NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) if len(glob.glob(locsearch)) > 0: # get latest NLLOc-location file if several are available - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -268,9 +268,9 @@ def autoPyLoT(inputfile): locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) if len(glob.glob(locsearch)) > 0: # get latest file if several are available - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) nlloccounter = 0 - while len(badpicks) > 0 and nlloccounter <= maxnumit: + while len(badpicks) > 0 and nlloccounter <= maxnumit: nlloccounter += 1 if nlloccounter > maxnumit: print("autoPyLoT: Number of maximum iterations reached, stop iterative picking!") @@ -285,28 +285,28 @@ def autoPyLoT(inputfile): locate(nlloccall, ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) # get updated NLLoc-location file - nllocfile = max(glob.glob(locsearch), key=os.path.getctime) + nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # check for bad picks badpicks = [] for key in picks: - if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: - badpicks.append([key, picks[key]['P']['mpp']]) + if picks[key]['P']['weight'] >= 4 or picks[key]['S']['weight'] >= 4: + badpicks.append([key, picks[key]['P']['mpp']]) print("autoPyLoT: After iteration No. %d: %d bad onsets found ..." % (nlloccounter, \ - len(badpicks))) + len(badpicks))) if len(badpicks) == 0: print("autoPyLoT: No more bad onsets found, stop iterative picking!") nlloccounter = maxnumit - + # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ + nllocfile, picks, parameter.getParam('rho'), \ + parameter.getParam('vp'), parameter.getParam('Qp'), \ + parameter.getParam('invdir')) # get network moment magntiude netMw = [] - for key in finalpicks.getpicdic(): - if finalpicks.getpicdic()[key]['P']['Mw'] is not None: - netMw.append(finalpicks.getpicdic()[key]['P']['Mw']) + for key in finalpicks.getpicdic(): + if finalpicks.getpicdic()[key]['P']['Mw'] is not None: + netMw.append(finalpicks.getpicdic()[key]['P']['Mw']) netMw = np.median(netMw) print("Network moment magnitude: %4.1f" % netMw) else: @@ -322,15 +322,15 @@ def autoPyLoT(inputfile): writephases(picks, 'HYPO71', hypo71file) else: writephases(picks, 'HYPO71', hypo71file) - + endsplash = '''------------------------------------------\n' - -----Finished event %s!-----\n' + -----Finished event %s!-----\n' ------------------------------------------'''.format \ (version=_getVersionString()) % parameter.getParam('eventID') print(endsplash) if locflag == 0: print("autoPyLoT was running in non-location mode!") - + endsp = '''####################################\n ************************************\n *********autoPyLoT terminates*******\n @@ -338,6 +338,7 @@ def autoPyLoT(inputfile): ************************************'''.format(version=_getVersionString()) print(endsp) + if __name__ == "__main__": # parse arguments parser = argparse.ArgumentParser( diff --git a/makePyLoT.py b/makePyLoT.py index bce8fd30..1b222c66 100644 --- a/makePyLoT.py +++ b/makePyLoT.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 +from __future__ import print_function + """ makePyLoT -- build and install PyLoT @@ -123,7 +125,7 @@ USAGE except KeyboardInterrupt: cleanUp(verbose) return 0 - except Exception, e: + except Exception as e: if DEBUG or TESTRUN: raise e indent = len(program_name) * " " @@ -139,7 +141,7 @@ def buildPyLoT(verbosity=None): "\n" " Current working directory: {1}\n" ).format(system, os.getcwd()) - print msg + print(msg) if system.startswith(('win', 'microsoft')): raise CLIError( "building on Windows system not tested yet; implementation pending") diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 2e8c4058..afcfd200 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -4,8 +4,9 @@ import numpy as np from pylot.core.active import seismicshot from pylot.core.active.surveyUtils import cleanUp + class Survey(object): - def __init__(self, path, sourcefile, receiverfile, useDefaultParas = False): + def __init__(self, path, sourcefile, receiverfile, useDefaultParas=False): ''' The Survey Class contains all shots [type: seismicshot] of a survey as well as the aquisition geometry and the topography. @@ -37,7 +38,7 @@ class Survey(object): shot_dict = {} shotlist = self.getShotlist() - for shotnumber in shotlist: # loop over data files + for shotnumber in shotlist: # loop over data files # generate filenames and read manual picks to a list obsfile = self._obsdir + str(shotnumber) + '_pickle.dat' if obsfile not in shot_dict.keys(): @@ -47,7 +48,7 @@ class Survey(object): self.data = shot_dict print ("Generated Survey object for %d shots" % len(shotlist)) - print ("Total number of traces: %d \n" %self.countAllTraces()) + print ("Total number of traces: %d \n" % self.countAllTraces()) def _removeAllEmptyTraces(self): filename = 'removeEmptyTraces.out' @@ -58,11 +59,11 @@ class Survey(object): if count == 0: outfile = open(filename, 'w') count += 1 outfile.writelines('shot: %s, removed empty traces: %s\n' - %(shot.getShotnumber(), removed)) - print ("\nremoveEmptyTraces: Finished! Removed %d traces" %count) + % (shot.getShotnumber(), removed)) + print ("\nremoveEmptyTraces: Finished! Removed %d traces" % count) if count > 0: print ("See %s for more information " - "on removed traces."%(filename)) + "on removed traces." % (filename)) outfile.close() def _updateShots(self): @@ -70,7 +71,8 @@ class Survey(object): Removes traces that do not exist in the dataset for any reason. ''' filename = 'updateShots.out' - count = 0; countTraces = 0 + count = 0; + countTraces = 0 for shot in self.data.values(): del_traceIDs = shot.updateTraceList() if len(del_traceIDs) > 0: @@ -79,13 +81,13 @@ class Survey(object): countTraces += len(del_traceIDs) outfile.writelines("shot: %s, removed traceID(s) %s because " "they were not found in the corresponding stream\n" - %(shot.getShotnumber(), del_traceIDs)) + % (shot.getShotnumber(), del_traceIDs)) print ("\nupdateShots: Finished! Updated %d shots and removed " - "%d traces" %(count, countTraces)) + "%d traces" % (count, countTraces)) if count > 0: print ("See %s for more information " - "on removed traces."%(filename)) + "on removed traces." % (filename)) outfile.close() def setArtificialPick(self, traceID, pick): @@ -96,9 +98,9 @@ class Survey(object): for shot in self.data.values(): shot.setPick(traceID, pick) - def setParametersForShots(self, cutwindow = (0, 0.2), tmovwind = 0.3, tsignal = 0.03, tgap = 0.0007): + def setParametersForShots(self, cutwindow=(0, 0.2), tmovwind=0.3, tsignal=0.03, tgap=0.0007): if (cutwindow == (0, 0.2) and tmovwind == 0.3 and - tsignal == 0.03 and tgap == 0.0007): + tsignal == 0.03 and tgap == 0.0007): print ("Warning: Standard values used for " "setParamters. This might not be clever.") # CHANGE this later. Parameters only needed for survey, not for each shot. @@ -107,12 +109,12 @@ class Survey(object): shot.setTmovwind(tmovwind) shot.setTsignal(tsignal) shot.setTgap(tgap) - shot.setOrder(order = 4) + shot.setOrder(order=4) print ("setParametersForShots: Parameters set to:\n" "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" - %(cutwindow, tmovwind, tsignal, tgap)) + % (cutwindow, tmovwind, tsignal, tgap)) - def setManualPicksFromFiles(self, directory = 'picks'): + def setManualPicksFromFiles(self, directory='picks'): ''' Read manual picks from *.pck files in a directory. The * must be identical with the shotnumber. @@ -135,7 +137,10 @@ class Survey(object): def plotDiffs(self): import matplotlib.pyplot as plt - diffs = []; dists = []; mpicks = []; picks = [] + diffs = []; + dists = []; + mpicks = []; + picks = [] diffsDic = self.getDiffsFromManual() for shot in self.data.values(): for traceID in shot.getTraceIDlist(): @@ -144,22 +149,22 @@ class Survey(object): mpicks.append(shot.getManualPick(traceID)) picks.append(shot.getPick(traceID)) diffs.append(diffsDic[shot][traceID]) - + labelm = 'manual picks' labela = 'automatic picks' fig = plt.figure() ax = fig.add_subplot(111) - sc_a = ax.scatter(dists, picks, c = '0.5', s=10, edgecolors='none', label = labela, alpha = 0.3) - sc = ax.scatter(dists, mpicks, c = diffs, s=5, edgecolors='none', label = labelm) + sc_a = ax.scatter(dists, picks, c='0.5', s=10, edgecolors='none', label=labela, alpha=0.3) + sc = ax.scatter(dists, mpicks, c=diffs, s=5, edgecolors='none', label=labelm) cbar = plt.colorbar(sc, fraction=0.05) cbar.set_label(labelm) ax.set_xlabel('Distance [m]') ax.set_ylabel('Time [s]') ax.text(0.5, 0.95, 'Plot of all MANUAL picks', transform=ax.transAxes, horizontalalignment='center') - def plotHist(self, nbins = 20, ax = None): + def plotHist(self, nbins=20, ax=None): import matplotlib.pyplot as plt plt.interactive(True) diffs = [] @@ -170,48 +175,51 @@ class Survey(object): for traceID in shot.getTraceIDlist(): if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: diffs.append(self.getDiffsFromManual()[shot][traceID]) - hist = plt.hist(diffs, nbins, histtype = 'step', normed = True, stacked = True) + hist = plt.hist(diffs, nbins, histtype='step', normed=True, stacked=True) plt.title('Histogram of the differences between automatic and manual pick') plt.xlabel('Difference in time (auto - manual) [s]') return diffs - def pickAllShots(self, windowsize, HosAic = 'hos', vmin = 333, vmax = 5500, folm = 0.6): + def pickAllShots(self, windowsize, HosAic='hos', vmin=333, vmax=5500, folm=0.6): ''' Automatically pick all traces of all shots of the survey. ''' from datetime import datetime starttime = datetime.now() - count = 0; tpicksum = starttime - starttime + count = 0; + tpicksum = starttime - starttime for shot in self.data.values(): - tstartpick = datetime.now(); count += 1 + tstartpick = datetime.now(); + count += 1 for traceID in shot.getTraceIDlist(): - distance = shot.getDistance(traceID) # receive distance + distance = shot.getDistance(traceID) # receive distance pickwin_used = shot.getCut() cutwindow = shot.getCut() # for higher distances use a linear vmin/vmax to cut out late/early regions with high noise if distance > 5.: - pwleft = distance/vmax ################## TEST - pwright = distance/vmin + pwleft = distance / vmax ################## TEST + pwright = distance / vmin if pwright > cutwindow[1]: pwright = cutwindow[1] pickwin_used = (pwleft, pwright) shot.setPickwindow(traceID, pickwin_used) - shot.pickTraces(traceID, windowsize, folm, HosAic) # picker + shot.pickTraces(traceID, windowsize, folm, HosAic) # picker shot.setSNR(traceID) - #if shot.getSNR(traceID)[0] < snrthreshold: + # if shot.getSNR(traceID)[0] < snrthreshold: if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): - shot.removePick(traceID) + shot.removePick(traceID) # set epp and lpp if SNR > 1 (else earllatepicker cant set values) if shot.getSNR(traceID)[0] > 1: shot.setEarllatepick(traceID) - tpicksum += (datetime.now() - tstartpick); tpick = tpicksum/count + tpicksum += (datetime.now() - tstartpick); + tpick = tpicksum / count tremain = (tpick * (len(self.getShotDict()) - count)) tend = datetime.now() + tremain progress = float(count) / float(len(self.getShotDict())) * 100 @@ -220,7 +228,7 @@ class Survey(object): ntraces = self.countAllTraces() pickedtraces = self.countAllPickedTraces() print('Picked %s / %s traces (%d %%)\n' - %(pickedtraces, ntraces, float(pickedtraces)/float(ntraces)*100.)) + % (pickedtraces, ntraces, float(pickedtraces) / float(ntraces) * 100.)) def cleanBySPE(self, maxSPE): for shot in self.data.values(): @@ -237,7 +245,7 @@ class Survey(object): if shot.getPickFlag(traceID) == 1: spe.append(shot.getSymmetricPickError(traceID)) spe.sort() - plt.plot(spe, label = 'SPE') + plt.plot(spe, label='SPE') plt.ylabel('Symmetric Pickerror') plt.legend() @@ -255,7 +263,7 @@ class Survey(object): shot.removePick(traceID) else: numpicks += 1 - print('Recovered %d picks'%numpicks) + print('Recovered %d picks' % numpicks) def setArtificialPick(self, traceID, pick): for shot in self.data.values(): @@ -265,13 +273,13 @@ class Survey(object): def countAllTraces(self): numtraces = 0 for shot in self.getShotlist(): - for rec in self.getReceiverlist(): ### shot.getReceiverlist etc. + for rec in self.getReceiverlist(): ### shot.getReceiverlist etc. numtraces += 1 return numtraces def getShotlist(self): filename = self.getPath() + self.getSourcefile() - srcfile = open(filename,'r') + srcfile = open(filename, 'r') shotlist = [] for line in srcfile.readlines(): line = line.split() @@ -281,7 +289,7 @@ class Survey(object): def getReceiverlist(self): filename = self.getPath() + self.getReceiverfile() - recfile = open(filename,'r') + recfile = open(filename, 'r') reclist = [] for line in recfile.readlines(): line = line.split() @@ -318,8 +326,8 @@ class Survey(object): pickedTraces += 1 info_dict[shot.getShotnumber()] = {'numtraces': numtraces, 'picked traces': [pickedTraces, - '%2.2f %%'%(float(pickedTraces) / - float(numtraces) * 100)], + '%2.2f %%' % (float(pickedTraces) / + float(numtraces) * 100)], 'mean SNR': np.mean(snrlist), 'mean distance': np.mean(dist)} @@ -330,7 +338,7 @@ class Survey(object): if shot.getShotnumber() == shotnumber: return shot - def exportFMTOMO(self, directory = 'FMTOMO_export', sourcefile = 'input_sf.in', ttFileExtension = '.tt'): + def exportFMTOMO(self, directory='FMTOMO_export', sourcefile='input_sf.in', ttFileExtension='.tt'): def getAngle(distance): PI = np.pi R = 6371. @@ -338,18 +346,22 @@ class Survey(object): return angle count = 0 - fmtomo_factor = 1000 # transforming [m/s] -> [km/s] - LatAll = []; LonAll = []; DepthAll = [] + fmtomo_factor = 1000 # transforming [m/s] -> [km/s] + LatAll = []; + LonAll = []; + DepthAll = [] srcfile = open(directory + '/' + sourcefile, 'w') - srcfile.writelines('%10s\n' %len(self.data)) # number of sources + srcfile.writelines('%10s\n' % len(self.data)) # number of sources for shotnumber in self.getShotlist(): shot = self.getShotForShotnumber(shotnumber) ttfilename = str(shotnumber) + ttFileExtension - (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) - srcfile.writelines('%10s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z)) # lat, lon, depth - LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) - srcfile.writelines('%10s\n' %1) # - srcfile.writelines('%10s %10s %10s\n' %(1, 1, ttfilename)) + (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) + srcfile.writelines('%10s %10s %10s\n' % (getAngle(y), getAngle(x), (-1) * z)) # lat, lon, depth + LatAll.append(getAngle(y)); + LonAll.append(getAngle(x)); + DepthAll.append((-1) * z) + srcfile.writelines('%10s\n' % 1) # + srcfile.writelines('%10s %10s %10s\n' % (1, 1, ttfilename)) ttfile = open(directory + '/' + ttfilename, 'w') traceIDlist = shot.getTraceIDlist() traceIDlist.sort() @@ -359,8 +371,10 @@ class Survey(object): pick = shot.getPick(traceID) * fmtomo_factor delta = shot.getSymmetricPickError(traceID) * fmtomo_factor (x, y, z) = shot.getRecLoc(traceID) - ttfile.writelines('%20s %20s %20s %10s %10s\n' %(getAngle(y), getAngle(x), (-1)*z, pick, delta)) - LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1)*z) + ttfile.writelines('%20s %20s %20s %10s %10s\n' % (getAngle(y), getAngle(x), (-1) * z, pick, delta)) + LatAll.append(getAngle(y)); + LonAll.append(getAngle(x)); + DepthAll.append((-1) * z) count += 1 ttfile.close() srcfile.close() @@ -393,7 +407,7 @@ class Survey(object): count += 1 return count - def plotAllShots(self, rows = 3, columns = 4, mode = '3d'): + def plotAllShots(self, rows=3, columns=4, mode='3d'): ''' Plots all shots as Matrices with the color corresponding to the traveltime for each receiver. IMPORTANT NOTE: Topography (z - coordinate) is not considered in the diagrams! @@ -408,8 +422,8 @@ class Survey(object): figPerSubplot = columns * rows index = 1 - #shotnames = [] - #shotnumbers = [] + # shotnames = [] + # shotnumbers = [] # for shot in self.data.values(): # shotnames.append(shot.getShotname()) @@ -419,24 +433,24 @@ class Survey(object): for shotnumber in self.getShotlist(): if index <= figPerSubplot: - #ax = fig.add_subplot(3,3,i, projection = '3d', title = 'shot:' - #+str(shot_dict[shotnumber].getShotnumber()), xlabel = 'X', ylabel = 'Y', zlabel = 'traveltime') - #shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) + # ax = fig.add_subplot(3,3,i, projection = '3d', title = 'shot:' + # +str(shot_dict[shotnumber].getShotnumber()), xlabel = 'X', ylabel = 'Y', zlabel = 'traveltime') + # shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) ax = fig.add_subplot(rows, columns, index) if mode == '3d': - self.getShot(shotnumber).matshow(ax = ax, colorbar = False, annotations = True, legend = False) + self.getShot(shotnumber).matshow(ax=ax, colorbar=False, annotations=True, legend=False) elif mode == '2d': self.getShot(shotnumber).plot2dttc(ax) self.getShot(shotnumber).plotmanual2dttc(ax) index += 1 if index > figPerSubplot: - fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1, wspace = 0, hspace = 0) + fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) fig = plt.figure() index = 1 - fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1, wspace = 0, hspace = 0) + fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) - def plotAllPicks(self, plotRemoved = False, colorByVal = 'log10SNR', ax = None, cbar = None, refreshPlot = False): + def plotAllPicks(self, plotRemoved=False, colorByVal='log10SNR', ax=None, cbar=None, refreshPlot=False): ''' Plots all picks over the distance between source and receiver. Returns (ax, region). Picks can be checked and removed by using region class (pylot.core.active.surveyPlotTools.regions) @@ -488,8 +502,8 @@ class Survey(object): spe.append(shot.getSymmetricPickError(traceID)) color = {'log10SNR': snrlog, - 'pickerror': pickerror, - 'spe': spe} + 'pickerror': pickerror, + 'spe': spe} self.color = color if refreshPlot is False: ax, cbar = self.createPlot(dist, pick, color[colorByVal], label='%s' % colorByVal) @@ -501,7 +515,7 @@ class Survey(object): ax.legend() return ax - def createPlot(self, dist, pick, inkByVal, label, ax = None, cbar = None): + def createPlot(self, dist, pick, inkByVal, label, ax=None, cbar=None): import matplotlib.pyplot as plt plt.interactive(True) cm = plt.cm.jet @@ -526,19 +540,19 @@ class Survey(object): def _update_progress(self, shotname, tend, progress): sys.stdout.write('Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r' % (shotname, - tend.hour, - tend.minute, - tend.second, - progress)) + tend.hour, + tend.minute, + tend.second, + progress)) sys.stdout.flush() - def saveSurvey(self, filename = 'survey.pickle'): + def saveSurvey(self, filename='survey.pickle'): import cPickle cleanUp(self) outfile = open(filename, 'wb') cPickle.dump(self, outfile, -1) - print('saved Survey to file %s'%(filename)) + print('saved Survey to file %s' % (filename)) @staticmethod def from_pickle(filename): diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py index cf503e22..2bb0aeb9 100644 --- a/pylot/core/active/fmtomo2vtk.py +++ b/pylot/core/active/fmtomo2vtk.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- import numpy as np -def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'abs', inputfileref = 'vgridsref.in'): + +def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', inputfileref='vgridsref.in'): ''' Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in :param: absOrRel, can be "abs" or "rel" for absolute or relative velocities. if "rel" inputfileref must be given :type: str ''' + def getDistance(angle): PI = np.pi R = 6371. @@ -23,7 +25,7 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a nPhi = int(vglines[1].split()[2]) print('readNumberOf Points: Awaiting %d grid points in %s' - %(nR*nTheta*nPhi, filename)) + % (nR * nTheta * nPhi, filename)) fin.close() return nR, nTheta, nPhi @@ -53,7 +55,8 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a ''' Reads in velocity from vgrids file and returns a list containing all values in the same order ''' - vel = []; count = 0 + vel = []; + count = 0 fin = open(filename, 'r') vglines = fin.readlines() @@ -62,10 +65,10 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a if count > 4: vel.append(float(line.split()[0])) - print("Read %d points out of file: %s" %(count - 4, filename)) + print("Read %d points out of file: %s" % (count - 4, filename)) return vel - R = 6371. # earth radius + R = 6371. # earth radius outfile = open(outputfile, 'w') # Theta, Phi in radians, R in km @@ -74,7 +77,9 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a sR, sTheta, sPhi = readStartpoints(inputfile) vel = readVelocity(inputfile) - nX = nPhi; nY = nTheta; nZ = nR + nX = nPhi; + nY = nTheta; + nZ = nR sZ = sR - R sX = getDistance(np.rad2deg(sPhi)) @@ -94,50 +99,51 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a outfile.writelines('ASCII\n') outfile.writelines('DATASET STRUCTURED_POINTS\n') - outfile.writelines('DIMENSIONS %d %d %d\n' %(nX, nY, nZ)) - outfile.writelines('ORIGIN %f %f %f\n' %(sX, sY, sZ)) - outfile.writelines('SPACING %f %f %f\n' %(dX, dY, dZ)) + outfile.writelines('DIMENSIONS %d %d %d\n' % (nX, nY, nZ)) + outfile.writelines('ORIGIN %f %f %f\n' % (sX, sY, sZ)) + outfile.writelines('SPACING %f %f %f\n' % (dX, dY, dZ)) - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('POINT_DATA %15d\n' % (nPoints)) if absOrRel == 'abs': - outfile.writelines('SCALARS velocity float %d\n' %(1)) + outfile.writelines('SCALARS velocity float %d\n' % (1)) elif absOrRel == 'rel': - outfile.writelines('SCALARS velChangePercent float %d\n' %(1)) + outfile.writelines('SCALARS velChangePercent float %d\n' % (1)) outfile.writelines('LOOKUP_TABLE default\n') # write velocity if absOrRel == 'abs': print("Writing velocity values to VTK file...") for velocity in vel: - outfile.writelines('%10f\n' %velocity) + outfile.writelines('%10f\n' % velocity) elif absOrRel == 'rel': velref = readVelocity(inputfileref) if not len(velref) == len(vel): - print('ERROR: Number of gridpoints mismatch for %s and %s'%(inputfile, inputfileref)) + print('ERROR: Number of gridpoints mismatch for %s and %s' % (inputfile, inputfileref)) return - #velrel = [((vel - velref) / velref * 100) for vel, velref in zip(vel, velref)] + # velrel = [((vel - velref) / velref * 100) for vel, velref in zip(vel, velref)] velrel = [] for velocities in zip(vel, velref): v, vref = velocities if not vref == 0: velrel.append((v - vref) / vref * 100) else: - velrel.append(0) + velrel.append(0) nR_ref, nTheta_ref, nPhi_ref = readNumberOfPoints(inputfileref) if not nR_ref == nR and nTheta_ref == nTheta and nPhi_ref == nPhi: - print('ERROR: Dimension mismatch of grids %s and %s'%(inputfile, inputfileref)) + print('ERROR: Dimension mismatch of grids %s and %s' % (inputfile, inputfileref)) return print("Writing velocity values to VTK file...") for velocity in velrel: - outfile.writelines('%10f\n' %velocity) - print('Pertubations: min: %s, max: %s'%(min(velrel), max(velrel))) + outfile.writelines('%10f\n' % velocity) + print('Pertubations: min: %s, max: %s' % (min(velrel), max(velrel))) outfile.close() - print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + print("Wrote velocity grid for %d points to file: %s" % (nPoints, outputfile)) return -def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): + +def rays2VTK(fnin, fdirout='./vtk_files/', nthPoint=50): ''' Writes VTK file(s) for FMTOMO rays from rays.dat. There is one file created for each ray. @@ -147,6 +153,7 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): :param: nthPoint, plot every nth point of the ray :type: integer ''' + def getDistance(angle): PI = np.pi R = 6371. @@ -164,12 +171,12 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): while True: raynumber += 1 firstline = infile.readline() - if firstline == '': break # break at EOF + if firstline == '': break # break at EOF raynumber = int(firstline.split()[0]) shotnumber = int(firstline.split()[1]) - rayValid = int(firstline.split()[4]) # is zero if the ray is invalid + rayValid = int(firstline.split()[4]) # is zero if the ray is invalid if rayValid == 0: - print('Invalid ray number %d for shot number %d'%(raynumber, shotnumber)) + print('Invalid ray number %d for shot number %d' % (raynumber, shotnumber)) continue nRayPoints = int(infile.readline().split()[0]) if not shotnumber in rays.keys(): @@ -178,14 +185,15 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): for index in range(nRayPoints): if index % nthPoint is 0 or index == (nRayPoints - 1): rad, lat, lon = infile.readline().split() - rays[shotnumber][raynumber].append([getDistance(np.rad2deg(float(lon))), getDistance(np.rad2deg(float(lat))), float(rad) - R]) + rays[shotnumber][raynumber].append( + [getDistance(np.rad2deg(float(lon))), getDistance(np.rad2deg(float(lat))), float(rad) - R]) else: dummy = infile.readline() infile.close() for shotnumber in rays.keys(): - fnameout = fdirout + 'rays%03d.vtk'%(shotnumber) + fnameout = fdirout + 'rays%03d.vtk' % (shotnumber) outfile = open(fnameout, 'w') nPoints = 0 @@ -194,43 +202,42 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): nPoints += 1 # write header - #print("Writing header for VTK file...") - print("Writing shot %d to file %s" %(shotnumber, fnameout)) + # print("Writing header for VTK file...") + print("Writing shot %d to file %s" % (shotnumber, fnameout)) outfile.writelines('# vtk DataFile Version 3.1\n') outfile.writelines('FMTOMO rays\n') outfile.writelines('ASCII\n') outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) + outfile.writelines('POINTS %15d float\n' % (nPoints)) # write coordinates - #print("Writing coordinates to VTK file...") + # print("Writing coordinates to VTK file...") for raynumber in rays[shotnumber].keys(): for raypoint in rays[shotnumber][raynumber]: - outfile.writelines('%10f %10f %10f \n' %(raypoint[0], raypoint[1], raypoint[2])) + outfile.writelines('%10f %10f %10f \n' % (raypoint[0], raypoint[1], raypoint[2])) - outfile.writelines('LINES %15d %15d\n' %(len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) + outfile.writelines('LINES %15d %15d\n' % (len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) # write indices - #print("Writing indices to VTK file...") + # print("Writing indices to VTK file...") count = 0 for raynumber in rays[shotnumber].keys(): - outfile.writelines('%d ' %(len(rays[shotnumber][raynumber]))) + outfile.writelines('%d ' % (len(rays[shotnumber][raynumber]))) for index in range(len(rays[shotnumber][raynumber])): - outfile.writelines('%d ' %(count)) + outfile.writelines('%d ' % (count)) count += 1 outfile.writelines('\n') - # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - # outfile.writelines('SCALARS rays float %d\n' %(1)) - # outfile.writelines('LOOKUP_TABLE default\n') + # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + # outfile.writelines('SCALARS rays float %d\n' %(1)) + # outfile.writelines('LOOKUP_TABLE default\n') - # # write velocity - # print("Writing velocity values to VTK file...") - # for velocity in vel: - # outfile.writelines('%10f\n' %velocity) - - # outfile.close() - # print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + # # write velocity + # print("Writing velocity values to VTK file...") + # for velocity in vel: + # outfile.writelines('%10f\n' %velocity) + # outfile.close() + # print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 558f2fd4..24feb4ba 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -2,11 +2,12 @@ import sys import numpy as np -def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'abs', inputfileref = 'vgridsref.in'): + +def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', inputfileref='vgridsref.in'): ''' Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in ''' - R = 6371. # earth radius + R = 6371. # earth radius outfile = open(outputfile, 'w') number, delta, start, vel = _readVgrid(inputfile) @@ -14,12 +15,14 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a nR, nTheta, nPhi = number dR, dTheta, dPhi = delta sR, sTheta, sPhi = start - + thetaGrid, phiGrid, rGrid = _generateGrids(number, delta, start) nPoints = nR * nTheta * nPhi - nX = nPhi; nY = nTheta; nZ = nR + nX = nPhi; + nY = nTheta; + nZ = nR sZ = sR - R sX = _getDistance(sPhi) @@ -36,50 +39,51 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a outfile.writelines('ASCII\n') outfile.writelines('DATASET STRUCTURED_POINTS\n') - outfile.writelines('DIMENSIONS %d %d %d\n' %(nX, nY, nZ)) - outfile.writelines('ORIGIN %f %f %f\n' %(sX, sY, sZ)) - outfile.writelines('SPACING %f %f %f\n' %(dX, dY, dZ)) + outfile.writelines('DIMENSIONS %d %d %d\n' % (nX, nY, nZ)) + outfile.writelines('ORIGIN %f %f %f\n' % (sX, sY, sZ)) + outfile.writelines('SPACING %f %f %f\n' % (dX, dY, dZ)) - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) + outfile.writelines('POINT_DATA %15d\n' % (nPoints)) if absOrRel == 'abs': - outfile.writelines('SCALARS velocity float %d\n' %(1)) + outfile.writelines('SCALARS velocity float %d\n' % (1)) elif absOrRel == 'rel': - outfile.writelines('SCALARS velChangePercent float %d\n' %(1)) + outfile.writelines('SCALARS velChangePercent float %d\n' % (1)) outfile.writelines('LOOKUP_TABLE default\n') # write velocity if absOrRel == 'abs': print("Writing velocity values to VTK file...") for velocity in vel: - outfile.writelines('%10f\n' %velocity) + outfile.writelines('%10f\n' % velocity) elif absOrRel == 'rel': nref, dref, sref, velref = _readVgrid(inputfileref) nR_ref, nTheta_ref, nPhi_ref = nref if not len(velref) == len(vel): - print('ERROR: Number of gridpoints mismatch for %s and %s'%(inputfile, inputfileref)) + print('ERROR: Number of gridpoints mismatch for %s and %s' % (inputfile, inputfileref)) return - #velrel = [((vel - velref) / velref * 100) for vel, velref in zip(vel, velref)] + # velrel = [((vel - velref) / velref * 100) for vel, velref in zip(vel, velref)] velrel = [] for velocities in zip(vel, velref): v, vref = velocities if not vref == 0: velrel.append((v - vref) / vref * 100) else: - velrel.append(0) + velrel.append(0) if not nR_ref == nR and nTheta_ref == nTheta and nPhi_ref == nPhi: - print('ERROR: Dimension mismatch of grids %s and %s'%(inputfile, inputfileref)) + print('ERROR: Dimension mismatch of grids %s and %s' % (inputfile, inputfileref)) return print("Writing velocity values to VTK file...") for velocity in velrel: - outfile.writelines('%10f\n' %velocity) - print('Pertubations: min: %s %%, max: %s %%'%(min(velrel), max(velrel))) + outfile.writelines('%10f\n' % velocity) + print('Pertubations: min: %s %%, max: %s %%' % (min(velrel), max(velrel))) outfile.close() - print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) + print("Wrote velocity grid for %d points to file: %s" % (nPoints, outputfile)) return -def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): + +def rays2VTK(fnin, fdirout='./vtk_files/', nthPoint=50): ''' Writes VTK file(s) for FMTOMO rays from rays.dat @@ -96,12 +100,12 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): while True: raynumber += 1 firstline = infile.readline() - if firstline == '': break # break at EOF + if firstline == '': break # break at EOF raynumber = int(firstline.split()[0]) shotnumber = int(firstline.split()[1]) - rayValid = int(firstline.split()[4]) # is zero if the ray is invalid + rayValid = int(firstline.split()[4]) # is zero if the ray is invalid if rayValid == 0: - print('Invalid ray number %d for shot number %d'%(raynumber, shotnumber)) + print('Invalid ray number %d for shot number %d' % (raynumber, shotnumber)) continue nRayPoints = int(infile.readline().split()[0]) if not shotnumber in rays.keys(): @@ -110,14 +114,15 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): for index in range(nRayPoints): if index % nthPoint is 0 or index == (nRayPoints - 1): rad, lat, lon = infile.readline().split() - rays[shotnumber][raynumber].append([_getDistance(np.rad2deg(float(lon))), _getDistance(np.rad2deg(float(lat))), float(rad) - R]) + rays[shotnumber][raynumber].append( + [_getDistance(np.rad2deg(float(lon))), _getDistance(np.rad2deg(float(lat))), float(rad) - R]) else: dummy = infile.readline() infile.close() for shotnumber in rays.keys(): - fnameout = fdirout + 'rays%03d.vtk'%(shotnumber) + fnameout = fdirout + 'rays%03d.vtk' % (shotnumber) outfile = open(fnameout, 'w') nPoints = 0 @@ -126,32 +131,33 @@ def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): nPoints += 1 # write header - #print("Writing header for VTK file...") - print("Writing shot %d to file %s" %(shotnumber, fnameout)) + # print("Writing header for VTK file...") + print("Writing shot %d to file %s" % (shotnumber, fnameout)) outfile.writelines('# vtk DataFile Version 3.1\n') outfile.writelines('FMTOMO rays\n') outfile.writelines('ASCII\n') outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) + outfile.writelines('POINTS %15d float\n' % (nPoints)) # write coordinates - #print("Writing coordinates to VTK file...") + # print("Writing coordinates to VTK file...") for raynumber in rays[shotnumber].keys(): for raypoint in rays[shotnumber][raynumber]: - outfile.writelines('%10f %10f %10f \n' %(raypoint[0], raypoint[1], raypoint[2])) + outfile.writelines('%10f %10f %10f \n' % (raypoint[0], raypoint[1], raypoint[2])) - outfile.writelines('LINES %15d %15d\n' %(len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) + outfile.writelines('LINES %15d %15d\n' % (len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) # write indices - #print("Writing indices to VTK file...") + # print("Writing indices to VTK file...") count = 0 for raynumber in rays[shotnumber].keys(): - outfile.writelines('%d ' %(len(rays[shotnumber][raynumber]))) + outfile.writelines('%d ' % (len(rays[shotnumber][raynumber]))) for index in range(len(rays[shotnumber][raynumber])): - outfile.writelines('%d ' %(count)) + outfile.writelines('%d ' % (count)) count += 1 outfile.writelines('\n') + def _readVgrid(filename): def readNumberOfPoints(filename): fin = open(filename, 'r') @@ -162,7 +168,7 @@ def _readVgrid(filename): nPhi = int(vglines[1].split()[2]) print('readNumberOf Points: Awaiting %d grid points in %s' - %(nR*nTheta*nPhi, filename)) + % (nR * nTheta * nPhi, filename)) fin.close() return nR, nTheta, nPhi @@ -189,10 +195,11 @@ def _readVgrid(filename): return sR, sTheta, sPhi def readVelocity(filename): - ''' + ''' Reads in velocity from vgrids file and returns a list containing all values in the same order ''' - vel = []; count = 0 + vel = []; + count = 0 fin = open(filename, 'r') vglines = fin.readlines() @@ -201,7 +208,7 @@ def _readVgrid(filename): if count > 4: vel.append(float(line.split()[0])) - print("Read %d points out of file: %s" %(count - 4, filename)) + print("Read %d points out of file: %s" % (count - 4, filename)) return vel # Theta, Phi in radians, R in km @@ -218,23 +225,25 @@ def _readVgrid(filename): start = (sR, sTheta, sPhi) return number, delta, start, vel + def _generateGrids(number, delta, start): nR, nTheta, nPhi = number dR, dTheta, dPhi = delta sR, sTheta, sPhi = start - + eR = sR + (nR - 1) * dR ePhi = sPhi + (nPhi - 1) * dPhi eTheta = sTheta + (nTheta - 1) * dTheta - thetaGrid = np.linspace(sTheta, eTheta, num = nTheta) - phiGrid = np.linspace(sPhi, ePhi, num = nPhi) - rGrid = np.linspace(sR, eR, num = nR) + thetaGrid = np.linspace(sTheta, eTheta, num=nTheta) + phiGrid = np.linspace(sPhi, ePhi, num=nPhi) + rGrid = np.linspace(sR, eR, num=nR) return (thetaGrid, phiGrid, rGrid) -def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', - outputfile = 'vgrids_cb.in', ampmethod = 'linear', rect = (None, None)): + +def addCheckerboard(spacing=10., pertubation=0.1, inputfile='vgrids.in', + outputfile='vgrids_cb.in', ampmethod='linear', rect=(None, None)): ''' Add a checkerboard to an existing vgrids.in velocity model. @@ -244,13 +253,14 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', :param: pertubation, pertubation (default: 0.1 = 10%) type: float ''' - def correctSpacing(spacing, delta, disttype = None): + + def correctSpacing(spacing, delta, disttype=None): if spacing > delta: spacing_corr = round(spacing / delta) * delta elif spacing < delta: spacing_corr = delta print('The spacing of the checkerboard of %s (%s) was corrected to ' - 'a value of %s to fit the grid spacing of %s.' %(spacing, disttype, spacing_corr, delta)) + 'a value of %s to fit the grid spacing of %s.' % (spacing, disttype, spacing_corr, delta)) return spacing_corr def linearAmp(InCell): @@ -265,7 +275,7 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', else: return 0 - def ampFunc(InCell, method = 'linear', rect = None): + def ampFunc(InCell, method='linear', rect=None): if method == 'linear': return linearAmp(InCell) if method == 'rect' and rect is not None: @@ -273,7 +283,7 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', else: print('ampFunc: Could not amplify cb pattern') - decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) outfile = open(outputfile, 'w') number, delta, start, vel = _readVgrid(inputfile) @@ -281,16 +291,16 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', nR, nTheta, nPhi = number dR, dTheta, dPhi = delta sR, sTheta, sPhi = start - + thetaGrid, phiGrid, rGrid = _generateGrids(number, delta, start) nPoints = nR * nTheta * nPhi # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' %(1, 1)) - outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' %(dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) - outfile.writelines('%10s %10s %10s\n' %(sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) + outfile.writelines('%10s %10s \n' % (1, 1)) + outfile.writelines('%10s %10s %10s\n' % (nR, nTheta, nPhi)) + outfile.writelines('%10s %10s %10s\n' % (dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) + outfile.writelines('%10s %10s %10s\n' % (sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) spacR = correctSpacing(spacing, dR, '[meter], R') spacTheta = correctSpacing(_getAngle(spacing), dTheta, '[degree], Theta') @@ -298,7 +308,8 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', count = 0 evenOdd = 1 - even = 0; odd = 0 + even = 0; + odd = 0 # In the following loop it is checked whether the positive distance from the border of the model # for a point on the grid divided by the spacing is even or odd and then pertubated. @@ -309,21 +320,21 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', # The amplification factor ampFactor comes from a linear relationship and ranges between 0 (cell border) # and 1 (cell middle) for radius in rGrid: - rInCell = (radius - sR - dR/2) / spacR + rInCell = (radius - sR - dR / 2) / spacR ampR = ampFunc(rInCell, ampmethod, rect) if np.floor(rInCell) % 2: evenOddR = 1 else: evenOddR = -1 for theta in thetaGrid: - thetaInCell = (theta - sTheta - dTheta/2) / spacTheta + thetaInCell = (theta - sTheta - dTheta / 2) / spacTheta ampTheta = ampFunc(thetaInCell, ampmethod, rect) if np.floor(thetaInCell) % 2: evenOddT = 1 else: evenOddT = -1 for phi in phiGrid: - phiInCell = (phi - sPhi - dPhi/2) / spacPhi + phiInCell = (phi - sPhi - dPhi / 2) / spacPhi ampPhi = ampFunc(phiInCell, ampmethod, rect) if np.floor(phiInCell) % 2: evenOddP = 1 @@ -334,19 +345,20 @@ def addCheckerboard(spacing = 10., pertubation = 0.1, inputfile = 'vgrids.in', evenOdd = evenOddR * evenOddT * evenOddP * ampFactor velocity += evenOdd * pertubation * velocity - outfile.writelines('%10s %10s\n'%(velocity, decm)) + outfile.writelines('%10s %10s\n' % (velocity, decm)) count += 1 progress = float(count) / float(nPoints) * 100 _update_progress(progress) print('Added checkerboard to the grid in file %s with a spacing of %s and a pertubation of %s %%. ' - 'Outputfile: %s.'%(inputfile, spacing, pertubation*100, outputfile)) + 'Outputfile: %s.' % (inputfile, spacing, pertubation * 100, outputfile)) outfile.close() -def addBox(x = (None, None), y = (None, None), z = (None, None), - boxvelocity = 1.0, inputfile = 'vgrids.in', - outputfile = 'vgrids_box.in'): + +def addBox(x=(None, None), y=(None, None), z=(None, None), + boxvelocity=1.0, inputfile='vgrids.in', + outputfile='vgrids_box.in'): ''' Add a box with constant velocity to an existing vgrids.in velocity model. @@ -363,7 +375,7 @@ def addBox(x = (None, None), y = (None, None), z = (None, None), type: float ''' R = 6371. - decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) outfile = open(outputfile, 'w') theta1 = _getAngle(y[0]) @@ -375,23 +387,23 @@ def addBox(x = (None, None), y = (None, None), z = (None, None), print('Adding box to grid with theta = (%s, %s), phi = (%s, %s), ' 'r = (%s, %s), velocity = %s [km/s]' - %(theta1, theta2, phi1, phi2, r1, r2, boxvelocity)) - + % (theta1, theta2, phi1, phi2, r1, r2, boxvelocity)) + number, delta, start, vel = _readVgrid(inputfile) nR, nTheta, nPhi = number dR, dTheta, dPhi = delta sR, sTheta, sPhi = start - + thetaGrid, phiGrid, rGrid = _generateGrids(number, delta, start) nPoints = nR * nTheta * nPhi # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' %(1, 1)) - outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' %(dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) - outfile.writelines('%10s %10s %10s\n' %(sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) + outfile.writelines('%10s %10s \n' % (1, 1)) + outfile.writelines('%10s %10s %10s\n' % (nR, nTheta, nPhi)) + outfile.writelines('%10s %10s %10s\n' % (dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) + outfile.writelines('%10s %10s %10s\n' % (sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) count = 0 for radius in rGrid: @@ -413,20 +425,22 @@ def addBox(x = (None, None), y = (None, None), z = (None, None), if rFlag * thetaFlag * phiFlag is not 0: velocity = boxvelocity - outfile.writelines('%10s %10s\n'%(velocity, decm)) + outfile.writelines('%10s %10s\n' % (velocity, decm)) count += 1 progress = float(count) / float(nPoints) * 100 _update_progress(progress) print('Added box to the grid in file %s. ' - 'Outputfile: %s.'%(inputfile, outputfile)) + 'Outputfile: %s.' % (inputfile, outputfile)) outfile.close() + def _update_progress(progress): - sys.stdout.write("%d%% done \r" % (progress) ) + sys.stdout.write("%d%% done \r" % (progress)) sys.stdout.flush() + def _getAngle(distance): ''' Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. @@ -436,9 +450,9 @@ def _getAngle(distance): angle = distance * 180. / (PI * R) return angle + def _getDistance(angle): PI = np.pi R = 6371. distance = angle / 180 * (PI * R) return distance - diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 22cd8486..275587a0 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -3,6 +3,7 @@ import sys import numpy as np from scipy.interpolate import griddata + class SeisArray(object): ''' Can be used to interpolate missing values of a receiver grid, if only support points were measured. @@ -15,6 +16,7 @@ class SeisArray(object): Supports vtk output for sources and receivers. Note: Source and Receiver files for FMTOMO will be generated by the Survey object (because traveltimes will be added directly). ''' + def __init__(self, recfile): self.recfile = recfile self._receiverlines = {} @@ -35,7 +37,7 @@ class SeisArray(object): ''' for receiver in self._receiverlist: traceID = int(receiver.split()[0]) - lineID = int(receiver.split()[1]) + lineID = int(receiver.split()[1]) if not lineID in self._receiverlines.keys(): self._receiverlines[lineID] = [] self._receiverlines[lineID].append(traceID) @@ -132,7 +134,7 @@ class SeisArray(object): if traceID2 < traceID1: direction = -1 return direction - print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) + print "Error: Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) def _checkCoordDirection(self, traceID1, traceID2, coordinate): ''' @@ -144,14 +146,15 @@ class SeisArray(object): if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): direction = -1 return direction - print "Error: Same Value for traceID1 = %s and traceID2 = %s" %(traceID1, traceID2) + print "Error: Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): ''' Returns the mean distance between two traceID's depending on the number of geophones in between ''' num_spaces = abs(self._getGeophoneNumber(traceID1) - self._getGeophoneNumber(traceID2)) - mean_distance = abs(self._getReceiverValue(traceID1, coordinate) - self._getReceiverValue(traceID2, coordinate))/num_spaces + mean_distance = abs( + self._getReceiverValue(traceID1, coordinate) - self._getReceiverValue(traceID2, coordinate)) / num_spaces return mean_distance def interpolateValues(self, coordinate): @@ -159,22 +162,22 @@ class SeisArray(object): Interpolates and sets all values (linear) for coordinate = 'X', 'Y' or 'Z' ''' for lineID in self._getReceiverlines().keys(): - number_measured = len(self._getReceiverlines()[lineID]) - for index, traceID1 in enumerate(self._getReceiverlines()[lineID]): - if index + 1 < number_measured: - traceID2 = self._getReceiverlines()[lineID][index + 1] + number_measured = len(self._getReceiverlines()[lineID]) + for index, traceID1 in enumerate(self._getReceiverlines()[lineID]): + if index + 1 < number_measured: + traceID2 = self._getReceiverlines()[lineID][index + 1] - traceID_dir = self._checkTraceIDdirection(traceID1, traceID2) - traceID_interp = traceID1 + traceID_dir + traceID_dir = self._checkTraceIDdirection(traceID1, traceID2) + traceID_interp = traceID1 + traceID_dir - coord_dir = self._checkCoordDirection(traceID1, traceID2, coordinate) - mean_distance = self._interpolateMeanDistances(traceID1, traceID2, coordinate) + coord_dir = self._checkCoordDirection(traceID1, traceID2, coordinate) + mean_distance = self._interpolateMeanDistances(traceID1, traceID2, coordinate) - while (traceID_dir * traceID_interp) < (traceID_dir * traceID2): - self._setValue(traceID_interp, coordinate, - (self._getReceiverValue(traceID_interp - traceID_dir, coordinate) - + coord_dir * (mean_distance))) - traceID_interp += traceID_dir + while (traceID_dir * traceID_interp) < (traceID_dir * traceID2): + self._setValue(traceID_interp, coordinate, + (self._getReceiverValue(traceID_interp - traceID_dir, coordinate) + + coord_dir * (mean_distance))) + traceID_interp += traceID_dir def addMeasuredTopographyPoints(self, filename): ''' @@ -206,7 +209,7 @@ class SeisArray(object): z = float(line[3]) self._sourceLocs[pointID] = (x, y, z) - def interpZcoords4rec(self, method = 'linear'): + def interpZcoords4rec(self, method='linear'): ''' Interpolates z values for all receivers. ''' @@ -214,7 +217,8 @@ class SeisArray(object): for traceID in self.getReceiverCoordinates().keys(): if type(self.getReceiverCoordinates()[traceID]) is not tuple: - z = griddata((measured_x, measured_y), measured_z, (self._getXreceiver(traceID), self._getYreceiver(traceID)), method = method) + z = griddata((measured_x, measured_y), measured_z, + (self._getXreceiver(traceID), self._getYreceiver(traceID)), method=method) self._setZvalue(traceID, float(z)) def _getAngle(self, distance): @@ -239,7 +243,9 @@ class SeisArray(object): ''' Returns a list of all measured receivers known to SeisArray. ''' - x = []; y = []; z = [] + x = []; + y = []; + z = [] for traceID in self.getMeasuredReceivers().keys(): x.append(self.getMeasuredReceivers()[traceID][0]) y.append(self.getMeasuredReceivers()[traceID][1]) @@ -250,7 +256,9 @@ class SeisArray(object): ''' Returns a list of all measured topography points known to the SeisArray. ''' - x = []; y = []; z = [] + x = []; + y = []; + z = [] for pointID in self.getMeasuredTopo().keys(): x.append(self.getMeasuredTopo()[pointID][0]) y.append(self.getMeasuredTopo()[pointID][1]) @@ -261,7 +269,9 @@ class SeisArray(object): ''' Returns a list of all measured source locations known to SeisArray. ''' - x = []; y = []; z = [] + x = []; + y = []; + z = [] for pointID in self.getSourceLocations().keys(): x.append(self.getSourceLocations()[pointID][0]) y.append(self.getSourceLocations()[pointID][1]) @@ -285,7 +295,9 @@ class SeisArray(object): ''' Returns a list of all receivers (measured and interpolated). ''' - x = []; y =[]; z = [] + x = []; + y = []; + z = [] for traceID in self.getReceiverCoordinates().keys(): x.append(self.getReceiverCoordinates()[traceID][0]) y.append(self.getReceiverCoordinates()[traceID][1]) @@ -303,7 +315,7 @@ class SeisArray(object): self._interpolateXY4rec() self.interpZcoords4rec() - def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, elevation = 0.25, method = 'linear'): + def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, elevation=0.25, method='linear'): ''' Interpolate Z values on a regular grid with cushion nodes e.g. to use it as FMTOMO topography interface. Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). @@ -325,7 +337,7 @@ class SeisArray(object): ''' return self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, elevation, method) - def interpolateOnRegularGrid(self, nTheta, nPhi, thetaSN, phiWE, elevation, method = 'linear'): + def interpolateOnRegularGrid(self, nTheta, nPhi, thetaSN, phiWE, elevation, method='linear'): ''' Interpolate Z values on a regular grid with cushion nodes e.g. to use it as FMTOMO topography interface. Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). @@ -349,8 +361,8 @@ class SeisArray(object): surface = [] print "Interpolating interface on regular grid with the dimensions:" - print "nTheta = %s, nPhi = %s, thetaSN = %s, phiWE = %s"%(nTheta, nPhi, thetaSN, phiWE) - print "method = %s, elevation = %s" %(method, elevation) + print "nTheta = %s, nPhi = %s, thetaSN = %s, phiWE = %s" % (nTheta, nPhi, thetaSN, phiWE) + print "method = %s, elevation = %s" % (method, elevation) thetaS, thetaN = thetaSN phiW, phiE = phiWE @@ -361,18 +373,19 @@ class SeisArray(object): deltaTheta = (thetaN - thetaS) / (nTheta - 1) deltaPhi = (phiE - phiW) / (nPhi - 1) - thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num = nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num = nPhi + 2) # +2 cushion nodes + thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num=nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num=nPhi + 2) # +2 cushion nodes - nTotal = len(thetaGrid) * len(phiGrid); count = 0 + nTotal = len(thetaGrid) * len(phiGrid); + count = 0 for theta in thetaGrid: for phi in phiGrid: xval = self._getDistance(phi) yval = self._getDistance(theta) - z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = method) + z = griddata((measured_x, measured_y), measured_z, (xval, yval), method=method) # in case the point lies outside, nan will be returned. Find nearest: if np.isnan(z) == True: - z = griddata((measured_x, measured_y), measured_z, (xval, yval), method = 'nearest') + z = griddata((measured_x, measured_y), measured_z, (xval, yval), method='nearest') z = float(z) + elevation surface.append((xval, yval, z)) count += 1 @@ -382,8 +395,8 @@ class SeisArray(object): return surface def generateFMTOMOinputFromArray(self, nPointsPropgrid, nPointsInvgrid, - zBotTop, cushionfactor, interpolationMethod = 'linear', - customgrid = 'mygrid.in', writeVTK = True): + zBotTop, cushionfactor, interpolationMethod='linear', + customgrid='mygrid.in', writeVTK=True): ''' Generate FMTOMO input files from the SeisArray dimensions. Generates: vgrids.in, interfaces.in, propgrid.in @@ -401,15 +414,15 @@ class SeisArray(object): :type: float ''' - nRP, nThetaP, nPhiP = nPointsPropgrid + nRP, nThetaP, nPhiP = nPointsPropgrid nRI, nThetaI, nPhiI = nPointsInvgrid print('\n------------------------------------------------------------') print('Automatically generating input for FMTOMO from array size.') - print('Propgrid: nR = %s, nTheta = %s, nPhi = %s'%(nRP, nThetaP, nPhiP)) - print('Interpolation Grid and Interfaces Grid: nR = %s, nTheta = %s, nPhi = %s'%(nRI, nThetaI, nPhiI)) - print('Bottom and Top of model: (%s, %s)'%(zBotTop[0], zBotTop[1])) - print('Method: %s, customgrid = %s'%(interpolationMethod, customgrid)) + print('Propgrid: nR = %s, nTheta = %s, nPhi = %s' % (nRP, nThetaP, nPhiP)) + print('Interpolation Grid and Interfaces Grid: nR = %s, nTheta = %s, nPhi = %s' % (nRI, nThetaI, nPhiI)) + print('Bottom and Top of model: (%s, %s)' % (zBotTop[0], zBotTop[1])) + print('Method: %s, customgrid = %s' % (interpolationMethod, customgrid)) print('------------------------------------------------------------') def getZmin(surface): @@ -418,31 +431,31 @@ class SeisArray(object): z.append(point[2]) return min(z) - self.generatePropgrid(nThetaP, nPhiP, nRP, zBotTop, cushionfactor = cushionfactor, - cushionpropgrid = 0.05) - surface = self.generateVgrid(nThetaI, nPhiI, nRI, zBotTop, method = interpolationMethod, - cushionfactor = cushionfactor, infilename = customgrid, - returnTopo = True) + self.generatePropgrid(nThetaP, nPhiP, nRP, zBotTop, cushionfactor=cushionfactor, + cushionpropgrid=0.05) + surface = self.generateVgrid(nThetaI, nPhiI, nRI, zBotTop, method=interpolationMethod, + cushionfactor=cushionfactor, infilename=customgrid, + returnTopo=True) - depthmax = abs(zBotTop[0] - getZmin(surface)) - 1.0 # cushioning for the bottom interface + depthmax = abs(zBotTop[0] - getZmin(surface)) - 1.0 # cushioning for the bottom interface - interf1, interf2 = self.generateInterfaces(nThetaI, nPhiI, depthmax, cushionfactor = cushionfactor, - returnInterfaces = True, method = interpolationMethod) + interf1, interf2 = self.generateInterfaces(nThetaI, nPhiI, depthmax, cushionfactor=cushionfactor, + returnInterfaces=True, method=interpolationMethod) if writeVTK == True: from pylot.core.active import fmtomoUtils - self.surface2VTK(interf1, filename = 'interface1.vtk') - self.surface2VTK(interf2, filename = 'interface2.vtk') + self.surface2VTK(interf1, filename='interface1.vtk') + self.surface2VTK(interf2, filename='interface2.vtk') self.receivers2VTK() self.sources2VTK() fmtomoUtils.vgrids2VTK() - def generateReceiversIn(self, outfilename = 'receivers.in'): + def generateReceiversIn(self, outfilename='receivers.in'): outfile = open(outfilename, 'w') recx, recy, recz = self.getReceiverLists() nsrc = len(self.getSourceLocations()) - outfile.writelines('%s\n'%(len(zip(recx, recy, recz)) * nsrc)) + outfile.writelines('%s\n' % (len(zip(recx, recy, recz)) * nsrc)) for index in range(nsrc): for point in zip(recx, recy, recz): @@ -450,17 +463,16 @@ class SeisArray(object): rad = - rz lat = self._getAngle(ry) lon = self._getAngle(rx) - outfile.writelines('%15s %15s %15s\n'%(rad, lat, lon)) - outfile.writelines('%15s\n'%(1)) - outfile.writelines('%15s\n'%(index + 1)) - outfile.writelines('%15s\n'%(1)) + outfile.writelines('%15s %15s %15s\n' % (rad, lat, lon)) + outfile.writelines('%15s\n' % (1)) + outfile.writelines('%15s\n' % (index + 1)) + outfile.writelines('%15s\n' % (1)) outfile.close() - - def generateInterfaces(self, nTheta, nPhi, depthmax, cushionfactor = 0.1, - outfilename = 'interfaces.in', method = 'linear', - returnInterfaces = False): + def generateInterfaces(self, nTheta, nPhi, depthmax, cushionfactor=0.1, + outfilename='interfaces.in', method='linear', + returnInterfaces=False): ''' Create an interfaces.in file for FMTOMO from the SeisArray boundaries. :param: nTheta, number of points in Theta @@ -470,7 +482,7 @@ class SeisArray(object): type: int :param: depthmax, maximum depth of the model (below topography) - type: float + type: float :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) type: float @@ -478,7 +490,7 @@ class SeisArray(object): print('\n------------------------------------------------------------') print('Generating interfaces...') - nInterfaces = 2 + nInterfaces = 2 # generate dimensions of the grid from array thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) @@ -494,22 +506,22 @@ class SeisArray(object): deltaPhi = abs(phiE - phiW) / float((nPhi - 1)) # write header for interfaces grid file (in RADIANS) - outfile.writelines('%10s\n' %(nInterfaces)) - outfile.writelines('%10s %10s\n' %(nTheta + 2, nPhi + 2)) # +2 cushion nodes - outfile.writelines('%10s %10s\n' %(np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) - outfile.writelines('%10s %10s\n' %(np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) + outfile.writelines('%10s\n' % (nInterfaces)) + outfile.writelines('%10s %10s\n' % (nTheta + 2, nPhi + 2)) # +2 cushion nodes + outfile.writelines('%10s %10s\n' % (np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) + outfile.writelines('%10s %10s\n' % (np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) - interface1 = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method) - interface2 = self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, -depthmax, method = method) + interface1 = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method=method) + interface2 = self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, -depthmax, method=method) for point in interface1: z = point[2] - outfile.writelines('%10s\n'%(z + R)) + outfile.writelines('%10s\n' % (z + R)) outfile.writelines('\n') for point in interface2: z = point[2] - outfile.writelines('%10s\n'%(z + R)) + outfile.writelines('%10s\n' % (z + R)) outfile.close() @@ -519,10 +531,10 @@ class SeisArray(object): print('Finished generating interfaces.') print('------------------------------------------------------------') - def getThetaPhiFromArray(self, cushionfactor = 0.1): + def getThetaPhiFromArray(self, cushionfactor=0.1): ''' Determine and returns PhiWE (tuple: (West, East)) and thetaSN (tuple (South, North)) from the SeisArray boundaries. - + :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) type: float ''' @@ -535,8 +547,8 @@ class SeisArray(object): thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) return thetaSN, phiWE - def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionfactor, cushionpropgrid = 0.05, - refinement = (5, 5), outfilename = 'propgrid.in'): + def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionfactor, cushionpropgrid=0.05, + refinement=(5, 5), outfilename='propgrid.in'): ''' Create a propergation grid file for FMTOMO using SeisArray boundaries @@ -566,10 +578,10 @@ class SeisArray(object): print('\n------------------------------------------------------------') print('Generating Propagation Grid for nTheta = %s, nPhi' - ' = %s, nR = %s and a cushioning of %s' - %(nTheta, nPhi, nR, cushionpropgrid)) - print('Bottom of the grid: %s, top of the grid %s' - %(Rbt[0], Rbt[1])) + ' = %s, nR = %s and a cushioning of %s' + % (nTheta, nPhi, nR, cushionpropgrid)) + print('Bottom of the grid: %s, top of the grid %s' + % (Rbt[0], Rbt[1])) thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) @@ -584,20 +596,20 @@ class SeisArray(object): deltaPhi = abs(phiE - phiW) / float(nPhi - 1) deltaR = abs(rbot - rtop) / float(nR - 1) - outfile.writelines('%10s %10s %10s\n' %(nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' %(deltaR, deltaTheta, deltaPhi)) - outfile.writelines('%10s %10s %10s\n' %(rtop, thetaS, phiW)) - outfile.writelines('%10s %10s\n' %refinement) + outfile.writelines('%10s %10s %10s\n' % (nR, nTheta, nPhi)) + outfile.writelines('%10s %10s %10s\n' % (deltaR, deltaTheta, deltaPhi)) + outfile.writelines('%10s %10s %10s\n' % (rtop, thetaS, phiW)) + outfile.writelines('%10s %10s\n' % refinement) outfile.close() - print('Created Propagation Grid and saved it to %s' %outfilename) + print('Created Propagation Grid and saved it to %s' % outfilename) print('------------------------------------------------------------') - def generateVgrid(self, nTheta, nPhi, nR, Rbt, thetaSN = None, - phiWE = None, cushionfactor = 0.1, - outfilename = 'vgrids.in', method = 'linear', - infilename = 'mygrid.in', returnTopo = False): + def generateVgrid(self, nTheta, nPhi, nR, Rbt, thetaSN=None, + phiWE=None, cushionfactor=0.1, + outfilename='vgrids.in', method='linear', + infilename='mygrid.in', returnTopo=False): ''' Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. @@ -641,11 +653,14 @@ class SeisArray(object): return nlayers def readMygrid(filename): - ztop = []; zbot = []; vtop = []; vbot = [] + ztop = []; + zbot = []; + vtop = []; + vbot = [] infile = open(filename, 'r') nlayers = readMygridNlayers(filename) - print('\nreadMygrid: Reading file %s.'%filename) + print('\nreadMygrid: Reading file %s.' % filename) for index in range(nlayers): line1 = infile.readline() line2 = infile.readline() @@ -655,11 +670,11 @@ class SeisArray(object): vbot.append(float(line2.split()[1])) print('Layer %s:\n[Top: v = %s [km/s], z = %s [m]]' '\n[Bot: v = %s [km/s], z = %s [m]]' - %(index + 1, vtop[index], ztop[index], - vbot[index], zbot[index])) + % (index + 1, vtop[index], ztop[index], + vbot[index], zbot[index])) if not ztop[0] == 0: - print('ERROR: there must be a velocity set for z = 0 in the file %s'%filename) + print('ERROR: there must be a velocity set for z = 0 in the file %s' % filename) print('e.g.:\n0 0.33\n-5 1.0\netc.') infile.close() @@ -667,7 +682,7 @@ class SeisArray(object): R = 6371. vmin = 0.34 - decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) + decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) outfile = open(outfilename, 'w') # generate dimensions of the grid from array @@ -685,28 +700,29 @@ class SeisArray(object): deltaR = abs(rbot - rtop) / float((nR - 1)) # create a regular grid including +2 cushion nodes in every direction - thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num = nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num = nPhi + 2) # +2 cushion nodes - rGrid = np.linspace(rbot - deltaR, rtop + deltaR, num = nR + 2) # +2 cushion nodes + thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num=nTheta + 2) # +2 cushion nodes + phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num=nPhi + 2) # +2 cushion nodes + rGrid = np.linspace(rbot - deltaR, rtop + deltaR, num=nR + 2) # +2 cushion nodes nTotal = len(rGrid) * len(thetaGrid) * len(phiGrid) - print("Total number of grid nodes: %s"%nTotal) + print("Total number of grid nodes: %s" % nTotal) # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' %(1, 1)) - outfile.writelines('%10s %10s %10s\n' %(nR + 2, nTheta + 2, nPhi + 2)) - outfile.writelines('%10s %10s %10s\n' %(deltaR, np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) - outfile.writelines('%10s %10s %10s\n' %(rbot - deltaR, np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) + outfile.writelines('%10s %10s \n' % (1, 1)) + outfile.writelines('%10s %10s %10s\n' % (nR + 2, nTheta + 2, nPhi + 2)) + outfile.writelines('%10s %10s %10s\n' % (deltaR, np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) + outfile.writelines( + '%10s %10s %10s\n' % (rbot - deltaR, np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) - surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method = method) + surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method=method) nlayers = readMygridNlayers(infilename) ztop, zbot, vtop, vbot = readMygrid(infilename) print("\nGenerating velocity grid for FMTOMO. " - "Output filename = %s, interpolation method = %s"%(outfilename, method)) + "Output filename = %s, interpolation method = %s" % (outfilename, method)) print("nTheta = %s, nPhi = %s, nR = %s, " - "thetaSN = %s, phiWE = %s, Rbt = %s"%(nTheta, nPhi, nR, thetaSN, phiWE, Rbt)) + "thetaSN = %s, phiWE = %s, Rbt = %s" % (nTheta, nPhi, nR, thetaSN, phiWE, Rbt)) count = 0 for radius in rGrid: @@ -721,32 +737,36 @@ class SeisArray(object): depth = -(R + topo - radius) if depth > 1: vel = 0.0 - elif 1 >= depth > 0: # cushioning around topography + elif 1 >= depth > 0: # cushioning around topography vel = vtop[0] else: for index in range(nlayers): if (ztop[index]) >= depth > (zbot[index]): - vel = (depth - ztop[index]) / (zbot[index] - ztop[index]) * (vbot[index] - vtop[index]) + vtop[index] + vel = (depth - ztop[index]) / (zbot[index] - ztop[index]) * ( + vbot[index] - vtop[index]) + vtop[index] break if not (ztop[index]) >= depth > (zbot[index]): - print('ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile'%(depth-topo)) + print( + 'ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile' % ( + depth - topo)) return count += 1 if vel < 0: - print('ERROR, vel <0; z, topo, zbot, vbot, vtop:', depth, topo, zbot[index], vbot[index], vtop[index]) - outfile.writelines('%10s %10s\n'%(vel, decm)) + print( + 'ERROR, vel <0; z, topo, zbot, vbot, vtop:', depth, topo, zbot[index], vbot[index], vtop[index]) + outfile.writelines('%10s %10s\n' % (vel, decm)) progress = float(count) / float(nTotal) * 100 self._update_progress(progress) - print('\nWrote %d points to file %s for %d layers'%(count, outfilename, nlayers)) + print('\nWrote %d points to file %s for %d layers' % (count, outfilename, nlayers)) print('------------------------------------------------------------') outfile.close() if returnTopo == True: return surface - def exportAll(self, filename = 'interpolated_receivers.out'): + def exportAll(self, filename='interpolated_receivers.out'): ''' Exports all receivers to an input file for ActiveSeismoPick3D. ''' @@ -755,11 +775,11 @@ class SeisArray(object): for traceID in self.getReceiverCoordinates().keys(): count += 1 x, y, z = self.getReceiverCoordinates()[traceID] - recfile_out.writelines('%5s %15s %15s %15s\n' %(traceID, x, y, z)) - print "Exported coordinates for %s traces to file > %s" %(count, filename) + recfile_out.writelines('%5s %15s %15s %15s\n' % (traceID, x, y, z)) + print "Exported coordinates for %s traces to file > %s" % (count, filename) recfile_out.close() - def plotArray2D(self, plot_topo = False, highlight_measured = False, annotations = True, pointsize = 10): + def plotArray2D(self, plot_topo=False, highlight_measured=False, annotations=True, pointsize=10): import matplotlib.pyplot as plt plt.interactive(True) fig = plt.figure() @@ -770,36 +790,36 @@ class SeisArray(object): xrc, yrc, zrc = self.getReceiverLists() if len(xrc) > 0: - ax.plot(xrc, yrc, 'k.', markersize = pointsize, label = 'all receivers') + ax.plot(xrc, yrc, 'k.', markersize=pointsize, label='all receivers') if len(xsc) > 0: - ax.plot(xsc, ysc, 'b*', markersize = pointsize, label = 'shot locations') + ax.plot(xsc, ysc, 'b*', markersize=pointsize, label='shot locations') if plot_topo == True: - ax.plot(xmt, ymt, 'b.', markersize = pointsize, label = 'measured topo points') + ax.plot(xmt, ymt, 'b.', markersize=pointsize, label='measured topo points') if highlight_measured == True: - ax.plot(xmr, ymr, 'r.', markersize = pointsize, label = 'measured receivers') + ax.plot(xmr, ymr, 'r.', markersize=pointsize, label='measured receivers') - plt.title('2D plot of seismic array %s'%self.recfile) + plt.title('2D plot of seismic array %s' % self.recfile) ax.set_xlabel('X [m]') ax.set_ylabel('Y [m]') ax.set_aspect('equal') plt.legend() if annotations == True: for traceID in self.getReceiverCoordinates().keys(): - ax.annotate((' ' + str(traceID)), xy = (self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize = 'x-small', color = 'k') + ax.annotate((' ' + str(traceID)), xy=(self._getXreceiver(traceID), self._getYreceiver(traceID)), + fontsize='x-small', color='k') for shotnumber in self.getSourceLocations().keys(): - ax.annotate((' ' + str(shotnumber)), xy = (self._getXshot(shotnumber), self._getYshot(shotnumber)), fontsize = 'x-small', color = 'b') + ax.annotate((' ' + str(shotnumber)), xy=(self._getXshot(shotnumber), self._getYshot(shotnumber)), + fontsize='x-small', color='b') - - - def plotArray3D(self, ax = None): + def plotArray3D(self, ax=None): import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D plt.interactive(True) if ax == None: fig = plt.figure() - ax = plt.axes(projection = '3d') + ax = plt.axes(projection='3d') xmt, ymt, zmt = self.getMeasuredTopoLists() xmr, ymr, zmr = self.getMeasuredReceiverLists() @@ -808,20 +828,21 @@ class SeisArray(object): plt.title('3D plot of seismic array.') if len(xmt) > 0: - ax.plot(xmt, ymt, zmt, 'b.', markersize = 10, label = 'measured topo points') + ax.plot(xmt, ymt, zmt, 'b.', markersize=10, label='measured topo points') if len(xrc) > 0: - ax.plot(xrc, yrc, zrc, 'k.', markersize = 10, label = 'all receivers') + ax.plot(xrc, yrc, zrc, 'k.', markersize=10, label='all receivers') if len(xmr) > 0: - ax.plot(xmr, ymr, zmr, 'ro', label = 'measured receivers') + ax.plot(xmr, ymr, zmr, 'ro', label='measured receivers') if len(xsc) > 0: - ax.plot(xsc, ysc, zsc, 'b*', label = 'shot locations') - ax.set_xlabel('X [m]'); ax.set_ylabel('Y [m]'); ax.set_zlabel('Z [m]') + ax.plot(xsc, ysc, zsc, 'b*', label='shot locations') + ax.set_xlabel('X [m]'); + ax.set_ylabel('Y [m]'); + ax.set_zlabel('Z [m]') ax.legend() return ax - - def plotSurface3D(self, ax = None, step = 0.5, method = 'linear', exag = False): + def plotSurface3D(self, ax=None, step=0.5, method='linear', exag=False): from matplotlib import cm import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D @@ -829,7 +850,7 @@ class SeisArray(object): if ax == None: fig = plt.figure() - ax = plt.axes(projection = '3d') + ax = plt.axes(projection='3d') xmt, ymt, zmt = self.getMeasuredTopoLists() xmr, ymr, zmr = self.getMeasuredReceiverLists() @@ -838,31 +859,33 @@ class SeisArray(object): y = ymt + ymr z = zmt + zmr - xaxis = np.arange(min(x)+1, max(x), step) - yaxis = np.arange(min(y)+1, max(y), step) + xaxis = np.arange(min(x) + 1, max(x), step) + yaxis = np.arange(min(y) + 1, max(y), step) xgrid, ygrid = np.meshgrid(xaxis, yaxis) - zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) + zgrid = griddata((x, y), z, (xgrid, ygrid), method=method) - surf = ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) + surf = ax.plot_surface(xgrid, ygrid, zgrid, linewidth=0, cmap=cm.jet, vmin=min(z), vmax=max(z)) cbar = plt.colorbar(surf) cbar.set_label('Elevation [m]') if exag == False: - ax.set_zlim(-(max(x) - min(x)/2),(max(x) - min(x)/2)) + ax.set_zlim(-(max(x) - min(x) / 2), (max(x) - min(x) / 2)) ax.set_aspect('equal') - ax.set_xlabel('X [m]'); ax.set_ylabel('Y [m]'); ax.set_zlabel('Z [m]') + ax.set_xlabel('X [m]'); + ax.set_ylabel('Y [m]'); + ax.set_zlabel('Z [m]') ax.legend() return ax def _update_progress(self, progress): - sys.stdout.write("%d%% done \r" % (progress) ) + sys.stdout.write("%d%% done \r" % (progress)) sys.stdout.flush() - def surface2VTK(self, surface, filename = 'surface.vtk'): + def surface2VTK(self, surface, filename='surface.vtk'): ''' Generates a vtk file from all points of a surface as generated by interpolateTopography. ''' @@ -876,7 +899,7 @@ class SeisArray(object): outfile.writelines('Surface Points\n') outfile.writelines('ASCII\n') outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) + outfile.writelines('POINTS %15d float\n' % (nPoints)) # write coordinates print("Writing coordinates to VTK file...") @@ -885,14 +908,14 @@ class SeisArray(object): y = point[1] z = point[2] - outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + outfile.writelines('%10f %10f %10f \n' % (x, y, z)) - outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + outfile.writelines('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) # write indices print("Writing indices to VTK file...") for index in range(nPoints): - outfile.writelines('%10d %10d\n' %(1, index)) + outfile.writelines('%10d %10d\n' % (1, index)) # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) # outfile.writelines('SCALARS traceIDs int %d\n' %(1)) @@ -904,10 +927,10 @@ class SeisArray(object): # outfile.writelines('%10d\n' %traceID) outfile.close() - print("Wrote %d points to file: %s" %(nPoints, filename)) + print("Wrote %d points to file: %s" % (nPoints, filename)) return - def receivers2VTK(self, filename = 'receivers.vtk'): + def receivers2VTK(self, filename='receivers.vtk'): ''' Generates a vtk file from all receivers of the SeisArray object. ''' @@ -925,7 +948,7 @@ class SeisArray(object): outfile.writelines('Receivers with traceIDs\n') outfile.writelines('ASCII\n') outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) + outfile.writelines('POINTS %15d float\n' % (nPoints)) # write coordinates print("Writing coordinates to VTK file...") @@ -934,29 +957,29 @@ class SeisArray(object): y = self._getYreceiver(traceID) z = self._getZreceiver(traceID) - outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + outfile.writelines('%10f %10f %10f \n' % (x, y, z)) - outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + outfile.writelines('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) # write indices print("Writing indices to VTK file...") for index in range(nPoints): - outfile.writelines('%10d %10d\n' %(1, index)) + outfile.writelines('%10d %10d\n' % (1, index)) - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - outfile.writelines('SCALARS traceIDs int %d\n' %(1)) + outfile.writelines('POINT_DATA %15d\n' % (nPoints)) + outfile.writelines('SCALARS traceIDs int %d\n' % (1)) outfile.writelines('LOOKUP_TABLE default\n') # write traceIDs print("Writing traceIDs to VTK file...") for traceID in traceIDs: - outfile.writelines('%10d\n' %traceID) + outfile.writelines('%10d\n' % traceID) outfile.close() - print("Wrote %d receiver for to file: %s" %(nPoints, filename)) + print("Wrote %d receiver for to file: %s" % (nPoints, filename)) return - def sources2VTK(self, filename = 'sources.vtk'): + def sources2VTK(self, filename='sources.vtk'): ''' Generates a vtk-file for all source locations in the SeisArray object. ''' @@ -974,7 +997,7 @@ class SeisArray(object): outfile.writelines('Shots with shotnumbers\n') outfile.writelines('ASCII\n') outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) + outfile.writelines('POINTS %15d float\n' % (nPoints)) # write coordinates print("Writing coordinates to VTK file...") @@ -983,35 +1006,34 @@ class SeisArray(object): y = self._getYshot(shotnumber) z = self._getZshot(shotnumber) - outfile.writelines('%10f %10f %10f \n' %(x, y, z)) + outfile.writelines('%10f %10f %10f \n' % (x, y, z)) - outfile.writelines('VERTICES %15d %15d\n' %(nPoints, 2 * nPoints)) + outfile.writelines('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) # write indices print("Writing indices to VTK file...") for index in range(nPoints): - outfile.writelines('%10d %10d\n' %(1, index)) + outfile.writelines('%10d %10d\n' % (1, index)) - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - outfile.writelines('SCALARS shotnumbers int %d\n' %(1)) + outfile.writelines('POINT_DATA %15d\n' % (nPoints)) + outfile.writelines('SCALARS shotnumbers int %d\n' % (1)) outfile.writelines('LOOKUP_TABLE default\n') # write shotnumber print("Writing shotnumbers to VTK file...") for shotnumber in shotnumbers: - outfile.writelines('%10d\n' %shotnumber) + outfile.writelines('%10d\n' % shotnumber) outfile.close() - print("Wrote %d sources to file: %s" %(nPoints, filename)) + print("Wrote %d sources to file: %s" % (nPoints, filename)) return - - def saveSeisArray(self, filename = 'seisArray.pickle'): + def saveSeisArray(self, filename='seisArray.pickle'): import cPickle outfile = open(filename, 'wb') cPickle.dump(self, outfile, -1) - print('saved SeisArray to file %s'%(filename)) + print('saved SeisArray to file %s' % (filename)) @staticmethod def from_pickle(filename): diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 23fd6022..68087561 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -11,12 +11,15 @@ from pylot.core.pick.charfuns import AICcf from pylot.core.pick.utils import getSNR from pylot.core.pick.utils import earllatepicker import matplotlib.pyplot as plt + plt.interactive('True') + class SeismicShot(object): ''' SuperClass for a seismic shot object. ''' + def __init__(self, obsfile): ''' Initialize seismic shot object giving an inputfile. @@ -29,8 +32,8 @@ class SeismicShot(object): self.srcCoordlist = None self.traceIDs = None self.picks = {} - self.pwindow= {} - self.manualpicks= {} + self.pwindow = {} + self.manualpicks = {} self.snr = {} self.snrthreshold = {} self.timeArray = {} @@ -61,10 +64,10 @@ class SeismicShot(object): if traceID == trace.stats.channel: self.traces.remove(trace) - # for traceID in TraceIDs: - # traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] - # if len(traces) is not 1: - # self.traces.remove(trace) + # for traceID in TraceIDs: + # traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] + # if len(traces) is not 1: + # self.traces.remove(trace) def updateTraceList(self): ''' @@ -87,22 +90,22 @@ class SeismicShot(object): self.setParameters('tmovwind', tmovwind) def setOrder(self, order): - self.setParameters('order', order) + self.setParameters('order', order) def setTsignal(self, tsignal): - self.setParameters('tsignal', tsignal) + self.setParameters('tsignal', tsignal) def setTgap(self, tgap): - self.setParameters('tgap', tgap) + self.setParameters('tgap', tgap) def setShotnumber(self, shotname): - self.setParameters('shotname', shotname) + self.setParameters('shotname', shotname) def setRecfile(self, recfile): - self.setParameters('recfile', recfile) + self.setParameters('recfile', recfile) def setSourcefile(self, sourcefile): - self.setParameters('sourcefile', sourcefile) + self.setParameters('sourcefile', sourcefile) def getParas(self): return self.paras @@ -144,15 +147,15 @@ class SeismicShot(object): def getManualLatest(self, traceID): return self.manualpicks[traceID]['lpp'] - def getPick(self, traceID, returnRemoved = False): + def getPick(self, traceID, returnRemoved=False): if not self.getPickFlag(traceID) == 0: return self.picks[traceID]['mpp'] if returnRemoved == True: - #print('getPick: Returned removed pick for shot %d, traceID %d' %(self.getShotnumber(), traceID)) + # print('getPick: Returned removed pick for shot %d, traceID %d' %(self.getShotnumber(), traceID)) return self.picks[traceID]['mpp'] def getPickIncludeRemoved(self, traceID): - return self.getPick(traceID, returnRemoved = True) + return self.getPick(traceID, returnRemoved=True) def getEarliest(self, traceID): return self.picks[traceID]['epp'] @@ -163,13 +166,13 @@ class SeismicShot(object): def getSymmetricPickError(self, traceID): pickerror = self.picks[traceID]['spe'] if np.isnan(pickerror) == True: - print "SPE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID) + print "SPE is NaN for shot %s, traceID %s" % (self.getShotnumber(), traceID) return pickerror def getPickError(self, traceID): pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) / 2 if np.isnan(pickerror) == True: - print("PE is NaN for shot %s, traceID %s"%(self.getShotnumber(), traceID)) + print("PE is NaN for shot %s, traceID %s" % (self.getShotnumber(), traceID)) return pickerror def getStreamTraceIDs(self): @@ -207,15 +210,15 @@ class SeismicShot(object): def getRecCoordlist(self): if self.recCoordlist is None: - coordlist = open(self.getRecfile(),'r').readlines() - #print 'Reading receiver coordinates from %s' %(self.getRecfile()) + coordlist = open(self.getRecfile(), 'r').readlines() + # print 'Reading receiver coordinates from %s' %(self.getRecfile()) self.recCoordlist = coordlist return self.recCoordlist def getSrcCoordlist(self): if self.srcCoordlist is None: - coordlist = open(self.getSourcefile(),'r').readlines() - #print 'Reading shot coordinates from %s' %(self.getSourcefile()) + coordlist = open(self.getSourcefile(), 'r').readlines() + # print 'Reading shot coordinates from %s' %(self.getSourcefile()) self.srcCoordlist = coordlist return self.srcCoordlist @@ -239,7 +242,7 @@ class SeismicShot(object): :type: int ''' return HOScf(self.getSingleStream(traceID), self.getCut(), - self.getTmovwind(), self.getOrder(), stealthMode = True) + self.getTmovwind(), self.getOrder(), stealthMode=True) def getAICcf(self, traceID): ''' @@ -262,7 +265,7 @@ class SeismicShot(object): tr_cf = Trace() tr_cf.data = self.getHOScf(traceID).getCF() st_cf += tr_cf - return AICcf(st_cf, self.getCut(), self.getTmovwind(), stealthMode = True) + return AICcf(st_cf, self.getCut(), self.getTmovwind(), stealthMode=True) def getSingleStream(self, traceID): ########## SEG2 / SEGY ? ########## ''' @@ -271,16 +274,16 @@ class SeismicShot(object): :param: traceID :type: int ''' - #traces = [trace for trace in self.traces if int(trace.stats.seg2['CHANNEL_NUMBER']) == traceID] + # traces = [trace for trace in self.traces if int(trace.stats.seg2['CHANNEL_NUMBER']) == traceID] traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] if len(traces) == 1: return Stream(traces) self.setPick(traceID, None) print 'Warning: ambigious or empty traceID: %s' % traceID - #raise ValueError('ambigious or empty traceID: %s' % traceID) + # raise ValueError('ambigious or empty traceID: %s' % traceID) - def pickTraces(self, traceID, windowsize, folm, HosAic = 'hos'): ########## input variables ########## + def pickTraces(self, traceID, windowsize, folm, HosAic='hos'): ########## input variables ########## # LOCALMAX NOT IMPLEMENTED! ''' Intitiate picking for a trace. @@ -306,7 +309,7 @@ class SeismicShot(object): :param: HosAic, get hos or aic pick (can be 'hos'(default) or 'aic') :type: 'string' ''' - hoscf = self.getHOScf(traceID) ### determination of both, HOS and AIC (need to change threshold-picker) ### + hoscf = self.getHOScf(traceID) ### determination of both, HOS and AIC (need to change threshold-picker) ### aiccf = self.getAICcf(traceID) self.folm = folm @@ -318,7 +321,7 @@ class SeismicShot(object): self.setPick(traceID, setHosAic[HosAic]) - def setEarllatepick(self, traceID, nfac = 1.5): + def setEarllatepick(self, traceID, nfac=1.5): tgap = self.getTgap() tsignal = self.getTsignal() tnoise = self.getPickIncludeRemoved(traceID) - tgap @@ -326,17 +329,17 @@ class SeismicShot(object): (self.picks[traceID]['epp'], self.picks[traceID]['lpp'], self.picks[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), - nfac, (tnoise, tgap, tsignal), - self.getPickIncludeRemoved(traceID), - stealthMode = True) + nfac, (tnoise, tgap, tsignal), + self.getPickIncludeRemoved(traceID), + stealthMode=True) if self.picks[traceID]['epp'] < 0: self.picks[traceID]['epp'] - #print('setEarllatepick: Set epp to 0 because it was < 0') + # print('setEarllatepick: Set epp to 0 because it was < 0') - # TEST OF 1/2 PICKERROR - # self.picks[traceID]['spe'] *= 0.5 - # TEST OF 1/2 PICKERROR + # TEST OF 1/2 PICKERROR + # self.picks[traceID]['spe'] *= 0.5 + # TEST OF 1/2 PICKERROR def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm): ''' @@ -365,10 +368,11 @@ class SeismicShot(object): leftb = int(pickwindow[0] / self.getCut()[1] * len(hoscflist)) rightb = int(pickwindow[1] / self.getCut()[1] * len(hoscflist)) - #threshold = folm * max(hoscflist[leftb : rightb]) # combination of local maximum and threshold + # threshold = folm * max(hoscflist[leftb : rightb]) # combination of local maximum and threshold ### TEST TEST - threshold = folm * (max(hoscflist[leftb : rightb]) - min(hoscflist[leftb : rightb])) + min(hoscflist[leftb : rightb]) # combination of local maximum and threshold + threshold = folm * (max(hoscflist[leftb: rightb]) - min(hoscflist[leftb: rightb])) + min( + hoscflist[leftb: rightb]) # combination of local maximum and threshold ### TEST TEST m = leftb @@ -378,8 +382,8 @@ class SeismicShot(object): hoscftime = list(hoscf.getTimeArray())[m] - lb = max(0, m - windowsize[0]) # if window exceeds t = 0 - aiccfcut = list(aiccf.getCF())[lb : m + windowsize[1]] + lb = max(0, m - windowsize[0]) # if window exceeds t = 0 + aiccfcut = list(aiccf.getCF())[lb: m + windowsize[1]] if len(aiccfcut) > 0: n = aiccfcut.index(min(aiccfcut)) else: @@ -401,13 +405,13 @@ class SeismicShot(object): ''' shotX, shotY, shotZ = self.getSrcLoc() recX, recY, recZ = self.getRecLoc(traceID) - dist = np.sqrt((shotX-recX)**2 + (shotY-recY)**2 + (shotZ-recZ)**2) + dist = np.sqrt((shotX - recX) ** 2 + (shotY - recY) ** 2 + (shotZ - recZ) ** 2) if np.isnan(dist) == True: - raise ValueError("Distance is NaN for traceID %s" %traceID) + raise ValueError("Distance is NaN for traceID %s" % traceID) return dist - #return abs(float(self.getSrcLoc(traceID))-float(self.getRecLoc(traceID))) + # return abs(float(self.getSrcLoc(traceID))-float(self.getRecLoc(traceID))) def getRecLoc(self, traceID): ########## input FILENAME ########## ''' @@ -417,7 +421,7 @@ class SeismicShot(object): :param: traceID :type: int ''' - if traceID == 0: # artificial traceID 0 with pick at t = 0 + if traceID == 0: # artificial traceID 0 with pick at t = 0 return self.getSrcLoc() coordlist = self.getRecCoordlist() @@ -428,9 +432,9 @@ class SeismicShot(object): z = coordlist[i].split()[3] return float(x), float(y), float(z) - #print "WARNING: traceID %s not found" % traceID + # print "WARNING: traceID %s not found" % traceID raise ValueError("traceID %s not found" % traceID) - #return float(self.getSingleStream(traceID)[0].stats.seg2['RECEIVER_LOCATION']) + # return float(self.getSingleStream(traceID)[0].stats.seg2['RECEIVER_LOCATION']) def getSrcLoc(self): ########## input FILENAME ########## ''' @@ -444,9 +448,10 @@ class SeismicShot(object): y = coordlist[i].split()[2] z = coordlist[i].split()[3] return float(x), float(y), float(z) - #return float(self.getSingleStream(traceID)[0].stats.seg2['SOURCE_LOCATION']) + # return float(self.getSingleStream(traceID)[0].stats.seg2['SOURCE_LOCATION']) - def getTraceIDs4Dist(self, distance = 0, distancebin = (0, 0)): ########## nur fuer 2D benutzt, 'distance bins' ########## + def getTraceIDs4Dist(self, distance=0, + distancebin=(0, 0)): ########## nur fuer 2D benutzt, 'distance bins' ########## ''' Returns the traceID(s) for a certain distance between source and receiver. Used for 2D Tomography. TO BE IMPROVED. @@ -460,39 +465,39 @@ class SeismicShot(object): traceID_list = [] for trace in self.traces: - #traceID = int(trace.stats.seg2['CHANNEL_NUMBER']) - traceID = int(trace.stats.channel) - if distance != 0: - if self.getDistance(traceID) == distance: - traceID_list.append(traceID) - if distancebin[0] >= 0 and distancebin[1] > 0: - if distancebin[0] < self.getDistance(traceID) < distancebin[1]: - traceID_list.append(traceID) + # traceID = int(trace.stats.seg2['CHANNEL_NUMBER']) + traceID = int(trace.stats.channel) + if distance != 0: + if self.getDistance(traceID) == distance: + traceID_list.append(traceID) + if distancebin[0] >= 0 and distancebin[1] > 0: + if distancebin[0] < self.getDistance(traceID) < distancebin[1]: + traceID_list.append(traceID) if len(traceID_list) > 0: return traceID_list - # def setManualPicks(self, traceID, picklist): ########## picklist momentan nicht allgemein, nur testweise benutzt ########## - # ''' - # Sets the manual picks for a receiver with the ID == traceID for comparison. + # def setManualPicks(self, traceID, picklist): ########## picklist momentan nicht allgemein, nur testweise benutzt ########## + # ''' + # Sets the manual picks for a receiver with the ID == traceID for comparison. - # :param: traceID - # :type: int + # :param: traceID + # :type: int - # :param: picklist, list containing the manual picks (mostlikely, earliest, latest). - # :type: list - # ''' - # picks = picklist[traceID - 1].split() - # mostlikely = float(picks[1]) - # earliest = float(picks[2]) - # latest = float(picks[3]) + # :param: picklist, list containing the manual picks (mostlikely, earliest, latest). + # :type: list + # ''' + # picks = picklist[traceID - 1].split() + # mostlikely = float(picks[1]) + # earliest = float(picks[2]) + # latest = float(picks[3]) - # if not self.manualpicks.has_key(traceID): - # self.manualpicks[traceID] = (mostlikely, earliest, latest) - #else: - # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) + # if not self.manualpicks.has_key(traceID): + # self.manualpicks[traceID] = (mostlikely, earliest, latest) + # else: + # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) - def setManualPicksFromFile(self, directory = 'picks'): + def setManualPicksFromFile(self, directory='picks'): ''' Read manual picks from *.pck file. The * must be identical with the shotnumber. @@ -517,8 +522,7 @@ class SeismicShot(object): else: self.setManualPickFlag(traceID, 1) - - def setPick(self, traceID, pick): ########## siehe Kommentar ########## + def setPick(self, traceID, pick): ########## siehe Kommentar ########## if not traceID in self.picks.keys(): self.picks[traceID] = {} self.picks[traceID]['mpp'] = pick @@ -568,7 +572,7 @@ class SeismicShot(object): tsignal = self.getTsignal() tnoise = self.getPick(traceID) - tgap - self.snr[traceID] = getSNR(self.getSingleStream(traceID), (tnoise,tgap,tsignal), self.getPick(traceID)) + self.snr[traceID] = getSNR(self.getSingleStream(traceID), (tnoise, tgap, tsignal), self.getPick(traceID)) def setSNRthreshold(self, traceID, snrthreshold): self.snrthreshold[traceID] = snrthreshold @@ -583,12 +587,11 @@ class SeismicShot(object): if self.getRecLoc(traceID)[0] > self.getSrcLoc()[0]: distancearray.append(self.getDistance(traceID)) elif self.getRecLoc(traceID)[0] <= self.getSrcLoc()[0]: - distancearray.append((-1)*self.getDistance(traceID)) + distancearray.append((-1) * self.getDistance(traceID)) return distancearray - - def plot2dttc(self, ax = None): ########## 2D ########## + def plot2dttc(self, ax=None): ########## 2D ########## ''' Function to plot the traveltime curve for automated picks of a shot. 2d only! ATM: X DIRECTION!! ''' @@ -605,15 +608,16 @@ class SeismicShot(object): # shotnumbers = [shotnumbers for (shotnumbers, shotnames) in sorted(zip(shotnumbers, shotnames))] plotarray = sorted(zip(self.getDistArray4ttcPlot(), picks)) - x = []; y = [] + x = []; + y = [] for point in plotarray: x.append(point[0]) y.append(point[1]) - ax.plot(x, y,'r', label = "Automatic Picks") - ax.text(0.5, 0.9, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes - , horizontalalignment = 'center') + ax.plot(x, y, 'r', label="Automatic Picks") + ax.text(0.5, 0.9, 'shot: %s' % self.getShotnumber(), transform=ax.transAxes + , horizontalalignment='center') - def plotmanual2dttc(self, ax = None): ########## 2D ########## + def plotmanual2dttc(self, ax=None): ########## 2D ########## ''' Function to plot the traveltime curve for manual picks of a shot. 2D only! ''' @@ -632,11 +636,12 @@ class SeismicShot(object): ax = fig.add_subplot(111) plotarray = sorted(zip(self.getDistArray4ttcPlot(), manualpicktimesarray)) - x = []; y = [] + x = []; + y = [] for point in plotarray: x.append(point[0]) y.append(point[1]) - ax.plot(x, y, 'b', label = "Manual Picks") + ax.plot(x, y, 'b', label="Manual Picks") # def plotpickwindow(self): ########## 2D ########## # ''' @@ -656,10 +661,10 @@ class SeismicShot(object): # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_lowerb, ':k') # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_upperb, ':k') - def plotTrace(self, traceID, plotSNR = True, lw = 1): + def plotTrace(self, traceID, plotSNR=True, lw=1): fig = plt.figure() ax = fig.add_subplot(111) - ax = self._drawStream(traceID, ax = ax) + ax = self._drawStream(traceID, ax=ax) tgap = self.getTgap() tsignal = self.getTsignal() @@ -667,31 +672,32 @@ class SeismicShot(object): tnoise = pick - tgap snr, snrdb, noiselevel = self.getSNR(traceID) - ax.plot([0, tnoise], [noiselevel, noiselevel], 'm', linewidth = lw, label = 'noise level') - ax.plot([tnoise, pick], [noiselevel, noiselevel], 'g:', linewidth = lw, label = 'gap') - ax.plot([tnoise + tgap, pick + tsignal], [noiselevel * snr, noiselevel * snr], 'b', linewidth = lw, label = 'signal level') + ax.plot([0, tnoise], [noiselevel, noiselevel], 'm', linewidth=lw, label='noise level') + ax.plot([tnoise, pick], [noiselevel, noiselevel], 'g:', linewidth=lw, label='gap') + ax.plot([tnoise + tgap, pick + tsignal], [noiselevel * snr, noiselevel * snr], 'b', linewidth=lw, + label='signal level') ax.legend() - ax.text(0.05, 0.9, 'SNR: %s' %snr, transform = ax.transAxes) + ax.text(0.05, 0.9, 'SNR: %s' % snr, transform=ax.transAxes) - def plot_traces(self, traceID): ########## 2D, muss noch mehr verbessert werden ########## + def plot_traces(self, traceID): ########## 2D, muss noch mehr verbessert werden ########## from matplotlib.widgets import Button def onclick(event): self.setPick(traceID, event.xdata) if self.getSNR(traceID)[0] > 1: self.setEarllatepick(traceID) - self._drawStream(traceID, refresh = True) - self._drawCFs(traceID, folm, refresh = True) + self._drawStream(traceID, refresh=True) + self._drawCFs(traceID, folm, refresh=True) fig.canvas.mpl_disconnect(self.traces4plot[traceID]['cid']) plt.draw() - def rmPick(event = None): + def rmPick(event=None): self.removePick(traceID) - self._drawStream(traceID, refresh = True) - self._drawCFs(traceID, folm, refresh = True) + self._drawStream(traceID, refresh=True) + self._drawCFs(traceID, folm, refresh=True) plt.draw() - def connectButton(event = None): + def connectButton(event=None): cid = fig.canvas.mpl_connect('button_press_event', onclick) self.traces4plot[traceID]['cid'] = cid @@ -701,13 +707,13 @@ class SeismicShot(object): folm = self.folm fig = plt.figure() - ax1 = fig.add_subplot(2,1,1) - ax2 = fig.add_subplot(2,1,2, sharex = ax1) + ax1 = fig.add_subplot(2, 1, 1) + ax2 = fig.add_subplot(2, 1, 2, sharex=ax1) axb1 = fig.add_axes([0.15, 0.91, 0.05, 0.03]) axb2 = fig.add_axes([0.22, 0.91, 0.05, 0.03]) - button1 = Button(axb1, 'repick', color = 'red', hovercolor = 'grey') + button1 = Button(axb1, 'repick', color='red', hovercolor='grey') button1.on_clicked(connectButton) - button2 = Button(axb2, 'delete', color = 'green', hovercolor = 'grey') + button2 = Button(axb2, 'delete', color='green', hovercolor='grey') button2.on_clicked(rmPick) fig.canvas.mpl_connect('close_event', cleanup) @@ -717,7 +723,7 @@ class SeismicShot(object): self._drawStream(traceID) self._drawCFs(traceID, folm) - def _drawStream(self, traceID, refresh = False, ax = None): + def _drawStream(self, traceID, refresh=False, ax=None): from pylot.core.util.utils import getGlobalTimes from pylot.core.util.utils import prepTimeAxis @@ -737,27 +743,27 @@ class SeismicShot(object): ax.set_ylim(ylim) ax.set_title('Shot: %s, traceID: %s, pick: %s' - %(self.getShotnumber(), traceID, self.getPick(traceID))) - ax.plot(timeaxis, stream[0].data, 'k', label = 'trace') + % (self.getShotnumber(), traceID, self.getPick(traceID))) + ax.plot(timeaxis, stream[0].data, 'k', label='trace') ax.plot([self.getPick(traceID), self.getPick(traceID)], [ax.get_ylim()[0], ax.get_ylim()[1]], - 'r', label = 'most likely') + 'r', label='most likely') if self.getEarliest(traceID) is not None: ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], [ax.get_ylim()[0], ax.get_ylim()[1]], - 'g:', label = 'earliest') + 'g:', label='earliest') if self.getLatest(traceID) is not None: ax.plot([self.getLatest(traceID), self.getLatest(traceID)], [ax.get_ylim()[0], ax.get_ylim()[1]], - 'b:', label = 'latest') + 'b:', label='latest') ax.legend() return ax - def _drawCFs(self, traceID, folm = None, refresh = False): + def _drawCFs(self, traceID, folm=None, refresh=False): hoscf = self.getHOScf(traceID) aiccf = self.getAICcf(traceID) ax = self.traces4plot[traceID]['ax2'] @@ -769,30 +775,30 @@ class SeismicShot(object): ax.set_xlim(xlim) ax.set_ylim(ylim) - ax.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label = 'HOS') - ax.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label = 'AIC') + ax.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label='HOS') + ax.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label='AIC') ax.plot([self.getPick(traceID), self.getPick(traceID)], [ax.get_ylim()[0], ax.get_ylim()[1]], - 'r', label = 'most likely') + 'r', label='most likely') if self.getEarliest(traceID) is not None: ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], [ax.get_ylim()[0], ax.get_ylim()[1]], - 'g:', label = 'earliest') + 'g:', label='earliest') if self.getLatest(traceID) is not None: ax.plot([self.getLatest(traceID), self.getLatest(traceID)], [ax.get_ylim()[0], ax.get_ylim()[1]], - 'b:', label = 'latest') + 'b:', label='latest') if folm is not None: ax.plot([0, self.getPick(traceID)], [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], - 'm:', label = 'folm = %s' %folm) + 'm:', label='folm = %s' % folm) ax.set_xlabel('Time [s]') ax.legend() - def plot3dttc(self, step = 0.5, contour = False, plotpicks = False, method = 'linear', ax = None): + def plot3dttc(self, step=0.5, contour=False, plotpicks=False, method='linear', ax=None): ''' Plots a 3D 'traveltime cone' as surface plot by interpolating on a regular grid over the traveltimes, not yet regarding the vertical offset of the receivers. @@ -824,20 +830,20 @@ class SeismicShot(object): xaxis = np.arange(min(x) + step, max(x), step) yaxis = np.arange(min(y) + step, max(y), step) xgrid, ygrid = np.meshgrid(xaxis, yaxis) - zgrid = griddata((x, y), z, (xgrid, ygrid), method = method) + zgrid = griddata((x, y), z, (xgrid, ygrid), method=method) if ax == None: fig = plt.figure() - ax = plt.axes(projection = '3d') + ax = plt.axes(projection='3d') xsrc, ysrc, zsrc = self.getSrcLoc() if contour == True: - ax.contour3D(xgrid,ygrid,zgrid,20) + ax.contour3D(xgrid, ygrid, zgrid, 20) else: - ax.plot_surface(xgrid, ygrid, zgrid, linewidth = 0, cmap = cm.jet, vmin = min(z), vmax = max(z)) - ax.plot([xsrc], [ysrc], [self.getPick(0)], 'k*', markersize = 20) # plot source location - ax.plot([xsrc], [ysrc], [self.getPick(0)], 'r*', markersize = 15) # plot source location + ax.plot_surface(xgrid, ygrid, zgrid, linewidth=0, cmap=cm.jet, vmin=min(z), vmax=max(z)) + ax.plot([xsrc], [ysrc], [self.getPick(0)], 'k*', markersize=20) # plot source location + ax.plot([xsrc], [ysrc], [self.getPick(0)], 'r*', markersize=15) # plot source location if plotpicks == True: ax.plot(x, y, z, 'k.') @@ -847,7 +853,7 @@ class SeismicShot(object): plotmethod[method](*args) - def matshow(self, ax = None, step = 0.5, method = 'linear', plotRec = True, annotations = True, colorbar = True, legend = True): + def matshow(self, ax=None, step=0.5, method='linear', plotRec=True, annotations=True, colorbar=True, legend=True): ''' Plots a 2D matrix of the interpolated traveltimes. This needs less performance than plot3dttc @@ -868,9 +874,12 @@ class SeismicShot(object): from matplotlib import cm cmap = cm.jet - x = []; xcut = [] - y = []; ycut = [] - z = []; zcut = [] + x = []; + xcut = [] + y = []; + ycut = [] + z = []; + zcut = [] for traceID in self.picks.keys(): if self.getPickFlag(traceID) != 0: @@ -882,7 +891,7 @@ class SeismicShot(object): ycut.append(self.getRecLoc(traceID)[1]) zcut.append(self.getPickIncludeRemoved(traceID)) - tmin = 0.8 * min(z) # 20% cushion for colorbar + tmin = 0.8 * min(z) # 20% cushion for colorbar tmax = 1.2 * max(z) xaxis = np.arange(min(x), max(x), step) @@ -895,10 +904,11 @@ class SeismicShot(object): ax = plt.axes() count = 0 - ax.imshow(zgrid, extent = [min(x), max(x), min(y), max(y)], vmin = tmin, vmax = tmax, cmap = cmap, origin = 'lower', alpha = 0.85) - ax.text(0.5, 0.95, 'shot: %s' %self.getShotnumber(), transform = ax.transAxes - , horizontalalignment = 'center') - sc = ax.scatter(x, y, c = z, s = 30, label = 'picked shots', vmin = tmin, vmax = tmax, cmap = cmap, linewidths = 1.5) + ax.imshow(zgrid, extent=[min(x), max(x), min(y), max(y)], vmin=tmin, vmax=tmax, cmap=cmap, origin='lower', + alpha=0.85) + ax.text(0.5, 0.95, 'shot: %s' % self.getShotnumber(), transform=ax.transAxes + , horizontalalignment='center') + sc = ax.scatter(x, y, c=z, s=30, label='picked shots', vmin=tmin, vmax=tmax, cmap=cmap, linewidths=1.5) label = None for xyz in zip(xcut, ycut, zcut): x, y, z = xyz @@ -907,7 +917,7 @@ class SeismicShot(object): z = 'w' if count == 1: label = 'cut out shots' - ax.scatter(x, y, c = z, s = 30, edgecolor = 'm', label = label, vmin = tmin, vmax = tmax, cmap = cmap, linewidths = 1.5) + ax.scatter(x, y, c=z, s=30, edgecolor='m', label=label, vmin=tmin, vmax=tmax, cmap=cmap, linewidths=1.5) if colorbar == True: cbar = plt.colorbar(sc) cbar.set_label('Time [s]') @@ -916,17 +926,15 @@ class SeismicShot(object): ax.legend() ax.set_xlabel('X') ax.set_ylabel('Y') - ax.plot(self.getSrcLoc()[0], self.getSrcLoc()[1],'*k', markersize = 15) # plot source location + ax.plot(self.getSrcLoc()[0], self.getSrcLoc()[1], '*k', markersize=15) # plot source location if annotations == True: for traceID in self.getTraceIDlist(): if self.getPickFlag(traceID) is not 0: - ax.annotate(' %s' %traceID , xy = (self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), - fontsize = 'x-small', color = 'k') + ax.annotate(' %s' % traceID, xy=(self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), + fontsize='x-small', color='k') else: - ax.annotate(' %s' %traceID , xy = (self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), - fontsize = 'x-small', color = 'r') + ax.annotate(' %s' % traceID, xy=(self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), + fontsize='x-small', color='r') plt.show() - - diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index dae7a9ea..5dd34d81 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -2,8 +2,10 @@ import matplotlib.pyplot as plt import math import numpy as np + plt.interactive(True) + class regions(object): ''' A class used for manual inspection and processing of all picks for the user. @@ -12,19 +14,19 @@ class regions(object): regions.chooseRectangles(): - lets the user choose several rectangular regions in the plot - + regions.plotTracesInActiveRegions(): - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) - + regions.setAllActiveRegionsForDeletion(): - highlights all shots in a the active regions for deletion - + regions.deleteAllMarkedPicks(): - deletes the picks (pick flag set to 0) for all shots set for deletion regions.deselectSelection(number): - deselects the region of number = number - + ''' def __init__(self, ax, cbar, survey): @@ -57,10 +59,10 @@ class regions(object): for shot in self.shot_dict.values(): for traceID in shot.getTraceIDlist(): allpicks.append((shot.getDistance(traceID), - shot.getPickIncludeRemoved(traceID), - shot.getShotnumber(), - traceID, - shot.getPickFlag(traceID))) + shot.getPickIncludeRemoved(traceID), + shot.getShotnumber(), + traceID, + shot.getPickFlag(traceID))) allpicks.sort() self._allpicks = allpicks @@ -74,9 +76,9 @@ class regions(object): def _onselect_clicks(self, eclick, erelease): '''eclick and erelease are matplotlib events at press and release''' print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)' % (eclick.xdata, - eclick.ydata, - erelease.xdata, - erelease.ydata) + eclick.ydata, + erelease.xdata, + erelease.ydata) x0 = min(eclick.xdata, erelease.xdata) x1 = max(eclick.xdata, erelease.xdata) y0 = min(eclick.ydata, erelease.ydata) @@ -105,18 +107,18 @@ class regions(object): self.disconnectPoly() self.printOutput('Disconnected polygon selection') - def addTextfield(self, xpos = 0, ypos = 0.95, width = 1, height = 0.03): + def addTextfield(self, xpos=0, ypos=0.95, width=1, height=0.03): ''' Adds an ax for text output to the plot. ''' self.axtext = self.ax.figure.add_axes([xpos, - ypos, - width, - height]) + ypos, + width, + height]) self.axtext.xaxis.set_visible(False) self.axtext.yaxis.set_visible(False) - def writeInTextfield(self, text = None): + def writeInTextfield(self, text=None): self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) self.axtext.clear() self.axtext.text(0.01, 0.5, text, verticalalignment='center', horizontalalignment='left') @@ -136,16 +138,16 @@ class regions(object): self.addButton('SelAll', self.setAllActiveRegionsForDeletion, xpos=xpos2 + 2 * dx) self.addButton('DelAll', self.deleteAllMarkedPicks, xpos=xpos2 + 3 * dx, color='red') - def addButton(self, name, action, xpos, ypos = 0.91, color = None): + def addButton(self, name, action, xpos, ypos=0.91, color=None): from matplotlib.widgets import Button self.buttons[name] = {'ax': None, - 'button': None, - 'action': action, - 'xpos': xpos} + 'button': None, + 'action': action, + 'xpos': xpos} ax = self.ax.figure.add_axes([xpos, - ypos, - 0.05, - 0.03]) + ypos, + 0.05, + 0.03]) button = Button(ax, name, color=color, hovercolor='grey') button.on_clicked(action) self.buttons[name]['ax'] = ax @@ -179,23 +181,24 @@ class regions(object): self.drawLastPolyLine() x = self._polyx y = self._polyy - self._polyx = []; self._polyy = [] + self._polyx = []; + self._polyy = [] key = self.getKey() - self.markPolygon(x, y, key = key) + self.markPolygon(x, y, key=key) shots, numtraces = self.findTracesInPoly(x, y) self.shots_found[key] = {'shots': shots, - 'selection': 'poly', - 'xvalues': x, - 'yvalues': y} + 'selection': 'poly', + 'xvalues': x, + 'yvalues': y} self.printOutput('Found %d traces in polygon: %s' % (numtraces, shots)) def printOutput(self, text): print text self.writeInTextfield(text) - def chooseRectangles(self, event = None): + def chooseRectangles(self, event=None): ''' Activates matplotlib widget RectangleSelector. ''' @@ -208,7 +211,7 @@ class regions(object): self._rectangle = RectangleSelector(self.ax, self._onselect_clicks) return self._rectangle - def choosePolygon(self, event = None): + def choosePolygon(self, event=None): ''' Activates matplotlib widget LassoSelector. ''' @@ -221,7 +224,7 @@ class regions(object): self._lasso = LassoSelector(self.ax, self._onselect_verts) return self._lasso - def disconnectPoly(self, event = None): + def disconnectPoly(self, event=None): if not hasattr(self, '_cidPoly'): self.printOutput('no poly selection found') return @@ -231,7 +234,7 @@ class regions(object): self._lasso.disconnect_events() print 'disconnected poly selection\n' - def disconnectRect(self, event = None): + def disconnectRect(self, event=None): if not hasattr(self, '_cidRect'): self.printOutput('no rectangle selection found') return @@ -240,14 +243,14 @@ class regions(object): self._rectangle.disconnect_events() print 'disconnected rectangle selection\n' - def deselectLastSelection(self, event = None): + def deselectLastSelection(self, event=None): if self.shots_found.keys() == []: self.printOutput('No selection found.') return key = max(self.shots_found.keys()) self.deselectSelection(key) - def deselectSelection(self, key, color = 'green', alpha = 0.1): + def deselectSelection(self, key, color='green', alpha=0.1): if key not in self.shots_found.keys(): self.printOutput('No selection found.') return @@ -255,17 +258,17 @@ class regions(object): if self.shots_found[key]['selection'] == 'rect': self.markRectangle(self.shots_found[key]['xvalues'], self.shots_found[key]['yvalues'], - key = key, color = color, alpha = alpha, - linewidth = 1) + key=key, color=color, alpha=alpha, + linewidth=1) elif self.shots_found[key]['selection'] == 'poly': self.markPolygon(self.shots_found[key]['xvalues'], self.shots_found[key]['yvalues'], - key = key, color = color, alpha = alpha, - linewidth = 1) + key=key, color=color, alpha=alpha, + linewidth=1) value = self.shots_found.pop(key) self.printOutput('Deselected selection number %d' % key) - def findTracesInPoly(self, x, y, picks = 'normal', highlight = True): + def findTracesInPoly(self, x, y, picks='normal', highlight=True): def dotproduct(v1, v2): return sum((a * b for a, b in zip(v1, v2))) @@ -279,21 +282,26 @@ class regions(object): angle = 0 epsilon = 1e-07 for index in range(len(x)): - xval1 = x[index - 1]; yval1 = y[index - 1] - xval2 = x[index]; yval2 = y[index] + xval1 = x[index - 1]; + yval1 = y[index - 1] + xval2 = x[index]; + yval2 = y[index] angle += getangle([xval1 - pickX, yval1 - pickY], [xval2 - pickX, yval2 - pickY]) - if 360 - epsilon <= angle <= 360 + epsilon: ### IMPROVE THAT?? + if 360 - epsilon <= angle <= 360 + epsilon: ### IMPROVE THAT?? return True if len(x) == 0 or len(y) == 0: self.printOutput('No polygon defined.') return - shots_found = {}; numtraces = 0 - x0 = min(x); x1 = max(x) - y0 = min(y); y1 = max(y) + shots_found = {}; + numtraces = 0 + x0 = min(x); + x1 = max(x) + y0 = min(y); + y1 = max(y) - shots, numtracesrect = self.findTracesInShotDict((x0, x1), (y0, y1), highlight = False) + shots, numtracesrect = self.findTracesInShotDict((x0, x1), (y0, y1), highlight=False) for shotnumber in shots.keys(): shot = self.shot_dict[shotnumber] for traceID in shots[shotnumber]: @@ -310,18 +318,21 @@ class regions(object): self.drawFigure() return shots_found, numtraces - - def findTracesInShotDict(self, (x0, x1), (y0, y1), picks = 'normal', highlight = True): + + def findTracesInShotDict(self, (x0, x1), (y0, y1), picks='normal', highlight=True): ''' Returns traces corresponding to a certain area in the plot with all picks over the distances. ''' - shots_found = {}; numtraces = 0 - if picks == 'normal': pickflag = 0 - elif picks == 'includeCutOut': pickflag = None + shots_found = {}; + numtraces = 0 + if picks == 'normal': + pickflag = 0 + elif picks == 'includeCutOut': + pickflag = None for line in self._allpicks: dist, pick, shotnumber, traceID, flag = line - if flag == pickflag: continue ### IMPROVE THAT + if flag == pickflag: continue ### IMPROVE THAT if (x0 <= dist <= x1 and y0 <= pick <= y1): if shotnumber not in shots_found.keys(): shots_found[shotnumber] = [] @@ -333,7 +344,7 @@ class regions(object): self.drawFigure() return shots_found, numtraces - def highlightPick(self, shot, traceID, annotations = True): + def highlightPick(self, shot, traceID, annotations=True): ''' Highlights a single pick for a shot(object)/shotnumber and traceID. If annotations == True: Displays shotnumber and traceID in the plot. @@ -344,9 +355,11 @@ class regions(object): if shot.getPickFlag(traceID) is 0: return - self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s = 50, marker = 'o', facecolors = 'none', edgecolors = 'm', alpha = 1) + self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s=50, marker='o', facecolors='none', + edgecolors='m', alpha=1) if annotations == True: - self.ax.annotate(s='s%s|t%s' % (shot.getShotnumber(), traceID), xy=(shot.getDistance(traceID), shot.getPick(traceID)), fontsize='xx-small') + self.ax.annotate(s='s%s|t%s' % (shot.getShotnumber(), traceID), + xy=(shot.getDistance(traceID), shot.getPick(traceID)), fontsize='xx-small') def highlightAllActiveRegions(self): ''' @@ -358,7 +371,7 @@ class regions(object): self.highlightPick(self.shot_dict[shotnumber], traceID) self.drawFigure() - def plotTracesInActiveRegions(self, event = None, keys = 'all', maxfigures = 20): + def plotTracesInActiveRegions(self, event=None, keys='all', maxfigures=20): ''' Plots all traces in the active region or for all specified keys. @@ -382,13 +395,14 @@ class regions(object): for traceID in self.shots_found[key]['shots'][shotnumber]: count += 1 if count > maxfigures: - print 'Maximum number of figures (%s) reached. %sth figure was not opened.' %(maxfigures, count) + print 'Maximum number of figures (%s) reached. %sth figure was not opened.' % ( + maxfigures, count) break shot.plot_traces(traceID) else: self.printOutput('No picks defined in that region(s)') - def setAllActiveRegionsForDeletion(self, event = None): + def setAllActiveRegionsForDeletion(self, event=None): keys = [] for key in self.shots_found.keys(): keys.append(key) @@ -405,7 +419,7 @@ class regions(object): for traceID in self.shots_found[key]['shots'][shotnumber]: if traceID not in self.shots_for_deletion[shotnumber]: self.shots_for_deletion[shotnumber].append(traceID) - self.deselectSelection(key, color = 'red', alpha = 0.2) + self.deselectSelection(key, color='red', alpha=0.2) self.deselectSelection(key, color='red', alpha=0.2) @@ -415,13 +429,12 @@ class regions(object): for key in self.shots_found.keys(): if self.shots_found[key]['selection'] == 'rect': self.markRectangle(self.shots_found[key]['xvalues'], - self.shots_found[key]['yvalues'], key = key) + self.shots_found[key]['yvalues'], key=key) if self.shots_found[key]['selection'] == 'poly': self.markPolygon(self.shots_found[key]['xvalues'], - self.shots_found[key]['yvalues'], key = key) - + self.shots_found[key]['yvalues'], key=key) - def markRectangle(self, (x0, x1), (y0, y1), key = None, color = 'grey', alpha = 0.1, linewidth = 1): + def markRectangle(self, (x0, x1), (y0, y1), key=None, color='grey', alpha=0.1, linewidth=1): ''' Mark a rectangular region on the axes. ''' @@ -431,7 +444,7 @@ class regions(object): self.ax.text(x0 + (x1 - x0) / 2, y0 + (y1 - y0) / 2, str(key)) self.drawFigure() - def markPolygon(self, x, y, key = None, color = 'grey', alpha = 0.1, linewidth = 1): + def markPolygon(self, x, y, key=None, color='grey', alpha=0.1, linewidth=1): from matplotlib.patches import Polygon poly = Polygon(np.array(zip(x, y)), color=color, alpha=alpha, lw=linewidth) self.ax.add_patch(poly) @@ -449,7 +462,7 @@ class regions(object): def getShotsForDeletion(self): return self.shots_for_deletion - def deleteAllMarkedPicks(self, event = None): + def deleteAllMarkedPicks(self, event=None): ''' Deletes all shots set for deletion. ''' @@ -462,11 +475,11 @@ class regions(object): if shot.getShotnumber() == shotnumber: for traceID in self.getShotsForDeletion()[shotnumber]: shot.removePick(traceID) - print "Deleted the pick for traceID %s on shot number %s" %(traceID, shotnumber) + print "Deleted the pick for traceID %s on shot number %s" % (traceID, shotnumber) self.clearShotsForDeletion() self.refreshFigure() - def highlightPicksForShot(self, shot, annotations = False): + def highlightPicksForShot(self, shot, annotations=False): ''' Highlight all picks for a given shot. ''' @@ -482,19 +495,19 @@ class regions(object): def setXYlim(self, xlim, ylim): self._xlim, self._ylim = xlim, ylim - def refreshLog10SNR(self, event = None): + def refreshLog10SNR(self, event=None): cbv = 'log10SNR' self.refreshFigure(self, colorByVal=cbv) - def refreshPickerror(self, event = None): + def refreshPickerror(self, event=None): cbv = 'pickerror' self.refreshFigure(self, colorByVal=cbv) - def refreshSPE(self, event = None): + def refreshSPE(self, event=None): cbv = 'spe' self.refreshFigure(self, colorByVal=cbv) - def refreshFigure(self, event = None, colorByVal = None): + def refreshFigure(self, event=None, colorByVal=None): if colorByVal == None: colorByVal = self.cbv else: @@ -508,7 +521,7 @@ class regions(object): self.drawFigure() self.printOutput('Done!') - def drawFigure(self, resetAxes = True): + def drawFigure(self, resetAxes=True): if resetAxes == True: self.ax.set_xlim(self._xlim) self.ax.set_ylim(self._ylim) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index e513d876..ff637612 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -1,6 +1,13 @@ -import numpy as np +from __future__ import print_function + def readParameters(parfile, parameter): + """ + + :param parfile: + :param parameter: + :return: + """ from ConfigParser import ConfigParser parameterConfig = ConfigParser() parameterConfig.read('parfile') @@ -9,14 +16,29 @@ def readParameters(parfile, parameter): return value + def setArtificialPick(shot_dict, traceID, pick): + """ + + :param shot_dict: + :param traceID: + :param pick: + :return: + """ for shot in shot_dict.values(): shot.setPick(traceID, pick) shot.setPickwindow(traceID, shot.getCut()) -def fitSNR4dist(shot_dict, shiftdist = 30, shiftSNR = 100): + +def fitSNR4dist(shot_dict, shiftdist=30, shiftSNR=100): + """ + + :param shot_dict: + :param shiftdist: + :param shiftSNR: + :return: + """ import numpy as np - import matplotlib.pyplot as plt dists = [] picks = [] snrs = [] @@ -29,54 +51,84 @@ def fitSNR4dist(shot_dict, shiftdist = 30, shiftSNR = 100): dists.append(shot.getDistance(traceID)) picks.append(shot.getPickIncludeRemoved(traceID)) snrs.append(shot.getSNR(traceID)[0]) - snr_sqrt_inv.append(1/np.sqrt(shot.getSNR(traceID)[0])) + snr_sqrt_inv.append(1 / np.sqrt(shot.getSNR(traceID)[0])) fit = np.polyfit(dists, snr_sqrt_inv, 1) fit_fn = np.poly1d(fit) for dist in dists: - snrBestFit.append((1/(fit_fn(dist)**2))) + snrBestFit.append((1 / (fit_fn(dist) ** 2))) dist += shiftdist - snrthresholds.append((1/(fit_fn(dist)**2)) - shiftSNR * np.exp(-0.05 * dist)) + snrthresholds.append((1 / (fit_fn(dist) ** 2)) - shiftSNR * np.exp(-0.05 * dist)) plotFittedSNR(dists, snrthresholds, snrs, snrBestFit) - return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben + return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben def plotFittedSNR(dists, snrthresholds, snrs, snrBestFit): + """ + + :param dists: + :param snrthresholds: + :param snrs: + :param snrBestFit: + :return: + """ import matplotlib.pyplot as plt plt.interactive(True) fig = plt.figure() - plt.plot(dists, snrs, 'b.', markersize = 2.0, label = 'SNR values') + plt.plot(dists, snrs, 'b.', markersize=2.0, label='SNR values') dists.sort() - snrthresholds.sort(reverse = True) - snrBestFit.sort(reverse = True) - plt.plot(dists, snrthresholds, 'r', markersize = 1, label = 'Fitted threshold') - plt.plot(dists, snrBestFit, 'k', markersize = 1, label = 'Best fitted curve') + snrthresholds.sort(reverse=True) + snrBestFit.sort(reverse=True) + plt.plot(dists, snrthresholds, 'r', markersize=1, label='Fitted threshold') + plt.plot(dists, snrBestFit, 'k', markersize=1, label='Best fitted curve') plt.xlabel('Distance[m]') plt.ylabel('SNR') plt.legend() -def setDynamicFittedSNR(shot_dict, shiftdist = 30, shiftSNR = 100, p1 = 0.004, p2 = -0.0007): + +def setDynamicFittedSNR(shot_dict, shiftdist=30, shiftSNR=100, p1=0.004, p2=-0.0007): + """ + + :param shot_dict: + :type shot_dict: dict + :param shiftdist: + :type shiftdist: int + :param shiftSNR: + :type shiftSNR: int + :param p1: + :type p1: float + :param p2: + :type p2: float + :return: + """ import numpy as np minSNR = 2.5 - #fit_fn = fitSNR4dist(shot_dict) + # fit_fn = fitSNR4dist(shot_dict) fit_fn = np.poly1d([p1, p2]) for shot in shot_dict.values(): - for traceID in shot.getTraceIDlist(): ### IMPROVE + for traceID in shot.getTraceIDlist(): ### IMPROVE dist = shot.getDistance(traceID) + shiftdist - snrthreshold = (1/(fit_fn(dist)**2)) - shiftSNR * np.exp(-0.05 * dist) + snrthreshold = (1 / (fit_fn(dist) ** 2)) - shiftSNR * np.exp(-0.05 * dist) if snrthreshold < minSNR: print('WARNING: SNR threshold %s lower %s. Set SNR threshold to %s.' - %(snrthreshold, minSNR, minSNR)) + % (snrthreshold, minSNR, minSNR)) shot.setSNRthreshold(traceID, minSNR) else: shot.setSNRthreshold(traceID, snrthreshold) - print "setDynamicFittedSNR: Finished setting of fitted SNR-threshold" + print("setDynamicFittedSNR: Finished setting of fitted SNR-threshold") -def setConstantSNR(shot_dict, snrthreshold = 2.5): - import numpy as np + +def setConstantSNR(shot_dict, snrthreshold=2.5): + """ + + :param shot_dict: + :param snrthreshold: + :return: + """ for shot in shot_dict.values(): for traceID in shot.getTraceIDlist(): shot.setSNRthreshold(traceID, snrthreshold) - print "setConstantSNR: Finished setting of SNR threshold to a constant value of %s"%snrthreshold + print("setConstantSNR: Finished setting of SNR threshold to a constant value of %s" % snrthreshold) + def findTracesInRanges(shot_dict, distancebin, pickbin): ''' @@ -94,8 +146,8 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): ''' shots_found = {} for shot in shot_dict.values(): - if shot.getTraceIDs4Dist(distancebin = distancebin) is not None: - for traceID in shot.getTraceIDs4Dist(distancebin = distancebin): + if shot.getTraceIDs4Dist(distancebin=distancebin) is not None: + for traceID in shot.getTraceIDs4Dist(distancebin=distancebin): if pickbin[0] < shot.getPick(traceID) < pickbin[1]: if shot.getShotnumber() not in shots_found.keys(): shots_found[shot.getShotnumber()] = [] @@ -103,11 +155,17 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): return shots_found -def cleanUp(survey): +def cleanUp(survey): + """ + + :param survey: + :return: + """ for shot in survey.data.values(): shot.traces4plot = {} + # def plotScatterStats(survey, key, ax = None): # import matplotlib.pyplot as plt # x = []; y = []; value = [] @@ -119,7 +177,7 @@ def cleanUp(survey): # value.append(stats[shotnumber][key]) # x.append(survey.data[shotnumber].getSrcLoc()[0]) # y.append(survey.data[shotnumber].getSrcLoc()[1]) - + # if ax == None: # fig = plt.figure() # ax = fig.add_subplot(111) @@ -131,14 +189,19 @@ def cleanUp(survey): # cbar.set_label(key) def plotScatterStats4Shots(survey, key): - ''' + """ Statistics, scatter plot. key can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' - ''' + :param survey: + :param key: + :return: + """ import matplotlib.pyplot as plt import numpy as np statsShot = {} - x = []; y = []; value = [] + x = [] + y = [] + value = [] for shot in survey.data.values(): for traceID in shot.getTraceIDlist(): if not shot in statsShot.keys(): @@ -147,7 +210,7 @@ def plotScatterStats4Shots(survey, key): 'SNR': [], 'SPE': [], 'picked traces': 0} - + statsShot[shot]['SNR'].append(shot.getSNR(traceID)[0]) if shot.getPickFlag(traceID) == 1: statsShot[shot]['picked traces'] += 1 @@ -171,7 +234,7 @@ def plotScatterStats4Shots(survey, key): for val in value: size.append(100 * val / max(value)) - sc = ax.scatter(x, y, s = size, c = value) + sc = ax.scatter(x, y, s=size, c=value) plt.title('Plot of all shots') plt.xlabel('X') plt.ylabel('Y') @@ -179,18 +242,24 @@ def plotScatterStats4Shots(survey, key): cbar.set_label(key) for shot in statsShot.keys(): - ax.annotate(' %s' %shot.getShotnumber() , xy = (shot.getSrcLoc()[0], shot.getSrcLoc()[1]), - fontsize = 'x-small', color = 'k') - + ax.annotate(' %s' % shot.getShotnumber(), xy=(shot.getSrcLoc()[0], shot.getSrcLoc()[1]), + fontsize='x-small', color='k') + + def plotScatterStats4Receivers(survey, key): - ''' + """ Statistics, scatter plot. key can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' - ''' + :param survey: + :param key: + :return: + """ import matplotlib.pyplot as plt import numpy as np statsRec = {} - x = []; y = []; value = [] + x = [] + y = [] + value = [] for shot in survey.data.values(): for traceID in shot.getTraceIDlist(): if not traceID in statsRec.keys(): @@ -199,13 +268,12 @@ def plotScatterStats4Receivers(survey, key): 'SNR': [], 'SPE': [], 'picked traces': 0} - + statsRec[traceID]['SNR'].append(shot.getSNR(traceID)[0]) if shot.getPickFlag(traceID) == 1: statsRec[traceID]['picked traces'] += 1 statsRec[traceID]['SPE'].append(shot.getSymmetricPickError(traceID)) - for traceID in statsRec.keys(): statsRec[traceID]['mean SNR'] = np.mean(statsRec[traceID]['SNR']) statsRec[traceID]['median SNR'] = np.median(statsRec[traceID]['SNR']) @@ -224,14 +292,14 @@ def plotScatterStats4Receivers(survey, key): for val in value: size.append(100 * val / max(value)) - sc = ax.scatter(x, y, s = size, c = value) + sc = ax.scatter(x, y, s=size, c=value) plt.title('Plot of all receivers') plt.xlabel('X') plt.ylabel('Y') cbar = plt.colorbar(sc) cbar.set_label(key) - + shot = survey.data.values()[0] for traceID in shot.getTraceIDlist(): - ax.annotate(' %s' %traceID , xy = (shot.getRecLoc(traceID)[0], shot.getRecLoc(traceID)[1]), - fontsize = 'x-small', color = 'k') + ax.annotate(' %s' % traceID, xy=(shot.getRecLoc(traceID)[0], shot.getRecLoc(traceID)[1]), + fontsize='x-small', color='k') diff --git a/pylot/core/analysis/coinctimes.py b/pylot/core/analysis/coinctimes.py index 1ecc7a9e..9b41b0c3 100644 --- a/pylot/core/analysis/coinctimes.py +++ b/pylot/core/analysis/coinctimes.py @@ -5,9 +5,7 @@ from obspy.core import read from obspy.signal.trigger import coincidenceTrigger - class CoincidenceTimes(object): - def __init__(self, st, comp='Z', coinum=4, sta=1., lta=10., on=5., off=1.): _type = 'recstalta' self.coinclist = self.createCoincTriggerlist(data=st, trigcomp=comp, diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 827d6055..ace6f0aa 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -15,6 +15,7 @@ from scipy.optimize import curve_fit from scipy import integrate, signal from pylot.core.read.data import Data + class Magnitude(object): ''' Superclass for calculating Wood-Anderson peak-to-peak @@ -72,7 +73,6 @@ class Magnitude(object): self.calcsourcespec() self.run_calcMoMw() - def getwfstream(self): return self.wfstream @@ -108,7 +108,7 @@ class Magnitude(object): def getrho(self): return self.rho - + def setvp(self, vp): self.vp = vp @@ -117,7 +117,7 @@ class Magnitude(object): def setQp(self, Qp): self.Qp = Qp - + def getQp(self): return self.Qp @@ -154,6 +154,7 @@ class Magnitude(object): def run_calcMoMw(self): self.pickdic = None + class WApp(Magnitude): ''' Method to derive peak-to-peak amplitude as seen on a Wood-Anderson- @@ -207,10 +208,10 @@ class WApp(Magnitude): class M0Mw(Magnitude): ''' Method to calculate seismic moment Mo and moment magnitude Mw. - Requires results of class calcsourcespec for calculating plateau w0 - and corner frequency fc of source spectrum, respectively. Uses - subfunction calcMoMw.py. Returns modified dictionary of picks including - Dc-value, corner frequency fc, seismic moment Mo and + Requires results of class calcsourcespec for calculating plateau w0 + and corner frequency fc of source spectrum, respectively. Uses + subfunction calcMoMw.py. Returns modified dictionary of picks including + Dc-value, corner frequency fc, seismic moment Mo and corresponding moment magntiude Mw. ''' @@ -222,44 +223,45 @@ class M0Mw(Magnitude): self.picdic = None for key in picks: - if picks[key]['P']['weight'] < 4: - # select waveform - selwf = wfdat.select(station=key) - if len(key) > 4: - Ppattern = '%s ? ? ? P' % key - elif len(key) == 4: - Ppattern = '%s ? ? ? P' % key - elif len(key) < 4: - Ppattern = '%s ? ? ? P' % key - nllocline = getPatternLine(nllocfile, Ppattern) - # get hypocentral distance, station azimuth and - # angle of incidence from NLLoc-location file - delta = float(nllocline.split(None)[21]) - az = float(nllocline.split(None)[22]) - inc = float(nllocline.split(None)[24]) - # call subfunction to estimate source spectrum - # and to derive w0 and fc - [w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \ - self.getinvdir(), self.getvp(), delta, az, \ - inc, self.getQp(), self.getiplot()) + if picks[key]['P']['weight'] < 4: + # select waveform + selwf = wfdat.select(station=key) + if len(key) > 4: + Ppattern = '%s ? ? ? P' % key + elif len(key) == 4: + Ppattern = '%s ? ? ? P' % key + elif len(key) < 4: + Ppattern = '%s ? ? ? P' % key + nllocline = getPatternLine(nllocfile, Ppattern) + # get hypocentral distance, station azimuth and + # angle of incidence from NLLoc-location file + delta = float(nllocline.split(None)[21]) + az = float(nllocline.split(None)[22]) + inc = float(nllocline.split(None)[24]) + # call subfunction to estimate source spectrum + # and to derive w0 and fc + [w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \ + self.getinvdir(), self.getvp(), delta, az, \ + inc, self.getQp(), self.getiplot()) - if w0 is not None: - # call subfunction to calculate Mo and Mw - zdat = selwf.select(component="Z") - if len(zdat) == 0: # check for other components - zdat = selwf.select(component="3") - [Mo, Mw] = calcMoMw(zdat, w0, self.getrho(), self.getvp(), \ - delta, self.getinvdir()) - else: - Mo = None - Mw = None + if w0 is not None: + # call subfunction to calculate Mo and Mw + zdat = selwf.select(component="Z") + if len(zdat) == 0: # check for other components + zdat = selwf.select(component="3") + [Mo, Mw] = calcMoMw(zdat, w0, self.getrho(), self.getvp(), \ + delta, self.getinvdir()) + else: + Mo = None + Mw = None + + # add w0, fc, Mo and Mw to dictionary + picks[key]['P']['w0'] = w0 + picks[key]['P']['fc'] = fc + picks[key]['P']['Mo'] = Mo + picks[key]['P']['Mw'] = Mw + self.picdic = picks - # add w0, fc, Mo and Mw to dictionary - picks[key]['P']['w0'] = w0 - picks[key]['P']['fc'] = fc - picks[key]['P']['Mo'] = Mo - picks[key]['P']['Mw'] = Mw - self.picdic = picks def calcMoMw(wfstream, w0, rho, vp, delta, inv): ''' @@ -271,7 +273,7 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): :param: w0, height of plateau of source spectrum :type: float - + :param: rho, rock density [kg/m³] :type: integer @@ -283,25 +285,24 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv): ''' tr = wfstream[0] - delta = delta * 1000 # hypocentral distance in [m] + delta = delta * 1000 # hypocentral distance in [m] print("calcMoMw: Calculating seismic moment Mo and moment magnitude Mw for station %s ..." \ - % tr.stats.station) + % tr.stats.station) # additional common parameters for calculating Mo - rP = 2 / np.sqrt(15) # average radiation pattern of P waves (Aki & Richards, 1980) - freesurf = 2.0 # free surface correction, assuming vertical incidence + rP = 2 / np.sqrt(15) # average radiation pattern of P waves (Aki & Richards, 1980) + freesurf = 2.0 # free surface correction, assuming vertical incidence - Mo = w0 * 4 * np.pi * rho * np.power(vp, 3) * delta / (rP * freesurf) + Mo = w0 * 4 * np.pi * rho * np.power(vp, 3) * delta / (rP * freesurf) - #Mw = np.log10(Mo * 1e07) * 2 / 3 - 10.7 # after Hanks & Kanamori (1979), defined for [dyn*cm]! - Mw = np.log10(Mo) * 2 / 3 - 6.7 # for metric units + # Mw = np.log10(Mo * 1e07) * 2 / 3 - 10.7 # after Hanks & Kanamori (1979), defined for [dyn*cm]! + Mw = np.log10(Mo) * 2 / 3 - 6.7 # for metric units print("calcMoMw: Calculated seismic moment Mo = %e Nm => Mw = %3.1f " % (Mo, Mw)) return Mo, Mw - def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp, iplot): ''' @@ -310,7 +311,7 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp source model. Has to be derived from instrument corrected displacement traces, thus restitution and integration necessary! Integrated traces are rotated into ray-coordinate system ZNE => LQT using Obspy's rotate modul! - + :param: wfstream :type: `~obspy.core.stream.Stream` @@ -346,7 +347,7 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp Q = int(qu[0]) # A, i.e. power of frequency A = float(qu[1]) - delta = delta * 1000 # hypocentral distance in [m] + delta = delta * 1000 # hypocentral distance in [m] fc = None w0 = None @@ -385,11 +386,11 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp # L: P-wave direction # Q: SV-wave direction # T: SH-wave direction - LQT=cordat_copy.rotate('ZNE->LQT',azimuth, incidence) + LQT = cordat_copy.rotate('ZNE->LQT', azimuth, incidence) ldat = LQT.select(component="L") if len(ldat) == 0: # if horizontal channels are 2 and 3 - # no azimuth information is available and thus no + # no azimuth information is available and thus no # rotation is possible! print("calcsourcespec: Azimuth information is missing, " "no rotation of components possible!") @@ -398,30 +399,30 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp # integrate to displacement # unrotated vertical component (for copmarison) inttrz = signal.detrend(integrate.cumtrapz(zdat[0].data, None, \ - zdat[0].stats.delta)) + zdat[0].stats.delta)) # rotated component Z => L Ldat = signal.detrend(integrate.cumtrapz(ldat[0].data, None, \ - ldat[0].stats.delta)) + ldat[0].stats.delta)) - # get window after P pulse for + # get window after P pulse for # calculating source spectrum if zdat[0].stats.sampling_rate <= 100: winzc = zdat[0].stats.sampling_rate elif zdat[0].stats.sampling_rate > 100 and \ - zdat[0].stats.sampling_rate <= 200: - winzc = 0.5 * zdat[0].stats.sampling_rate + zdat[0].stats.sampling_rate <= 200: + winzc = 0.5 * zdat[0].stats.sampling_rate elif zdat[0].stats.sampling_rate > 200 and \ - zdat[0].stats.sampling_rate <= 400: - winzc = 0.2 * zdat[0].stats.sampling_rate + zdat[0].stats.sampling_rate <= 400: + winzc = 0.2 * zdat[0].stats.sampling_rate elif zdat[0].stats.sampling_rate > 400: - winzc = zdat[0].stats.sampling_rate + winzc = zdat[0].stats.sampling_rate tstart = UTCDateTime(zdat[0].stats.starttime) - tonset = onset.timestamp -tstart.timestamp + tonset = onset.timestamp - tstart.timestamp impickP = tonset * zdat[0].stats.sampling_rate - wfzc = Ldat[impickP : impickP + winzc] + wfzc = Ldat[impickP: impickP + winzc] # get time array t = np.arange(0, len(inttrz) * zdat[0].stats.delta, \ - zdat[0].stats.delta) + zdat[0].stats.delta) # calculate spectrum using only first cycles of # waveform after P onset! zc = crossings_nonzero_all(wfzc) @@ -441,14 +442,14 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp fny = zdat[0].stats.sampling_rate / 2 l = len(xdat) / zdat[0].stats.sampling_rate # number of fft bins after Bath - n = zdat[0].stats.sampling_rate * l + n = zdat[0].stats.sampling_rate * l # find next power of 2 of data length m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2))) N = int(np.power(m, 2)) y = zdat[0].stats.delta * np.fft.fft(xdat, N) - Y = abs(y[: N/2]) + Y = abs(y[: N / 2]) L = (N - 1) / zdat[0].stats.sampling_rate - f = np.arange(0, fny, 1/L) + f = np.arange(0, fny, 1 / L) # remove zero-frequency and frequencies above # corner frequency of seismometer (assumed @@ -457,10 +458,10 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp F = f[fi] YY = Y[fi] - # correction for attenuation - wa = 2 * np.pi * F #angular frequency - D = np.exp((wa * delta) / (2 * vp * Q*F**A)) - YYcor = YY.real*D + # correction for attenuation + wa = 2 * np.pi * F # angular frequency + D = np.exp((wa * delta) / (2 * vp * Q * F ** A)) + YYcor = YY.real * D # get plateau (DC value) and corner frequency # initial guess of plateau @@ -477,24 +478,24 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp fc1 = optspecfit[1] print ("calcsourcespec: Determined w0-value: %e m/Hz, \n" "Determined corner frequency: %f Hz" % (w01, fc1)) - - # use of conventional fitting + + # use of conventional fitting [w02, fc2] = fitSourceModel(F, YYcor, Fcin, iplot) - - # get w0 and fc as median of both - # source spectrum fits + + # get w0 and fc as median of both + # source spectrum fits w0 = np.median([w01, w02]) fc = np.median([fc1, fc2]) print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % (w0, fc)) - + except TypeError as er: raise TypeError('''{0}'''.format(er)) if iplot > 1: f1 = plt.figure() tLdat = np.arange(0, len(Ldat) * zdat[0].stats.delta, \ - zdat[0].stats.delta) - plt.subplot(2,1,1) + zdat[0].stats.delta) + plt.subplot(2, 1, 1) # show displacement in mm p1, = plt.plot(t, np.multiply(inttrz, 1000), 'k') p2, = plt.plot(tLdat, np.multiply(Ldat, 1000)) @@ -502,26 +503,26 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp if plotflag == 1: plt.plot(t[iwin], np.multiply(xdat, 1000), 'g') plt.title('Seismogram and P Pulse, Station %s-%s' \ - % (zdat[0].stats.station, zdat[0].stats.channel)) + % (zdat[0].stats.station, zdat[0].stats.channel)) else: plt.title('Seismogram, Station %s-%s' \ - % (zdat[0].stats.station, zdat[0].stats.channel)) + % (zdat[0].stats.station, zdat[0].stats.channel)) plt.xlabel('Time since %s' % zdat[0].stats.starttime) plt.ylabel('Displacement [mm]') if plotflag == 1: - plt.subplot(2,1,2) + plt.subplot(2, 1, 2) p1, = plt.loglog(f, Y.real, 'k') p2, = plt.loglog(F, YY.real) p3, = plt.loglog(F, YYcor, 'r') p4, = plt.loglog(F, fit, 'g') - plt.loglog([fc, fc], [w0/100, w0], 'g') + plt.loglog([fc, fc], [w0 / 100, w0], 'g') plt.legend([p1, p2, p3, p4], ['Raw Spectrum', \ 'Used Raw Spectrum', \ 'Q-Corrected Spectrum', \ 'Fit to Spectrum']) plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \ - % (w0, fc)) + % (w0, fc)) plt.xlabel('Frequency [Hz]') plt.ylabel('Amplitude [m/Hz]') plt.grid() @@ -530,7 +531,7 @@ def calcsourcespec(wfstream, onset, inventory, vp, delta, azimuth, incidence, Qp plt.close(f1) return w0, fc - + def synthsourcespec(f, omega0, fcorner): ''' @@ -547,7 +548,7 @@ def synthsourcespec(f, omega0, fcorner): :type: float ''' - #ssp = omega0 / (pow(2, (1 + f / fcorner))) + # ssp = omega0 / (pow(2, (1 + f / fcorner))) ssp = omega0 / (1 + pow(2, (f / fcorner))) return ssp @@ -556,8 +557,8 @@ def synthsourcespec(f, omega0, fcorner): def fitSourceModel(f, S, fc0, iplot): ''' Calculates synthetic source spectrum by varying corner frequency fc. - Returns best approximated plateau omega0 and corner frequency, i.e. with least - common standard deviations. + Returns best approximated plateau omega0 and corner frequency, i.e. with least + common standard deviations. :param: f, frequencies :type: array @@ -569,7 +570,7 @@ def fitSourceModel(f, S, fc0, iplot): :type: float ''' - w0 = [] + w0 = [] stdw0 = [] fc = [] stdfc = [] @@ -577,17 +578,17 @@ def fitSourceModel(f, S, fc0, iplot): # get window around initial corner frequency for trials fcstopl = fc0 - max(1, len(f) / 10) - il = np.argmin(abs(f-fcstopl)) + il = np.argmin(abs(f - fcstopl)) fcstopl = f[il] - fcstopr = fc0 + min(len(f), len(f) /10) - ir = np.argmin(abs(f-fcstopr)) + fcstopr = fc0 + min(len(f), len(f) / 10) + ir = np.argmin(abs(f - fcstopr)) fcstopr = f[ir] iF = np.where((f >= fcstopl) & (f <= fcstopr)) # vary corner frequency around initial point - for i in range(il, ir): + for i in range(il, ir): FC = f[i] - indexdc = np.where((f > 0 ) & (f <= FC)) + indexdc = np.where((f > 0) & (f <= FC)) dc = np.mean(S[indexdc]) stddc = np.std(dc - S[indexdc]) w0.append(dc) @@ -595,7 +596,7 @@ def fitSourceModel(f, S, fc0, iplot): fc.append(FC) # slope indexfc = np.where((f >= FC) & (f <= fcstopr)) - yi = dc/(1+(f[indexfc]/FC)**2) + yi = dc / (1 + (f[indexfc] / FC) ** 2) stdFC = np.std(yi - S[indexfc]) stdfc.append(stdFC) STD.append(stddc + stdFC) @@ -607,31 +608,31 @@ def fitSourceModel(f, S, fc0, iplot): elif len(STD) == 0: fc = fc0 w0 = max(S) - + print("fitSourceModel: best fc: %fHz, best w0: %e m/Hz" \ - % (fc, w0)) + % (fc, w0)) if iplot > 1: plt.figure(iplot) plt.loglog(f, S, 'k') plt.loglog([f[0], fc], [w0, w0], 'g') - plt.loglog([fc, fc], [w0/100, w0], 'g') + plt.loglog([fc, fc], [w0 / 100, w0], 'g') plt.title('Calculated Source Spectrum, Omega0=%e m/Hz, fc=%6.2f Hz' \ - % (w0, fc)) + % (w0, fc)) plt.xlabel('Frequency [Hz]') plt.ylabel('Amplitude [m/Hz]') plt.grid() - plt.figure(iplot+1) + plt.figure(iplot + 1) plt.subplot(311) - plt.plot(f[il:ir], STD,'*') + plt.plot(f[il:ir], STD, '*') plt.title('Common Standard Deviations') plt.xticks([]) plt.subplot(312) - plt.plot(f[il:ir], stdw0,'*') + plt.plot(f[il:ir], stdw0, '*') plt.title('Standard Deviations of w0-Values') plt.xticks([]) plt.subplot(313) - plt.plot(f[il:ir],stdfc,'*') + plt.plot(f[il:ir], stdfc, '*') plt.title('Standard Deviations of Corner Frequencies') plt.xlabel('Corner Frequencies [Hz]') plt.show() @@ -639,10 +640,3 @@ def fitSourceModel(f, S, fc0, iplot): plt.close() return w0, fc - - - - - - - diff --git a/pylot/core/analysis/trigger.py b/pylot/core/analysis/trigger.py index aa900cef..a1e61b36 100644 --- a/pylot/core/analysis/trigger.py +++ b/pylot/core/analysis/trigger.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from obspy.signal.trigger import recSTALTA, triggerOnset +from obspy.signal.trigger import recursive_sta_lta, trigger_onset def createSingleTriggerlist(st, station='ZV01', trigcomp='Z', stalta=(1, 10), @@ -24,8 +24,8 @@ def createSingleTriggerlist(st, station='ZV01', trigcomp='Z', stalta=(1, 10), tr = st.copy().select(component=trigcomp, station=station)[0] df = tr.stats.sampling_rate - cft = recSTALTA(tr.data, int(stalta[0] * df), int(stalta[1] * df)) - triggers = triggerOnset(cft, trigonoff[0], trigonoff[1]) + cft = recursive_sta_lta(tr.data, int(stalta[0] * df), int(stalta[1] * df)) + triggers = trigger_onset(cft, trigonoff[0], trigonoff[1]) trigg = [] for time in triggers: trigg.append(tr.stats.starttime + time[0] / df) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index 6c27ac43..bd4ba4cf 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -9,9 +9,10 @@ from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() + def picksExport(picks, locrt, phasefile): ''' - Take dictionary and exports picking data to a NLLOC-obs + Take dictionary and exports picking data to a NLLOC-obs without creating an ObsPy event object. :param picks: picking data dictionary @@ -26,6 +27,7 @@ def picksExport(picks, locrt, phasefile): # write phases to NLLoc-phase file writephases(picks, locrt, phasefile) + def modifyInputFile(ctrfn, root, nllocoutn, phasefn, tttn): ''' :param ctrfn: name of NLLoc-control file @@ -35,18 +37,18 @@ def modifyInputFile(ctrfn, root, nllocoutn, phasefn, tttn): :type: str :param nllocoutn: name of NLLoc-location output file - :type: str + :type: str :param phasefn: name of NLLoc-input phase file :type: str :param tttn: pattern of precalculated NLLoc traveltime tables - :type: str + :type: str ''' # For locating the event the NLLoc-control file has to be modified! # create comment line for NLLoc-control file NLLoc-output file ctrfile = os.path.join(root, 'run', ctrfn) - nllocout = os.path.join(root,'loc', nllocoutn) + nllocout = os.path.join(root, 'loc', nllocoutn) phasefile = os.path.join(root, 'obs', phasefn) tttable = os.path.join(root, 'time', tttn) locfiles = 'LOCFILES %s NLLOC_OBS %s %s 0\n' % (phasefile, tttable, nllocout) @@ -63,6 +65,7 @@ def modifyInputFile(ctrfn, root, nllocoutn, phasefn, tttn): nllfile.write(filedata) nllfile.close() + def locate(call, fnin): ''' Takes paths to NLLoc executable and input parameter file @@ -78,8 +81,10 @@ def locate(call, fnin): # locate the event subprocess.call([call, fnin]) + def readLocation(fn): pass -if __name__=='__main__': + +if __name__ == '__main__': pass diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index a1f9fd28..8d6de16c 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -144,7 +144,7 @@ def autopickstation(wfstream, pickparam, verbose=False): Sflag = 0 Pmarker = [] Ao = None # Wood-Anderson peak-to-peak amplitude - picker = 'autoPyLoT' # name of the picking programm + picker = 'autoPyLoT' # name of the picking programm # split components zdat = wfstream.select(component="Z") @@ -867,19 +867,19 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): pickparameter.setParam(noisefactor=1.0) pickparameter.setParam(zfac=1.0) print( - "iteratepicker: The following picking parameters have been modified for iterative picking:") + "iteratepicker: The following picking parameters have been modified for iterative picking:") print( - "pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) + "pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) print( - "pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) + "pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) print( - "sstop: %fs => %fs" % (sstop_old, pickparameter.getParam('sstop'))) + "sstop: %fs => %fs" % (sstop_old, pickparameter.getParam('sstop'))) print("pickwinP: %fs => %fs" % ( - pickwinP_old, pickparameter.getParam('pickwinP'))) + pickwinP_old, pickparameter.getParam('pickwinP'))) print("Precalcwin: %fs => %fs" % ( - Precalcwin_old, pickparameter.getParam('Precalcwin'))) + Precalcwin_old, pickparameter.getParam('Precalcwin'))) print("noisefactor: %f => %f" % ( - noisefactor_old, pickparameter.getParam('noisefactor'))) + noisefactor_old, pickparameter.getParam('noisefactor'))) print("zfac: %f => %f" % (zfac_old, pickparameter.getParam('zfac'))) # repick station diff --git a/pylot/core/pick/charfuns.py b/pylot/core/pick/charfuns.py index 78efd083..cb3ad543 100644 --- a/pylot/core/pick/charfuns.py +++ b/pylot/core/pick/charfuns.py @@ -21,10 +21,12 @@ import matplotlib.pyplot as plt import numpy as np from obspy.core import Stream + class CharacteristicFunction(object): ''' SuperClass for different types of characteristic functions. ''' + def __init__(self, data, cut, t2=None, order=None, t1=None, fnoise=None, stealthMode=False): ''' Initialize data type object with information from the original @@ -103,9 +105,9 @@ class CharacteristicFunction(object): def setARdetStep(self, t1): if t1: - self.ARdetStep = [] - self.ARdetStep.append(t1 / 4) - self.ARdetStep.append(int(np.ceil(self.getTime2() / self.getIncrement()) / 4)) + self.ARdetStep = [] + self.ARdetStep.append(t1 / 4) + self.ARdetStep.append(int(np.ceil(self.getTime2() / self.getIncrement()) / 4)) def getOrder(self): return self.order @@ -150,14 +152,14 @@ class CharacteristicFunction(object): if cut is not None: if len(self.orig_data) == 1: if self.cut[0] == 0 and self.cut[1] == 0: - start = 0 - stop = len(self.orig_data[0]) + start = 0 + stop = len(self.orig_data[0]) elif self.cut[0] == 0 and self.cut[1] is not 0: - start = 0 - stop = self.cut[1] / self.dt + start = 0 + stop = self.cut[1] / self.dt else: - start = self.cut[0] / self.dt - stop = self.cut[1] / self.dt + start = self.cut[0] / self.dt + stop = self.cut[1] / self.dt zz = self.orig_data.copy() z1 = zz[0].copy() zz[0].data = z1.data[int(start):int(stop)] @@ -165,16 +167,16 @@ class CharacteristicFunction(object): return data elif len(self.orig_data) == 2: if self.cut[0] == 0 and self.cut[1] == 0: - start = 0 - stop = min([len(self.orig_data[0]), len(self.orig_data[1])]) + start = 0 + stop = min([len(self.orig_data[0]), len(self.orig_data[1])]) elif self.cut[0] == 0 and self.cut[1] is not 0: - start = 0 - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), - len(self.orig_data[1])]) + start = 0 + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1])]) else: - start = max([0, self.cut[0] / self.dt]) - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), - len(self.orig_data[1])]) + start = max([0, self.cut[0] / self.dt]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1])]) hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() @@ -184,16 +186,16 @@ class CharacteristicFunction(object): return data elif len(self.orig_data) == 3: if self.cut[0] == 0 and self.cut[1] == 0: - start = 0 - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), - len(self.orig_data[1]), len(self.orig_data[2])]) + start = 0 + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1]), len(self.orig_data[2])]) elif self.cut[0] == 0 and self.cut[1] is not 0: - start = 0 - stop = self.cut[1] / self.dt + start = 0 + stop = self.cut[1] / self.dt else: - start = max([0, self.cut[0] / self.dt]) - stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), - len(self.orig_data[1]), len(self.orig_data[2])]) + start = max([0, self.cut[0] / self.dt]) + stop = min([self.cut[1] / self.dt, len(self.orig_data[0]), + len(self.orig_data[1]), len(self.orig_data[2])]) hh = self.orig_data.copy() h1 = hh[0].copy() h2 = hh[1].copy() @@ -223,13 +225,13 @@ class AICcf(CharacteristicFunction): def calcCF(self, data): - #if self._getStealthMode() is False: + # if self._getStealthMode() is False: # print 'Calculating AIC ...' x = self.getDataArray() xnp = x[0].data nn = np.isnan(xnp) if len(nn) > 1: - xnp[nn] = 0 + xnp[nn] = 0 datlen = len(xnp) k = np.arange(1, datlen) cf = np.zeros(datlen) @@ -247,6 +249,7 @@ class AICcf(CharacteristicFunction): self.cf = cf - np.mean(cf) self.xcf = x + class HOScf(CharacteristicFunction): ''' Function to calculate skewness (statistics of order 3) or kurtosis @@ -257,38 +260,38 @@ class HOScf(CharacteristicFunction): def calcCF(self, data): x = self.getDataArray(self.getCut()) - xnp =x[0].data + xnp = x[0].data nn = np.isnan(xnp) if len(nn) > 1: - xnp[nn] = 0 + xnp[nn] = 0 if self.getOrder() == 3: # this is skewness - #if self._getStealthMode() is False: + # if self._getStealthMode() is False: # print 'Calculating skewness ...' y = np.power(xnp, 3) y1 = np.power(xnp, 2) elif self.getOrder() == 4: # this is kurtosis - #if self._getStealthMode() is False: + # if self._getStealthMode() is False: # print 'Calculating kurtosis ...' y = np.power(xnp, 4) y1 = np.power(xnp, 2) - #Initialisation - #t2: long term moving window + # Initialisation + # t2: long term moving window ilta = int(round(self.getTime2() / self.getIncrement())) lta = y[0] lta1 = y1[0] - #moving windows + # moving windows LTA = np.zeros(len(xnp)) for j in range(0, len(xnp)): if j < 4: LTA[j] = 0 elif j <= ilta: - lta = (y[j] + lta * (j-1)) / j - lta1 = (y1[j] + lta1 * (j-1)) / j + lta = (y[j] + lta * (j - 1)) / j + lta1 = (y1[j] + lta1 * (j - 1)) / j else: lta = (y[j] - y[j - ilta]) / ilta + lta lta1 = (y1[j] - y1[j - ilta]) / ilta + lta1 - #define LTA + # define LTA if self.getOrder() == 3: LTA[j] = lta / np.power(lta1, 1.5) elif self.getOrder() == 4: @@ -296,13 +299,12 @@ class HOScf(CharacteristicFunction): nn = np.isnan(LTA) if len(nn) > 1: - LTA[nn] = 0 + LTA[nn] = 0 self.cf = LTA self.xcf = x class ARZcf(CharacteristicFunction): - def calcCF(self, data): print 'Calculating AR-prediction error from single trace ...' @@ -310,33 +312,33 @@ class ARZcf(CharacteristicFunction): xnp = x[0].data nn = np.isnan(xnp) if len(nn) > 1: - xnp[nn] = 0 - #some parameters needed - #add noise to time series + xnp[nn] = 0 + # some parameters needed + # add noise to time series xnoise = xnp + np.random.normal(0.0, 1.0, len(xnp)) * self.getFnoise() * max(abs(xnp)) tend = len(xnp) - #Time1: length of AR-determination window [sec] - #Time2: length of AR-prediction window [sec] - ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] - lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] + # Time1: length of AR-determination window [sec] + # Time2: length of AR-prediction window [sec] + ldet = int(round(self.getTime1() / self.getIncrement())) # length of AR-determination window [samples] + lpred = int(np.ceil(self.getTime2() / self.getIncrement())) # length of AR-prediction window [samples] cf = np.zeros(len(xnp)) loopstep = self.getARdetStep() - arcalci = ldet + self.getOrder() #AR-calculation index + arcalci = ldet + self.getOrder() # AR-calculation index for i in range(ldet + self.getOrder(), tend - lpred - 1): if i == arcalci: - #determination of AR coefficients - #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! - self.arDetZ(xnoise, self.getOrder(), i-ldet, i) + # determination of AR coefficients + # to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! + self.arDetZ(xnoise, self.getOrder(), i - ldet, i) arcalci = arcalci + loopstep[1] - #AR prediction of waveform using calculated AR coefficients + # AR prediction of waveform using calculated AR coefficients self.arPredZ(xnp, self.arpara, i + 1, lpred) - #prediction error = CF - cf[i + lpred-1] = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred-1] - xnp[i:i + lpred-1], 2)) / lpred) + # prediction error = CF + cf[i + lpred - 1] = np.sqrt(np.sum(np.power(self.xpred[i:i + lpred - 1] - xnp[i:i + lpred - 1], 2)) / lpred) nn = np.isnan(cf) if len(nn) > 1: - cf[nn] = 0 - #remove zeros and artefacts + cf[nn] = 0 + # remove zeros and artefacts tap = np.hanning(len(cf)) cf = tap * cf io = np.where(cf == 0) @@ -366,25 +368,25 @@ class ARZcf(CharacteristicFunction): Output: AR parameters arpara ''' - #recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) + # recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) rhs = np.zeros(self.getOrder()) for k in range(0, self.getOrder()): - for i in range(rind, ldet+1): + for i in range(rind, ldet + 1): ki = k + 1 rhs[k] = rhs[k] + data[i] * data[i - ki] - #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) - A = np.zeros((self.getOrder(),self.getOrder())) + # recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) + A = np.zeros((self.getOrder(), self.getOrder())) for k in range(1, self.getOrder() + 1): for j in range(1, k + 1): - for i in range(rind, ldet+1): + for i in range(rind, ldet + 1): ki = k - 1 ji = j - 1 - A[ki,ji] = A[ki,ji] + data[i - j] * data[i - k] + A[ki, ji] = A[ki, ji] + data[i - j] * data[i - k] - A[ji,ki] = A[ki,ji] + A[ji, ki] = A[ki, ji] - #apply Moore-Penrose inverse for SVD yielding the AR-parameters + # apply Moore-Penrose inverse for SVD yielding the AR-parameters self.arpara = np.dot(np.linalg.pinv(A), rhs) def arPredZ(self, data, arpara, rind, lpred): @@ -406,10 +408,10 @@ class ARZcf(CharacteristicFunction): Output: predicted waveform z ''' - #be sure of the summation indeces + # be sure of the summation indeces if rind < len(arpara): rind = len(arpara) - if rind > len(data) - lpred : + if rind > len(data) - lpred: rind = len(data) - lpred if lpred < 1: lpred = 1 @@ -426,7 +428,6 @@ class ARZcf(CharacteristicFunction): class ARHcf(CharacteristicFunction): - def calcCF(self, data): print 'Calculating AR-prediction error from both horizontal traces ...' @@ -434,41 +435,42 @@ class ARHcf(CharacteristicFunction): xnp = self.getDataArray(self.getCut()) n0 = np.isnan(xnp[0].data) if len(n0) > 1: - xnp[0].data[n0] = 0 + xnp[0].data[n0] = 0 n1 = np.isnan(xnp[1].data) if len(n1) > 1: - xnp[1].data[n1] = 0 + xnp[1].data[n1] = 0 - #some parameters needed - #add noise to time series + # some parameters needed + # add noise to time series xenoise = xnp[0].data + np.random.normal(0.0, 1.0, len(xnp[0].data)) * self.getFnoise() * max(abs(xnp[0].data)) xnnoise = xnp[1].data + np.random.normal(0.0, 1.0, len(xnp[1].data)) * self.getFnoise() * max(abs(xnp[1].data)) - Xnoise = np.array( [xenoise.tolist(), xnnoise.tolist()] ) + Xnoise = np.array([xenoise.tolist(), xnnoise.tolist()]) tend = len(xnp[0].data) - #Time1: length of AR-determination window [sec] - #Time2: length of AR-prediction window [sec] - ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] - lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] + # Time1: length of AR-determination window [sec] + # Time2: length of AR-prediction window [sec] + ldet = int(round(self.getTime1() / self.getIncrement())) # length of AR-determination window [samples] + lpred = int(np.ceil(self.getTime2() / self.getIncrement())) # length of AR-prediction window [samples] cf = np.zeros(len(xenoise)) loopstep = self.getARdetStep() - arcalci = lpred + self.getOrder() - 1 #AR-calculation index - #arcalci = ldet + self.getOrder() - 1 #AR-calculation index + arcalci = lpred + self.getOrder() - 1 # AR-calculation index + # arcalci = ldet + self.getOrder() - 1 #AR-calculation index for i in range(lpred + self.getOrder() - 1, tend - 2 * lpred + 1): if i == arcalci: - #determination of AR coefficients - #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! - self.arDetH(Xnoise, self.getOrder(), i-ldet, i) + # determination of AR coefficients + # to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! + self.arDetH(Xnoise, self.getOrder(), i - ldet, i) arcalci = arcalci + loopstep[1] - #AR prediction of waveform using calculated AR coefficients + # AR prediction of waveform using calculated AR coefficients self.arPredH(xnp, self.arpara, i + 1, lpred) - #prediction error = CF + # prediction error = CF cf[i + lpred] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ - + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2)) / (2 * lpred)) + + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2)) / ( + 2 * lpred)) nn = np.isnan(cf) if len(nn) > 1: - cf[nn] = 0 - #remove zeros and artefacts + cf[nn] = 0 + # remove zeros and artefacts tap = np.hanning(len(cf)) cf = tap * cf io = np.where(cf == 0) @@ -500,24 +502,24 @@ class ARHcf(CharacteristicFunction): Output: AR parameters arpara ''' - #recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) + # recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) rhs = np.zeros(self.getOrder()) for k in range(0, self.getOrder()): for i in range(rind, ldet): - rhs[k] = rhs[k] + data[0,i] * data[0,i - k] + data[1,i] * data[1,i - k] + rhs[k] = rhs[k] + data[0, i] * data[0, i - k] + data[1, i] * data[1, i - k] - #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) - A = np.zeros((4,4)) + # recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) + A = np.zeros((4, 4)) for k in range(1, self.getOrder() + 1): for j in range(1, k + 1): for i in range(rind, ldet): ki = k - 1 ji = j - 1 - A[ki,ji] = A[ki,ji] + data[0,i - ji] * data[0,i - ki] + data[1,i - ji] *data[1,i - ki] + A[ki, ji] = A[ki, ji] + data[0, i - ji] * data[0, i - ki] + data[1, i - ji] * data[1, i - ki] - A[ji,ki] = A[ki,ji] + A[ji, ki] = A[ki, ji] - #apply Moore-Penrose inverse for SVD yielding the AR-parameters + # apply Moore-Penrose inverse for SVD yielding the AR-parameters self.arpara = np.dot(np.linalg.pinv(A), rhs) def arPredH(self, data, arpara, rind, lpred): @@ -540,7 +542,7 @@ class ARHcf(CharacteristicFunction): Output: predicted waveform z :type: structured array ''' - #be sure of the summation indeces + # be sure of the summation indeces if rind < len(arpara) + 1: rind = len(arpara) + 1 if rind > len(data[0]) - lpred + 1: @@ -558,11 +560,11 @@ class ARHcf(CharacteristicFunction): z1[i] = z1[i] + arpara[ji] * z1[i - ji] z2[i] = z2[i] + arpara[ji] * z2[i - ji] - z = np.array( [z1.tolist(), z2.tolist()] ) + z = np.array([z1.tolist(), z2.tolist()]) self.xpred = z -class AR3Ccf(CharacteristicFunction): +class AR3Ccf(CharacteristicFunction): def calcCF(self, data): print 'Calculating AR-prediction error from all 3 components ...' @@ -570,46 +572,47 @@ class AR3Ccf(CharacteristicFunction): xnp = self.getDataArray(self.getCut()) n0 = np.isnan(xnp[0].data) if len(n0) > 1: - xnp[0].data[n0] = 0 + xnp[0].data[n0] = 0 n1 = np.isnan(xnp[1].data) if len(n1) > 1: - xnp[1].data[n1] = 0 + xnp[1].data[n1] = 0 n2 = np.isnan(xnp[2].data) if len(n2) > 1: - xnp[2].data[n2] = 0 + xnp[2].data[n2] = 0 - #some parameters needed - #add noise to time series + # some parameters needed + # add noise to time series xenoise = xnp[0].data + np.random.normal(0.0, 1.0, len(xnp[0].data)) * self.getFnoise() * max(abs(xnp[0].data)) xnnoise = xnp[1].data + np.random.normal(0.0, 1.0, len(xnp[1].data)) * self.getFnoise() * max(abs(xnp[1].data)) xznoise = xnp[2].data + np.random.normal(0.0, 1.0, len(xnp[2].data)) * self.getFnoise() * max(abs(xnp[2].data)) - Xnoise = np.array( [xenoise.tolist(), xnnoise.tolist(), xznoise.tolist()] ) + Xnoise = np.array([xenoise.tolist(), xnnoise.tolist(), xznoise.tolist()]) tend = len(xnp[0].data) - #Time1: length of AR-determination window [sec] - #Time2: length of AR-prediction window [sec] - ldet = int(round(self.getTime1() / self.getIncrement())) #length of AR-determination window [samples] - lpred = int(np.ceil(self.getTime2() / self.getIncrement())) #length of AR-prediction window [samples] + # Time1: length of AR-determination window [sec] + # Time2: length of AR-prediction window [sec] + ldet = int(round(self.getTime1() / self.getIncrement())) # length of AR-determination window [samples] + lpred = int(np.ceil(self.getTime2() / self.getIncrement())) # length of AR-prediction window [samples] cf = np.zeros(len(xenoise)) loopstep = self.getARdetStep() - arcalci = ldet + self.getOrder() - 1 #AR-calculation index + arcalci = ldet + self.getOrder() - 1 # AR-calculation index for i in range(ldet + self.getOrder() - 1, tend - 2 * lpred + 1): if i == arcalci: - #determination of AR coefficients - #to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! - self.arDet3C(Xnoise, self.getOrder(), i-ldet, i) + # determination of AR coefficients + # to speed up calculation, AR-coefficients are calculated only every i+loopstep[1]! + self.arDet3C(Xnoise, self.getOrder(), i - ldet, i) arcalci = arcalci + loopstep[1] - #AR prediction of waveform using calculated AR coefficients + # AR prediction of waveform using calculated AR coefficients self.arPred3C(xnp, self.arpara, i + 1, lpred) - #prediction error = CF + # prediction error = CF cf[i + lpred] = np.sqrt(np.sum(np.power(self.xpred[0][i:i + lpred] - xnp[0][i:i + lpred], 2) \ - + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2) \ - + np.power(self.xpred[2][i:i + lpred] - xnp[2][i:i + lpred], 2)) / (3 * lpred)) + + np.power(self.xpred[1][i:i + lpred] - xnp[1][i:i + lpred], 2) \ + + np.power(self.xpred[2][i:i + lpred] - xnp[2][i:i + lpred], 2)) / ( + 3 * lpred)) nn = np.isnan(cf) if len(nn) > 1: - cf[nn] = 0 - #remove zeros and artefacts + cf[nn] = 0 + # remove zeros and artefacts tap = np.hanning(len(cf)) cf = tap * cf io = np.where(cf == 0) @@ -641,26 +644,26 @@ class AR3Ccf(CharacteristicFunction): Output: AR parameters arpara ''' - #recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) + # recursive calculation of data vector (right part of eq. 6.5 in Kueperkoch et al. (2012) rhs = np.zeros(self.getOrder()) for k in range(0, self.getOrder()): for i in range(rind, ldet): - rhs[k] = rhs[k] + data[0,i] * data[0,i - k] + data[1,i] * data[1,i - k] \ - + data[2,i] * data[2,i - k] + rhs[k] = rhs[k] + data[0, i] * data[0, i - k] + data[1, i] * data[1, i - k] \ + + data[2, i] * data[2, i - k] - #recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) - A = np.zeros((4,4)) + # recursive calculation of data array (second sum at left part of eq. 6.5 in Kueperkoch et al. 2012) + A = np.zeros((4, 4)) for k in range(1, self.getOrder() + 1): for j in range(1, k + 1): for i in range(rind, ldet): ki = k - 1 ji = j - 1 - A[ki,ji] = A[ki,ji] + data[0,i - ji] * data[0,i - ki] + data[1,i - ji] *data[1,i - ki] \ - + data[2,i - ji] *data[2,i - ki] + A[ki, ji] = A[ki, ji] + data[0, i - ji] * data[0, i - ki] + data[1, i - ji] * data[1, i - ki] \ + + data[2, i - ji] * data[2, i - ki] - A[ji,ki] = A[ki,ji] + A[ji, ki] = A[ki, ji] - #apply Moore-Penrose inverse for SVD yielding the AR-parameters + # apply Moore-Penrose inverse for SVD yielding the AR-parameters self.arpara = np.dot(np.linalg.pinv(A), rhs) def arPred3C(self, data, arpara, rind, lpred): @@ -683,7 +686,7 @@ class AR3Ccf(CharacteristicFunction): Output: predicted waveform z :type: structured array ''' - #be sure of the summation indeces + # be sure of the summation indeces if rind < len(arpara) + 1: rind = len(arpara) + 1 if rind > len(data[0]) - lpred + 1: @@ -703,5 +706,5 @@ class AR3Ccf(CharacteristicFunction): z2[i] = z2[i] + arpara[ji] * z2[i - ji] z3[i] = z3[i] + arpara[ji] * z3[i - ji] - z = np.array( [z1.tolist(), z2.tolist(), z3.tolist()] ) + z = np.array([z1.tolist(), z2.tolist(), z3.tolist()]) self.xpred = z diff --git a/pylot/core/pick/picker.py b/pylot/core/pick/picker.py index da91d833..cc6ee7d9 100644 --- a/pylot/core/pick/picker.py +++ b/pylot/core/pick/picker.py @@ -25,6 +25,7 @@ from pylot.core.pick.utils import getnoisewin, getsignalwin from pylot.core.pick.charfuns import CharacteristicFunction import warnings + class AutoPicker(object): ''' Superclass of different, automated picking algorithms applied on a CF determined @@ -87,7 +88,6 @@ class AutoPicker(object): Tsmooth=self.getTsmooth(), Pick1=self.getpick1()) - def getTSNR(self): return self.TSNR @@ -152,14 +152,14 @@ class AICPicker(AutoPicker): self.Pick = None self.slope = None self.SNR = None - #find NaN's + # find NaN's nn = np.isnan(self.cf) if len(nn) > 1: self.cf[nn] = 0 - #taper AIC-CF to get rid off side maxima + # taper AIC-CF to get rid off side maxima tap = np.hanning(len(self.cf)) aic = tap * self.cf + max(abs(self.cf)) - #smooth AIC-CF + # smooth AIC-CF ismooth = int(round(self.Tsmooth / self.dt)) aicsmooth = np.zeros(len(aic)) if len(aic) < ismooth: @@ -171,32 +171,32 @@ class AICPicker(AutoPicker): ii1 = i - ismooth aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth else: - aicsmooth[i] = np.mean(aic[1 : i]) - #remove offset + aicsmooth[i] = np.mean(aic[1: i]) + # remove offset offset = abs(min(aic) - min(aicsmooth)) aicsmooth = aicsmooth - offset - #get maximum of 1st derivative of AIC-CF (more stable!) as starting point + # get maximum of 1st derivative of AIC-CF (more stable!) as starting point diffcf = np.diff(aicsmooth) - #find NaN's + # find NaN's nn = np.isnan(diffcf) if len(nn) > 1: diffcf[nn] = 0 - #taper CF to get rid off side maxima + # taper CF to get rid off side maxima tap = np.hanning(len(diffcf)) diffcf = tap * diffcf * max(abs(aicsmooth)) icfmax = np.argmax(diffcf) - #find minimum in AIC-CF front of maximum + # find minimum in AIC-CF front of maximum lpickwindow = int(round(self.PickWindow / self.dt)) for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): if aicsmooth[i - 1] >= aicsmooth[i]: self.Pick = self.Tcf[i] break - #if no minimum could be found: - #search in 1st derivative of AIC-CF + # if no minimum could be found: + # search in 1st derivative of AIC-CF if self.Pick is None: - for i in range(icfmax -1, max([icfmax -lpickwindow, 2]), -1): - if diffcf[i -1] >= diffcf[i]: + for i in range(icfmax - 1, max([icfmax - lpickwindow, 2]), -1): + if diffcf[i - 1] >= diffcf[i]: self.Pick = self.Tcf[i] break @@ -215,7 +215,7 @@ class AICPicker(AutoPicker): max(abs(aic[inoise] - np.mean(aic[inoise]))) # calculate slope from CF after initial pick # get slope window - tslope = self.TSNR[3] #slope determination window + tslope = self.TSNR[3] # slope determination window islope = np.where((self.Tcf <= min([self.Pick + tslope, len(self.Data[0].data)])) \ & (self.Tcf >= self.Pick)) # find maximum within slope determination window @@ -237,7 +237,7 @@ class AICPicker(AutoPicker): raw_input() plt.close(p) return - islope = islope[0][0 :imax] + islope = islope[0][0:imax] dataslope = self.Data[0].data[islope] # calculate slope as polynomal fit of order 1 xslope = np.arange(0, len(dataslope), 1) @@ -258,7 +258,7 @@ class AICPicker(AutoPicker): p1, = plt.plot(self.Tcf, x / max(x), 'k') p2, = plt.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r') if self.Pick is not None: - p3, = plt.plot([self.Pick, self.Pick], [-0.1 , 0.5], 'b', linewidth=2) + p3, = plt.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', linewidth=2) plt.legend([p1, p2, p3], ['(HOS-/AR-) Data', 'Smoothed AIC-CF', 'AIC-Pick']) else: plt.legend([p1, p2], ['(HOS-/AR-) Data', 'Smoothed AIC-CF']) @@ -273,7 +273,8 @@ class AICPicker(AutoPicker): p13, = plt.plot(self.Tcf[isignal], self.Data[0].data[isignal], 'r') p14, = plt.plot(self.Tcf[islope], dataslope, 'g--') p15, = plt.plot(self.Tcf[islope], datafit, 'g', linewidth=2) - plt.legend([p11, p12, p13, p14, p15], ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], + plt.legend([p11, p12, p13, p14, p15], + ['Data', 'Noise Window', 'Signal Window', 'Slope Window', 'Slope'], loc='best') plt.title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station, self.SNR, self.slope)) @@ -303,7 +304,7 @@ class PragPicker(AutoPicker): self.SNR = None self.slope = None pickflag = 0 - #smooth CF + # smooth CF ismooth = int(round(self.Tsmooth / self.dt)) cfsmooth = np.zeros(len(self.cf)) if len(self.cf) < ismooth: @@ -315,28 +316,28 @@ class PragPicker(AutoPicker): ii1 = i - ismooth cfsmooth[i] = cfsmooth[i - 1] + (self.cf[i] - self.cf[ii1]) / ismooth else: - cfsmooth[i] = np.mean(self.cf[1 : i]) + cfsmooth[i] = np.mean(self.cf[1: i]) - #select picking window - #which is centered around tpick1 + # select picking window + # which is centered around tpick1 ipick = np.where((self.Tcf >= self.getpick1() - self.PickWindow / 2) \ & (self.Tcf <= self.getpick1() + self.PickWindow / 2)) cfipick = self.cf[ipick] - np.mean(self.cf[ipick]) Tcfpick = self.Tcf[ipick] - cfsmoothipick = cfsmooth[ipick]- np.mean(self.cf[ipick]) + cfsmoothipick = cfsmooth[ipick] - np.mean(self.cf[ipick]) ipick1 = np.argmin(abs(self.Tcf - self.getpick1())) cfpick1 = 2 * self.cf[ipick1] - #check trend of CF, i.e. differences of CF and adjust aus regarding this trend - #prominent trend: decrease aus - #flat: use given aus + # check trend of CF, i.e. differences of CF and adjust aus regarding this trend + # prominent trend: decrease aus + # flat: use given aus cfdiff = np.diff(cfipick) i0diff = np.where(cfdiff > 0) cfdiff = cfdiff[i0diff] minaus = min(cfdiff * (1 + self.aus)) aus1 = max([minaus, self.aus]) - #at first we look to the right until the end of the pick window is reached + # at first we look to the right until the end of the pick window is reached flagpick_r = 0 flagpick_l = 0 cfpick_r = 0 @@ -380,8 +381,8 @@ class PragPicker(AutoPicker): if self.getiplot() > 1: p = plt.figure(self.getiplot()) - p1, = plt.plot(Tcfpick,cfipick, 'k') - p2, = plt.plot(Tcfpick,cfsmoothipick, 'r') + p1, = plt.plot(Tcfpick, cfipick, 'k') + p2, = plt.plot(Tcfpick, cfsmoothipick, 'r') if pickflag > 0: p3, = plt.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], 'b', linewidth=2) plt.legend([p1, p2, p3], ['CF', 'Smoothed CF', 'Pick']) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 2044b388..9f9ef832 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -15,7 +15,7 @@ from obspy.core import Stream, UTCDateTime import warnings -def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): +def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode=False): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) as reasonable uncertainties. Latest possible pick is based on noise level, @@ -70,7 +70,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): # get earliest possible pick - EPick = np.nan; count = 0 + EPick = np.nan; + count = 0 pis = isignal # if EPick stays NaN the signal window size will be doubled @@ -78,10 +79,10 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): if count > 0: if stealthMode is False: print("\nearllatepicker: Doubled signal window size %s time(s) " - "because of NaN for earliest pick." %count) + "because of NaN for earliest pick." % count) isigDoubleWinStart = pis[-1] + 1 isignalDoubleWin = np.arange(isigDoubleWinStart, - isigDoubleWinStart + len(pis)) + isigDoubleWinStart + len(pis)) if (isigDoubleWinStart + len(pis)) < X[0].data.size: pis = np.concatenate((pis, isignalDoubleWin)) else: @@ -92,8 +93,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode = False): zc = crossings_nonzero_all(x[pis] - x[pis].mean()) # calculate mean half period T0 of signal as the average of the T0 = np.mean(np.diff(zc)) * X[0].stats.delta # this is half wave length! - EPick = Pick1 - T0 # half wavelength as suggested by Diehl et al. - + EPick = Pick1 - T0 # half wavelength as suggested by Diehl et al. # get symmetric pick error as mean from earliest and latest possible pick # by weighting latest possible pick two times earliest possible pick @@ -395,7 +395,7 @@ def getnoisewin(t, t1, tnoise, tgap): # get noise window inoise, = np.where((t <= max([t1 - tgap, 0])) \ - & (t >= max([t1 - tnoise - tgap, 0]))) + & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: print ("getnoisewin: Empty array inoise, check noise window!") @@ -419,7 +419,7 @@ def getsignalwin(t, t1, tsignal): # get signal window isignal, = np.where((t <= min([t1 + tsignal, len(t)])) \ - & (t >= t1)) + & (t >= t1)) if np.size(isignal) < 1: print ("getsignalwin: Empty array isignal, check signal window!") @@ -460,7 +460,7 @@ def getResolutionWindow(snr): else: time_resolution = res_wins['HRW'] - return time_resolution/2 + return time_resolution / 2 def wadaticheck(pickdic, dttolerance, iplot): @@ -488,17 +488,16 @@ def wadaticheck(pickdic, dttolerance, iplot): SPtimes = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: - # calculate S-P time - spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] - # add S-P time to dictionary - pickdic[key]['SPt'] = spt - # add P onsets and corresponding S-P times to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) - Ppicks.append(UTCPpick.timestamp) - Spicks.append(UTCSpick.timestamp) - SPtimes.append(spt) - + # calculate S-P time + spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] + # add S-P time to dictionary + pickdic[key]['SPt'] = spt + # add P onsets and corresponding S-P times to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) + Ppicks.append(UTCPpick.timestamp) + Spicks.append(UTCSpick.timestamp) + SPtimes.append(spt) if len(SPtimes) >= 3: # calculate slope @@ -530,7 +529,7 @@ def wadaticheck(pickdic, dttolerance, iplot): ibad += 1 else: marker = 'goodWadatiCheck' - checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) + checkedPpick = UTCDateTime(pickdic[key]['P']['mpp']) checkedPpicks.append(checkedPpick.timestamp) checkedSpick = UTCDateTime(pickdic[key]['S']['mpp']) checkedSpicks.append(checkedSpick.timestamp) @@ -642,7 +641,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): # calculate minimum adjusted signal level minsiglevel = max(rms[inoise]) * nfac # minimum adjusted number of samples over minimum signal level - minnum = len(isignal) * minpercent/100 + minnum = len(isignal) * minpercent / 100 # get number of samples above minimum adjusted signal level numoverthr = len(np.where(rms[isignal] >= minsiglevel)[0]) @@ -657,10 +656,10 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot): if iplot == 2: plt.figure(iplot) - p1, = plt.plot(t,rms, 'k') + p1, = plt.plot(t, rms, 'k') p2, = plt.plot(t[inoise], rms[inoise], 'c') - p3, = plt.plot(t[isignal],rms[isignal], 'r') - p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal)-1]]], + p3, = plt.plot(t[isignal], rms[isignal], 'r') + p4, = plt.plot([t[isignal[0]], t[isignal[len(isignal) - 1]]], [minsiglevel, minsiglevel], 'g', linewidth=2) p5, = plt.plot([pick, pick], [min(rms), max(rms)], 'b', linewidth=2) plt.legend([p1, p2, p3, p4, p5], ['RMS Data', 'RMS Noise Window', @@ -701,15 +700,15 @@ def checkPonsets(pickdic, dttolerance, iplot): stations = [] for key in pickdic: if pickdic[key]['P']['weight'] < 4: - # add P onsets to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) - Ppicks.append(UTCPpick.timestamp) - stations.append(key) + # add P onsets to list + UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + Ppicks.append(UTCPpick.timestamp) + stations.append(key) # apply jackknife bootstrapping on variance of P onsets print ("###############################################") print ("checkPonsets: Apply jackknife bootstrapping on P-onset times ...") - [xjack,PHI_pseudo,PHI_sub] = jackknife(Ppicks, 'VAR', 1) + [xjack, PHI_pseudo, PHI_sub] = jackknife(Ppicks, 'VAR', 1) # get pseudo variances smaller than average variances # (times safety factor), these picks passed jackknife test ij = np.where(PHI_pseudo <= 2 * xjack) @@ -730,7 +729,7 @@ def checkPonsets(pickdic, dttolerance, iplot): print ("checkPonsets: %d pick(s) deviate too much from median!" % len(ibad)) print ("checkPonsets: Skipped %d P pick(s) out of %d" % (len(badstations) \ - + len(badjkstations), len(stations))) + + len(badjkstations), len(stations))) goodmarker = 'goodPonsetcheck' badmarker = 'badPonsetcheck' @@ -881,10 +880,9 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): if len(ndat) == 0: # check for other components ndat = X.select(component="1") - z = zdat[0].data tz = np.arange(0, zdat[0].stats.npts / zdat[0].stats.sampling_rate, - zdat[0].stats.delta) + zdat[0].stats.delta) # calculate RMS trace from vertical component absz = np.sqrt(np.power(z, 2)) @@ -916,9 +914,9 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): if iplot > 1: te = np.arange(0, edat[0].stats.npts / edat[0].stats.sampling_rate, - edat[0].stats.delta) + edat[0].stats.delta) tn = np.arange(0, ndat[0].stats.npts / ndat[0].stats.sampling_rate, - ndat[0].stats.delta) + ndat[0].stats.delta) plt.plot(tz, z / max(z), 'k') plt.plot(tz[isignal], z[isignal] / max(z), 'r') plt.plot(te, edat[0].data / max(edat[0].data) + 1, 'k') @@ -960,65 +958,64 @@ def writephases(arrivals, fformat, filename): :type: string ''' - if fformat == 'NLLoc': print ("Writing phases to %s for NLLoc" % filename) fid = open("%s" % filename, 'w') # write header fid.write('# EQEVENT: Label: EQ001 Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n') for key in arrivals: - # P onsets - if arrivals[key]['P']: - fm = arrivals[key]['P']['fm'] - if fm == None: - fm = '?' - onset = arrivals[key]['P']['mpp'] - year = onset.year - month = onset.month - day = onset.day - hh = onset.hour - mm = onset.minute - ss = onset.second - ms = onset.microsecond - ss_ms = ss + ms / 1000000.0 - if arrivals[key]['P']['weight'] < 4: - pweight = 1 # use pick - else: - pweight = 0 # do not use pick - fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, - fm, - year, - month, - day, - hh, - mm, - ss_ms, - pweight)) - # S onsets - if arrivals[key]['S']: - fm = '?' - onset = arrivals[key]['S']['mpp'] - year = onset.year - month = onset.month - day = onset.day - hh = onset.hour - mm = onset.minute - ss = onset.second - ms = onset.microsecond - ss_ms = ss + ms / 1000000.0 - if arrivals[key]['S']['weight'] < 4: - sweight = 1 # use pick - else: - sweight = 0 # do not use pick - fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, - fm, - year, - month, - day, - hh, - mm, - ss_ms, - sweight)) + # P onsets + if arrivals[key]['P']: + fm = arrivals[key]['P']['fm'] + if fm == None: + fm = '?' + onset = arrivals[key]['P']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + if arrivals[key]['P']['weight'] < 4: + pweight = 1 # use pick + else: + pweight = 0 # do not use pick + fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms, + pweight)) + # S onsets + if arrivals[key]['S']: + fm = '?' + onset = arrivals[key]['S']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + if arrivals[key]['S']['weight'] < 4: + sweight = 1 # use pick + else: + sweight = 0 # do not use pick + fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms, + sweight)) fid.close() @@ -1043,9 +1040,9 @@ def writephases(arrivals, fformat, filename): Ao = str('%7.2f' % Ao) year = Ponset.year if year >= 2000: - year = year -2000 + year = year - 2000 else: - year = year - 1900 + year = year - 1900 month = Ponset.month day = Ponset.day hh = Ponset.hour @@ -1054,9 +1051,9 @@ def writephases(arrivals, fformat, filename): ms = Ponset.microsecond ss_ms = ss + ms / 1000000.0 if pweight < 2: - pstr = 'I' + pstr = 'I' elif pweight >= 2: - pstr = 'E' + pstr = 'E' if arrivals[key]['S']['weight'] < 4: Sss = Sonset.second Sms = Sonset.microsecond @@ -1067,35 +1064,36 @@ def writephases(arrivals, fformat, filename): elif sweight >= 2: sstr = 'E' fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s%sS %d %s\n' % (key, - pstr, - fm, - pweight, - year, - month, - day, - hh, - mm, - ss_ms, - Sss_ms, - sstr, - sweight, - Ao)) + pstr, + fm, + pweight, + year, + month, + day, + hh, + mm, + ss_ms, + Sss_ms, + sstr, + sweight, + Ao)) else: fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s\n' % (key, - pstr, - fm, - pweight, - year, - month, - day, - hh, - mm, - ss_ms, - Ao)) + pstr, + fm, + pweight, + year, + month, + day, + hh, + mm, + ss_ms, + Ao)) fid.close() if __name__ == '__main__': import doctest + doctest.testmod() diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index e59e69f1..d2d466c4 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -81,7 +81,6 @@ class Data(object): picks_str += str(pick) + '\n' return picks_str - def getParent(self): """ diff --git a/pylot/core/read/inputs.py b/pylot/core/read/inputs.py index d4b30a49..035835f7 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/read/inputs.py @@ -3,6 +3,7 @@ from pylot.core.util.errors import ParameterError + class AutoPickParameter(object): ''' AutoPickParameters is a parameter type object capable to read and/or write @@ -50,7 +51,7 @@ class AutoPickParameter(object): parFileCont[key] = val if self.__filename is not None: - inputFile = open(self.__filename, 'r') + inputFile = open(self.__filename, 'r') else: return try: @@ -148,7 +149,7 @@ class AutoPickParameter(object): def setParam(self, **kwargs): for param, value in kwargs.items(): self.__setitem__(param, value) - #print(self) + # print(self) @staticmethod def _printParameterError(errmsg): @@ -193,6 +194,7 @@ class FilterOptions(object): ``'highpass'`` Butterworth-Highpass ''' + def __init__(self, filtertype='bandpass', freq=[2., 5.], order=3, **kwargs): self._order = order diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index 675aa150..cd9b2149 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -7,9 +7,10 @@ import scipy.io as sio import obspy.core.event as ope from obspy.core import UTCDateTime -from pylot.core.util.utils import getOwner, createPick, createArrival,\ +from pylot.core.util.utils import getOwner, createPick, createArrival, \ createEvent, createOrigin, createMagnitude + def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): """ readPILOTEvent - function @@ -133,5 +134,3 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): except AttributeError as e: raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ insufficient data!'.format(e, phasfn, locfn)) - - diff --git a/pylot/core/scripts/pylot-pick-earliest-latest.py b/pylot/core/scripts/pylot-pick-earliest-latest.py index ab241880..b17efd01 100755 --- a/pylot/core/scripts/pylot-pick-earliest-latest.py +++ b/pylot/core/scripts/pylot-pick-earliest-latest.py @@ -14,11 +14,12 @@ import argparse import obspy from pylot.core.pick.utils import earllatepicker - if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') + parser.add_argument('--X', type=~obspy.core.stream.Stream, + help='time series (seismogram) read with obspy module read') + parser.add_argument('--nfac', type=int, + help='(noise factor), nfac times noise level to calculate latest possible pick') parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ [s] (Tnoise, Tgap, Tsignal)') parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') diff --git a/pylot/core/scripts/pylot-pick-firstmotion.py b/pylot/core/scripts/pylot-pick-firstmotion.py index 2952480e..a62d7bb4 100755 --- a/pylot/core/scripts/pylot-pick-firstmotion.py +++ b/pylot/core/scripts/pylot-pick-firstmotion.py @@ -13,11 +13,12 @@ from pylot.core.pick.utils import fmpicker if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--Xraw', type=obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') - parser.add_argument('--Xfilt', type=obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') + parser.add_argument('--Xraw', type=obspy.core.stream.Stream, + help='unfiltered time series (seismogram) read with obspy module read') + parser.add_argument('--Xfilt', type=obspy.core.stream.Stream, + help='filtered time series (seismogram) read with obspy module read') parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') args = parser.parse_args() fmpicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) - diff --git a/pylot/core/pick/run_makeCF.py b/pylot/core/scripts/run_makeCF.py similarity index 100% rename from pylot/core/pick/run_makeCF.py rename to pylot/core/scripts/run_makeCF.py diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index 523ff579..c74b1332 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -11,6 +11,7 @@ from pylot.core.loc import nll from pylot.core.loc import hsat from pylot.core.loc import velest + def readFilterInformation(fname): def convert2FreqRange(*args): if len(args) > 1: @@ -18,6 +19,7 @@ def readFilterInformation(fname): elif len(args) == 1: return float(args[0]) return None + filter_file = open(fname, 'r') filter_information = dict() for filter_line in filter_file.readlines(): @@ -26,14 +28,14 @@ def readFilterInformation(fname): if pos == '\n': filter_line[n] = '' filter_information[filter_line[0]] = {'filtertype': filter_line[1] - if filter_line[1] - else None, + if filter_line[1] + else None, 'order': int(filter_line[2]) - if filter_line[1] - else None, + if filter_line[1] + else None, 'freq': convert2FreqRange(*filter_line[3:]) - if filter_line[1] - else None} + if filter_line[1] + else None} return filter_information @@ -41,15 +43,15 @@ FILTERDEFAULTS = readFilterInformation(os.path.join(os.path.expanduser('~'), '.pylot', 'filter.in')) -OUTPUTFORMATS = {'.xml':'QUAKEML', - '.cnv':'CNV', - '.obs':'NLLOC_OBS'} +OUTPUTFORMATS = {'.xml': 'QUAKEML', + '.cnv': 'CNV', + '.obs': 'NLLOC_OBS'} -LOCTOOLS = dict(nll = nll, hsat = hsat, velest = velest) +LOCTOOLS = dict(nll=nll, hsat=hsat, velest=velest) -COMPPOSITION_MAP = dict(Z = 2, N = 1, E = 0) +COMPPOSITION_MAP = dict(Z=2, N=1, E=0) COMPPOSITION_MAP['1'] = 1 COMPPOSITION_MAP['2'] = 0 COMPPOSITION_MAP['3'] = 2 -COMPNAME_MAP = dict(Z = '3', N = '1', E = '2') +COMPNAME_MAP = dict(Z='3', N='1', E='2') diff --git a/pylot/core/util/errors.py b/pylot/core/util/errors.py index 8b28a348..a6015e55 100644 --- a/pylot/core/util/errors.py +++ b/pylot/core/util/errors.py @@ -21,5 +21,6 @@ class DatastructureError(Exception): class OverwriteError(IOError): pass + class ParameterError(Exception): - pass \ No newline at end of file + pass diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index a4a47715..a9f5e7f6 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -2,6 +2,7 @@ import sys from PySide.QtCore import QThread, Signal + class AutoPickThread(QThread): message = Signal(str) finished = Signal() @@ -28,6 +29,5 @@ class AutoPickThread(QThread): sys.stdout = sys.__stdout__ self.finished.emit() - def write(self, text): self.message.emit(text) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 0f72fdf6..c0eb0185 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,6 +10,7 @@ import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope + def createAmplitude(pickID, amp, unit, category, cinfo): ''' @@ -28,6 +29,7 @@ def createAmplitude(pickID, amp, unit, category, cinfo): amplitude.pick_id = pickID return amplitude + def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): ''' createArrival - function to create an Obspy Arrival @@ -56,6 +58,7 @@ def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): arrival.distance = dist return arrival + def createCreationInfo(agency_id=None, creation_time=None, author=None): ''' @@ -71,6 +74,7 @@ def createCreationInfo(agency_id=None, creation_time=None, author=None): return ope.CreationInfo(agency_id=agency_id, author=author, creation_time=creation_time) + def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, authority_id=None): ''' @@ -115,6 +119,7 @@ def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, event.origins = [o] return event + def createMagnitude(originID, cinfo): ''' createMagnitude - function to create an ObsPy Magnitude object @@ -129,6 +134,7 @@ def createMagnitude(originID, cinfo): magnitude.origin_id = originID return magnitude + def createOrigin(origintime, cinfo, latitude, longitude, depth): ''' createOrigin - function to create an ObsPy Origin @@ -158,6 +164,7 @@ def createOrigin(origintime, cinfo, latitude, longitude, depth): origin.depth = depth return origin + def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, wfseedstr, authority_id): ''' @@ -196,6 +203,7 @@ def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') return pick + def createResourceID(timetohash, restype, authority_id=None, hrstr=None): ''' @@ -220,6 +228,7 @@ def createResourceID(timetohash, restype, authority_id=None, hrstr=None): resID.convertIDToQuakeMLURI(authority_id=authority_id) return resID + def demeanTrace(trace, window): """ returns the DATA where each trace is demean by the average value within @@ -234,6 +243,7 @@ def demeanTrace(trace, window): trace.data -= trace.data[window].mean() return trace + def findComboBoxIndex(combo_box, val): """ Function findComboBoxIndex takes a QComboBox object and a string and @@ -246,6 +256,7 @@ def findComboBoxIndex(combo_box, val): """ return combo_box.findText(val) if combo_box.findText(val) is not -1 else 0 + def find_nearest(array, value): ''' Function find_nearest takes an array and a value and returns the @@ -254,7 +265,8 @@ def find_nearest(array, value): :param value: :return: ''' - return (np.abs(array-value)).argmin() + return (np.abs(array - value)).argmin() + def fnConstructor(s): ''' @@ -277,6 +289,7 @@ def fnConstructor(s): fn = '_' + fn return fn + def getGlobalTimes(stream): ''' @@ -293,6 +306,7 @@ def getGlobalTimes(stream): max_end = trace.stats.endtime return min_start, max_end + def getHash(time): ''' :param time: time object for which a hash should be calculated @@ -303,6 +317,7 @@ def getHash(time): hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f')) return hg.hexdigest() + def getLogin(): ''' @@ -310,6 +325,7 @@ def getLogin(): ''' return pwd.getpwuid(os.getuid())[0] + def getOwner(fn): ''' @@ -319,6 +335,7 @@ def getOwner(fn): ''' return pwd.getpwuid(os.stat(fn).st_uid).pw_name + def getPatternLine(fn, pattern): """ Takes a file name and a pattern string to search for in the file and @@ -343,6 +360,7 @@ def getPatternLine(fn, pattern): return None + def isSorted(iterable): ''' @@ -352,6 +370,7 @@ def isSorted(iterable): ''' return sorted(iterable) == iterable + def prepTimeAxis(stime, trace): ''' @@ -378,6 +397,7 @@ def prepTimeAxis(stime, trace): 'delta: {2}'.format(nsamp, len(time_ax), tincr)) return time_ax + def scaleWFData(data, factor=None, components='all'): """ produce scaled waveforms from given waveform data and a scaling factor, @@ -409,6 +429,7 @@ def scaleWFData(data, factor=None, components='all'): return data + def runProgram(cmd, parameter=None): """ run an external program specified by cmd with parameters input returning the @@ -427,8 +448,10 @@ def runProgram(cmd, parameter=None): cmd += ' %s 2>&1' % parameter output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), - shell = True) + shell=True) + if __name__ == "__main__": import doctest + doctest.testmod() diff --git a/pylot/core/util/version.py b/pylot/core/util/version.py index fd298f3e..c4006c12 100755 --- a/pylot/core/util/version.py +++ b/pylot/core/util/version.py @@ -31,16 +31,19 @@ # # include RELEASE-VERSION +from __future__ import print_function + __all__ = "get_git_version" # NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time) import os import inspect from subprocess import Popen, PIPE + # NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time) script_dir = os.path.abspath(os.path.dirname(inspect.getfile( - inspect.currentframe()))) + inspect.currentframe()))) PYLOT_ROOT = os.path.abspath(os.path.join(script_dir, os.pardir, os.pardir, os.pardir)) VERSION_FILE = os.path.join(PYLOT_ROOT, "pylot", "RELEASE-VERSION") @@ -108,4 +111,4 @@ def get_git_version(abbrev=4): if __name__ == "__main__": - print get_git_version() + print(get_git_version()) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 99236cc3..baf7a6f6 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -9,6 +9,7 @@ import datetime import numpy as np from matplotlib.figure import Figure + try: from matplotlib.backends.backend_qt4agg import FigureCanvas except ImportError: @@ -23,9 +24,9 @@ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime from pylot.core.read.inputs import FilterOptions -from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin,\ +from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \ getResolutionWindow -from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS,\ +from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS, \ COMPPOSITION_MAP from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ demeanTrace, isSorted, findComboBoxIndex @@ -164,9 +165,10 @@ class MPLWidget(FigureCanvas): def insertLabel(self, pos, text): pos = pos / max(self.getAxes().ylim) axann = self.getAxes().annotate(text, xy=(.03, pos), - xycoords='axes fraction') + xycoords='axes fraction') axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) + class PickDlg(QDialog): def __init__(self, parent=None, data=None, station=None, picks=None, rotate=False): @@ -263,8 +265,8 @@ class PickDlg(QDialog): tip='Zoom into waveform', checkable=True) self.resetZoomAction = createAction(parent=self, text='Home', - slot=self.resetZoom, icon=home_icon, - tip='Reset zoom to original limits') + slot=self.resetZoom, icon=home_icon, + tip='Reset zoom to original limits') self.resetPicksAction = createAction(parent=self, text='Delete Picks', slot=self.delPicks, icon=del_icon, tip='Delete current picks.') @@ -516,7 +518,6 @@ class PickDlg(QDialog): inoise = getnoisewin(t, ini_pick, noise_win, gap_win) trace = demeanTrace(trace=trace, window=inoise) - self.setXLims([ini_pick - x_res, ini_pick + x_res]) self.setYLims(np.array([-noiselevel * 2.5, noiselevel * 2.5]) + trace_number) @@ -575,8 +576,8 @@ class PickDlg(QDialog): traces = self.getTraceID(horiz_comp) traces.sort() self.setYLims(tuple(np.array([-0.5, +0.5]) + - np.array(traces))) - noiselevels = [trace + 1 / (2.5 * 2) for trace in traces] +\ + np.array(traces))) + noiselevels = [trace + 1 / (2.5 * 2) for trace in traces] + \ [trace - 1 / (2.5 * 2) for trace in traces] self.getPlotWidget().plotWFData(wfdata=data, @@ -757,7 +758,6 @@ class PickDlg(QDialog): self.drawPicks() self.draw() - def setPlotLabels(self): # get channel labels @@ -1041,7 +1041,7 @@ class LocalisationTab(PropTab): self.binlabel.setText("{0} bin directory".format(curtool)) def selectDirectory(self, edit): - selected_directory = QFileDialog.getExistingDirectory() + selected_directory = QFileDialog.getExistingDirectory() edit.setText(selected_directory) def getValues(self): @@ -1052,7 +1052,6 @@ class LocalisationTab(PropTab): return values - class NewEventDlg(QDialog): def __init__(self, parent=None, titleString="Create a new event"): """ @@ -1293,6 +1292,8 @@ class HelpForm(QDialog): def updatePageTitle(self): self.pageLabel.setText(self.webBrowser.documentTitle()) + if __name__ == '__main__': import doctest + doctest.testmod() From 7a712ca37c422ad793abb13aba36d7d671e5ceba Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 30 Mar 2016 09:12:56 +0200 Subject: [PATCH 0853/1144] [fixes #194] automatic filtering works also when switching phase selection Switching the phase selection without picking the last onset did not filter according to the selected phase filter parameter. With this revision a displaying bug showing muliple '( filtered, filtered, filtered)' statements due to switching the phases is fixed as well. --- pylot/core/util/widgets.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index baf7a6f6..fcbd0ca5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -5,6 +5,7 @@ Created on Wed Mar 19 11:27:35 2014 @author: sebastianw """ +import warnings import datetime import numpy as np @@ -348,6 +349,10 @@ class PickDlg(QDialog): return widget.mpl_connect('button_release_event', slot) def verifyPhaseSelection(self): + if self.pick_block: + self.pick_block = self.togglePickBlocker() + warnings.warn('Changed selection before phase was set!', + UserWarning) phase = self.selectPhase.currentText() self.updateCurrentLimits() if phase: @@ -731,13 +736,15 @@ class PickDlg(QDialog): filtoptions = filtoptions.parseFilterOptions() if filtoptions is not None: data.filter(**filtoptions) - if old_title.endswith(')'): - title = old_title[:-1] + ', filtered)' - else: + if not old_title.endswith(')'): title = old_title + ' (filtered)' + elif not old_title.endswith(' (filtered)') and not old_title.endswith(', filtered)'): + title = old_title[:-1] + ', filtered)' else: if old_title.endswith(' (filtered)'): title = old_title.replace(' (filtered)', '') + elif old_title.endswith(', filtered)'): + title = old_title.replace(', filtered)', ')') if title is None: title = old_title self.getPlotWidget().plotWFData(wfdata=data, title=title, From 76788c022382e2fa8efebf40c9904cf8d0b1e2c5 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 30 Mar 2016 11:45:49 +0200 Subject: [PATCH 0854/1144] [refs #195] initial import of new interfaces module --- pylot/core/pick/compare.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 pylot/core/pick/compare.py diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py new file mode 100644 index 00000000..0f51b97b --- /dev/null +++ b/pylot/core/pick/compare.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pylot.core.util.pdf import ProbabilityDensityFunction +from pylot.core.util.version import get_git_version as _getVersionString + +__version__ = _getVersionString() +__author__ = 'sebastianw' From bd2bad736750d37574a6609f882c94bf0d040d88 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 30 Mar 2016 15:25:06 +0200 Subject: [PATCH 0855/1144] [adresses #195] task related new function introduced --- QtPyLoT.py | 30 ++---------------------------- pylot/core/pick/compare.py | 23 +++++++++++++++++++++++ pylot/core/read/io.py | 37 +++++++++++++++++++++++++++++++++++++ pylot/core/util/pdf.py | 8 ++++++-- 4 files changed, 68 insertions(+), 30 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a2476e9c..08a67127 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -43,6 +43,7 @@ from obspy import UTCDateTime from pylot.core.read.data import Data from pylot.core.read.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent +from pylot.core.read.io import picks_from_evt from pylot.core.loc.nll import locate as locateNll from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP from pylot.core.util.errors import FormatError, DatastructureError, \ @@ -734,34 +735,7 @@ class MainWindow(QMainWindow): return rval def updatePicks(self, type='manual'): - evt = self.getData().getEvtData() - picks = {} - for pick in evt.picks: - phase = {} - station = pick.waveform_id.station_code - try: - onsets = picks[station] - except KeyError as e: - print(e) - onsets = {} - mpp = pick.time - lpp = mpp + pick.time_errors.upper_uncertainty - epp = mpp - pick.time_errors.lower_uncertainty - spe = pick.time_errors.uncertainty - phase['mpp'] = mpp - phase['epp'] = epp - phase['lpp'] = lpp - phase['spe'] = spe - try: - picker = str(pick.method_id) - if picker.startswith('smi:local/'): - picker = picker.split('smi:local/')[1] - phase['picker'] = picker - except IndexError: - pass - - onsets[pick.phase_hint] = phase.copy() - picks[station] = onsets.copy() + picks = picks_from_evt(evt=self.getData().getEvtData()) if type == 'manual': self.picks.update(picks) elif type == 'auto': diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 0f51b97b..c0be3fed 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -1,8 +1,31 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from obspy import read_events + +from pylot.core.read.io import picks_from_evt from pylot.core.util.pdf import ProbabilityDensityFunction from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() __author__ = 'sebastianw' + + +def readData(fn): + """ + Reads pick data from QuakeML files named FN and returns a dictionary + containing a ProbabilityDensityFunction object for each pick. + :param fn: name of the QuakeML file which contains the picks + :type fn: str + :return: a dictionary containing the picks represented as pdfs + """ + pdf_picks = picks_from_evt(read_events(fn)[0]) + + for station, phases in pdf_picks.items(): + for phase, values in phases.items(): + phases[phase] = ProbabilityDensityFunction.fromPick(values['epp'], + values['mpp'], + values['lpp'], + type='exp') + + return pdf_picks diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index cd9b2149..b499d911 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -134,3 +134,40 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): except AttributeError as e: raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ insufficient data!'.format(e, phasfn, locfn)) + +def picks_from_evt(evt): + ''' + Takes an Event object and return the pick dictionary commonly used within + PyLoT + :param evt: Event object contain all available information + :type evt: `~obspy.core.event.Event` + :return: pick dictionary + ''' + picks = {} + for pick in evt.picks: + phase = {} + station = pick.waveform_id.station_code + try: + onsets = picks[station] + except KeyError as e: + print(e) + onsets = {} + mpp = pick.time + lpp = mpp + pick.time_errors.upper_uncertainty + epp = mpp - pick.time_errors.lower_uncertainty + spe = pick.time_errors.uncertainty + phase['mpp'] = mpp + phase['epp'] = epp + phase['lpp'] = lpp + phase['spe'] = spe + try: + picker = str(pick.method_id) + if picker.startswith('smi:local/'): + picker = picker.split('smi:local/')[1] + phase['picker'] = picker + except IndexError: + pass + + onsets[pick.phase_hint] = phase.copy() + picks[station] = onsets.copy() + return picks \ No newline at end of file diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 556f36ca..89481ce6 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -180,7 +180,8 @@ class ProbabilityDensityFunction(object): self._x = np.array(x) @classmethod - def fromPick(self, incr, lbound, barycentre, rbound, decfact=0.01, type='gauss'): + def fromPick(self, lbound, barycentre, rbound, incr=0.005, decfact=0.01, + type='gauss'): ''' Initialize a new ProbabilityDensityFunction object. Takes incr, lbound, barycentre and rbound to derive x0 and the number @@ -199,7 +200,10 @@ class ProbabilityDensityFunction(object): try: midpoint = (rbound + lbound) / 2 except TypeError: - midpoint = (rbound + float(lbound)) / 2 + try: + midpoint = (rbound + float(lbound)) / 2 + except TypeError: + midpoint = float(rbound + float(lbound)) / 2 # find x0 on a grid point and sufficient npts n = int(np.ceil((barycentre - midpoint) / incr)) From 1fecec16965a396fac84e1619a34772c44c634f3 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 31 Mar 2016 08:50:09 +0200 Subject: [PATCH 0856/1144] [adresses #195] read_data now working correctly on QuakeML data --- pylot/core/pick/compare.py | 2 +- pylot/core/util/pdf.py | 26 +++++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index c0be3fed..f4abb7e4 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -11,7 +11,7 @@ __version__ = _getVersionString() __author__ = 'sebastianw' -def readData(fn): +def read_data(fn): """ Reads pick data from QuakeML files named FN and returns a dictionary containing a ProbabilityDensityFunction object for each pick. diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 89481ce6..c9e79e54 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -152,7 +152,7 @@ class ProbabilityDensityFunction(object): def __nonzero__(self): prec = self.precision(self.incr) - gtzero = np.any(self.data >= 0) + gtzero = np.all(self.data >= 0) probone = bool(np.round(self.prob_gt_val(self.axis[0]), prec) == 1.) return (gtzero and probone) @@ -161,7 +161,8 @@ class ProbabilityDensityFunction(object): @staticmethod def precision(incr): - return int(np.ceil(np.abs(np.log10(incr)))) + prec = int(np.ceil(np.abs(np.log10(incr)))) - 2 + return prec if prec >= 0 else 0 @property def data(self): @@ -180,7 +181,7 @@ class ProbabilityDensityFunction(object): self._x = np.array(x) @classmethod - def fromPick(self, lbound, barycentre, rbound, incr=0.005, decfact=0.01, + def fromPick(self, lbound, barycentre, rbound, incr=0.0001, decfact=0.01, type='gauss'): ''' Initialize a new ProbabilityDensityFunction object. @@ -194,7 +195,7 @@ class ProbabilityDensityFunction(object): ''' # derive adequate window of definition - margin = 1.5 * np.max([barycentre - lbound, rbound - barycentre]) + margin = 2. * np.max([barycentre - lbound, rbound - barycentre]) # find midpoint accounting also for `~obspy.UTCDateTime` object usage try: @@ -206,6 +207,10 @@ class ProbabilityDensityFunction(object): midpoint = float(rbound + float(lbound)) / 2 # find x0 on a grid point and sufficient npts + was_datetime = None + if isinstance(barycentre, UTCDateTime): + barycentre = float(barycentre) + was_datetime = True n = int(np.ceil((barycentre - midpoint) / incr)) m = int(np.ceil((margin / incr))) midpoint = barycentre - n * incr @@ -213,6 +218,9 @@ class ProbabilityDensityFunction(object): x0 = midpoint - margin npts = 2 * m + if was_datetime: + barycentre = UTCDateTime(barycentre) + # calculate parameter for pdf representing function params = parameter[type](lbound, barycentre, rbound, decfact) @@ -275,7 +283,7 @@ class ProbabilityDensityFunction(object): return None return self.data[find_nearest(self.axis, value)] * self.incr - def plot(self): + def plot(self, label=None): import matplotlib.pyplot as plt plt.plot(self.axis, self.data) @@ -283,9 +291,13 @@ class ProbabilityDensityFunction(object): plt.ylabel('f(x)') plt.autoscale(axis='x', tight=True) if self: - plt.title('Probability density function') + title_str = 'Probability density function ' + if label: + title_str += label + title_str.strip() else: - plt.title('Function not suitable as probability density function') + title_str = 'Function not suitable as probability density function' + plt.title(title_str) plt.show() def commonlimits(self, incr, other, max_npts=1e5): From 5fcaddb02831449746bd8760f56030c8b742e33b Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 31 Mar 2016 09:00:49 +0200 Subject: [PATCH 0857/1144] [adresses #195] started to implement comparison function --- pylot/core/pick/compare.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index f4abb7e4..e996bffe 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -11,7 +11,7 @@ __version__ = _getVersionString() __author__ = 'sebastianw' -def read_data(fn): +def read_data(fn, type='exp'): """ Reads pick data from QuakeML files named FN and returns a dictionary containing a ProbabilityDensityFunction object for each pick. @@ -26,6 +26,27 @@ def read_data(fn): phases[phase] = ProbabilityDensityFunction.fromPick(values['epp'], values['mpp'], values['lpp'], - type='exp') + type=type) return pdf_picks + + +def compare_picksets(a, b): + """ + Compare two picksets A and B and return a dictionary compiling the results. + Comparison is carried out with the help of pdf representation of the picks + and a probabilistic approach to the time difference of two onset + measurements. + :param a: filename for pickset A + :type a: str + :param b: filename for pickset B + :type b: str + :return: dictionary containing the resulting comparison pdfs for all picks + :rtype: dict + """ + pdf_a = read_data(a) + pdf_b = read_data(b) + + compare_pdfs = dict() + + return compare_pdfs \ No newline at end of file From a475b366d47c23a329698db3ce7e41a40bf21f27 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 31 Mar 2016 14:29:29 +0200 Subject: [PATCH 0858/1144] [adresses #195] comparison dictionaries can now be compiled from QuakeML files --- pylot/core/pick/compare.py | 11 ++++++++++- pylot/core/util/pdf.py | 13 +++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index e996bffe..4e1025ea 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -49,4 +49,13 @@ def compare_picksets(a, b): compare_pdfs = dict() - return compare_pdfs \ No newline at end of file + for station, phases in pdf_a.items(): + if station in pdf_b.keys(): + compare_pdf = dict() + for phase in phases: + if phase in pdf_b[station].keys(): + compare_pdf[phase] = phases[phase] - pdf_b[station][phase] + if compare_pdf is not None: + compare_pdfs[station] = compare_pdf + + return compare_pdfs diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index c9e79e54..05104ee1 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -270,12 +270,21 @@ class ProbabilityDensityFunction(object): def prob_lt_val(self, value): if value <= self.axis[0] or value > self.axis[-1]: raise ValueError('value out of bounds: {0}'.format(value)) - return self.data[self.axis <= value].sum() * self.incr + return self.prob_limits((self.axis[0], value)) def prob_gt_val(self, value): if value < self.axis[0] or value >= self.axis[-1]: raise ValueError('value out of bounds: {0}'.format(value)) - return self.data[self.axis >= value].sum() * self.incr + return self.prob_limits((value, self.axis[-1])) + + def prob_limits(self, limits): + lim_ind = np.logical_and(limits[0] <= self.axis, self.axis <= limits[1]) + data = self.data[lim_ind] + min_est, max_est = 0., 0. + for n in range(len(data) - 1): + min_est += min(data[n], data[n + 1]) + max_est += max(data[n], data[n + 1]) + return (min_est + max_est) / 2. * self.incr def prob_val(self, value): if not (self.axis[0] <= value <= self.axis[-1]): From 5f9a9242d1aae41b5cd0968ec34335ef95a31a44 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 5 Apr 2016 22:19:55 +0200 Subject: [PATCH 0859/1144] [refs #195] realized an object oriented implementation of comparison comparing pdf represented picks should be easy, thus objects returning everything needed are implemented; histograms and other plots are planned next --- pylot/core/pick/compare.py | 149 ++++++++++++++++++++++++++++--------- 1 file changed, 112 insertions(+), 37 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 4e1025ea..156cffd7 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import copy + from obspy import read_events from pylot.core.read.io import picks_from_evt @@ -11,51 +13,124 @@ __version__ = _getVersionString() __author__ = 'sebastianw' -def read_data(fn, type='exp'): +class Comparison(object): """ - Reads pick data from QuakeML files named FN and returns a dictionary - containing a ProbabilityDensityFunction object for each pick. - :param fn: name of the QuakeML file which contains the picks - :type fn: str - :return: a dictionary containing the picks represented as pdfs + A Comparison object contains information on the evaluated picks' probability + density function and compares these in terms of building the difference of + compared pick sets. The results can be displayed as histograms showing its + properties. """ - pdf_picks = picks_from_evt(read_events(fn)[0]) + def __init__(self, **kwargs): + names = list() + self._pdfs = dict() + for name, fn in kwargs: + self._pdfs[name] = PDFDictionary.from_quakeml(fn) + names.append(name) + if len(names) > 2: + raise ValueError('Comparison is only defined for two ' + 'arguments!') + self._names = names - for station, phases in pdf_picks.items(): - for phase, values in phases.items(): - phases[phase] = ProbabilityDensityFunction.fromPick(values['epp'], - values['mpp'], - values['lpp'], - type=type) + def __nonzero__(self): + if not len(self.names) == 2 or not self._pdfs: + return False + return True - return pdf_picks + def get(self, name): + return self._pdfs[name] + + @property + def names(self): + return self._names + + @names.setter + def names(self, names): + assert isinstance(names, list) and len(names) == 2, 'variable "names"' \ + ' is either not a' \ + ' list or its ' \ + 'length is not 2:' \ + 'names : {names}'.format(names=names) + self._names = names + + def compare_picksets(self): + """ + Compare two picksets A and B and return a dictionary compiling the results. + Comparison is carried out with the help of pdf representation of the picks + and a probabilistic approach to the time difference of two onset + measurements. + :param a: filename for pickset A + :type a: str + :param b: filename for pickset B + :type b: str + :return: dictionary containing the resulting comparison pdfs for all picks + :rtype: dict + """ + compare_pdfs = dict() + + pdf_a = self.get(self.names[0]) + pdf_b = self.get(self.names[1]) + + for station, phases in pdf_a.items(): + if station in pdf_b.keys(): + compare_pdf = dict() + for phase in phases: + if phase in pdf_b[station].keys(): + compare_pdf[phase] = phases[phase] - pdf_b[station][ + phase] + if compare_pdf is not None: + compare_pdfs[station] = compare_pdf + + return compare_pdfs -def compare_picksets(a, b): +class PDFDictionary(object): """ - Compare two picksets A and B and return a dictionary compiling the results. - Comparison is carried out with the help of pdf representation of the picks - and a probabilistic approach to the time difference of two onset - measurements. - :param a: filename for pickset A - :type a: str - :param b: filename for pickset B - :type b: str - :return: dictionary containing the resulting comparison pdfs for all picks - :rtype: dict + A PDFDictionary is a dictionary like object containing structured data on + the probability density function of seismic phase onsets. """ - pdf_a = read_data(a) - pdf_b = read_data(b) + def __init__(self, data): + self._pickdata = data - compare_pdfs = dict() + def __nonzero__(self): + if len(self.pick_data) < 1: + return False + else: + return True - for station, phases in pdf_a.items(): - if station in pdf_b.keys(): - compare_pdf = dict() - for phase in phases: - if phase in pdf_b[station].keys(): - compare_pdf[phase] = phases[phase] - pdf_b[station][phase] - if compare_pdf is not None: - compare_pdfs[station] = compare_pdf + @property + def pick_data(self): + return self._pickdata + + @pick_data.setter + def pick_data(self, data): + self._pickdata = data + + @classmethod + def from_quakeml(self, fn): + cat = read_events(fn) + if len(cat) > 1: + raise NotImplementedError('reading more than one event at the same ' + 'time is not implemented yet! Sorry!') + self.pick_data = picks_from_evt(cat[0]) + + def pdf_data(self, type='exp'): + """ + Returns probabiliy density function dictionary containing the + representation of the actual pick_data. + :param type: type of the returned + `~pylot.core.util.pdf.ProbabilityDensityFunction` object + :type type: str + :return: a dictionary containing the picks represented as pdfs + """ + + pdf_picks = copy.deepcopy(self.pick_data) + + for station, phases in pdf_picks.items(): + for phase, values in phases.items(): + phases[phase] = ProbabilityDensityFunction.fromPick(values['epp'], + values['mpp'], + values['lpp'], + type=type) + + return pdf_picks - return compare_pdfs From 27e334609c0da65f10d05b6ebb8917acfee4411e Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 6 Apr 2016 11:27:09 +0200 Subject: [PATCH 0860/1144] [refs #195] plotting method for Comparison object implemented --- pylot/core/pick/compare.py | 73 +++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 156cffd7..5eaccb4c 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -3,6 +3,10 @@ import copy +import numpy as np + +import matplotlib.pyplot as plt + from obspy import read_events from pylot.core.read.io import picks_from_evt @@ -20,16 +24,18 @@ class Comparison(object): compared pick sets. The results can be displayed as histograms showing its properties. """ + def __init__(self, **kwargs): names = list() self._pdfs = dict() - for name, fn in kwargs: + for name, fn in kwargs.items(): self._pdfs[name] = PDFDictionary.from_quakeml(fn) names.append(name) if len(names) > 2: raise ValueError('Comparison is only defined for two ' - 'arguments!') + 'arguments!') self._names = names + self._compare = self.compare_picksets() def __nonzero__(self): if not len(self.names) == 2 or not self._pdfs: @@ -49,10 +55,23 @@ class Comparison(object): ' is either not a' \ ' list or its ' \ 'length is not 2:' \ - 'names : {names}'.format(names=names) + 'names : {names}'.format( + names=names) self._names = names - def compare_picksets(self): + @property + def comparison(self): + return self._compare + + @property + def stations(self): + return self.comparison.keys() + + @property + def nstations(self): + return len(self.stations) + + def compare_picksets(self, type='exp'): """ Compare two picksets A and B and return a dictionary compiling the results. Comparison is carried out with the help of pdf representation of the picks @@ -67,8 +86,8 @@ class Comparison(object): """ compare_pdfs = dict() - pdf_a = self.get(self.names[0]) - pdf_b = self.get(self.names[1]) + pdf_a = self.get(self.names[0]).pdf_data(type) + pdf_b = self.get(self.names[1]).pdf_data(type) for station, phases in pdf_a.items(): if station in pdf_b.keys(): @@ -82,12 +101,42 @@ class Comparison(object): return compare_pdfs + def plot(self): + nstations = self.nstations + stations = self.stations + istations = range(nstations) + fig, axarr = plt.subplots(nstations, 2, sharex='col', sharey='row') + + for n in istations: + station = stations[n] + compare_pdf = self.comparison[station] + for l, phase in enumerate(compare_pdf.keys()): + axarr[n, l].plot(compare_pdf[phase].axis, + compare_pdf[phase].data) + if n is 0: + axarr[n, l].set_title(phase) + if l is 0: + axann = axarr[n, l].annotate(station, xy=(.05, .5), + xycoords='axes fraction') + bbox_props = dict(boxstyle='round', facecolor='lightgrey', + alpha=.7) + axann.set_bbox(bbox_props) + if n == int(np.median(istations)) and l is 0: + label = 'probability density (qualitative)' + axarr[n, l].set_ylabel(label) + plt.setp([a.get_xticklabels() for a in axarr[0, :]], visible=False) + plt.setp([a.get_yticklabels() for a in axarr[:, 1]], visible=False) + plt.setp([a.get_yticklabels() for a in axarr[:, 0]], visible=False) + + plt.show() + class PDFDictionary(object): """ A PDFDictionary is a dictionary like object containing structured data on the probability density function of seismic phase onsets. """ + def __init__(self, data): self._pickdata = data @@ -111,7 +160,7 @@ class PDFDictionary(object): if len(cat) > 1: raise NotImplementedError('reading more than one event at the same ' 'time is not implemented yet! Sorry!') - self.pick_data = picks_from_evt(cat[0]) + return PDFDictionary(picks_from_evt(cat[0])) def pdf_data(self, type='exp'): """ @@ -127,10 +176,10 @@ class PDFDictionary(object): for station, phases in pdf_picks.items(): for phase, values in phases.items(): - phases[phase] = ProbabilityDensityFunction.fromPick(values['epp'], - values['mpp'], - values['lpp'], - type=type) + phases[phase] = ProbabilityDensityFunction.fromPick( + values['epp'], + values['mpp'], + values['lpp'], + type=type) return pdf_picks - From b5345bb5d35913f846064c2e8b3bf616257d1cad Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 7 Apr 2016 15:47:11 +0200 Subject: [PATCH 0861/1144] [refs #195] implementation of histogram plots --- pylot/core/pick/compare.py | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 5eaccb4c..e4c5222b 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -130,6 +130,46 @@ class Comparison(object): plt.show() + def get_expectation_array(self, phase): + pdf_dict = self.comparison + exp_array = list() + for station, phases in pdf_dict.items(): + exp_array.append(phases[phase].expectation()) + return exp_array + + def get_std_array(self, phase): + pdf_dict = self.comparison + std_array = list() + for station, phases in pdf_dict.items(): + std_array.append(phases[phase].standard_deviation()) + return std_array + + def hist_expectation(self, phases='all', bins=50, normed=False): + phases.strip() + if phases.find('all') == 0: + phases = 'ps' + phases = phases.upper() + nsp = len(phases) + fig, axarray = plt.subplots(1, nsp, sharey=True) + for n, phase in enumerate(phases): + ax = axarray[0, n] + data = self.get_expectation_array(phase) + xlims = [np.floor(min(data)), np.ceil(max(data))] + ax.hist(data, xlims=xlims, bins=bins, normed=normed) + title_str = 'phase: {0}, samples: {1}'.format(phase, len(data)) + ax.title(title_str) + ax.xlabel('expectation [s]') + if n is 0: + ax.ylabel('abundance [-]') + plt.setp([a.get_yticklabels() for a in axarray[0, 1:]], visible=False) + plt.show() + + def hist_standard_deviation(self): + pass + + def hist(self, type='std'): + pass + class PDFDictionary(object): """ From f15e27e81df3befca57df9224a474cb9bac67976 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 7 Apr 2016 16:29:29 +0200 Subject: [PATCH 0862/1144] [refs #195] hitsogram plots implemented for expectation and standard deviation --- pylot/core/pick/compare.py | 43 +++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index e4c5222b..201403f4 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -145,6 +145,26 @@ class Comparison(object): return std_array def hist_expectation(self, phases='all', bins=50, normed=False): + phases.strip() + if phases.find('all') is 0: + phases = 'ps' + phases = phases.upper() + nsp = len(phases) + fig, axarray = plt.subplots(1, nsp, sharey=True) + for n, phase in enumerate(phases): + ax = axarray[n] + data = self.get_expectation_array(phase) + xlims = [np.floor(min(data)), np.ceil(max(data))] + ax.hist(data, range=xlims, bins=bins, normed=normed) + title_str = 'phase: {0}, samples: {1}'.format(phase, len(data)) + ax.set_title(title_str) + ax.set_xlabel('expectation [s]') + if n is 0: + ax.set_ylabel('abundance [-]') + plt.setp([a.get_yticklabels() for a in axarray[1:]], visible=False) + plt.show() + + def hist_standard_deviation(self, phases='all', bins=50, normed=False): phases.strip() if phases.find('all') == 0: phases = 'ps' @@ -152,21 +172,18 @@ class Comparison(object): nsp = len(phases) fig, axarray = plt.subplots(1, nsp, sharey=True) for n, phase in enumerate(phases): - ax = axarray[0, n] - data = self.get_expectation_array(phase) + ax = axarray[n] + data = self.get_std_array(phase) xlims = [np.floor(min(data)), np.ceil(max(data))] - ax.hist(data, xlims=xlims, bins=bins, normed=normed) + ax.hist(data, range=xlims, bins=bins, normed=normed) title_str = 'phase: {0}, samples: {1}'.format(phase, len(data)) - ax.title(title_str) - ax.xlabel('expectation [s]') + ax.set_title(title_str) + ax.set_xlabel('standard deviation [s]') if n is 0: - ax.ylabel('abundance [-]') - plt.setp([a.get_yticklabels() for a in axarray[0, 1:]], visible=False) + ax.set_ylabel('abundance [-]') + plt.setp([a.get_yticklabels() for a in axarray[1:]], visible=False) plt.show() - def hist_standard_deviation(self): - pass - def hist(self, type='std'): pass @@ -223,3 +240,9 @@ class PDFDictionary(object): type=type) return pdf_picks + + +comp_obj = Comparison(manual='/home/sebastianp/Data/Insheim/e0057.026.13.xml', auto='/home/sebastianp/Data/Insheim/e0057.026.13.xml') +comp_obj.plot() +comp_obj.hist_expectation() +comp_obj.hist_standard_deviation() \ No newline at end of file From 2a8729c39b7570d7537613b18cbb7214d186be7c Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 8 Apr 2016 14:35:20 +0200 Subject: [PATCH 0863/1144] [refs #195] changed some default parameters for plotting the histograms --- pylot/core/pick/compare.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 201403f4..9498683e 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -144,7 +144,7 @@ class Comparison(object): std_array.append(phases[phase].standard_deviation()) return std_array - def hist_expectation(self, phases='all', bins=50, normed=False): + def hist_expectation(self, phases='all', bins=20, normed=False): phases.strip() if phases.find('all') is 0: phases = 'ps' @@ -154,7 +154,7 @@ class Comparison(object): for n, phase in enumerate(phases): ax = axarray[n] data = self.get_expectation_array(phase) - xlims = [np.floor(min(data)), np.ceil(max(data))] + xlims = [min(data), max(data)] ax.hist(data, range=xlims, bins=bins, normed=normed) title_str = 'phase: {0}, samples: {1}'.format(phase, len(data)) ax.set_title(title_str) @@ -164,7 +164,7 @@ class Comparison(object): plt.setp([a.get_yticklabels() for a in axarray[1:]], visible=False) plt.show() - def hist_standard_deviation(self, phases='all', bins=50, normed=False): + def hist_standard_deviation(self, phases='all', bins=20, normed=False): phases.strip() if phases.find('all') == 0: phases = 'ps' @@ -174,7 +174,7 @@ class Comparison(object): for n, phase in enumerate(phases): ax = axarray[n] data = self.get_std_array(phase) - xlims = [np.floor(min(data)), np.ceil(max(data))] + xlims = [min(data), max(data)] ax.hist(data, range=xlims, bins=bins, normed=normed) title_str = 'phase: {0}, samples: {1}'.format(phase, len(data)) ax.set_title(title_str) From a9cd53886b8d7336adcb12c1c215264ed5566020 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 11 Apr 2016 05:47:38 +0200 Subject: [PATCH 0864/1144] [enhancement, task] there was no routine to read obs pick files available; default values for autopicking should be the same for all parts of PyLoT, thus defaults should be defined only once --- QtPyLoT.py | 6 +++--- pylot/core/read/io.py | 20 +++++++++++++++++++- pylot/core/util/defaults.py | 4 ++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 08a67127..285e2ffb 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -45,7 +45,8 @@ from pylot.core.read.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent from pylot.core.read.io import picks_from_evt from pylot.core.loc.nll import locate as locateNll -from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP +from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP,\ + AUTOMATIC_DEFAULTS from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError from pylot.core.util.connection import checkurl @@ -689,8 +690,7 @@ class MainWindow(QMainWindow): self.logDockWidget.setWidget(self.listWidget) self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget) self.addListItem('loading default values for local data ...') - home = os.path.expanduser("~") - autopick_parameter = AutoPickParameter('%s/.pylot/autoPyLoT_local.in' % home) + autopick_parameter = AutoPickParameter(AUTOMATIC_DEFAULTS) self.addListItem(str(autopick_parameter)) # Create the worker thread and run it diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py index b499d911..f34a7616 100644 --- a/pylot/core/read/io.py +++ b/pylot/core/read/io.py @@ -135,6 +135,24 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ insufficient data!'.format(e, phasfn, locfn)) +def picks_from_obs(fn): + picks = dict() + station_name = str() + for line in open(fn, 'r'): + if line.startswith('#'): + continue + else: + phase_line = line.split() + if not station_name == phase_line[0]: + phase = dict() + station_name = phase_line[0] + phase_name = phase_line[4].upper() + pick = UTCDateTime(phase_line[6] + phase_line[7] + phase_line[8]) + phase[phase_name] = dict(mpp=pick, fm=phase_line[5]) + picks[station_name] = phase + return picks + + def picks_from_evt(evt): ''' Takes an Event object and return the pick dictionary commonly used within @@ -170,4 +188,4 @@ def picks_from_evt(evt): onsets[pick.phase_hint] = phase.copy() picks[station] = onsets.copy() - return picks \ No newline at end of file + return picks diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index c74b1332..d42a0786 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -43,6 +43,10 @@ FILTERDEFAULTS = readFilterInformation(os.path.join(os.path.expanduser('~'), '.pylot', 'filter.in')) +AUTOMATIC_DEFAULTS = os.path.join(os.path.expanduser('~'), + '.pylot', + 'autoPyLoT.in') + OUTPUTFORMATS = {'.xml': 'QUAKEML', '.cnv': 'CNV', '.obs': 'NLLOC_OBS'} From 2139674afa54dcbfa3ec4e2b009f5ad10153eedc Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 11 Apr 2016 12:39:46 +0200 Subject: [PATCH 0865/1144] [bugfix] guessed that checking the same variable twice was not intended instead of checking for mpickX's twice it was probably meant to check for lpickX's value --- pylot/core/pick/autopick.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 8d6de16c..57eb21e5 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -773,7 +773,7 @@ def autopickstation(wfstream, pickparam, verbose=False): plt.close() ########################################################################## # calculate "real" onset times - if mpickP is not None and epickP is not None and mpickP is not None: + if mpickP is not None and epickP is not None and lpickP is not None: lpickP = zdat[0].stats.starttime + lpickP epickP = zdat[0].stats.starttime + epickP mpickP = zdat[0].stats.starttime + mpickP @@ -784,7 +784,7 @@ def autopickstation(wfstream, pickparam, verbose=False): epickP = zdat[0].stats.starttime mpickP = zdat[0].stats.starttime - if mpickS is not None and epickS is not None and mpickS is not None: + if mpickS is not None and epickS is not None and lpickS is not None: lpickS = edat[0].stats.starttime + lpickS epickS = edat[0].stats.starttime + epickS mpickS = edat[0].stats.starttime + mpickS From 95193722474ac184754a8ac39ea55a73dfc1c2a0 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 11 Apr 2016 13:57:59 +0200 Subject: [PATCH 0866/1144] [bugfix] autopick accidently interchanged latest and earliest possible pick --- pylot/core/pick/autopick.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 57eb21e5..da244cd2 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -305,7 +305,7 @@ def autopickstation(wfstream, pickparam, verbose=False): if mpickP is not None: # quality assessment # get earliest/latest possible pick and symmetrized uncertainty - [lpickP, epickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, + [epickP, lpickP, Perror] = earllatepicker(z_copy, nfacP, tsnrz, mpickP, iplot) # get SNR @@ -491,11 +491,11 @@ def autopickstation(wfstream, pickparam, verbose=False): # quality assessment # get earliest/latest possible pick and symmetrized uncertainty h_copy[0].data = trH1_filt.data - [lpickS1, epickS1, Serror1] = earllatepicker(h_copy, nfacS, + [epickS1, lpickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, iplot) h_copy[0].data = trH2_filt.data - [lpickS2, epickS2, Serror2] = earllatepicker(h_copy, nfacS, + [epickS2, lpickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, iplot) if epickS1 is not None and epickS2 is not None: @@ -511,7 +511,7 @@ def autopickstation(wfstream, pickparam, verbose=False): elif epickS1 is not None and epickS2 is not None: ipick = np.argmin([epickS1, epickS2]) elif algoS == 'AR3': - [lpickS3, epickS3, Serror3] = earllatepicker(h_copy, + [epickS3, lpickS3, Serror3] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, From 9359338e816ba39f27335488d0d27f9b8d7d0891 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 11 Apr 2016 13:59:32 +0200 Subject: [PATCH 0867/1144] [refs #195] make autoPyLoT export automatic picks in QuakeML format --- autoPyLoT.py | 14 ++++++++++++-- pylot/core/read/data.py | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index e67ea8f3..eb218ca7 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -202,10 +202,15 @@ def autoPyLoT(inputfile): if hasattr(finalpicks, 'getpicdic'): if finalpicks.getpicdic() is not None: writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + data.applyEVTData(finalpicks.getpicdic()) else: writephases(picks, 'HYPO71', hypo71file) + data.applyEVTData(picks) else: writephases(picks, 'HYPO71', hypo71file) + data.applyEVTData(picks) + fnqml = '%s/autoPyLoT' % event + data.exportEvent(fnqml) endsplash = '''------------------------------------------\n' -----Finished event %s!-----\n' @@ -318,10 +323,15 @@ def autoPyLoT(inputfile): if hasattr(finalpicks, 'getpicdic'): if finalpicks.getpicdic() is not None: writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) + data.applyEVTData(finalpicks.getpicdic()) else: writephases(picks, 'HYPO71', hypo71file) + data.applyEVTData(picks) else: writephases(picks, 'HYPO71', hypo71file) + data.applyEVTData(picks) + fnqml = '%s/%s/autoPyLoT' % (datapath, parameter.getParam('eventID')) + data.exportEvent(fnqml) endsplash = '''------------------------------------------\n' -----Finished event %s!-----\n' @@ -340,6 +350,7 @@ def autoPyLoT(inputfile): if __name__ == "__main__": + from pylot.core.util.defaults import AUTOMATIC_DEFAULTS # parse arguments parser = argparse.ArgumentParser( description='''autoPyLoT automatically picks phase onset times using higher order statistics, @@ -349,8 +360,7 @@ if __name__ == "__main__": action='store', help='''full path to the file containing the input parameters for autoPyLoT''', - default=os.path.join(os.path.expanduser('~'), '.pylot', - 'autoPyLoT.in') + default=AUTOMATIC_DEFAULTS ) parser.add_argument('-v', '-V', '--version', action='version', version='autoPyLoT ' + __version__, diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index d2d466c4..b7b961d7 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -417,6 +417,8 @@ class Data(object): for station, onsets in picks.items(): print('Reading picks on station %s' % station) for label, phase in onsets.items(): + if not isinstance(phase, dict): + continue onset = phase['mpp'] epp = phase['epp'] lpp = phase['lpp'] From 4a9c02e1d1e3c6ae10af9bb1968f22260afc3395 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 12 Apr 2016 04:55:04 +0200 Subject: [PATCH 0868/1144] [closes 195] in principle comparing automatic and manual picks works --- pylot/core/pick/compare.py | 16 +++++++++++----- pylot/core/util/pdf.py | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 9498683e..c3384b50 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -134,7 +134,12 @@ class Comparison(object): pdf_dict = self.comparison exp_array = list() for station, phases in pdf_dict.items(): - exp_array.append(phases[phase].expectation()) + try: + exp_array.append(phases[phase].expectation()) + except KeyError as e: + print('{err_msg}; station = {station}, phase = {phase}'.format( + err_msg=str(e), station=station, phase=phase)) + continue return exp_array def get_std_array(self, phase): @@ -242,7 +247,8 @@ class PDFDictionary(object): return pdf_picks -comp_obj = Comparison(manual='/home/sebastianp/Data/Insheim/e0057.026.13.xml', auto='/home/sebastianp/Data/Insheim/e0057.026.13.xml') -comp_obj.plot() -comp_obj.hist_expectation() -comp_obj.hist_standard_deviation() \ No newline at end of file +#comp_obj = Comparison(manual='/home/sebastianp/Data/Insheim/e0019.048.13.xml', +# auto='/data/Geothermie/Insheim/EVENT_DATA/LOCAL/RefDB/e0019.048.13/autoPyLoT.xml') +#comp_obj.plot() +#comp_obj.hist_expectation() +#comp_obj.hist_standard_deviation() diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 05104ee1..dddb260f 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -181,7 +181,7 @@ class ProbabilityDensityFunction(object): self._x = np.array(x) @classmethod - def fromPick(self, lbound, barycentre, rbound, incr=0.0001, decfact=0.01, + def fromPick(self, lbound, barycentre, rbound, incr=0.001, decfact=0.01, type='gauss'): ''' Initialize a new ProbabilityDensityFunction object. From 6181829ef64ed12c56f0de64783de4da68a44eb8 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 12 Apr 2016 14:47:49 +0200 Subject: [PATCH 0869/1144] [bugfix] make uncertainties from autoPyLoT may be corrupted consequently there is minimum uncertainty gap between mpp and lpp/epp --- pylot/core/pick/autopick.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index da244cd2..b6fa4d33 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -494,6 +494,7 @@ def autopickstation(wfstream, pickparam, verbose=False): [epickS1, lpickS1, Serror1] = earllatepicker(h_copy, nfacS, tsnrh, mpickS, iplot) + h_copy[0].data = trH2_filt.data [epickS2, lpickS2, Serror2] = earllatepicker(h_copy, nfacS, tsnrh, @@ -773,6 +774,10 @@ def autopickstation(wfstream, pickparam, verbose=False): plt.close() ########################################################################## # calculate "real" onset times + if lpickP is not None and lpickP == mpickP: + lpickP += timeerrorsP[0] + if epickP is not None and epickP == mpickP: + epickP -= timeerrorsP[0] if mpickP is not None and epickP is not None and lpickP is not None: lpickP = zdat[0].stats.starttime + lpickP epickP = zdat[0].stats.starttime + epickP @@ -780,10 +785,14 @@ def autopickstation(wfstream, pickparam, verbose=False): else: # dummy values (start of seismic trace) in order to derive # theoretical onset times for iteratve picking - lpickP = zdat[0].stats.starttime - epickP = zdat[0].stats.starttime + lpickP = zdat[0].stats.starttime + timeerrorsP[3] + epickP = zdat[0].stats.starttime - timeerrorsP[3] mpickP = zdat[0].stats.starttime + if lpickS is not None and lpickS == mpickS: + lpickS += timeerrorsS[0] + if epickS is not None and epickS == mpickS: + epickS -= timeerrorsS[0] if mpickS is not None and epickS is not None and lpickS is not None: lpickS = edat[0].stats.starttime + lpickS epickS = edat[0].stats.starttime + epickS @@ -791,8 +800,8 @@ def autopickstation(wfstream, pickparam, verbose=False): else: # dummy values (start of seismic trace) in order to derive # theoretical onset times for iteratve picking - lpickS = edat[0].stats.starttime - epickS = edat[0].stats.starttime + lpickS = edat[0].stats.starttime + timeerrorsS[3] + epickS = edat[0].stats.starttime - timeerrorsS[3] mpickS = edat[0].stats.starttime # create dictionary From 416f4c7aa7d4fde5cec0874b038c7729e82df368 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 12 Apr 2016 14:48:57 +0200 Subject: [PATCH 0870/1144] [bugfix] special method __nonzero__ should always return type bool --- pylot/core/util/pdf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index dddb260f..6f986666 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -154,7 +154,7 @@ class ProbabilityDensityFunction(object): prec = self.precision(self.incr) gtzero = np.all(self.data >= 0) probone = bool(np.round(self.prob_gt_val(self.axis[0]), prec) == 1.) - return (gtzero and probone) + return bool(gtzero and probone) def __str__(self): return str(self.data) From c489fad6ba69b94add5f5735dcdb67f87abdc75f Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 12 Apr 2016 14:50:21 +0200 Subject: [PATCH 0871/1144] [bugfix] comparison should not through an KeyError due to missing picks --- pylot/core/pick/compare.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index c3384b50..49fb5414 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -146,7 +146,12 @@ class Comparison(object): pdf_dict = self.comparison std_array = list() for station, phases in pdf_dict.items(): - std_array.append(phases[phase].standard_deviation()) + try: + std_array.append(phases[phase].standard_deviation()) + except KeyError as e: + print('{err_msg}; station = {station}, phase = {phase}'.format( + err_msg=str(e), station=station, phase=phase)) + continue return std_array def hist_expectation(self, phases='all', bins=20, normed=False): From 37f9292c39246b327d3630995ca2521725c6cdd7 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 28 Apr 2016 14:03:32 +0200 Subject: [PATCH 0872/1144] renamed class method --- pylot/core/pick/compare.py | 2 +- pylot/core/util/pdf.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 49fb5414..a5db47c5 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -243,7 +243,7 @@ class PDFDictionary(object): for station, phases in pdf_picks.items(): for phase, values in phases.items(): - phases[phase] = ProbabilityDensityFunction.fromPick( + phases[phase] = ProbabilityDensityFunction.from_pick( values['epp'], values['mpp'], values['lpp'], diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 6f986666..776192aa 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -181,8 +181,8 @@ class ProbabilityDensityFunction(object): self._x = np.array(x) @classmethod - def fromPick(self, lbound, barycentre, rbound, incr=0.001, decfact=0.01, - type='gauss'): + def from_pick(self, lbound, barycentre, rbound, incr=0.001, decfact=0.01, + type='gauss'): ''' Initialize a new ProbabilityDensityFunction object. Takes incr, lbound, barycentre and rbound to derive x0 and the number @@ -323,8 +323,8 @@ class ProbabilityDensityFunction(object): :return: ''' - # >>> manu = ProbabilityDensityFunction.fromPick(0.01, 0.3, 0.5, 0.54) - # >>> auto = ProbabilityDensityFunction.fromPick(0.01, 0.3, 0.34, 0.54) + # >>> manu = ProbabilityDensityFunction.from_pick(0.01, 0.3, 0.5, 0.54) + # >>> auto = ProbabilityDensityFunction.from_pick(0.01, 0.3, 0.34, 0.54) # >>> manu.commonlimits(0.01, auto) # ( From df44979337b8a2373f4c32e90bcc503228dd6089 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 28 Apr 2016 14:18:42 +0200 Subject: [PATCH 0873/1144] [reorganize] some reorganization done to hand program to partner --- autoPyLoT.in => inputs/autoPyLoT.in | 0 autoPyLoT_local.in => inputs/autoPyLoT_local.in | 0 autoPyLoT_regional.in => inputs/autoPyLoT_regional.in | 0 filter.in => inputs/filter.in | 0 pylot/testing/__init__.py | 0 testHelpForm.py => pylot/testing/testHelpForm.py | 0 testPickDlg.py => pylot/testing/testPickDlg.py | 0 testPropDlg.py => pylot/testing/testPropDlg.py | 0 testUIcomponents.py => pylot/testing/testUIcomponents.py | 0 pylot/{core/util => testing}/testUtils.py | 0 pylot/{core/util => testing}/testWidgets.py | 0 test_autopick.py => pylot/testing/test_autopick.py | 0 {pylot/core/scripts => scripts}/pylot-noisewindow.py | 0 {pylot/core/scripts => scripts}/pylot-pick-earliest-latest.py | 0 {pylot/core/scripts => scripts}/pylot-pick-firstmotion.py | 0 {pylot/core/scripts => scripts}/pylot-signalwindow.py | 0 {pylot/core/scripts => scripts}/pylot-snr.py | 0 {pylot/core/scripts => scripts}/run_makeCF.py | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename autoPyLoT.in => inputs/autoPyLoT.in (100%) rename autoPyLoT_local.in => inputs/autoPyLoT_local.in (100%) rename autoPyLoT_regional.in => inputs/autoPyLoT_regional.in (100%) rename filter.in => inputs/filter.in (100%) create mode 100644 pylot/testing/__init__.py rename testHelpForm.py => pylot/testing/testHelpForm.py (100%) rename testPickDlg.py => pylot/testing/testPickDlg.py (100%) rename testPropDlg.py => pylot/testing/testPropDlg.py (100%) rename testUIcomponents.py => pylot/testing/testUIcomponents.py (100%) rename pylot/{core/util => testing}/testUtils.py (100%) rename pylot/{core/util => testing}/testWidgets.py (100%) rename test_autopick.py => pylot/testing/test_autopick.py (100%) rename {pylot/core/scripts => scripts}/pylot-noisewindow.py (100%) rename {pylot/core/scripts => scripts}/pylot-pick-earliest-latest.py (100%) rename {pylot/core/scripts => scripts}/pylot-pick-firstmotion.py (100%) rename {pylot/core/scripts => scripts}/pylot-signalwindow.py (100%) rename {pylot/core/scripts => scripts}/pylot-snr.py (100%) rename {pylot/core/scripts => scripts}/run_makeCF.py (100%) diff --git a/autoPyLoT.in b/inputs/autoPyLoT.in similarity index 100% rename from autoPyLoT.in rename to inputs/autoPyLoT.in diff --git a/autoPyLoT_local.in b/inputs/autoPyLoT_local.in similarity index 100% rename from autoPyLoT_local.in rename to inputs/autoPyLoT_local.in diff --git a/autoPyLoT_regional.in b/inputs/autoPyLoT_regional.in similarity index 100% rename from autoPyLoT_regional.in rename to inputs/autoPyLoT_regional.in diff --git a/filter.in b/inputs/filter.in similarity index 100% rename from filter.in rename to inputs/filter.in diff --git a/pylot/testing/__init__.py b/pylot/testing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/testHelpForm.py b/pylot/testing/testHelpForm.py similarity index 100% rename from testHelpForm.py rename to pylot/testing/testHelpForm.py diff --git a/testPickDlg.py b/pylot/testing/testPickDlg.py similarity index 100% rename from testPickDlg.py rename to pylot/testing/testPickDlg.py diff --git a/testPropDlg.py b/pylot/testing/testPropDlg.py similarity index 100% rename from testPropDlg.py rename to pylot/testing/testPropDlg.py diff --git a/testUIcomponents.py b/pylot/testing/testUIcomponents.py similarity index 100% rename from testUIcomponents.py rename to pylot/testing/testUIcomponents.py diff --git a/pylot/core/util/testUtils.py b/pylot/testing/testUtils.py similarity index 100% rename from pylot/core/util/testUtils.py rename to pylot/testing/testUtils.py diff --git a/pylot/core/util/testWidgets.py b/pylot/testing/testWidgets.py similarity index 100% rename from pylot/core/util/testWidgets.py rename to pylot/testing/testWidgets.py diff --git a/test_autopick.py b/pylot/testing/test_autopick.py similarity index 100% rename from test_autopick.py rename to pylot/testing/test_autopick.py diff --git a/pylot/core/scripts/pylot-noisewindow.py b/scripts/pylot-noisewindow.py similarity index 100% rename from pylot/core/scripts/pylot-noisewindow.py rename to scripts/pylot-noisewindow.py diff --git a/pylot/core/scripts/pylot-pick-earliest-latest.py b/scripts/pylot-pick-earliest-latest.py similarity index 100% rename from pylot/core/scripts/pylot-pick-earliest-latest.py rename to scripts/pylot-pick-earliest-latest.py diff --git a/pylot/core/scripts/pylot-pick-firstmotion.py b/scripts/pylot-pick-firstmotion.py similarity index 100% rename from pylot/core/scripts/pylot-pick-firstmotion.py rename to scripts/pylot-pick-firstmotion.py diff --git a/pylot/core/scripts/pylot-signalwindow.py b/scripts/pylot-signalwindow.py similarity index 100% rename from pylot/core/scripts/pylot-signalwindow.py rename to scripts/pylot-signalwindow.py diff --git a/pylot/core/scripts/pylot-snr.py b/scripts/pylot-snr.py similarity index 100% rename from pylot/core/scripts/pylot-snr.py rename to scripts/pylot-snr.py diff --git a/pylot/core/scripts/run_makeCF.py b/scripts/run_makeCF.py similarity index 100% rename from pylot/core/scripts/run_makeCF.py rename to scripts/run_makeCF.py From 28fc2267506e6641de2ded5ae61ed11162fb5204 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 28 Apr 2016 15:20:46 +0200 Subject: [PATCH 0874/1144] [update] update installation script in order to distribute current version --- makePyLoT.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/makePyLoT.py b/makePyLoT.py index 1b222c66..3cfaddd1 100644 --- a/makePyLoT.py +++ b/makePyLoT.py @@ -34,7 +34,7 @@ from argparse import RawDescriptionHelpFormatter __all__ = [] __version__ = 0.1 __date__ = '2014-11-26' -__updated__ = '2014-11-26' +__updated__ = '2016-04-28' DEBUG = 0 TESTRUN = 0 @@ -69,19 +69,19 @@ def main(argv=None): # IGNORE:C0111 program_version_message = 'makePyLoT %s (%s)' % ( program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] - program_license = '''%s + program_license = '''{0:s} - Created by Sebastian Wehling-Benatelli on %s. - Copyright 2014 MAGS2 EP3 Working Group. All rights reserved. + Created by Sebastian Wehling-Benatelli on {1:s}. + Copyright 2014 MAGS2 EP3 Working Group. All rights reserved. - GNU Lesser General Public License, Version 3 + GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) - Distributed on an "AS IS" basis without warranties - or conditions of any kind, either express or implied. + Distributed on an "AS IS" basis without warranties + or conditions of any kind, either express or implied. -USAGE -''' % (program_shortdesc, str(__date__)) + USAGE + '''.format(program_shortdesc, str(__date__)) try: # Setup argument parser @@ -109,17 +109,17 @@ USAGE if verbose > 0: print("Verbose mode on") - if install and not directory: - raise CLIError("""Trying to install without appropriate - destination; please specify an installation - directory!""") - if build and install: - print("Building and installing PyLoT ...\n") - buildPyLoT(verbose) - installPyLoT(verbose) - elif build and not install: - print("Building PyLoT without installing! Please wait ...\n") - buildPyLoT(verbose) + if install and not directory: + raise CLIError("""Trying to install without appropriate + destination; please specify an installation + directory!""") + if build and install: + print("Building and installing PyLoT ...\n") + buildPyLoT(verbose) + installPyLoT(verbose) + elif build and not install: + print("Building PyLoT without installing! Please wait ...\n") + buildPyLoT(verbose) cleanUp() return 0 except KeyboardInterrupt: From 72fa9e8feb3153fb1923180212870b299f0f8d3e Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 29 Apr 2016 11:50:28 +0200 Subject: [PATCH 0875/1144] [install] did some work on the implementation of an installation script --- makePyLoT.py | 43 ++++++++++++++++++++++++++++++++++++++++--- setup.py | 14 ++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 setup.py diff --git a/makePyLoT.py b/makePyLoT.py index 3cfaddd1..1e87f1fb 100644 --- a/makePyLoT.py +++ b/makePyLoT.py @@ -27,6 +27,8 @@ updated: Updated import glob import os import sys +import shutil +import copy from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter @@ -123,7 +125,7 @@ def main(argv=None): # IGNORE:C0111 cleanUp() return 0 except KeyboardInterrupt: - cleanUp(verbose) + cleanUp(1) return 0 except Exception as e: if DEBUG or TESTRUN: @@ -156,11 +158,46 @@ def buildPyLoT(verbosity=None): def installPyLoT(verbosity=None): - pass + files_to_copy = {'autoPyLoT_local.in':['~', '.pylot'], + 'autoPyLoT_regional.in':['~', '.pylot'], + 'filter.in':['~', '.pylot']} + if verbosity > 0: + print ('starting installation of PyLoT ...') + if verbosity > 1: + print ('copying input files into destination folder ...') + ans = input('please specify scope of interest ' + '([0]=local, 1=regional) :') or 0 + if not isinstance(ans, int): + ans = int(ans) + ans = 'local' if ans is 0 else 'regional' + link_dest = [] + for file, destination in files_to_copy.items(): + link_file = ans in file + if link_file: + link_dest = copy.deepcopy(destination) + link_dest.append('autoPyLoT.in') + link_dest = os.path.join(*link_dest) + destination.append(file) + destination = os.path.join(*destination) + srcfile = os.path.join('input', file) + assert not os.path.isabs(srcfile), 'source files seem to be ' \ + 'corrupted ...' + if verbosity > 1: + print ('copying file {file} to folder {dest}'.format(file=file, dest=destination)) + shutil.copyfile(srcfile, destination) + if link_file: + if verbosity: + print('linking input file for autoPyLoT ...') + os.symlink(destination, link_dest) + + def cleanUp(verbosity=None): - pass + if verbosity >= 1: + print('cleaning up build files...') + if sys.platform == 'darwin': + os.remove('./PyLoT') if __name__ == "__main__": diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..3687255e --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ +from distutils.core import setup + +setup( + name='PyLoT', + version='0.1a1', + packages=['pylot', 'pylot.core', 'pylot.core.loc', 'pylot.core.pick', + 'pylot.core.read', 'pylot.core.util', 'pylot.core.active', + 'pylot.core.analysis', 'pylot.testing'], + url='dummy', + license='LGPLv3', + author='Sebastian Wehling-Benatelli', + author_email='sebastian.wehling@rub.de', + description='Comprehensive Python picking and Location Toolbox for seismological data.' +) From edd8920d545ed6fb97e31f422bcdf556b6da1a88 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sun, 1 May 2016 21:10:30 +0200 Subject: [PATCH 0876/1144] WALL-E --- Small robot, big job! Restructuring the code and preparing implementation of a re-assessment tool for PILOT phases. --- QtPyLoT.py | 6 +- autoPyLoT.py | 20 +- pylot/core/analysis/magnitude.py | 2 +- pylot/core/{read => io}/__init__.py | 0 pylot/core/{read => io}/data.py | 2 +- pylot/core/{read => io}/inputs.py | 4 +- pylot/core/io/phases.py | 385 +++++++++++++++++++++++++++ pylot/core/loc/nll.py | 2 +- pylot/core/pick/autopick.py | 4 +- pylot/core/pick/compare.py | 8 +- pylot/core/pick/utils.py | 163 +----------- pylot/core/read/io.py | 191 ------------- pylot/core/util/structure.py | 2 +- pylot/core/util/widgets.py | 2 +- scripts/pylot-reasses-pilot-event.py | 18 ++ setup.py | 2 +- 16 files changed, 431 insertions(+), 380 deletions(-) rename pylot/core/{read => io}/__init__.py (100%) rename pylot/core/{read => io}/data.py (99%) rename pylot/core/{read => io}/inputs.py (98%) create mode 100644 pylot/core/io/phases.py delete mode 100644 pylot/core/read/io.py create mode 100644 scripts/pylot-reasses-pilot-event.py diff --git a/QtPyLoT.py b/QtPyLoT.py index 285e2ffb..a96d23c3 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -40,10 +40,10 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ import numpy as np from obspy import UTCDateTime -from pylot.core.read.data import Data -from pylot.core.read.inputs import FilterOptions, AutoPickParameter +from pylot.core.io.data import Data +from pylot.core.io.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent -from pylot.core.read.io import picks_from_evt +from pylot.core.io.phases import picks_from_evt from pylot.core.loc.nll import locate as locateNll from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP,\ AUTOMATIC_DEFAULTS diff --git a/autoPyLoT.py b/autoPyLoT.py index eb218ca7..cf606edf 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- from __future__ import print_function -import os + import argparse import glob -import subprocess import string + import numpy as np -from obspy.core import read, UTCDateTime -from pylot.core.read.data import Data -from pylot.core.read.inputs import AutoPickParameter -from pylot.core.util.structure import DATASTRUCTURE -from pylot.core.pick.autopick import autopickevent, iteratepicker -from pylot.core.loc.nll import * -from pylot.core.util.version import get_git_version as _getVersionString + from pylot.core.analysis.magnitude import M0Mw +from pylot.core.io.data import Data +from pylot.core.io.inputs import AutoPickParameter +from pylot.core.loc.nll import * +from pylot.core.pick.autopick import autopickevent, iteratepicker +from pylot.core.util.structure import DATASTRUCTURE +from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -27,7 +27,7 @@ def autoPyLoT(inputfile): :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.read.input.AutoPickParameter` + `~pylot.core.io.inputs.AutoPickParameter` :type inputfile: str :return: diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index ace6f0aa..1e494560 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -13,7 +13,7 @@ from pylot.core.pick.utils import getsignalwin, crossings_nonzero_all from pylot.core.util.utils import getPatternLine from scipy.optimize import curve_fit from scipy import integrate, signal -from pylot.core.read.data import Data +from pylot.core.io.data import Data class Magnitude(object): diff --git a/pylot/core/read/__init__.py b/pylot/core/io/__init__.py similarity index 100% rename from pylot/core/read/__init__.py rename to pylot/core/io/__init__.py diff --git a/pylot/core/read/data.py b/pylot/core/io/data.py similarity index 99% rename from pylot/core/read/data.py rename to pylot/core/io/data.py index b7b961d7..2b95048d 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/io/data.py @@ -9,7 +9,7 @@ from obspy.core import read, Stream, UTCDateTime from obspy import read_events, read_inventory from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID -from pylot.core.read.io import readPILOTEvent +from pylot.core.io.phases import readPILOTEvent from pylot.core.util.utils import fnConstructor, getGlobalTimes from pylot.core.util.errors import FormatError, OverwriteError diff --git a/pylot/core/read/inputs.py b/pylot/core/io/inputs.py similarity index 98% rename from pylot/core/read/inputs.py rename to pylot/core/io/inputs.py index 035835f7..f9ebdfc9 100644 --- a/pylot/core/read/inputs.py +++ b/pylot/core/io/inputs.py @@ -40,13 +40,13 @@ class AutoPickParameter(object): ''' Initialize parameter object: - read content of an ASCII file an form a type consistent dictionary + io content of an ASCII file an form a type consistent dictionary contain all parameters. ''' self.__filename = fnin parFileCont = {} - # read from parsed arguments alternatively + # io from parsed arguments alternatively for key, val in kwargs.items(): parFileCont[key] = val diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py new file mode 100644 index 00000000..98977b69 --- /dev/null +++ b/pylot/core/io/phases.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os + +import scipy.io as sio +import obspy.core.event as ope +from obspy.core import UTCDateTime + +from pylot.core.util.utils import getOwner, createPick, createArrival, \ + createEvent, createOrigin, createMagnitude + + +def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): + """ + readPILOTEvent - function + + Reads Matlab PHASES and LOC files written by Matlab versions of PILOT and + converts the data into an ObsPy Event object which is returned to the + calling program. + + :rtype : ~obspy.core.event.Event + :param eventID: + :param authority: + :param kwargs: + :param phasfn: filename of the old PILOT Matlab PHASES file + :param locfn: filename of the old PILOT Matlab LOC file + :return event: event object containing event and phase information + """ + sdir = os.path.split(phasfn)[0] + if phasfn is not None and os.path.isfile(phasfn): + phases = sio.loadmat(phasfn) + phasctime = UTCDateTime(os.path.getmtime(phasfn)) + phasauthor = getOwner(phasfn) + else: + phases = None + phasctime = None + phasauthor = None + if locfn is not None and os.path.isfile(locfn): + loc = sio.loadmat(locfn) + locctime = UTCDateTime(os.path.getmtime(locfn)) + locauthor = getOwner(locfn) + else: + loc = None + locctime = None + locauthor = None + pickcinfo = ope.CreationInfo(agency_id=authority_id, + author=phasauthor, + creation_time=phasctime) + loccinfo = ope.CreationInfo(agency_id=authority_id, + author=locauthor, + creation_time=locctime) + np = 0 + try: + eventNum = loc['ID'][0] + + # retrieve eventID for the actual database + idsplit = eventNum.split('.') + + # retrieve date information + julday = int(idsplit[1]) + year = int(idsplit[2]) + hour = int(loc['hh']) + minute = int(loc['mm']) + second = int(loc['ss']) + + if year + 2000 < UTCDateTime.utcnow().year: + year += 2000 + else: + year += 1900 + + eventDate = UTCDateTime(year=year, julday=julday, hour=hour, + minute=minute, second=second) + + stations = [stat for stat in phases['stat'][0:-1:3]] + + event = createEvent(eventDate, loccinfo, etype='earthquake', resID=eventNum, + authority_id=authority_id) + + lat = float(loc['LAT']) + lon = float(loc['LON']) + dep = float(loc['DEP']) + + origin = createOrigin(eventDate, loccinfo, lat, lon, dep) + for n, pick in enumerate(phases['Ptime']): + if pick[0] > 0: + kwargs = {'year': int(pick[0]), + 'month': int(pick[1]), + 'day': int(pick[2]), + 'hour': int(pick[3]), + 'minute': int(pick[4]), + 'second': int(str(pick[5]).split('.')[0]), + 'microsecond': int(str(pick[5]).split('.')[1][0:6])} + spick = phases['Stime'][n] + if spick[0] > 0: + skwargs = {'year': int(spick[0]), + 'month': int(spick[1]), + 'day': int(spick[2]), + 'hour': int(spick[3]), + 'minute': int(spick[4]), + 'second': int(str(spick[5]).split('.')[0]), + 'microsecond': int(str(spick[5]).split('.')[1][0:6])} + spicktime = UTCDateTime(**skwargs) + else: + spicktime = None + ppicktime = UTCDateTime(**kwargs) + + for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: + if picktime is not None: + if phase == 'P': + wffn = os.path.join(sdir, '{0}*{1}*'.format( + stations[n].strip(), 'z')) + else: + wffn = os.path.join(sdir, '{0}*{1}*'.format( + stations[n].strip(), '[ne]')) + print(wffn) + pick = createPick(eventDate, np, picktime, eventNum, pickcinfo, + phase, stations[n], wffn, authority_id) + event.picks.append(pick) + pickID = pick.get('id') + arrival = createArrival(pickID, pickcinfo, phase) + origin.arrivals.append(arrival) + np += 1 + + magnitude = createMagnitude(origin.get('id'), loccinfo) + magnitude.mag = float(loc['Mnet']) + magnitude.magnitude_type = 'Ml' + + event.picks.append(pick) + event.origins.append(origin) + event.magnitudes.append(magnitude) + return event + + except AttributeError as e: + raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ + insufficient data!'.format(e, phasfn, locfn)) + + +def picks_from_pilot(fn): + picks = dict() + phases_pilot = sio.loadmat(fn) + stations = stations_from_pilot(phases_pilot['stat']) + for n, station in enumerate(stations): + phases = dict() + for onset_name in 'PS': + onset_label = '{0}time'.format(onset_name) + pick = phases_pilot[onset_label][n] + if not pick[0]: + continue + pick = convert_pilot_times(pick) + phases[onset_name] = dict(mpp=pick) + picks[station] = phases + + return picks + + +def stations_from_pilot(stat_array): + stations = list() + cur_stat = None + for stat in stat_array: + if stat == cur_stat: + continue + cur_stat = stat + stations.append(stat.strip()) + + return stations + + +def convert_pilot_times(time_array): + times = [int(time) for time in time_array] + microseconds = int((time_array[-1] - times[-1]) * 1e6) + times.append(microseconds) + return UTCDateTime(*times) + + +def picks_from_obs(fn): + picks = dict() + station_name = str() + for line in open(fn, 'r'): + if line.startswith('#'): + continue + else: + phase_line = line.split() + if not station_name == phase_line[0]: + phase = dict() + station_name = phase_line[0] + phase_name = phase_line[4].upper() + pick = UTCDateTime(phase_line[6] + phase_line[7] + phase_line[8]) + phase[phase_name] = dict(mpp=pick, fm=phase_line[5]) + picks[station_name] = phase + return picks + + +def picks_from_evt(evt): + ''' + Takes an Event object and return the pick dictionary commonly used within + PyLoT + :param evt: Event object contain all available information + :type evt: `~obspy.core.event.Event` + :return: pick dictionary + ''' + picks = {} + for pick in evt.picks: + phase = {} + station = pick.waveform_id.station_code + try: + onsets = picks[station] + except KeyError as e: + print(e) + onsets = {} + mpp = pick.time + lpp = mpp + pick.time_errors.upper_uncertainty + epp = mpp - pick.time_errors.lower_uncertainty + spe = pick.time_errors.uncertainty + phase['mpp'] = mpp + phase['epp'] = epp + phase['lpp'] = lpp + phase['spe'] = spe + try: + picker = str(pick.method_id) + if picker.startswith('smi:local/'): + picker = picker.split('smi:local/')[1] + phase['picker'] = picker + except IndexError: + pass + + onsets[pick.phase_hint] = phase.copy() + picks[station] = onsets.copy() + return picks + + +def writephases(arrivals, fformat, filename): + ''' + Function of methods to write phases to the following standard file + formats used for locating earthquakes: + + HYPO71, NLLoc, VELEST, HYPOSAT, and hypoDD + + :param: arrivals + :type: dictionary containing all phase information including + station ID, phase, first motion, weight (uncertainty), + .... + + :param: fformat + :type: string, chosen file format (location routine), + choose between NLLoc, HYPO71, HYPOSAT, VELEST, + HYPOINVERSE, and hypoDD + + :param: filename, full path and name of phase file + :type: string + ''' + + if fformat == 'NLLoc': + print ("Writing phases to %s for NLLoc" % filename) + fid = open("%s" % filename, 'w') + # write header + fid.write('# EQEVENT: Label: EQ001 Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n') + for key in arrivals: + # P onsets + if arrivals[key]['P']: + fm = arrivals[key]['P']['fm'] + if fm == None: + fm = '?' + onset = arrivals[key]['P']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + if arrivals[key]['P']['weight'] < 4: + pweight = 1 # use pick + else: + pweight = 0 # do not use pick + fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms, + pweight)) + # S onsets + if arrivals[key]['S']: + fm = '?' + onset = arrivals[key]['S']['mpp'] + year = onset.year + month = onset.month + day = onset.day + hh = onset.hour + mm = onset.minute + ss = onset.second + ms = onset.microsecond + ss_ms = ss + ms / 1000000.0 + if arrivals[key]['S']['weight'] < 4: + sweight = 1 # use pick + else: + sweight = 0 # do not use pick + fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, + fm, + year, + month, + day, + hh, + mm, + ss_ms, + sweight)) + + fid.close() + + elif fformat == 'HYPO71': + print ("Writing phases to %s for HYPO71" % filename) + fid = open("%s" % filename, 'w') + # write header + fid.write(' EQ001\n') + for key in arrivals: + if arrivals[key]['P']['weight'] < 4: + Ponset = arrivals[key]['P']['mpp'] + Sonset = arrivals[key]['S']['mpp'] + pweight = arrivals[key]['P']['weight'] + sweight = arrivals[key]['S']['weight'] + fm = arrivals[key]['P']['fm'] + if fm is None: + fm = '-' + Ao = arrivals[key]['S']['Ao'] + if Ao is None: + Ao = '' + else: + Ao = str('%7.2f' % Ao) + year = Ponset.year + if year >= 2000: + year = year - 2000 + else: + year = year - 1900 + month = Ponset.month + day = Ponset.day + hh = Ponset.hour + mm = Ponset.minute + ss = Ponset.second + ms = Ponset.microsecond + ss_ms = ss + ms / 1000000.0 + if pweight < 2: + pstr = 'I' + elif pweight >= 2: + pstr = 'E' + if arrivals[key]['S']['weight'] < 4: + Sss = Sonset.second + Sms = Sonset.microsecond + Sss_ms = Sss + Sms / 1000000.0 + Sss_ms = str('%5.02f' % Sss_ms) + if sweight < 2: + sstr = 'I' + elif sweight >= 2: + sstr = 'E' + fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s%sS %d %s\n' % (key, + pstr, + fm, + pweight, + year, + month, + day, + hh, + mm, + ss_ms, + Sss_ms, + sstr, + sweight, + Ao)) + else: + fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s\n' % (key, + pstr, + fm, + pweight, + year, + month, + day, + hh, + mm, + ss_ms, + Ao)) + + fid.close() diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index bd4ba4cf..956e8e50 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -3,7 +3,7 @@ import subprocess import os -from pylot.core.pick.utils import writephases +from pylot.core.io.phases import writephases from pylot.core.util.utils import getPatternLine from pylot.core.util.version import get_git_version as _getVersionString diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index b6fa4d33..f6b5e01f 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,14 +11,14 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np -from pylot.core.read.inputs import AutoPickParameter +from pylot.core.io.inputs import AutoPickParameter from pylot.core.pick.picker import AICPicker, PragPicker from pylot.core.pick.charfuns import CharacteristicFunction from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \ getSNR, fmpicker, checkPonsets, wadaticheck from pylot.core.util.utils import getPatternLine -from pylot.core.read.data import Data +from pylot.core.io.data import Data from pylot.core.analysis.magnitude import WApp diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index a5db47c5..27a9f1b0 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -9,7 +9,7 @@ import matplotlib.pyplot as plt from obspy import read_events -from pylot.core.read.io import picks_from_evt +from pylot.core.io.phases import picks_from_evt from pylot.core.util.pdf import ProbabilityDensityFunction from pylot.core.util.version import get_git_version as _getVersionString @@ -251,9 +251,3 @@ class PDFDictionary(object): return pdf_picks - -#comp_obj = Comparison(manual='/home/sebastianp/Data/Insheim/e0019.048.13.xml', -# auto='/data/Geothermie/Insheim/EVENT_DATA/LOCAL/RefDB/e0019.048.13/autoPyLoT.xml') -#comp_obj.plot() -#comp_obj.hist_expectation() -#comp_obj.hist_standard_deviation() diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9f9ef832..6e7fe2f5 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -9,11 +9,12 @@ :author: Ludger Kueperkoch / MAGS2 EP3 working group """ -import numpy as np -import matplotlib.pyplot as plt -from obspy.core import Stream, UTCDateTime import warnings +import matplotlib.pyplot as plt +import numpy as np +from obspy.core import Stream, UTCDateTime + def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode=False): ''' @@ -937,162 +938,6 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): return returnflag -def writephases(arrivals, fformat, filename): - ''' - Function of methods to write phases to the following standard file - formats used for locating earthquakes: - - HYPO71, NLLoc, VELEST, HYPOSAT, and hypoDD - - :param: arrivals - :type: dictionary containing all phase information including - station ID, phase, first motion, weight (uncertainty), - .... - - :param: fformat - :type: string, chosen file format (location routine), - choose between NLLoc, HYPO71, HYPOSAT, VELEST, - HYPOINVERSE, and hypoDD - - :param: filename, full path and name of phase file - :type: string - ''' - - if fformat == 'NLLoc': - print ("Writing phases to %s for NLLoc" % filename) - fid = open("%s" % filename, 'w') - # write header - fid.write('# EQEVENT: Label: EQ001 Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n') - for key in arrivals: - # P onsets - if arrivals[key]['P']: - fm = arrivals[key]['P']['fm'] - if fm == None: - fm = '?' - onset = arrivals[key]['P']['mpp'] - year = onset.year - month = onset.month - day = onset.day - hh = onset.hour - mm = onset.minute - ss = onset.second - ms = onset.microsecond - ss_ms = ss + ms / 1000000.0 - if arrivals[key]['P']['weight'] < 4: - pweight = 1 # use pick - else: - pweight = 0 # do not use pick - fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, - fm, - year, - month, - day, - hh, - mm, - ss_ms, - pweight)) - # S onsets - if arrivals[key]['S']: - fm = '?' - onset = arrivals[key]['S']['mpp'] - year = onset.year - month = onset.month - day = onset.day - hh = onset.hour - mm = onset.minute - ss = onset.second - ms = onset.microsecond - ss_ms = ss + ms / 1000000.0 - if arrivals[key]['S']['weight'] < 4: - sweight = 1 # use pick - else: - sweight = 0 # do not use pick - fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, - fm, - year, - month, - day, - hh, - mm, - ss_ms, - sweight)) - - fid.close() - - elif fformat == 'HYPO71': - print ("Writing phases to %s for HYPO71" % filename) - fid = open("%s" % filename, 'w') - # write header - fid.write(' EQ001\n') - for key in arrivals: - if arrivals[key]['P']['weight'] < 4: - Ponset = arrivals[key]['P']['mpp'] - Sonset = arrivals[key]['S']['mpp'] - pweight = arrivals[key]['P']['weight'] - sweight = arrivals[key]['S']['weight'] - fm = arrivals[key]['P']['fm'] - if fm is None: - fm = '-' - Ao = arrivals[key]['S']['Ao'] - if Ao is None: - Ao = '' - else: - Ao = str('%7.2f' % Ao) - year = Ponset.year - if year >= 2000: - year = year - 2000 - else: - year = year - 1900 - month = Ponset.month - day = Ponset.day - hh = Ponset.hour - mm = Ponset.minute - ss = Ponset.second - ms = Ponset.microsecond - ss_ms = ss + ms / 1000000.0 - if pweight < 2: - pstr = 'I' - elif pweight >= 2: - pstr = 'E' - if arrivals[key]['S']['weight'] < 4: - Sss = Sonset.second - Sms = Sonset.microsecond - Sss_ms = Sss + Sms / 1000000.0 - Sss_ms = str('%5.02f' % Sss_ms) - if sweight < 2: - sstr = 'I' - elif sweight >= 2: - sstr = 'E' - fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s%sS %d %s\n' % (key, - pstr, - fm, - pweight, - year, - month, - day, - hh, - mm, - ss_ms, - Sss_ms, - sstr, - sweight, - Ao)) - else: - fid.write('%s%sP%s%d %02d%02d%02d%02d%02d%5.2f %s\n' % (key, - pstr, - fm, - pweight, - year, - month, - day, - hh, - mm, - ss_ms, - Ao)) - - fid.close() - - if __name__ == '__main__': import doctest diff --git a/pylot/core/read/io.py b/pylot/core/read/io.py deleted file mode 100644 index f34a7616..00000000 --- a/pylot/core/read/io.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os - -import scipy.io as sio -import obspy.core.event as ope -from obspy.core import UTCDateTime - -from pylot.core.util.utils import getOwner, createPick, createArrival, \ - createEvent, createOrigin, createMagnitude - - -def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): - """ - readPILOTEvent - function - - Reads Matlab PHASES and LOC files written by Matlab versions of PILOT and - converts the data into an ObsPy Event object which is returned to the - calling program. - - :rtype : ~obspy.core.event.Event - :param eventID: - :param authority: - :param kwargs: - :param phasfn: filename of the old PILOT Matlab PHASES file - :param locfn: filename of the old PILOT Matlab LOC file - :return event: event object containing event and phase information - """ - sdir = os.path.split(phasfn)[0] - if phasfn is not None and os.path.isfile(phasfn): - phases = sio.loadmat(phasfn) - phasctime = UTCDateTime(os.path.getmtime(phasfn)) - phasauthor = getOwner(phasfn) - else: - phases = None - phasctime = None - phasauthor = None - if locfn is not None and os.path.isfile(locfn): - loc = sio.loadmat(locfn) - locctime = UTCDateTime(os.path.getmtime(locfn)) - locauthor = getOwner(locfn) - else: - loc = None - locctime = None - locauthor = None - pickcinfo = ope.CreationInfo(agency_id=authority_id, - author=phasauthor, - creation_time=phasctime) - loccinfo = ope.CreationInfo(agency_id=authority_id, - author=locauthor, - creation_time=locctime) - np = 0 - try: - eventNum = loc['ID'][0] - - # retrieve eventID for the actual database - idsplit = eventNum.split('.') - - # retrieve date information - julday = int(idsplit[1]) - year = int(idsplit[2]) - hour = int(loc['hh']) - minute = int(loc['mm']) - second = int(loc['ss']) - - if year + 2000 < UTCDateTime.utcnow().year: - year += 2000 - else: - year += 1900 - - eventDate = UTCDateTime(year=year, julday=julday, hour=hour, - minute=minute, second=second) - - stations = [stat for stat in phases['stat'][0:-1:3]] - - event = createEvent(eventDate, loccinfo, etype='earthquake', resID=eventNum, - authority_id=authority_id) - - lat = float(loc['LAT']) - lon = float(loc['LON']) - dep = float(loc['DEP']) - - origin = createOrigin(eventDate, loccinfo, lat, lon, dep) - for n, pick in enumerate(phases['Ptime']): - if pick[0] > 0: - kwargs = {'year': int(pick[0]), - 'month': int(pick[1]), - 'day': int(pick[2]), - 'hour': int(pick[3]), - 'minute': int(pick[4]), - 'second': int(str(pick[5]).split('.')[0]), - 'microsecond': int(str(pick[5]).split('.')[1][0:6])} - spick = phases['Stime'][n] - if spick[0] > 0: - skwargs = {'year': int(spick[0]), - 'month': int(spick[1]), - 'day': int(spick[2]), - 'hour': int(spick[3]), - 'minute': int(spick[4]), - 'second': int(str(spick[5]).split('.')[0]), - 'microsecond': int(str(spick[5]).split('.')[1][0:6])} - spicktime = UTCDateTime(**skwargs) - else: - spicktime = None - ppicktime = UTCDateTime(**kwargs) - - for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: - if picktime is not None: - if phase == 'P': - wffn = os.path.join(sdir, '{0}*{1}*'.format( - stations[n].strip(), 'z')) - else: - wffn = os.path.join(sdir, '{0}*{1}*'.format( - stations[n].strip(), '[ne]')) - print wffn - pick = createPick(eventDate, np, picktime, eventNum, pickcinfo, - phase, stations[n], wffn, authority_id) - event.picks.append(pick) - pickID = pick.get('id') - arrival = createArrival(pickID, pickcinfo, phase) - origin.arrivals.append(arrival) - np += 1 - - magnitude = createMagnitude(origin.get('id'), loccinfo) - magnitude.mag = float(loc['Mnet']) - magnitude.magnitude_type = 'Ml' - - event.picks.append(pick) - event.origins.append(origin) - event.magnitudes.append(magnitude) - return event - - except AttributeError as e: - raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ - insufficient data!'.format(e, phasfn, locfn)) - -def picks_from_obs(fn): - picks = dict() - station_name = str() - for line in open(fn, 'r'): - if line.startswith('#'): - continue - else: - phase_line = line.split() - if not station_name == phase_line[0]: - phase = dict() - station_name = phase_line[0] - phase_name = phase_line[4].upper() - pick = UTCDateTime(phase_line[6] + phase_line[7] + phase_line[8]) - phase[phase_name] = dict(mpp=pick, fm=phase_line[5]) - picks[station_name] = phase - return picks - - -def picks_from_evt(evt): - ''' - Takes an Event object and return the pick dictionary commonly used within - PyLoT - :param evt: Event object contain all available information - :type evt: `~obspy.core.event.Event` - :return: pick dictionary - ''' - picks = {} - for pick in evt.picks: - phase = {} - station = pick.waveform_id.station_code - try: - onsets = picks[station] - except KeyError as e: - print(e) - onsets = {} - mpp = pick.time - lpp = mpp + pick.time_errors.upper_uncertainty - epp = mpp - pick.time_errors.lower_uncertainty - spe = pick.time_errors.uncertainty - phase['mpp'] = mpp - phase['epp'] = epp - phase['lpp'] = lpp - phase['spe'] = spe - try: - picker = str(pick.method_id) - if picker.startswith('smi:local/'): - picker = picker.split('smi:local/')[1] - phase['picker'] = picker - except IndexError: - pass - - onsets[pick.phase_hint] = phase.copy() - picks[station] = onsets.copy() - return picks diff --git a/pylot/core/util/structure.py b/pylot/core/util/structure.py index 05a0c7f9..68a16552 100644 --- a/pylot/core/util/structure.py +++ b/pylot/core/util/structure.py @@ -6,7 +6,7 @@ Created on Wed Jan 26 17:47:25 2015 @author: sebastianw """ -from pylot.core.read.data import SeiscompDataStructure, PilotDataStructure +from pylot.core.io.data import SeiscompDataStructure, PilotDataStructure DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure, None: None} diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index fcbd0ca5..8b0bdfaf 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -24,7 +24,7 @@ from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime -from pylot.core.read.inputs import FilterOptions +from pylot.core.io.inputs import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \ getResolutionWindow from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS, \ diff --git a/scripts/pylot-reasses-pilot-event.py b/scripts/pylot-reasses-pilot-event.py new file mode 100644 index 00000000..e76de3fa --- /dev/null +++ b/scripts/pylot-reasses-pilot-event.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse +from pylot.core.util.version import get_git_version as _getVersionString +from pylot.core.io.phases import reasses_pilot_event + +__version__ = _getVersionString() +__author__ = 'sebastianw' + +def reassess_pilot_event(): + pass + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + args = parser.parse_args() + reasses_pilot_event(args.id) diff --git a/setup.py b/setup.py index 3687255e..ca588c8e 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name='PyLoT', version='0.1a1', packages=['pylot', 'pylot.core', 'pylot.core.loc', 'pylot.core.pick', - 'pylot.core.read', 'pylot.core.util', 'pylot.core.active', + 'pylot.core.io', 'pylot.core.util', 'pylot.core.active', 'pylot.core.analysis', 'pylot.testing'], url='dummy', license='LGPLv3', From f906211064282132de0c2929337521bd65c985ce Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 2 May 2016 11:19:06 +0200 Subject: [PATCH 0877/1144] fmtomo2vtk merged with fmtomoUtils --- pylot/core/active/fmtomo2vtk.py | 236 ------------------------------- pylot/core/active/fmtomoUtils.py | 17 +++ 2 files changed, 17 insertions(+), 236 deletions(-) delete mode 100644 pylot/core/active/fmtomo2vtk.py diff --git a/pylot/core/active/fmtomo2vtk.py b/pylot/core/active/fmtomo2vtk.py deleted file mode 100644 index cf503e22..00000000 --- a/pylot/core/active/fmtomo2vtk.py +++ /dev/null @@ -1,236 +0,0 @@ -# -*- coding: utf-8 -*- -import numpy as np - -def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'abs', inputfileref = 'vgridsref.in'): - ''' - Generate a vtk-file readable by e.g. paraview from FMTOMO output vgrids.in - - :param: absOrRel, can be "abs" or "rel" for absolute or relative velocities. if "rel" inputfileref must be given - :type: str - ''' - def getDistance(angle): - PI = np.pi - R = 6371. - distance = angle / 180 * (PI * R) - return distance - - def readNumberOfPoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - nR = int(vglines[1].split()[0]) - nTheta = int(vglines[1].split()[1]) - nPhi = int(vglines[1].split()[2]) - - print('readNumberOf Points: Awaiting %d grid points in %s' - %(nR*nTheta*nPhi, filename)) - fin.close() - return nR, nTheta, nPhi - - def readDelta(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - dR = float(vglines[2].split()[0]) - dTheta = float(vglines[2].split()[1]) - dPhi = float(vglines[2].split()[2]) - - fin.close() - return dR, dTheta, dPhi - - def readStartpoints(filename): - fin = open(filename, 'r') - vglines = fin.readlines() - - sR = float(vglines[3].split()[0]) - sTheta = float(vglines[3].split()[1]) - sPhi = float(vglines[3].split()[2]) - - fin.close() - return sR, sTheta, sPhi - - def readVelocity(filename): - ''' - Reads in velocity from vgrids file and returns a list containing all values in the same order - ''' - vel = []; count = 0 - fin = open(filename, 'r') - vglines = fin.readlines() - - for line in vglines: - count += 1 - if count > 4: - vel.append(float(line.split()[0])) - - print("Read %d points out of file: %s" %(count - 4, filename)) - return vel - - R = 6371. # earth radius - outfile = open(outputfile, 'w') - - # Theta, Phi in radians, R in km - nR, nTheta, nPhi = readNumberOfPoints(inputfile) - dR, dTheta, dPhi = readDelta(inputfile) - sR, sTheta, sPhi = readStartpoints(inputfile) - vel = readVelocity(inputfile) - - nX = nPhi; nY = nTheta; nZ = nR - - sZ = sR - R - sX = getDistance(np.rad2deg(sPhi)) - sY = getDistance(np.rad2deg(sTheta)) - - dX = getDistance(np.rad2deg(dPhi)) - dY = getDistance(np.rad2deg(dTheta)) - - nPoints = nX * nY * nZ - - dZ = dR - - # write header - print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Velocity on FMTOMO vgrids.in points\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET STRUCTURED_POINTS\n') - - outfile.writelines('DIMENSIONS %d %d %d\n' %(nX, nY, nZ)) - outfile.writelines('ORIGIN %f %f %f\n' %(sX, sY, sZ)) - outfile.writelines('SPACING %f %f %f\n' %(dX, dY, dZ)) - - outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - if absOrRel == 'abs': - outfile.writelines('SCALARS velocity float %d\n' %(1)) - elif absOrRel == 'rel': - outfile.writelines('SCALARS velChangePercent float %d\n' %(1)) - outfile.writelines('LOOKUP_TABLE default\n') - - # write velocity - if absOrRel == 'abs': - print("Writing velocity values to VTK file...") - for velocity in vel: - outfile.writelines('%10f\n' %velocity) - elif absOrRel == 'rel': - velref = readVelocity(inputfileref) - if not len(velref) == len(vel): - print('ERROR: Number of gridpoints mismatch for %s and %s'%(inputfile, inputfileref)) - return - #velrel = [((vel - velref) / velref * 100) for vel, velref in zip(vel, velref)] - velrel = [] - for velocities in zip(vel, velref): - v, vref = velocities - if not vref == 0: - velrel.append((v - vref) / vref * 100) - else: - velrel.append(0) - - nR_ref, nTheta_ref, nPhi_ref = readNumberOfPoints(inputfileref) - if not nR_ref == nR and nTheta_ref == nTheta and nPhi_ref == nPhi: - print('ERROR: Dimension mismatch of grids %s and %s'%(inputfile, inputfileref)) - return - print("Writing velocity values to VTK file...") - for velocity in velrel: - outfile.writelines('%10f\n' %velocity) - print('Pertubations: min: %s, max: %s'%(min(velrel), max(velrel))) - - outfile.close() - print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) - return - -def rays2VTK(fnin, fdirout = './vtk_files/', nthPoint = 50): - ''' - Writes VTK file(s) for FMTOMO rays from rays.dat. There is one file created for each ray. - - :param: fdirout, output directory, must exist before - :type: str - - :param: nthPoint, plot every nth point of the ray - :type: integer - ''' - def getDistance(angle): - PI = np.pi - R = 6371. - distance = angle / 180 * (PI * R) - return distance - - infile = open(fnin, 'r') - R = 6371 - rays = {} - raynumber = 0 - nPoints = 0 - - ### NOTE: rays.dat seems to be in km and radians - - while True: - raynumber += 1 - firstline = infile.readline() - if firstline == '': break # break at EOF - raynumber = int(firstline.split()[0]) - shotnumber = int(firstline.split()[1]) - rayValid = int(firstline.split()[4]) # is zero if the ray is invalid - if rayValid == 0: - print('Invalid ray number %d for shot number %d'%(raynumber, shotnumber)) - continue - nRayPoints = int(infile.readline().split()[0]) - if not shotnumber in rays.keys(): - rays[shotnumber] = {} - rays[shotnumber][raynumber] = [] - for index in range(nRayPoints): - if index % nthPoint is 0 or index == (nRayPoints - 1): - rad, lat, lon = infile.readline().split() - rays[shotnumber][raynumber].append([getDistance(np.rad2deg(float(lon))), getDistance(np.rad2deg(float(lat))), float(rad) - R]) - else: - dummy = infile.readline() - - infile.close() - - for shotnumber in rays.keys(): - fnameout = fdirout + 'rays%03d.vtk'%(shotnumber) - outfile = open(fnameout, 'w') - - nPoints = 0 - for raynumber in rays[shotnumber]: - for ray in rays[shotnumber][raynumber]: - nPoints += 1 - - # write header - #print("Writing header for VTK file...") - print("Writing shot %d to file %s" %(shotnumber, fnameout)) - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('FMTOMO rays\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' %(nPoints)) - - # write coordinates - #print("Writing coordinates to VTK file...") - - for raynumber in rays[shotnumber].keys(): - for raypoint in rays[shotnumber][raynumber]: - outfile.writelines('%10f %10f %10f \n' %(raypoint[0], raypoint[1], raypoint[2])) - - outfile.writelines('LINES %15d %15d\n' %(len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) - - # write indices - #print("Writing indices to VTK file...") - - count = 0 - for raynumber in rays[shotnumber].keys(): - outfile.writelines('%d ' %(len(rays[shotnumber][raynumber]))) - for index in range(len(rays[shotnumber][raynumber])): - outfile.writelines('%d ' %(count)) - count += 1 - outfile.writelines('\n') - - # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - # outfile.writelines('SCALARS rays float %d\n' %(1)) - # outfile.writelines('LOOKUP_TABLE default\n') - - # # write velocity - # print("Writing velocity values to VTK file...") - # for velocity in vel: - # outfile.writelines('%10f\n' %velocity) - - # outfile.close() - # print("Wrote velocity grid for %d points to file: %s" %(nPoints, outputfile)) - diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 558f2fd4..6bef81f8 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -43,15 +43,32 @@ def vgrids2VTK(inputfile = 'vgrids.in', outputfile = 'vgrids.vtk', absOrRel = 'a outfile.writelines('POINT_DATA %15d\n' %(nPoints)) if absOrRel == 'abs': outfile.writelines('SCALARS velocity float %d\n' %(1)) + if absOrRel == 'relDepth': + outfile.writelines('SCALARS velocity2depthMean float %d\n' %(1)) elif absOrRel == 'rel': outfile.writelines('SCALARS velChangePercent float %d\n' %(1)) outfile.writelines('LOOKUP_TABLE default\n') + pointsPerR = nTheta * nPhi + # write velocity if absOrRel == 'abs': print("Writing velocity values to VTK file...") for velocity in vel: outfile.writelines('%10f\n' %velocity) + elif absOrRel == 'relDepth': + print("Writing velocity values to VTK file relative to mean of each depth...") + index = 0; count = 0 + veldepth = [] + for velocity in vel: + count += 1 + veldepth.append(velocity) + if count%pointsPerR == 0: + velmean = np.mean(veldepth) + #print velmean, count, count/pointsPerR + for vel in veldepth: + outfile.writelines('%10f\n' %(vel - velmean)) + veldepth = [] elif absOrRel == 'rel': nref, dref, sref, velref = _readVgrid(inputfileref) nR_ref, nTheta_ref, nPhi_ref = nref From ec42e1bd155598c2d2e127b641baf085d8fcc00c Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 2 May 2016 12:08:58 +0200 Subject: [PATCH 0878/1144] added first version of fmtomo_parallel script --- pylot/core/active/fmtomoUtils.py | 280 +++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index ef66807a..ec9bcbb3 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -1,7 +1,287 @@ # -*- coding: utf-8 -*- +import os import sys +import subprocess +import datetime import numpy as np +class Tomo3d(object): + def __init__(self, nproc): + self.defParas() + self.nproc = nproc + self.sources = self.readSrcFile() + self.traces = self.readTraces() + + def defParas(self): + self.fmm = 'fm3d' + self.cvg = 'vgrids.in' + self.cig = 'interfaces.in' + self.csl = 'sources.in' + self.pg = 'propgrid.in' + self.rec = 'receivers.in' + #self.ot = 'otimes.dat' + self.frech = 'frechet.in' + self.mode = 'mode_set.in' + self.cwd = subprocess.check_output(['pwd'])[0:-1] + '/' + self.folder = 'test_' + + def runFmm(self, directory, logfile, processes): + os.chdir(directory) + processes.append(subprocess.Popen('fm3d', stdout = None)) +# os.system('%s > %s &'%(self.fmm, logfile)) + os.chdir(self.cwd) + return processes + + def makeDIR(self, directory): + err = os.system('mkdir %s'%directory) + if err is 256: + response = raw_input('Warning: Directory already existing. Continue (y/n)?\n') + if response == 'y': + print('Overwriting existing files.') + else: + sys.exit('Aborted') + + def readNsrc(self): + srcfile = open(self.csl, 'r') + nsrc = int(srcfile.readline()) + srcfile.close() + return nsrc + + def readNtraces(self): + recfile = open(self.rec, 'r') + nrec = int(recfile.readline()) + recfile.close() + return nrec + + def calcSrcPerKernel(self): + nsrc = self.readNsrc() + return nsrc/self.nproc, nsrc%self.nproc + + def srcIDs4Kernel(self, procID): + proc = procID - 1 + nsrc = self.readNsrc() + srcPK, remain = self.calcSrcPerKernel() + if procID > self.nproc: + sys.exit('STOP: Kernel ID exceeds available number.') + if proc < remain: + start = (srcPK + 1) * (proc) + 1 + return range(start, start + srcPK + 1) + elif proc == remain: + start = (srcPK + 1) * (proc) + 1 + return range(start, start + srcPK) + elif proc > remain: + start = (srcPK + 1) * remain + srcPK * (proc - remain) + 1 + return range(start, start + srcPK) + + def startTomo(self): + starttime = datetime.datetime.now() + processes = [] + for procID in range(1, self.nproc + 1): + directory = self.getProcDir(procID) + log_out = self.cwd + 'fm3dlog_' + str(procID) + '.out' + + self.makeDIR(directory) # Problem bei Iteration + self.writeSrcFile(procID, directory) + self.writeTracesFile(procID, directory) + os.system('cp %s %s %s %s %s %s %s' + %(self.cvg, self.cig, self.frech, + self.fmm, self.mode, self.pg, directory)) + processes = self.runFmm(directory, log_out, processes) + + for p in processes: + p.wait() + + self.mergeOutput() + + tdelta = datetime.datetime.now() - starttime + print('Finished after %s'%tdelta) + + def readSrcFile(self): + nsrc = self.readNsrc() + srcfile = open(self.csl, 'r') + + sources = {} + + temp = srcfile.readline() + for index in range(nsrc): + teleflag = int(srcfile.readline()) + coords = srcfile.readline().split() + numpaths = int(srcfile.readline()) + steps = int(srcfile.readline()) + interactions = srcfile.readline().split() + veltype = int(srcfile.readline()) + if teleflag is not 0: + sys.exit('Script not yet usable for teleseismic.') + if numpaths is not 1: + sys.exit('Script not yet usable for more than one path per source.') + + sources[index + 1] = {'teleflag': teleflag, + 'coords': coords, + 'numpaths': numpaths, + 'steps': steps, + 'interactions': interactions, + 'veltype': veltype + } + + return sources + + def readTraces(self): + recfile = open(self.rec, 'r') + ntraces = self.readNtraces() + + traces = {} + + temp = recfile.readline() + for index in range(ntraces): + coords = recfile.readline().split() + paths = int(recfile.readline()) + source = int(recfile.readline()) + path = int(recfile.readline()) + + traces[index + 1] = { 'coords': coords, + 'paths': paths, + 'source': source, + 'path': path + } + + return traces + + def writeSrcFile(self, procID, directory): + srcfile = open('%s/sources.in'%directory, 'w') + sourceIDs = self.srcIDs4Kernel(procID) + + srcfile.writelines('%s\n'%len(sourceIDs)) + for sourceID in sourceIDs: + source = self.sources[sourceID] + coords = source['coords'] + interactions = source['interactions'] + srcfile.writelines('%s\n'%source['teleflag']) + srcfile.writelines('%s %s %s\n'%(float(coords[0]), float(coords[1]), float(coords[2]))) + srcfile.writelines('%s\n'%source['numpaths']) + srcfile.writelines('%s\n'%source['steps']) + srcfile.writelines('%s %s\n'%(int(interactions[0]), int(interactions[1]))) + srcfile.writelines('%s\n'%source['veltype']) + + def writeTracesFile(self, procID, directory): + recfile = open('%s/receivers.in'%directory, 'w') + sourceIDs = self.srcIDs4Kernel(procID) + traceIDs = self.getTraceIDs4Sources(sourceIDs) + + recfile.writelines('%s\n'%len(traceIDs)) + for traceID in traceIDs: + trace = self.traces[traceID] + coords = trace['coords'] + source = int(trace['source']) - sourceIDs[0] + 1 + recfile.writelines('%s %s %s\n'%(float(coords[0]), float(coords[1]), float(coords[2]))) + recfile.writelines('%s\n'%trace['paths']) + recfile.writelines('%s\n'%source) + recfile.writelines('%s\n'%trace['path']) + + def getTraceIDs4Sources(self, sourceIDs): + traceIDs = [] + for traceID in self.traces.keys(): + if self.traces[traceID]['source'] in sourceIDs: + traceIDs.append(traceID) + return traceIDs + + def getTraceIDs4Source(self, sourceID): + traceIDs = [] + for traceID in self.traces.keys(): + if self.traces[traceID]['source'] == sourceID: + traceIDs.append(traceID) + return traceIDs + + def readArrivals(self, procID): + directory = self.getProcDir(procID) + arrfile = open(directory + '/arrivals.dat', 'r') + sourceIDs = self.srcIDs4Kernel(procID) + + arrivals = [] + for sourceID in sourceIDs: + traceIDs = self.getTraceIDs4Source(sourceID) + for traceID in traceIDs: + line = arrfile.readline().split() + if line != []: + # recID and srcID for the individual processor will not be needed + recID_proc, srcID_proc, ray, normal, arrtime, diff, head = line + arrivals.append([traceID, sourceID, ray, normal, arrtime, diff, head]) + + return arrivals + + def readFrechet(self, procID): + directory = self.getProcDir(procID) + frechfile = open(directory + '/frechet.dat', 'r') + sourceIDs = self.srcIDs4Kernel(procID) + + frechet = {} + for sourceID in sourceIDs: + traceIDs = self.getTraceIDs4Source(sourceID) + for traceID in traceIDs: + line = frechfile.readline().split() + if line != []: + # recID and srcID for the individual processor will not be needed + PDEV = [] + recID_proc, srcID_proc, ray, normal, NPDEV = line + for i in range(int(NPDEV)): + PDEV.append(frechfile.readline()) + + frechet[traceID] = {'sourceID': sourceID, + 'raypath': ray, + 'normal': normal, + 'NPDEV': NPDEV, + 'PDEV': PDEV + } + return frechet + + def mergeArrivals(self): + arrivalsOut = open(self.cwd + '/arrivals.dat', 'w') + print('Merging arrivals.dat...') + for procID in range(1, self.nproc + 1): + arrivals = self.readArrivals(procID) + for line in arrivals: + arrivalsOut.writelines('%6s %6s %6s %6s %15s %5s %5s\n'%tuple(line)) + + def mergeFrechet(self): + print('Merging frechet.dat...') + frechetOut = open(self.cwd + '/frechet.dat', 'w') + for procID in range(1, self.nproc + 1): + frechet = self.readFrechet(procID) + traceIDs = frechet.keys() + traceIDs.sort() + for traceID in traceIDs: + frech = frechet[traceID] + frechetOut.writelines('%6s %6s %6s %6s %6s\n'% + (traceID, + frech['sourceID'], + frech['raypath'], + frech['normal'], + frech['NPDEV'])) + for pdev in frech['PDEV']: + frechetOut.writelines(pdev) + + def mergeRays(self): + print('Merging rays.dat...') + filenames = [] + for procID in range(1, self.nproc + 1): + directory = self.getProcDir(procID) + filenames.append(directory + '/rays.dat') + + with open(self.cwd + 'rays.dat', 'w') as outfile: + for fname in filenames: + with open(fname) as infile: + for line in infile: + outfile.write(line) + + def getProcDir(self, procID): + return self.cwd + self.folder + str(procID) + + def mergeOutput(self): + self.mergeArrivals() + self.mergeFrechet() + self.mergeRays() + + + def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', inputfileref='vgridsref.in'): ''' From 8cb4f11bf804aa4944527bdee7dfd1ce70ebec4b Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 2 May 2016 14:10:11 +0200 Subject: [PATCH 0879/1144] [edit] implementing reassessment functions --- pylot/core/pick/utils.py | 4 ++++ scripts/pylot-reasses-pilot-event.py | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 6e7fe2f5..b8cf358d 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -938,6 +938,10 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): return returnflag +def reassess_pilot_event(): + pass + + if __name__ == '__main__': import doctest diff --git a/scripts/pylot-reasses-pilot-event.py b/scripts/pylot-reasses-pilot-event.py index e76de3fa..14f12d76 100644 --- a/scripts/pylot-reasses-pilot-event.py +++ b/scripts/pylot-reasses-pilot-event.py @@ -2,17 +2,19 @@ # -*- coding: utf-8 -*- import argparse + +from pylot.core.pick.utils import reassess_pilot_event from pylot.core.util.version import get_git_version as _getVersionString -from pylot.core.io.phases import reasses_pilot_event __version__ = _getVersionString() __author__ = 'sebastianw' -def reassess_pilot_event(): - pass - if __name__ == '__main__': parser = argparse.ArgumentParser() + parser.add_argument( + '--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ + [s] (Tnoise, Tgap, Tsignal)', action=store_value + ) args = parser.parse_args() - reasses_pilot_event(args.id) + reassess_pilot_event(args.db, args.id, args.TSNR) From 210d39882d21cb2998cc89c312919ad8defa6fa8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 3 May 2016 08:46:13 +0200 Subject: [PATCH 0880/1144] further development on the reassessment routines for PILOT data --- QtPyLoT.py | 4 +- autoPyLoT.py | 66 ++++++------ pylot/core/io/data.py | 37 +------ pylot/core/io/inputs.py | 2 +- pylot/core/io/phases.py | 83 ++++++++++++++- pylot/core/pick/autopick.py | 144 +++++++++++++-------------- pylot/core/pick/compare.py | 4 +- scripts/pylot-reasses-pilot-event.py | 15 ++- setup.py | 3 + 9 files changed, 208 insertions(+), 150 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a96d23c3..11aa6719 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -43,7 +43,7 @@ from obspy import UTCDateTime from pylot.core.io.data import Data from pylot.core.io.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent -from pylot.core.io.phases import picks_from_evt +from pylot.core.io.phases import picks_to_dict from pylot.core.loc.nll import locate as locateNll from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP,\ AUTOMATIC_DEFAULTS @@ -735,7 +735,7 @@ class MainWindow(QMainWindow): return rval def updatePicks(self, type='manual'): - picks = picks_from_evt(evt=self.getData().getEvtData()) + picks = picks_to_dict(evt=self.getData().getEvtData()) if type == 'manual': self.picks.update(picks) elif type == 'auto': diff --git a/autoPyLoT.py b/autoPyLoT.py index cf606edf..89898ec9 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -55,15 +55,15 @@ def autoPyLoT(inputfile): # getting information on data structure if parameter.hasParam('datastructure'): - datastructure = DATASTRUCTURE[parameter.getParam('datastructure')]() - dsfields = {'root': parameter.getParam('rootpath'), - 'dpath': parameter.getParam('datapath'), - 'dbase': parameter.getParam('database')} + datastructure = DATASTRUCTURE[parameter.get('datastructure')]() + dsfields = {'root': parameter.get('rootpath'), + 'dpath': parameter.get('datapath'), + 'dbase': parameter.get('database')} exf = ['root', 'dpath', 'dbase'] if parameter.hasParam('eventID'): - dsfields['eventID'] = parameter.getParam('eventID') + dsfields['eventID'] = parameter.get('eventID') exf.append('eventID') datastructure.modifyFields(**dsfields) @@ -73,20 +73,20 @@ def autoPyLoT(inputfile): if parameter.hasParam('nllocbin'): locflag = 1 # get NLLoc-root path - nllocroot = parameter.getParam('nllocroot') + nllocroot = parameter.get('nllocroot') # get path to NLLoc executable - nllocbin = parameter.getParam('nllocbin') + nllocbin = parameter.get('nllocbin') nlloccall = '%s/NLLoc' % nllocbin # get name of phase file - phasef = parameter.getParam('phasefile') + phasef = parameter.get('phasefile') phasefile = '%s/obs/%s' % (nllocroot, phasef) # get name of NLLoc-control file - ctrf = parameter.getParam('ctrfile') + ctrf = parameter.get('ctrfile') ctrfile = '%s/run/%s' % (nllocroot, ctrf) # pattern of NLLoc ttimes from location grid - ttpat = parameter.getParam('ttpatter') + ttpat = parameter.get('ttpatter') # pattern of NLLoc-output file - nllocoutpatter = parameter.getParam('outpatter') + nllocoutpatter = parameter.get('outpatter') maxnumit = 3 # maximum number of iterations for re-picking else: locflag = 0 @@ -140,10 +140,10 @@ def autoPyLoT(inputfile): # get latest NLLoc-location file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.get('iplot'), \ + nllocfile, picks, parameter.get('rho'), \ + parameter.get('vp'), parameter.get('Qp'), \ + parameter.get('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -182,10 +182,10 @@ def autoPyLoT(inputfile): nlloccounter = maxnumit # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.get('iplot'), \ + nllocfile, picks, parameter.get('rho'), \ + parameter.get('vp'), parameter.get('Qp'), \ + parameter.get('invdir')) # get network moment magntiude netMw = [] for key in finalpicks.getpicdic(): @@ -222,8 +222,8 @@ def autoPyLoT(inputfile): # single event processing else: - data.setWFData(glob.glob(os.path.join(datapath, parameter.getParam('eventID'), '*'))) - print("Working on event {0}".format(parameter.getParam('eventID'))) + data.setWFData(glob.glob(os.path.join(datapath, parameter.get('eventID'), '*'))) + print("Working on event {0}".format(parameter.get('eventID'))) print(data) wfdat = data.getWFData() # all available streams @@ -238,7 +238,7 @@ def autoPyLoT(inputfile): picksExport(picks, 'NLLoc', phasefile) # For locating the event the NLLoc-control file has to be modified! - nllocout = '%s_%s' % (parameter.getParam('eventID'), nllocoutpatter) + nllocout = '%s_%s' % (parameter.get('eventID'), nllocoutpatter) # create comment line for NLLoc-control file modifyInputFile(ctrf, nllocroot, nllocout, phasef, ttpat) @@ -261,10 +261,10 @@ def autoPyLoT(inputfile): # get latest NLLOc-location file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.get('iplot'), \ + nllocfile, picks, parameter.get('rho'), \ + parameter.get('vp'), parameter.get('Qp'), \ + parameter.get('invdir')) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -303,10 +303,10 @@ def autoPyLoT(inputfile): nlloccounter = maxnumit # calculating seismic moment Mo and moment magnitude Mw - finalpicks = M0Mw(wfdat, None, None, parameter.getParam('iplot'), \ - nllocfile, picks, parameter.getParam('rho'), \ - parameter.getParam('vp'), parameter.getParam('Qp'), \ - parameter.getParam('invdir')) + finalpicks = M0Mw(wfdat, None, None, parameter.get('iplot'), \ + nllocfile, picks, parameter.get('rho'), \ + parameter.get('vp'), parameter.get('Qp'), \ + parameter.get('invdir')) # get network moment magntiude netMw = [] for key in finalpicks.getpicdic(): @@ -319,7 +319,7 @@ def autoPyLoT(inputfile): ########################################################## # write phase files for various location routines # HYPO71 - hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.getParam('eventID')) + hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.get('eventID')) if hasattr(finalpicks, 'getpicdic'): if finalpicks.getpicdic() is not None: writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) @@ -330,13 +330,13 @@ def autoPyLoT(inputfile): else: writephases(picks, 'HYPO71', hypo71file) data.applyEVTData(picks) - fnqml = '%s/%s/autoPyLoT' % (datapath, parameter.getParam('eventID')) + fnqml = '%s/%s/autoPyLoT' % (datapath, parameter.get('eventID')) data.exportEvent(fnqml) endsplash = '''------------------------------------------\n' -----Finished event %s!-----\n' ------------------------------------------'''.format \ - (version=_getVersionString()) % parameter.getParam('eventID') + (version=_getVersionString()) % parameter.get('eventID') print(endsplash) if locflag == 0: print("autoPyLoT was running in non-location mode!") diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 2b95048d..faa07cb8 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -3,13 +3,13 @@ import os import glob -import warnings + from obspy.io.xseed import Parser from obspy.core import read, Stream, UTCDateTime from obspy import read_events, read_inventory from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID -from pylot.core.io.phases import readPILOTEvent +from pylot.core.io.phases import readPILOTEvent, picks_from_dict from pylot.core.util.utils import fnConstructor, getGlobalTimes from pylot.core.util.errors import FormatError, OverwriteError @@ -414,35 +414,8 @@ class Data(object): firstonset = None if self.getEvtData().picks: raise OverwriteError('Actual picks would be overwritten!') - for station, onsets in picks.items(): - print('Reading picks on station %s' % station) - for label, phase in onsets.items(): - if not isinstance(phase, dict): - continue - onset = phase['mpp'] - epp = phase['epp'] - lpp = phase['lpp'] - error = phase['spe'] - try: - picker = phase['picker'] - except KeyError as e: - warnings.warn(str(e), Warning) - picker = 'Unknown' - pick = Pick() - pick.time = onset - pick.time_errors.lower_uncertainty = onset - epp - pick.time_errors.upper_uncertainty = lpp - onset - pick.time_errors.uncertainty = error - pick.phase_hint = label - pick.method_id = ResourceIdentifier(id=picker) - pick.waveform_id = WaveformStreamID(station_code=station) - self.getEvtData().picks.append(pick) - try: - polarity = phase['fm'] - except KeyError as e: - print('No polarity information found for %s' % phase) - if firstonset is None or firstonset > onset: - firstonset = onset + picks, firstonset = picks_from_dict(picks) + self.getEvtData().picks = picks if 'smi:local' in self.getID() and firstonset: fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') @@ -501,7 +474,7 @@ class GenericDataStructure(object): if not self.extraAllowed(): kwargs = self.updateNotAllowed(kwargs) - for key, value in kwargs.iteritems(): + for key, value in kwargs.items(): key = str(key).lower() if value is not None: if type(value) not in (str, int, float): diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index f9ebdfc9..431ac731 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -131,7 +131,7 @@ class AutoPickParameter(object): return True return False - def getParam(self, *args): + def get(self, *args): try: for param in args: try: diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 98977b69..f3c6c96a 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -2,7 +2,8 @@ # -*- coding: utf-8 -*- import os - +import glob +import warnings import scipy.io as sio import obspy.core.event as ope from obspy.core import UTCDateTime @@ -173,7 +174,7 @@ def convert_pilot_times(time_array): return UTCDateTime(*times) -def picks_from_obs(fn): +def picksdict_from_obs(fn): picks = dict() station_name = str() for line in open(fn, 'r'): @@ -191,7 +192,7 @@ def picks_from_obs(fn): return picks -def picks_from_evt(evt): +def picks_to_dict(evt): ''' Takes an Event object and return the pick dictionary commonly used within PyLoT @@ -228,6 +229,82 @@ def picks_from_evt(evt): picks[station] = onsets.copy() return picks +def picks_from_dict(picks): + firstonset = None + for station, onsets in picks.items(): + print('Reading picks on station %s' % station) + for label, phase in onsets.items(): + if not isinstance(phase, dict): + continue + onset = phase['mpp'] + epp = phase['epp'] + lpp = phase['lpp'] + error = phase['spe'] + try: + picker = phase['picker'] + except KeyError as e: + warnings.warn(str(e), Warning) + picker = 'Unknown' + pick = ope.Pick() + pick.time = onset + pick.time_errors.lower_uncertainty = onset - epp + pick.time_errors.upper_uncertainty = lpp - onset + pick.time_errors.uncertainty = error + pick.phase_hint = label + pick.method_id = ope.ResourceIdentifier(id=picker) + pick.waveform_id = ope.WaveformStreamID(station_code=station) + try: + polarity = phase['fm'] + if polarity == 'U' or '+': + pick.polarity = 'positive' + elif polarity == 'D' or '-': + pick.polarity = 'negative' + else: + pick.polarity = 'undecidable' + except KeyError as e: + print('No polarity information found for %s' % phase) + if firstonset is None or firstonset > onset: + firstonset = onset + + +def reassess_pilot_event(root_dir, event_id): + from obspy import read + from pylot.core.util.defaults import AUTOMATIC_DEFAULTS + from pylot.core.io.inputs import AutoPickParameter + from pylot.core.pick.utils import earllatepicker + + default = AutoPickParameter(AUTOMATIC_DEFAULTS) + + search_base = os.path.join(root_dir, event_id) + phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat')) + picks_dict = picks_from_pilot(phases_file) + for station in picks_dict.keys(): + fn_pattern = os.path.join(search_base, '{0}*'.format(station)) + try: + st = read(fn_pattern) + except TypeError as e: + print(e.message) + st = read(fn_pattern, format='GSE2') + if not st: + raise RuntimeError('no waveform data found for station {station}'.format(station=station)) + for phase in picks_dict[station].keys(): + try: + mpp = picks_dict[station][phase]['mpp'] + except KeyError as e: + print(e.message, station) + continue + epp, lpp, spe = earllatepicker(st, + default.get('nfac{0}'.format(phase)), + default.get('tsnrz' if phase == 'P' else 'tsnrh'), + mpp + ) + picks_dict[station][phase] = dict(epp=epp, mpp=mpp, lpp=lpp, spe=spe) + # create Event object for export + evt = ope.Event(resource_id=event_id) + evt.picks = picks_from_dict(picks_dict) + # write phase information to file + evt.write('{0}.xml'.format(event_id), format='QUAKEML') + def writephases(arrivals, fformat, filename): ''' diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index f6b5e01f..9879feb7 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -28,9 +28,9 @@ def autopickevent(data, param): # get some parameters for quality control from # parameter input file (usually autoPyLoT.in). - wdttolerance = param.getParam('wdttolerance') - mdttolerance = param.getParam('mdttolerance') - iplot = param.getParam('iplot') + wdttolerance = param.get('wdttolerance') + mdttolerance = param.get('mdttolerance') + iplot = param.get('iplot') for n in range(len(data)): station = data[n].stats.station if station not in stations: @@ -66,60 +66,60 @@ def autopickstation(wfstream, pickparam, verbose=False): # read your autoPyLoT.in for details! # special parameters for P picking - algoP = pickparam.getParam('algoP') - iplot = pickparam.getParam('iplot') - pstart = pickparam.getParam('pstart') - pstop = pickparam.getParam('pstop') - thosmw = pickparam.getParam('tlta') - tsnrz = pickparam.getParam('tsnrz') - hosorder = pickparam.getParam('hosorder') - bpz1 = pickparam.getParam('bpz1') - bpz2 = pickparam.getParam('bpz2') - pickwinP = pickparam.getParam('pickwinP') - tsmoothP = pickparam.getParam('tsmoothP') - ausP = pickparam.getParam('ausP') - nfacP = pickparam.getParam('nfacP') - tpred1z = pickparam.getParam('tpred1z') - tdet1z = pickparam.getParam('tdet1z') - Parorder = pickparam.getParam('Parorder') - addnoise = pickparam.getParam('addnoise') - Precalcwin = pickparam.getParam('Precalcwin') - minAICPslope = pickparam.getParam('minAICPslope') - minAICPSNR = pickparam.getParam('minAICPSNR') - timeerrorsP = pickparam.getParam('timeerrorsP') + algoP = pickparam.get('algoP') + iplot = pickparam.get('iplot') + pstart = pickparam.get('pstart') + pstop = pickparam.get('pstop') + thosmw = pickparam.get('tlta') + tsnrz = pickparam.get('tsnrz') + hosorder = pickparam.get('hosorder') + bpz1 = pickparam.get('bpz1') + bpz2 = pickparam.get('bpz2') + pickwinP = pickparam.get('pickwinP') + tsmoothP = pickparam.get('tsmoothP') + ausP = pickparam.get('ausP') + nfacP = pickparam.get('nfacP') + tpred1z = pickparam.get('tpred1z') + tdet1z = pickparam.get('tdet1z') + Parorder = pickparam.get('Parorder') + addnoise = pickparam.get('addnoise') + Precalcwin = pickparam.get('Precalcwin') + minAICPslope = pickparam.get('minAICPslope') + minAICPSNR = pickparam.get('minAICPSNR') + timeerrorsP = pickparam.get('timeerrorsP') # special parameters for S picking - algoS = pickparam.getParam('algoS') - sstart = pickparam.getParam('sstart') - sstop = pickparam.getParam('sstop') - bph1 = pickparam.getParam('bph1') - bph2 = pickparam.getParam('bph2') - tsnrh = pickparam.getParam('tsnrh') - pickwinS = pickparam.getParam('pickwinS') - tpred1h = pickparam.getParam('tpred1h') - tdet1h = pickparam.getParam('tdet1h') - tpred2h = pickparam.getParam('tpred2h') - tdet2h = pickparam.getParam('tdet2h') - Sarorder = pickparam.getParam('Sarorder') - aictsmoothS = pickparam.getParam('aictsmoothS') - tsmoothS = pickparam.getParam('tsmoothS') - ausS = pickparam.getParam('ausS') - minAICSslope = pickparam.getParam('minAICSslope') - minAICSSNR = pickparam.getParam('minAICSSNR') - Srecalcwin = pickparam.getParam('Srecalcwin') - nfacS = pickparam.getParam('nfacS') - timeerrorsS = pickparam.getParam('timeerrorsS') + algoS = pickparam.get('algoS') + sstart = pickparam.get('sstart') + sstop = pickparam.get('sstop') + bph1 = pickparam.get('bph1') + bph2 = pickparam.get('bph2') + tsnrh = pickparam.get('tsnrh') + pickwinS = pickparam.get('pickwinS') + tpred1h = pickparam.get('tpred1h') + tdet1h = pickparam.get('tdet1h') + tpred2h = pickparam.get('tpred2h') + tdet2h = pickparam.get('tdet2h') + Sarorder = pickparam.get('Sarorder') + aictsmoothS = pickparam.get('aictsmoothS') + tsmoothS = pickparam.get('tsmoothS') + ausS = pickparam.get('ausS') + minAICSslope = pickparam.get('minAICSslope') + minAICSSNR = pickparam.get('minAICSSNR') + Srecalcwin = pickparam.get('Srecalcwin') + nfacS = pickparam.get('nfacS') + timeerrorsS = pickparam.get('timeerrorsS') # parameters for first-motion determination - minFMSNR = pickparam.getParam('minFMSNR') - fmpickwin = pickparam.getParam('fmpickwin') - minfmweight = pickparam.getParam('minfmweight') + minFMSNR = pickparam.get('minFMSNR') + fmpickwin = pickparam.get('fmpickwin') + minfmweight = pickparam.get('minfmweight') # parameters for checking signal length - minsiglength = pickparam.getParam('minsiglength') - minpercent = pickparam.getParam('minpercent') - nfacsl = pickparam.getParam('noisefactor') + minsiglength = pickparam.get('minsiglength') + minpercent = pickparam.get('minpercent') + nfacsl = pickparam.get('noisefactor') # parameter to check for spuriously picked S onset - zfac = pickparam.getParam('zfac') + zfac = pickparam.get('zfac') # path to inventory-, dataless- or resp-files - invdir = pickparam.getParam('invdir') + invdir = pickparam.get('invdir') # initialize output Pweight = 4 # weight for P onset @@ -857,39 +857,39 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter): wf2pick = wf.select(station=badpicks[i][0]) # modify some picking parameters - pstart_old = pickparameter.getParam('pstart') - pstop_old = pickparameter.getParam('pstop') - sstop_old = pickparameter.getParam('sstop') - pickwinP_old = pickparameter.getParam('pickwinP') - Precalcwin_old = pickparameter.getParam('Precalcwin') - noisefactor_old = pickparameter.getParam('noisefactor') - zfac_old = pickparameter.getParam('zfac') + pstart_old = pickparameter.get('pstart') + pstop_old = pickparameter.get('pstop') + sstop_old = pickparameter.get('sstop') + pickwinP_old = pickparameter.get('pickwinP') + Precalcwin_old = pickparameter.get('Precalcwin') + noisefactor_old = pickparameter.get('noisefactor') + zfac_old = pickparameter.get('zfac') pickparameter.setParam( pstart=max([0, badpicks[i][1] - wf2pick[0].stats.starttime \ - - pickparameter.getParam('tlta')])) - pickparameter.setParam(pstop=pickparameter.getParam('pstart') + \ - (3 * pickparameter.getParam('tlta'))) - pickparameter.setParam(sstop=pickparameter.getParam('sstop') / 2) - pickparameter.setParam(pickwinP=pickparameter.getParam('pickwinP') / 2) + - pickparameter.get('tlta')])) + pickparameter.setParam(pstop=pickparameter.get('pstart') + \ + (3 * pickparameter.get('tlta'))) + pickparameter.setParam(sstop=pickparameter.get('sstop') / 2) + pickparameter.setParam(pickwinP=pickparameter.get('pickwinP') / 2) pickparameter.setParam( - Precalcwin=pickparameter.getParam('Precalcwin') / 2) + Precalcwin=pickparameter.get('Precalcwin') / 2) pickparameter.setParam(noisefactor=1.0) pickparameter.setParam(zfac=1.0) print( "iteratepicker: The following picking parameters have been modified for iterative picking:") print( - "pstart: %fs => %fs" % (pstart_old, pickparameter.getParam('pstart'))) + "pstart: %fs => %fs" % (pstart_old, pickparameter.get('pstart'))) print( - "pstop: %fs => %fs" % (pstop_old, pickparameter.getParam('pstop'))) + "pstop: %fs => %fs" % (pstop_old, pickparameter.get('pstop'))) print( - "sstop: %fs => %fs" % (sstop_old, pickparameter.getParam('sstop'))) + "sstop: %fs => %fs" % (sstop_old, pickparameter.get('sstop'))) print("pickwinP: %fs => %fs" % ( - pickwinP_old, pickparameter.getParam('pickwinP'))) + pickwinP_old, pickparameter.get('pickwinP'))) print("Precalcwin: %fs => %fs" % ( - Precalcwin_old, pickparameter.getParam('Precalcwin'))) + Precalcwin_old, pickparameter.get('Precalcwin'))) print("noisefactor: %f => %f" % ( - noisefactor_old, pickparameter.getParam('noisefactor'))) - print("zfac: %f => %f" % (zfac_old, pickparameter.getParam('zfac'))) + noisefactor_old, pickparameter.get('noisefactor'))) + print("zfac: %f => %f" % (zfac_old, pickparameter.get('zfac'))) # repick station newpicks = autopickstation(wf2pick, pickparameter) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 27a9f1b0..d4927ead 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -9,7 +9,7 @@ import matplotlib.pyplot as plt from obspy import read_events -from pylot.core.io.phases import picks_from_evt +from pylot.core.io.phases import picks_to_dict from pylot.core.util.pdf import ProbabilityDensityFunction from pylot.core.util.version import get_git_version as _getVersionString @@ -227,7 +227,7 @@ class PDFDictionary(object): if len(cat) > 1: raise NotImplementedError('reading more than one event at the same ' 'time is not implemented yet! Sorry!') - return PDFDictionary(picks_from_evt(cat[0])) + return PDFDictionary(picks_to_dict(cat[0])) def pdf_data(self, type='exp'): """ diff --git a/scripts/pylot-reasses-pilot-event.py b/scripts/pylot-reasses-pilot-event.py index e76de3fa..f81d738d 100644 --- a/scripts/pylot-reasses-pilot-event.py +++ b/scripts/pylot-reasses-pilot-event.py @@ -3,16 +3,21 @@ import argparse from pylot.core.util.version import get_git_version as _getVersionString -from pylot.core.io.phases import reasses_pilot_event +from pylot.core.io.phases import reassess_pilot_event __version__ = _getVersionString() __author__ = 'sebastianw' -def reassess_pilot_event(): - pass - if __name__ == '__main__': parser = argparse.ArgumentParser() + parser.add_argument( + '--directory', '-d', type=str, help='specifies the root directory (in ' + 'most cases PILOT database folder)' + ) + parser.add_argument( + '--eventid', '-i', type=str, help='PILOT event identifier' + ) + args = parser.parse_args() - reasses_pilot_event(args.id) + reassess_pilot_event(args.dir, args.id) diff --git a/setup.py b/setup.py index ca588c8e..e2a9b8b4 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- from distutils.core import setup setup( @@ -6,6 +8,7 @@ setup( packages=['pylot', 'pylot.core', 'pylot.core.loc', 'pylot.core.pick', 'pylot.core.io', 'pylot.core.util', 'pylot.core.active', 'pylot.core.analysis', 'pylot.testing'], + requires=['obspy', 'PySide'], url='dummy', license='LGPLv3', author='Sebastian Wehling-Benatelli', From a82c1d39c6a54f1f72edba04c59371d655c361f6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 3 May 2016 09:35:31 +0200 Subject: [PATCH 0881/1144] [fix] fixing obvious problems in advance --- pylot/core/io/data.py | 17 ++++++++--------- pylot/core/io/phases.py | 12 +++++++----- pylot/core/pick/utils.py | 4 ---- scripts/pylot-reasses-pilot-event.py | 1 - 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index faa07cb8..01c36d4f 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -411,19 +411,18 @@ class Data(object): :raise OverwriteError: raises an OverwriteError if the picks list is not empty. The GUI will then ask for a decision. """ - firstonset = None + + #firstonset = find_firstonset(picks) if self.getEvtData().picks: raise OverwriteError('Actual picks would be overwritten!') - picks, firstonset = picks_from_dict(picks) + picks = picks_from_dict(picks) self.getEvtData().picks = picks + # if 'smi:local' in self.getID() and firstonset: + # fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') + # ID = ResourceIdentifier('event/' + fonset_str) + # ID.convertIDToQuakeMLURI(authority_id=authority_id) + # self.getEvtData().resource_id = ID - if 'smi:local' in self.getID() and firstonset: - fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') - ID = ResourceIdentifier('event/' + fonset_str) - ID.convertIDToQuakeMLURI(authority_id=authority_id) - self.getEvtData().resource_id = ID - else: - print('No picks to apply!') def applyArrivals(arrivals): """ diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index f3c6c96a..50d4f228 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -230,7 +230,7 @@ def picks_to_dict(evt): return picks def picks_from_dict(picks): - firstonset = None + picks_list = list() for station, onsets in picks.items(): print('Reading picks on station %s' % station) for label, phase in onsets.items(): @@ -262,9 +262,9 @@ def picks_from_dict(picks): else: pick.polarity = 'undecidable' except KeyError as e: - print('No polarity information found for %s' % phase) - if firstonset is None or firstonset > onset: - firstonset = onset + print(e.message, 'No polarity information found for %s' % phase) + picks_list.append(pick) + return picks_list def reassess_pilot_event(root_dir, event_id): @@ -303,7 +303,9 @@ def reassess_pilot_event(root_dir, event_id): evt = ope.Event(resource_id=event_id) evt.picks = picks_from_dict(picks_dict) # write phase information to file - evt.write('{0}.xml'.format(event_id), format='QUAKEML') + fnout_prefix = os.path.join(root_dir, event_id, '{0}.'.format(event_id)) + evt.write(fnout_prefix + 'xml', format='QUAKEML') + evt.write(fnout_prefix + 'cnv', format='VELEST') def writephases(arrivals, fformat, filename): diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index b8cf358d..6e7fe2f5 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -938,10 +938,6 @@ def checkZ4S(X, pick, zfac, checkwin, iplot): return returnflag -def reassess_pilot_event(): - pass - - if __name__ == '__main__': import doctest diff --git a/scripts/pylot-reasses-pilot-event.py b/scripts/pylot-reasses-pilot-event.py index 074f5218..f48a08f2 100644 --- a/scripts/pylot-reasses-pilot-event.py +++ b/scripts/pylot-reasses-pilot-event.py @@ -3,7 +3,6 @@ import argparse -from pylot.core.pick.utils import reassess_pilot_event from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.io.phases import reassess_pilot_event From 41991c5d81929736cef32c92779335d906d628bb Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 3 May 2016 13:54:59 +0200 Subject: [PATCH 0882/1144] [edit] further restructuring and bugfixing --- QtPyLoT.py | 4 ++-- pylot/core/io/data.py | 4 ++-- pylot/core/io/phases.py | 26 +++++++++++++++----------- pylot/core/pick/compare.py | 4 ++-- pylot/core/pick/utils.py | 31 +++++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 11aa6719..6daaffa6 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -43,7 +43,7 @@ from obspy import UTCDateTime from pylot.core.io.data import Data from pylot.core.io.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent -from pylot.core.io.phases import picks_to_dict +from pylot.core.io.phases import picksdict_from_picks from pylot.core.loc.nll import locate as locateNll from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP,\ AUTOMATIC_DEFAULTS @@ -735,7 +735,7 @@ class MainWindow(QMainWindow): return rval def updatePicks(self, type='manual'): - picks = picks_to_dict(evt=self.getData().getEvtData()) + picks = picksdict_from_picks(evt=self.getData().getEvtData()) if type == 'manual': self.picks.update(picks) elif type == 'auto': diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 01c36d4f..4f869932 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -9,7 +9,7 @@ from obspy.core import read, Stream, UTCDateTime from obspy import read_events, read_inventory from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID -from pylot.core.io.phases import readPILOTEvent, picks_from_dict +from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict from pylot.core.util.utils import fnConstructor, getGlobalTimes from pylot.core.util.errors import FormatError, OverwriteError @@ -415,7 +415,7 @@ class Data(object): #firstonset = find_firstonset(picks) if self.getEvtData().picks: raise OverwriteError('Actual picks would be overwritten!') - picks = picks_from_dict(picks) + picks = picks_from_picksdict(picks) self.getEvtData().picks = picks # if 'smi:local' in self.getID() and firstonset: # fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 50d4f228..707b57ed 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -8,9 +8,10 @@ import scipy.io as sio import obspy.core.event as ope from obspy.core import UTCDateTime +from pylot.core.pick.utils import select_for_phase from pylot.core.util.utils import getOwner, createPick, createArrival, \ createEvent, createOrigin, createMagnitude - +from pylot.core.util.defaults import AUTOMATIC_DEFAULTS def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): """ @@ -192,7 +193,7 @@ def picksdict_from_obs(fn): return picks -def picks_to_dict(evt): +def picksdict_from_picks(evt): ''' Takes an Event object and return the pick dictionary commonly used within PyLoT @@ -229,7 +230,7 @@ def picks_to_dict(evt): picks[station] = onsets.copy() return picks -def picks_from_dict(picks): +def picks_from_picksdict(picks): picks_list = list() for station, onsets in picks.items(): print('Reading picks on station %s' % station) @@ -267,17 +268,19 @@ def picks_from_dict(picks): return picks_list -def reassess_pilot_event(root_dir, event_id): +def reassess_pilot_event(root_dir, event_id, fn_param=AUTOMATIC_DEFAULTS): from obspy import read - from pylot.core.util.defaults import AUTOMATIC_DEFAULTS + from pylot.core.io.inputs import AutoPickParameter from pylot.core.pick.utils import earllatepicker - default = AutoPickParameter(AUTOMATIC_DEFAULTS) + default = AutoPickParameter(fn_param) search_base = os.path.join(root_dir, event_id) phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat')) - picks_dict = picks_from_pilot(phases_file) + if not phases_file: + return + picks_dict = picks_from_pilot(phases_file[0]) for station in picks_dict.keys(): fn_pattern = os.path.join(search_base, '{0}*'.format(station)) try: @@ -293,19 +296,20 @@ def reassess_pilot_event(root_dir, event_id): except KeyError as e: print(e.message, station) continue - epp, lpp, spe = earllatepicker(st, + sel_st = select_for_phase(st, phase) + epp, lpp, spe = earllatepicker(sel_st, default.get('nfac{0}'.format(phase)), default.get('tsnrz' if phase == 'P' else 'tsnrh'), - mpp + mpp, None, True ) picks_dict[station][phase] = dict(epp=epp, mpp=mpp, lpp=lpp, spe=spe) # create Event object for export evt = ope.Event(resource_id=event_id) - evt.picks = picks_from_dict(picks_dict) + evt.picks = picks_from_picksdict(picks_dict) # write phase information to file fnout_prefix = os.path.join(root_dir, event_id, '{0}.'.format(event_id)) evt.write(fnout_prefix + 'xml', format='QUAKEML') - evt.write(fnout_prefix + 'cnv', format='VELEST') + #evt.write(fnout_prefix + 'cnv', format='VELEST') def writephases(arrivals, fformat, filename): diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index d4927ead..74a193ac 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -9,7 +9,7 @@ import matplotlib.pyplot as plt from obspy import read_events -from pylot.core.io.phases import picks_to_dict +from pylot.core.io.phases import picksdict_from_picks from pylot.core.util.pdf import ProbabilityDensityFunction from pylot.core.util.version import get_git_version as _getVersionString @@ -227,7 +227,7 @@ class PDFDictionary(object): if len(cat) > 1: raise NotImplementedError('reading more than one event at the same ' 'time is not implemented yet! Sorry!') - return PDFDictionary(picks_to_dict(cat[0])) + return PDFDictionary(picksdict_from_picks(cat[0])) def pdf_data(self, type='exp'): """ diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 6e7fe2f5..1577abb3 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -464,6 +464,37 @@ def getResolutionWindow(snr): return time_resolution / 2 +def select_for_phase(st, phase): + ''' + takes a STream object and a phase name and returns that particular component + which presumably shows the chosen PHASE best + + :param st: stream object containing one or more component[s] + :type st: `~obspy.core.stream.Stream` + :param phase: label of the phase for which the stream selection is carried + out; 'P' or 'S' + :type phase: str + :return: + ''' + from pylot.core.util.defaults import COMPNAME_MAP + + sel_st = Stream() + if phase.upper() is 'P': + comp = 'Z' + alter_comp = COMPNAME_MAP[comp] + sel_st += st.select(component=comp) + sel_st += st.select(component=alter_comp) + elif phase.upper() is 'S': + comps = 'NE' + for comp in comps: + alter_comp = COMPNAME_MAP[comp] + sel_st += st.select(component=comp) + sel_st += st.select(component=alter_comp) + else: + raise TypeError('Unknown phase label: {0}'.format(phase)) + return sel_st + + def wadaticheck(pickdic, dttolerance, iplot): ''' Function to calculate Wadati-diagram from given P and S onsets in order From fd27a43110be683e5a14b8cf6f91668b4f886099 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 3 May 2016 15:09:51 +0200 Subject: [PATCH 0883/1144] fixing import problems --- pylot/core/io/phases.py | 16 ++++++++++++---- pylot/core/pick/utils.py | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 707b57ed..4604576a 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -10,8 +10,7 @@ from obspy.core import UTCDateTime from pylot.core.pick.utils import select_for_phase from pylot.core.util.utils import getOwner, createPick, createArrival, \ - createEvent, createOrigin, createMagnitude -from pylot.core.util.defaults import AUTOMATIC_DEFAULTS + createEvent, createOrigin, createMagnitude, getGlobalTimes def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): """ @@ -268,12 +267,16 @@ def picks_from_picksdict(picks): return picks_list -def reassess_pilot_event(root_dir, event_id, fn_param=AUTOMATIC_DEFAULTS): +def reassess_pilot_event(root_dir, event_id, fn_param=None): from obspy import read from pylot.core.io.inputs import AutoPickParameter from pylot.core.pick.utils import earllatepicker + if fn_param is None: + import pylot.core.util.defaults as defaults + fn_param = defaults.AUTOMATIC_DEFAULTS + default = AutoPickParameter(fn_param) search_base = os.path.join(root_dir, event_id) @@ -297,11 +300,16 @@ def reassess_pilot_event(root_dir, event_id, fn_param=AUTOMATIC_DEFAULTS): print(e.message, station) continue sel_st = select_for_phase(st, phase) + stime, etime = getGlobalTimes(sel_st) + rel_pick = mpp - stime epp, lpp, spe = earllatepicker(sel_st, default.get('nfac{0}'.format(phase)), default.get('tsnrz' if phase == 'P' else 'tsnrh'), - mpp, None, True + Pick1=rel_pick, + iplot=None, ) + epp = stime + epp + lpp = stime + lpp picks_dict[station][phase] = dict(epp=epp, mpp=mpp, lpp=lpp, spe=spe) # create Event object for export evt = ope.Event(resource_id=event_id) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 1577abb3..e603cfa4 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -479,12 +479,12 @@ def select_for_phase(st, phase): from pylot.core.util.defaults import COMPNAME_MAP sel_st = Stream() - if phase.upper() is 'P': + if phase.upper() == 'P': comp = 'Z' alter_comp = COMPNAME_MAP[comp] sel_st += st.select(component=comp) sel_st += st.select(component=alter_comp) - elif phase.upper() is 'S': + elif phase.upper() == 'S': comps = 'NE' for comp in comps: alter_comp = COMPNAME_MAP[comp] From f58c5a5f9d7a74d1ab4a2c93c28e078454ebed4d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 4 May 2016 10:21:39 +0200 Subject: [PATCH 0884/1144] merge correction --- pylot/core/active/fmtomoUtils.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index ec9bcbb3..92ef7e5d 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -325,13 +325,9 @@ def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', i outfile.writelines('POINT_DATA %15d\n' % (nPoints)) if absOrRel == 'abs': -<<<<<<< HEAD outfile.writelines('SCALARS velocity float %d\n' %(1)) if absOrRel == 'relDepth': outfile.writelines('SCALARS velocity2depthMean float %d\n' %(1)) -======= - outfile.writelines('SCALARS velocity float %d\n' % (1)) ->>>>>>> 37f9292c39246b327d3630995ca2521725c6cdd7 elif absOrRel == 'rel': outfile.writelines('SCALARS velChangePercent float %d\n' % (1)) outfile.writelines('LOOKUP_TABLE default\n') @@ -342,7 +338,6 @@ def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', i if absOrRel == 'abs': print("Writing velocity values to VTK file...") for velocity in vel: -<<<<<<< HEAD outfile.writelines('%10f\n' %velocity) elif absOrRel == 'relDepth': print("Writing velocity values to VTK file relative to mean of each depth...") @@ -357,9 +352,6 @@ def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', i for vel in veldepth: outfile.writelines('%10f\n' %(vel - velmean)) veldepth = [] -======= - outfile.writelines('%10f\n' % velocity) ->>>>>>> 37f9292c39246b327d3630995ca2521725c6cdd7 elif absOrRel == 'rel': nref, dref, sref, velref = _readVgrid(inputfileref) nR_ref, nTheta_ref, nPhi_ref = nref From 4480854ee5edd8cf32caa438ec5d3f99c75ec6b9 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 6 May 2016 12:04:27 +0200 Subject: [PATCH 0885/1144] [workaround] set minimum difference of mpp with lpp and epp this workaround elevates the difference between the uncertainty-picks and the mpp to a minimum value of three samples (needed for a reasonable pdf represetation of the pick) --- pylot/core/io/phases.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 4604576a..13ad3eeb 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -310,6 +310,11 @@ def reassess_pilot_event(root_dir, event_id, fn_param=None): ) epp = stime + epp lpp = stime + lpp + min_diff = 3 * st[0].stats.delta + if lpp - mpp < min_diff: + lpp = mpp + min_diff + if mpp - epp < min_diff: + epp = mpp - min_diff picks_dict[station][phase] = dict(epp=epp, mpp=mpp, lpp=lpp, spe=spe) # create Event object for export evt = ope.Event(resource_id=event_id) From 3705eb567bc624ec700a4e72d56cbe096bf91649 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 9 May 2016 12:06:47 +0200 Subject: [PATCH 0886/1144] [fix] overcome numerical instabilities due to the usage of large absolute values (timestamp) on time axis the estimation of the expectation value gets instable --- pylot/core/util/pdf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 776192aa..1a233aed 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -256,9 +256,10 @@ class ProbabilityDensityFunction(object): ''' rval = 0 - for n, x in enumerate(self.axis): + axis = self.axis - self.x0 + for n, x in enumerate(axis): rval += x * self.data[n] - return rval * self.incr + return rval * self.incr + self.x0 def standard_deviation(self): mu = self.expectation() From c7d7acd7e3770f9fe3d7e6cab8169b064c3b2d6b Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 10 May 2016 09:54:21 +0200 Subject: [PATCH 0887/1144] [edit] implemented loop over PILOT database for reassessment additionally the output of reassess_pilot_event is more verbose now --- pylot/core/io/phases.py | 18 ++++++++++++++++-- scripts/pylot-reasses-pilot-event.py | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 13ad3eeb..a46c263b 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -267,7 +267,16 @@ def picks_from_picksdict(picks): return picks_list -def reassess_pilot_event(root_dir, event_id, fn_param=None): +def reassess_pilot_db(root_dir, out_dir=None, fn_param=None): + import glob + + evt_list = glob.glob1(root_dir,'e????.???.??') + + for evt in evt_list: + reassess_pilot_event(root_dir, evt, out_dir, fn_param) + + +def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None): from obspy import read from pylot.core.io.inputs import AutoPickParameter @@ -283,7 +292,9 @@ def reassess_pilot_event(root_dir, event_id, fn_param=None): phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat')) if not phases_file: return + print('Opening PILOT phases file: {fn}'.format(fn=phases_file[0])) picks_dict = picks_from_pilot(phases_file[0]) + print('Dictionary read from PHASES.mat:\n{0}'.format(picks_dict)) for station in picks_dict.keys(): fn_pattern = os.path.join(search_base, '{0}*'.format(station)) try: @@ -320,7 +331,10 @@ def reassess_pilot_event(root_dir, event_id, fn_param=None): evt = ope.Event(resource_id=event_id) evt.picks = picks_from_picksdict(picks_dict) # write phase information to file - fnout_prefix = os.path.join(root_dir, event_id, '{0}.'.format(event_id)) + if not out_dir: + fnout_prefix = os.path.join(root_dir, event_id, '{0}.'.format(event_id)) + else: + fnout_prefix = os.path.join(out_dir, event_id, '{0}.'.format(event_id)) evt.write(fnout_prefix + 'xml', format='QUAKEML') #evt.write(fnout_prefix + 'cnv', format='VELEST') diff --git a/scripts/pylot-reasses-pilot-event.py b/scripts/pylot-reasses-pilot-event.py index f48a08f2..939bf860 100644 --- a/scripts/pylot-reasses-pilot-event.py +++ b/scripts/pylot-reasses-pilot-event.py @@ -21,4 +21,4 @@ if __name__ == '__main__': ) args = parser.parse_args() - reassess_pilot_event(args.dir, args.id) + reassess_pilot_event(args.dir, args.id, None) From 63ac0107d01b440993ad7de88b4042888c4b92c6 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 11 May 2016 06:01:26 +0200 Subject: [PATCH 0888/1144] [edit] implemented a plotting method for pdf dictionaries --- pylot/core/pick/compare.py | 70 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 74a193ac..2be4d7aa 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -86,8 +86,8 @@ class Comparison(object): """ compare_pdfs = dict() - pdf_a = self.get(self.names[0]).pdf_data(type) - pdf_b = self.get(self.names[1]).pdf_data(type) + pdf_a = self.get(self.names[0]).generate_pdf_data(type) + pdf_b = self.get(self.names[1]).generate_pdf_data(type) for station, phases in pdf_a.items(): if station in pdf_b.keys(): @@ -206,6 +206,7 @@ class PDFDictionary(object): def __init__(self, data): self._pickdata = data + self._pdfdata = self.generate_pdf_data() def __nonzero__(self): if len(self.pick_data) < 1: @@ -213,6 +214,14 @@ class PDFDictionary(object): else: return True + @property + def pdf_data(self): + return self._pdfdata + + @pdf_data.setter + def pdf_data(self, data): + self._pdfdata = data + @property def pick_data(self): return self._pickdata @@ -221,6 +230,14 @@ class PDFDictionary(object): def pick_data(self, data): self._pickdata = data + @property + def stations(self): + return self.pick_data.keys() + + @property + def nstations(self): + return len(self.stations) + @classmethod def from_quakeml(self, fn): cat = read_events(fn) @@ -229,7 +246,7 @@ class PDFDictionary(object): 'time is not implemented yet! Sorry!') return PDFDictionary(picksdict_from_picks(cat[0])) - def pdf_data(self, type='exp'): + def generate_pdf_data(self, type='exp'): """ Returns probabiliy density function dictionary containing the representation of the actual pick_data. @@ -251,3 +268,50 @@ class PDFDictionary(object): return pdf_picks + def plot(self, stations=None): + assert stations is not None or not isinstance(stations, list), \ + 'parameter stations should be a list not {0}'.format(type(stations)) + if not stations: + nstations = self.nstations + stations = self.stations + else: + nstations = len(stations) + + istations = range(nstations) + fig, axarr = plt.subplots(nstations, 2, sharex='col', sharey='row') + + for n in istations: + station = stations[n] + pdfs = self.pdf_data[station] + for l, phase in enumerate(pdfs.keys()): + hide_labels = True + try: + axarr[n, l].plot(pdfs[phase].axis, pdfs[phase].data) + if n is 0: + axarr[n, l].set_title(phase) + if l is 0: + axann = axarr[n, l].annotate(station, xy=(.05, .5), + xycoords='axes fraction') + bbox_props = dict(boxstyle='round', facecolor='lightgrey', + alpha=.7) + axann.set_bbox(bbox_props) + if n == int(np.median(istations)) and l is 0: + label = 'probability density (qualitative)' + axarr[n, l].set_ylabel(label) + except IndexError as e: + print('trying aligned plotting\n{0}'.format(e)) + hide_labels = False + axarr[l].plot(pdfs[phase].axis, pdfs[phase].data) + axarr[l].set_title(phase) + if l is 0: + axann = axarr[l].annotate(station, xy=(.05, .5), + xycoords='axes fraction') + bbox_props = dict(boxstyle='round', facecolor='lightgrey', + alpha=.7) + axann.set_bbox(bbox_props) + if hide_labels: + plt.setp([a.get_xticklabels() for a in axarr[0, :]], visible=False) + plt.setp([a.get_yticklabels() for a in axarr[:, 1]], visible=False) + plt.setp([a.get_yticklabels() for a in axarr[:, 0]], visible=False) + + plt.show() From f8807a7ea68b64ee5db5ab808d80c40ba88a0a9a Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 11 May 2016 09:49:19 +0200 Subject: [PATCH 0889/1144] [edit] docstring added and returning the figure instead of showing the plot additionally hide_labels is now defined only once before going into the loop --- pylot/core/pick/compare.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 2be4d7aa..1a828c2a 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -269,6 +269,13 @@ class PDFDictionary(object): return pdf_picks def plot(self, stations=None): + ''' + plots the all probability density function for either desired STATIONS + or all available date + :param stations: list of stations to be plotted + :type stations: list + :return: matplotlib figure object containing the plot + ''' assert stations is not None or not isinstance(stations, list), \ 'parameter stations should be a list not {0}'.format(type(stations)) if not stations: @@ -279,12 +286,12 @@ class PDFDictionary(object): istations = range(nstations) fig, axarr = plt.subplots(nstations, 2, sharex='col', sharey='row') + hide_labels = True for n in istations: station = stations[n] pdfs = self.pdf_data[station] for l, phase in enumerate(pdfs.keys()): - hide_labels = True try: axarr[n, l].plot(pdfs[phase].axis, pdfs[phase].data) if n is 0: @@ -314,4 +321,4 @@ class PDFDictionary(object): plt.setp([a.get_yticklabels() for a in axarr[:, 1]], visible=False) plt.setp([a.get_yticklabels() for a in axarr[:, 0]], visible=False) - plt.show() + return fig From 366ac294813fa8cacfa3bfe04a9940c10e64082f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 May 2016 09:52:59 +0200 Subject: [PATCH 0890/1144] Additional input parameter apverbose to control terminal output --- inputs/autoPyLoT_local.in | 1 + 1 file changed, 1 insertion(+) diff --git a/inputs/autoPyLoT_local.in b/inputs/autoPyLoT_local.in index 329a0fe2..725a9dec 100644 --- a/inputs/autoPyLoT_local.in +++ b/inputs/autoPyLoT_local.in @@ -11,6 +11,7 @@ EVENT_DATA/LOCAL #datapath# %data path /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything +True #apverbose# %choose 'True' or 'False' for terminal output %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #NLLoc settings# /home/ludger/NLLOC #nllocbin# %path to NLLoc executable From 97ca69a07d2da605a196e6097fcf6975edda7147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 May 2016 09:54:33 +0200 Subject: [PATCH 0891/1144] Modified for controlling amount of terminal output using new input parameter apverbose --- pylot/core/pick/autopick.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 9879feb7..3edbea9c 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -31,6 +31,7 @@ def autopickevent(data, param): wdttolerance = param.get('wdttolerance') mdttolerance = param.get('mdttolerance') iplot = param.get('iplot') + apverbose = param.get('apverbose') for n in range(len(data)): station = data[n].stats.station if station not in stations: @@ -40,7 +41,7 @@ def autopickevent(data, param): for station in stations: topick = data.select(station=station) - all_onsets[station] = autopickstation(topick, param) + all_onsets[station] = autopickstation(topick, param, verbose=apverbose) # quality control # median check and jackknife on P-onset times From c34fbe1228a7f1e6df90c0d7008aaa970de0b131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 May 2016 10:51:33 +0200 Subject: [PATCH 0892/1144] Removed needless terminal output --- pylot/core/pick/autopick.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 3edbea9c..efbceff8 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -367,7 +367,6 @@ def autopickstation(wfstream, pickparam, verbose=False): round(min([mpickP + sstop, Lwf]))] if algoS == 'ARH': - if verbose: print(edat, ndat) # re-create stream object including both horizontal components hdat = edat.copy() hdat += ndat @@ -384,7 +383,6 @@ def autopickstation(wfstream, pickparam, verbose=False): h_copy[0].data = trH1_filt.data h_copy[1].data = trH2_filt.data elif algoS == 'AR3': - if verbose: print(zdat, edat, ndat) # re-create stream object including all components hdat = zdat.copy() hdat += edat From f7e1247060b13e5798e69f21db0cbba83d58f9d5 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 11 May 2016 12:01:10 +0200 Subject: [PATCH 0893/1144] [fix] make all scripts executable new script for pilot data base reassessmant --- scripts/pylot-noisewindow.py | 0 scripts/pylot-reasses-pilot-db.py | 33 ++++++++++++++++++++++++++++ scripts/pylot-reasses-pilot-event.py | 22 ++++++++++++++----- scripts/pylot-signalwindow.py | 0 scripts/pylot-snr.py | 0 5 files changed, 50 insertions(+), 5 deletions(-) mode change 100644 => 100755 scripts/pylot-noisewindow.py create mode 100755 scripts/pylot-reasses-pilot-db.py mode change 100644 => 100755 scripts/pylot-reasses-pilot-event.py mode change 100644 => 100755 scripts/pylot-signalwindow.py mode change 100644 => 100755 scripts/pylot-snr.py diff --git a/scripts/pylot-noisewindow.py b/scripts/pylot-noisewindow.py old mode 100644 new mode 100755 diff --git a/scripts/pylot-reasses-pilot-db.py b/scripts/pylot-reasses-pilot-db.py new file mode 100755 index 00000000..5bb4441d --- /dev/null +++ b/scripts/pylot-reasses-pilot-db.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse + +from pylot.core.util.version import get_git_version as _getVersionString +from pylot.core.io.phases import reassess_pilot_db + +__version__ = _getVersionString() +__author__ = 'S. Wehling-Benatelli' + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='reassess old PILOT event data base in terms of consistent ' + 'automatic uncertainty estimation', + epilog='Script written by {author} belonging to PyLoT version' + ' {version}\n'.format(author=__author__, + version=__version__) + ) + + parser.add_argument( + 'dbroot', type=str, help='specifies the root directory (in ' + 'most cases PILOT database folder)' + ) + parser.add_argument( + '--output', '-o', type=str, help='path to the output directory', dest='output' + ) + parser.add_argument( + '--parameterfile', '-p', type=str, help='full path to the parameterfile', dest='parfile' + ) + + args = parser.parse_args() + reassess_pilot_db(args.dbroot, args.output, args.parfile) diff --git a/scripts/pylot-reasses-pilot-event.py b/scripts/pylot-reasses-pilot-event.py old mode 100644 new mode 100755 index 939bf860..1852e680 --- a/scripts/pylot-reasses-pilot-event.py +++ b/scripts/pylot-reasses-pilot-event.py @@ -7,18 +7,30 @@ from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.io.phases import reassess_pilot_event __version__ = _getVersionString() -__author__ = 'sebastianw' +__author__ = 'S. Wehling-Benatelli' if __name__ == '__main__': - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser( + description='reassess old PILOT event data in terms of consistent ' + 'automatic uncertainty estimation', + epilog='Script written by {author} belonging to PyLoT version' + ' {version}\n'.format(author=__author__, + version=__version__) + ) parser.add_argument( - '--directory', '-d', type=str, help='specifies the root directory (in ' + 'dbroot', type=str, help='specifies the root directory (in ' 'most cases PILOT database folder)' ) parser.add_argument( - '--eventid', '-i', type=str, help='PILOT event identifier' + 'id', type=str, help='PILOT event identifier' + ) + parser.add_argument( + '--output', '-o', type=str, help='path to the output directory', dest='output' + ) + parser.add_argument( + '--parameterfile', '-p', type=str, help='full path to the parameterfile', dest='parfile' ) args = parser.parse_args() - reassess_pilot_event(args.dir, args.id, None) + reassess_pilot_event(args.dbroot, args.id, args.output, args.parfile) diff --git a/scripts/pylot-signalwindow.py b/scripts/pylot-signalwindow.py old mode 100644 new mode 100755 diff --git a/scripts/pylot-snr.py b/scripts/pylot-snr.py old mode 100644 new mode 100755 From 46c152b7a139a1cf1e1e81bfb3ba5454d7d289b8 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 11 May 2016 12:02:09 +0200 Subject: [PATCH 0894/1144] improved speed on writing to file (write instead of writelines) --- pylot/core/active/fmtomoUtils.py | 438 +++++++++++++------ pylot/core/active/seismicArrayPreparation.py | 137 +++--- 2 files changed, 375 insertions(+), 200 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 92ef7e5d..7dfe928b 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -6,32 +6,152 @@ import datetime import numpy as np class Tomo3d(object): - def __init__(self, nproc): + def __init__(self, nproc, iterations): + self.setCWD() self.defParas() self.nproc = nproc + self.iter = iterations # number of iterations + self.citer = 0 # current iteration self.sources = self.readSrcFile() self.traces = self.readTraces() - + self.directories = [] + def defParas(self): - self.fmm = 'fm3d' + self.defFMMParas() + self.defInvParas() + + def defFMMParas(self): + self.fmm = '{0}/fm3d'.format(self.cwd) + self.frechgen = '{0}/frechgen'.format(self.cwd) self.cvg = 'vgrids.in' self.cig = 'interfaces.in' self.csl = 'sources.in' self.pg = 'propgrid.in' self.rec = 'receivers.in' - #self.ot = 'otimes.dat' self.frech = 'frechet.in' + self.ot = 'otimes.dat' + self.ttim = 'arrivals.dat' self.mode = 'mode_set.in' - self.cwd = subprocess.check_output(['pwd'])[0:-1] + '/' - self.folder = 'test_' + self.folder = '.proc_' + + def defInvParas(self): + # Name of program for performing inversion + self.inv = '{0}/invert3d'.format(self.cwd) + # Name of file containing current model traveltimes + self.mtrav = 'mtimes.dat' + # Name of file containing reference model traveltimes + self.rtrav = 'rtimes.dat' + # Name of file containing initial velocity grid + self.ivg = 'vgridsref.in' + # Name of file containing initial interface grid + self.iig = 'interfacesref.in' + # Name of file containing initial source locations + self.isl = 'sourcesref.in' + # Name of program for calculating traveltime residuals + self.resid = '{0}/residuals'.format(self.cwd) + # Name of output file for calculating traveltime residuals + self.resout = 'residuals.dat' + + def copyRef(self): + # Attention: Copies reference grids to used grids (e.g. sourcesref.in to sources.in) + os.system('cp %s %s'%(self. ivg, self.cvg)) + os.system('cp %s %s'%(self.iig, self.cig)) + os.system('cp %s %s'%(self.isl, self.csl)) + + def setCWD(self): + self.cwd = subprocess.check_output(['pwd'])[0:-1] + print('Working directory is pwd: %s'%self.cwd) + + def runFrech(self): + os.system(self.frechgen) def runFmm(self, directory, logfile, processes): os.chdir(directory) - processes.append(subprocess.Popen('fm3d', stdout = None)) -# os.system('%s > %s &'%(self.fmm, logfile)) + fout = open(logfile, 'w') + processes.append(subprocess.Popen(self.fmm, stdout = fout)) + fout.close() os.chdir(self.cwd) return processes + def calcRes(self): + resout = os.path.join(self.cwd, self.resout) + if self.citer == 0: + os.system('%s > %s'%(self.resid, resout)) + else: + os.system('%s >> %s'%(self.resid, resout)) + + with open(resout, 'r') as infile: + residuals = infile.readlines() + RMS, var, chi2 = residuals[-1].split() + print('Residuals: RMS = %s, var = %s, Chi^2 = %s.'%(RMS, var, chi2)) + + def runTOMO3D(self): + starttime = datetime.datetime.now() + print('Starting TOMO3D on %s parallel processes for %s iteration(s).' + %(self.nproc, self.iter)) + if self.citer == 0: + self.startForward() + self.raiseIter() + + while self.citer <= self.iter: + self.startInversion() + self.startForward() + self.raiseIter() + + tdelta = datetime.datetime.now() - starttime + print('runTOMO3D: Finished %s iterations after %s.'%(self.iter, tdelta)) + + def _printLine(self): + print('----------------------------------------') + + def raiseIter(self): + self.citer +=1 + self._printLine() + + def startInversion(self): + print('Calling %s...'%self.inv) + os.system(self.inv) + + def startForward(self): + self._printLine() + print('Starting forward simulation for iteration %s.'%(self.citer)) + self.makeDirectories() + starttime = datetime.datetime.now() + processes = [] + + if self.citer == 0: + self.copyRef() + self.runFrech() + + for procID in range(1, self.nproc + 1): + directory = self.getProcDir(procID) + log_out = self.cwd + '/fm3dlog_' + str(procID) + '.out' + + self.writeSrcFile(procID, directory) + self.writeTracesFile(procID, directory) + os.system('cp {cvg} {cig} {mode} {pg} {frechout} {dest}' + .format(cvg=self.cvg, cig=self.cig, frechout=self.frech, + mode=self.mode, pg=self.pg, dest=directory)) + processes = self.runFmm(directory, log_out, processes) + + for p in processes: + p.wait() + + self.mergeOutput() + self.clearDirectories() + self.copyArrivals() + if self.citer == 0: + self.copyArrivals(self.rtrav) + + self.calcRes() + tdelta = datetime.datetime.now() - starttime + print('Finished Forward calculation after %s'%tdelta) + + def copyArrivals(self, target = None): + if target == None: + target = self.mtrav + os.system('cp %s %s'%(self.ttim, target)) + def makeDIR(self, directory): err = os.system('mkdir %s'%directory) if err is 256: @@ -40,6 +160,21 @@ class Tomo3d(object): print('Overwriting existing files.') else: sys.exit('Aborted') + + self.directories.append(directory) + + def clearDIR(self, directory): + err = os.system('rm -r %s'%directory) + + def makeDirectories(self): + for procID in range(1, self.nproc + 1): + directory = self.getProcDir(procID) + self.makeDIR(directory) + + def clearDirectories(self): + for directory in self.directories: + self.clearDIR(directory) + self.directories = [] def readNsrc(self): srcfile = open(self.csl, 'r') @@ -73,29 +208,6 @@ class Tomo3d(object): start = (srcPK + 1) * remain + srcPK * (proc - remain) + 1 return range(start, start + srcPK) - def startTomo(self): - starttime = datetime.datetime.now() - processes = [] - for procID in range(1, self.nproc + 1): - directory = self.getProcDir(procID) - log_out = self.cwd + 'fm3dlog_' + str(procID) + '.out' - - self.makeDIR(directory) # Problem bei Iteration - self.writeSrcFile(procID, directory) - self.writeTracesFile(procID, directory) - os.system('cp %s %s %s %s %s %s %s' - %(self.cvg, self.cig, self.frech, - self.fmm, self.mode, self.pg, directory)) - processes = self.runFmm(directory, log_out, processes) - - for p in processes: - p.wait() - - self.mergeOutput() - - tdelta = datetime.datetime.now() - starttime - print('Finished after %s'%tdelta) - def readSrcFile(self): nsrc = self.readNsrc() srcfile = open(self.csl, 'r') @@ -150,32 +262,32 @@ class Tomo3d(object): srcfile = open('%s/sources.in'%directory, 'w') sourceIDs = self.srcIDs4Kernel(procID) - srcfile.writelines('%s\n'%len(sourceIDs)) + srcfile.write('%s\n'%len(sourceIDs)) for sourceID in sourceIDs: source = self.sources[sourceID] coords = source['coords'] interactions = source['interactions'] - srcfile.writelines('%s\n'%source['teleflag']) - srcfile.writelines('%s %s %s\n'%(float(coords[0]), float(coords[1]), float(coords[2]))) - srcfile.writelines('%s\n'%source['numpaths']) - srcfile.writelines('%s\n'%source['steps']) - srcfile.writelines('%s %s\n'%(int(interactions[0]), int(interactions[1]))) - srcfile.writelines('%s\n'%source['veltype']) + srcfile.write('%s\n'%source['teleflag']) + srcfile.write('%s %s %s\n'%(float(coords[0]), float(coords[1]), float(coords[2]))) + srcfile.write('%s\n'%source['numpaths']) + srcfile.write('%s\n'%source['steps']) + srcfile.write('%s %s\n'%(int(interactions[0]), int(interactions[1]))) + srcfile.write('%s\n'%source['veltype']) def writeTracesFile(self, procID, directory): recfile = open('%s/receivers.in'%directory, 'w') sourceIDs = self.srcIDs4Kernel(procID) traceIDs = self.getTraceIDs4Sources(sourceIDs) - recfile.writelines('%s\n'%len(traceIDs)) + recfile.write('%s\n'%len(traceIDs)) for traceID in traceIDs: trace = self.traces[traceID] coords = trace['coords'] source = int(trace['source']) - sourceIDs[0] + 1 - recfile.writelines('%s %s %s\n'%(float(coords[0]), float(coords[1]), float(coords[2]))) - recfile.writelines('%s\n'%trace['paths']) - recfile.writelines('%s\n'%source) - recfile.writelines('%s\n'%trace['path']) + recfile.write('%s %s %s\n'%(float(coords[0]), float(coords[1]), float(coords[2]))) + recfile.write('%s\n'%trace['paths']) + recfile.write('%s\n'%source) + recfile.write('%s\n'%trace['path']) def getTraceIDs4Sources(self, sourceIDs): traceIDs = [] @@ -208,79 +320,139 @@ class Tomo3d(object): return arrivals - def readFrechet(self, procID): - directory = self.getProcDir(procID) - frechfile = open(directory + '/frechet.dat', 'r') - sourceIDs = self.srcIDs4Kernel(procID) - - frechet = {} - for sourceID in sourceIDs: - traceIDs = self.getTraceIDs4Source(sourceID) - for traceID in traceIDs: - line = frechfile.readline().split() - if line != []: - # recID and srcID for the individual processor will not be needed - PDEV = [] - recID_proc, srcID_proc, ray, normal, NPDEV = line - for i in range(int(NPDEV)): - PDEV.append(frechfile.readline()) - - frechet[traceID] = {'sourceID': sourceID, - 'raypath': ray, - 'normal': normal, - 'NPDEV': NPDEV, - 'PDEV': PDEV - } - return frechet - def mergeArrivals(self): - arrivalsOut = open(self.cwd + '/arrivals.dat', 'w') + arrivalsOut = open(os.path.join(self.cwd, self.ttim), 'w') print('Merging arrivals.dat...') for procID in range(1, self.nproc + 1): arrivals = self.readArrivals(procID) for line in arrivals: - arrivalsOut.writelines('%6s %6s %6s %6s %15s %5s %5s\n'%tuple(line)) + arrivalsOut.write('%6s %6s %6s %6s %15s %5s %5s\n'%tuple(line)) - def mergeFrechet(self): - print('Merging frechet.dat...') - frechetOut = open(self.cwd + '/frechet.dat', 'w') - for procID in range(1, self.nproc + 1): - frechet = self.readFrechet(procID) - traceIDs = frechet.keys() - traceIDs.sort() + # def mergeFrechet(self): + # print('Merging frechet.dat...') + # frechetOut = open(self.cwd + '/frechet.dat', 'w') + # for procID in range(1, self.nproc + 1): + # frechet = self.readFrechet(procID) + # traceIDs = frechet.keys() + # traceIDs.sort() + # for traceID in traceIDs: + # frech = frechet[traceID] + # frechetOut.write('%6s %6s %6s %6s %6s\n'% + # (traceID, + # frech['sourceID'], + # frech['raypath'], + # frech['normal'], + # frech['NPDEV'])) + # for pdev in frech['PDEV']: + # frechetOut.writelines(pdev) + + def readRays(self, procID): + directory = self.getProcDir(procID) + raysfile = open(directory + '/rays.dat', 'r') + sourceIDs = self.srcIDs4Kernel(procID) + + rays = {} + for sourceID in sourceIDs: + traceIDs = self.getTraceIDs4Source(sourceID) for traceID in traceIDs: - frech = frechet[traceID] - frechetOut.writelines('%6s %6s %6s %6s %6s\n'% - (traceID, - frech['sourceID'], - frech['raypath'], - frech['normal'], - frech['NPDEV'])) - for pdev in frech['PDEV']: - frechetOut.writelines(pdev) + line1 = raysfile.readline().split() + if line1 != []: + # recID and srcID for the individual processor will not be needed + recID_proc, srcID_proc, ray, normal, nsec = line1 + raysecs = {} + + for sec in range(int(nsec)): + line2 = raysfile.readline.split() + npoints, region, diff, head = line2 + raypoints = [] + + for j in range(int(npoints)): + raypoints.append(raysfile.readline() + '\n') + + raysecs[sec] = {'npoints': npoints, + 'region': region, + 'diff': diff, + 'head': head, + 'raypoints': raypoints + } + + + rays[traceID] = {'sourceID': sourceID, + 'raypath': ray, + 'normal': normal, + 'nsec': nsec, + 'raysections': raysecs + } + return rays def mergeRays(self): print('Merging rays.dat...') - filenames = [] - for procID in range(1, self.nproc + 1): - directory = self.getProcDir(procID) - filenames.append(directory + '/rays.dat') - with open(self.cwd + 'rays.dat', 'w') as outfile: - for fname in filenames: - with open(fname) as infile: - for line in infile: - outfile.write(line) + for procID in range(1, self.nproc + 1): + rays = self.readRays(procID) + for traceID in rays: + ray = rays[traceID] + outfile.write('%6s %6s %6s %6s %6s'%(traceID, + ray['sourceID'], + ray['raypath'], + ray['normal'], + ray['nsec'])) + for sec in range(int(ray['nsec'])): + raysec = ray['raysections'][sec] + outfile.write('%6s %6s %6s %6s'%(raysec['npoints'], + raysec['region'], + raysec['diff'], + raysec['head'])) + outfile.writelines(raysec['raypoints']) + + + # def readFrechet(self, procID): + # directory = self.getProcDir(procID) + # frechfile = open(directory + '/frechet.dat', 'r') + # sourceIDs = self.srcIDs4Kernel(procID) + + # frechet = {} + # for sourceID in sourceIDs: + # traceIDs = self.getTraceIDs4Source(sourceID) + # for traceID in traceIDs: + # line = frechfile.readline().split() + # if line != []: + # # recID and srcID for the individual processor will not be needed + # PDEV = [] + # recID_proc, srcID_proc, ray, normal, NPDEV = line + # for i in range(int(NPDEV)): + # PDEV.append(frechfile.readline()) + + # frechet[traceID] = {'sourceID': sourceID, + # 'raypath': ray, + # 'normal': normal, + # 'NPDEV': NPDEV, + # 'PDEV': PDEV + # } + # return frechet + + def mergeFrechet(self): + print('Merging frechet.dat...') + + with open(self.cwd + '/frechet.dat', 'w') as outfile: + for procID in range(1, self.nproc + 1): + filename = self.getProcDir(procID) + '/frechet.dat' + with open(filename) as infile: + for sourceID in self.srcIDs4Kernel(procID): + for traceID in self.getTraceIDs4Source(sourceID): + recID_proc, srcID_proc, ray, normal, NPDEV = infile.readline().split() + outfile.write('%6s %6s %6s %6s %6s\n'%(traceID, sourceID, ray, normal, NPDEV)) + for index in range(int(NPDEV)): + outfile.write(infile.readline()) + def getProcDir(self, procID): - return self.cwd + self.folder + str(procID) + return os.path.join(self.cwd, self.folder) + str(procID) def mergeOutput(self): self.mergeArrivals() self.mergeFrechet() self.mergeRays() - - def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', inputfileref='vgridsref.in'): @@ -314,23 +486,23 @@ def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', i # write header print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Velocity on FMTOMO vgrids.in points\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET STRUCTURED_POINTS\n') + outfile.write('# vtk DataFile Version 3.1\n') + outfile.write('Velocity on FMTOMO vgrids.in points\n') + outfile.write('ASCII\n') + outfile.write('DATASET STRUCTURED_POINTS\n') - outfile.writelines('DIMENSIONS %d %d %d\n' % (nX, nY, nZ)) - outfile.writelines('ORIGIN %f %f %f\n' % (sX, sY, sZ)) - outfile.writelines('SPACING %f %f %f\n' % (dX, dY, dZ)) + outfile.write('DIMENSIONS %d %d %d\n' % (nX, nY, nZ)) + outfile.write('ORIGIN %f %f %f\n' % (sX, sY, sZ)) + outfile.write('SPACING %f %f %f\n' % (dX, dY, dZ)) - outfile.writelines('POINT_DATA %15d\n' % (nPoints)) + outfile.write('POINT_DATA %15d\n' % (nPoints)) if absOrRel == 'abs': - outfile.writelines('SCALARS velocity float %d\n' %(1)) + outfile.write('SCALARS velocity float %d\n' %(1)) if absOrRel == 'relDepth': - outfile.writelines('SCALARS velocity2depthMean float %d\n' %(1)) + outfile.write('SCALARS velocity2depthMean float %d\n' %(1)) elif absOrRel == 'rel': - outfile.writelines('SCALARS velChangePercent float %d\n' % (1)) - outfile.writelines('LOOKUP_TABLE default\n') + outfile.write('SCALARS velChangePercent float %d\n' % (1)) + outfile.write('LOOKUP_TABLE default\n') pointsPerR = nTheta * nPhi @@ -338,7 +510,7 @@ def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', i if absOrRel == 'abs': print("Writing velocity values to VTK file...") for velocity in vel: - outfile.writelines('%10f\n' %velocity) + outfile.write('%10f\n' %velocity) elif absOrRel == 'relDepth': print("Writing velocity values to VTK file relative to mean of each depth...") index = 0; count = 0 @@ -350,7 +522,7 @@ def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', i velmean = np.mean(veldepth) #print velmean, count, count/pointsPerR for vel in veldepth: - outfile.writelines('%10f\n' %(vel - velmean)) + outfile.write('%10f\n' %(vel - velmean)) veldepth = [] elif absOrRel == 'rel': nref, dref, sref, velref = _readVgrid(inputfileref) @@ -372,7 +544,7 @@ def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', i return print("Writing velocity values to VTK file...") for velocity in velrel: - outfile.writelines('%10f\n' % velocity) + outfile.write('%10f\n' % velocity) print('Pertubations: min: %s %%, max: %s %%' % (min(velrel), max(velrel))) outfile.close() @@ -430,29 +602,29 @@ def rays2VTK(fnin, fdirout='./vtk_files/', nthPoint=50): # write header # print("Writing header for VTK file...") print("Writing shot %d to file %s" % (shotnumber, fnameout)) - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('FMTOMO rays\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' % (nPoints)) + outfile.write('# vtk DataFile Version 3.1\n') + outfile.write('FMTOMO rays\n') + outfile.write('ASCII\n') + outfile.write('DATASET POLYDATA\n') + outfile.write('POINTS %15d float\n' % (nPoints)) # write coordinates # print("Writing coordinates to VTK file...") for raynumber in rays[shotnumber].keys(): for raypoint in rays[shotnumber][raynumber]: - outfile.writelines('%10f %10f %10f \n' % (raypoint[0], raypoint[1], raypoint[2])) + outfile.write('%10f %10f %10f \n' % (raypoint[0], raypoint[1], raypoint[2])) - outfile.writelines('LINES %15d %15d\n' % (len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) + outfile.write('LINES %15d %15d\n' % (len(rays[shotnumber]), len(rays[shotnumber]) + nPoints)) # write indices # print("Writing indices to VTK file...") count = 0 for raynumber in rays[shotnumber].keys(): - outfile.writelines('%d ' % (len(rays[shotnumber][raynumber]))) + outfile.write('%d ' % (len(rays[shotnumber][raynumber]))) for index in range(len(rays[shotnumber][raynumber])): - outfile.writelines('%d ' % (count)) + outfile.write('%d ' % (count)) count += 1 - outfile.writelines('\n') + outfile.write('\n') def _readVgrid(filename): @@ -594,10 +766,10 @@ def addCheckerboard(spacing=10., pertubation=0.1, inputfile='vgrids.in', nPoints = nR * nTheta * nPhi # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' % (1, 1)) - outfile.writelines('%10s %10s %10s\n' % (nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' % (dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) - outfile.writelines('%10s %10s %10s\n' % (sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) + outfile.write('%10s %10s \n' % (1, 1)) + outfile.write('%10s %10s %10s\n' % (nR, nTheta, nPhi)) + outfile.write('%10s %10s %10s\n' % (dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) + outfile.write('%10s %10s %10s\n' % (sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) spacR = correctSpacing(spacing, dR, '[meter], R') spacTheta = correctSpacing(_getAngle(spacing), dTheta, '[degree], Theta') @@ -642,7 +814,7 @@ def addCheckerboard(spacing=10., pertubation=0.1, inputfile='vgrids.in', evenOdd = evenOddR * evenOddT * evenOddP * ampFactor velocity += evenOdd * pertubation * velocity - outfile.writelines('%10s %10s\n' % (velocity, decm)) + outfile.write('%10s %10s\n' % (velocity, decm)) count += 1 progress = float(count) / float(nPoints) * 100 @@ -697,10 +869,10 @@ def addBox(x=(None, None), y=(None, None), z=(None, None), nPoints = nR * nTheta * nPhi # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' % (1, 1)) - outfile.writelines('%10s %10s %10s\n' % (nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' % (dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) - outfile.writelines('%10s %10s %10s\n' % (sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) + outfile.write('%10s %10s \n' % (1, 1)) + outfile.write('%10s %10s %10s\n' % (nR, nTheta, nPhi)) + outfile.write('%10s %10s %10s\n' % (dR, np.deg2rad(dTheta), np.deg2rad(dPhi))) + outfile.write('%10s %10s %10s\n' % (sR, np.deg2rad(sTheta), np.deg2rad(sPhi))) count = 0 for radius in rGrid: @@ -722,7 +894,7 @@ def addBox(x=(None, None), y=(None, None), z=(None, None), if rFlag * thetaFlag * phiFlag is not 0: velocity = boxvelocity - outfile.writelines('%10s %10s\n' % (velocity, decm)) + outfile.write('%10s %10s\n' % (velocity, decm)) count += 1 progress = float(count) / float(nPoints) * 100 diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 275587a0..3fc10b9b 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -134,7 +134,8 @@ class SeisArray(object): if traceID2 < traceID1: direction = -1 return direction - print "Error: Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) + err_msg = "Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) + raise RuntimeError(err_msg) def _checkCoordDirection(self, traceID1, traceID2, coordinate): ''' @@ -146,7 +147,8 @@ class SeisArray(object): if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): direction = -1 return direction - print "Error: Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) + err_msg = "Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) + raise RuntimeError(err_msg) def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): ''' @@ -455,7 +457,7 @@ class SeisArray(object): recx, recy, recz = self.getReceiverLists() nsrc = len(self.getSourceLocations()) - outfile.writelines('%s\n' % (len(zip(recx, recy, recz)) * nsrc)) + outfile.write('%s\n' % (len(zip(recx, recy, recz)) * nsrc)) for index in range(nsrc): for point in zip(recx, recy, recz): @@ -463,10 +465,10 @@ class SeisArray(object): rad = - rz lat = self._getAngle(ry) lon = self._getAngle(rx) - outfile.writelines('%15s %15s %15s\n' % (rad, lat, lon)) - outfile.writelines('%15s\n' % (1)) - outfile.writelines('%15s\n' % (index + 1)) - outfile.writelines('%15s\n' % (1)) + outfile.write('%15s %15s %15s\n' % (rad, lat, lon)) + outfile.write('%15s\n' % (1)) + outfile.write('%15s\n' % (index + 1)) + outfile.write('%15s\n' % (1)) outfile.close() @@ -506,22 +508,22 @@ class SeisArray(object): deltaPhi = abs(phiE - phiW) / float((nPhi - 1)) # write header for interfaces grid file (in RADIANS) - outfile.writelines('%10s\n' % (nInterfaces)) - outfile.writelines('%10s %10s\n' % (nTheta + 2, nPhi + 2)) # +2 cushion nodes - outfile.writelines('%10s %10s\n' % (np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) - outfile.writelines('%10s %10s\n' % (np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) + outfile.write('%10s\n' % (nInterfaces)) + outfile.write('%10s %10s\n' % (nTheta + 2, nPhi + 2)) # +2 cushion nodes + outfile.write('%10s %10s\n' % (np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) + outfile.write('%10s %10s\n' % (np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) interface1 = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method=method) interface2 = self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, -depthmax, method=method) for point in interface1: z = point[2] - outfile.writelines('%10s\n' % (z + R)) + outfile.write('%10s\n' % (z + R)) - outfile.writelines('\n') + outfile.write('\n') for point in interface2: z = point[2] - outfile.writelines('%10s\n' % (z + R)) + outfile.write('%10s\n' % (z + R)) outfile.close() @@ -596,10 +598,10 @@ class SeisArray(object): deltaPhi = abs(phiE - phiW) / float(nPhi - 1) deltaR = abs(rbot - rtop) / float(nR - 1) - outfile.writelines('%10s %10s %10s\n' % (nR, nTheta, nPhi)) - outfile.writelines('%10s %10s %10s\n' % (deltaR, deltaTheta, deltaPhi)) - outfile.writelines('%10s %10s %10s\n' % (rtop, thetaS, phiW)) - outfile.writelines('%10s %10s\n' % refinement) + outfile.write('%10s %10s %10s\n' % (nR, nTheta, nPhi)) + outfile.write('%10s %10s %10s\n' % (deltaR, deltaTheta, deltaPhi)) + outfile.write('%10s %10s %10s\n' % (rtop, thetaS, phiW)) + outfile.write('%10s %10s\n' % refinement) outfile.close() @@ -708,10 +710,10 @@ class SeisArray(object): print("Total number of grid nodes: %s" % nTotal) # write header for velocity grid file (in RADIANS) - outfile.writelines('%10s %10s \n' % (1, 1)) - outfile.writelines('%10s %10s %10s\n' % (nR + 2, nTheta + 2, nPhi + 2)) - outfile.writelines('%10s %10s %10s\n' % (deltaR, np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) - outfile.writelines( + outfile.write('%10s %10s \n' % (1, 1)) + outfile.write('%10s %10s %10s\n' % (nR + 2, nTheta + 2, nPhi + 2)) + outfile.write('%10s %10s %10s\n' % (deltaR, np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) + outfile.write( '%10s %10s %10s\n' % (rbot - deltaR, np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method=method) @@ -746,15 +748,16 @@ class SeisArray(object): vbot[index] - vtop[index]) + vtop[index] break if not (ztop[index]) >= depth > (zbot[index]): - print( - 'ERROR in grid inputfile, could not find velocity for a z-value of %s in the inputfile' % ( - depth - topo)) - return + err_msg = 'ERROR in grid inputfile, could not find velocity' + 'for a z-value of %s in the inputfile' % (depth - topo) + raise ValueError(err_msg) count += 1 - if vel < 0: - print( - 'ERROR, vel <0; z, topo, zbot, vbot, vtop:', depth, topo, zbot[index], vbot[index], vtop[index]) - outfile.writelines('%10s %10s\n' % (vel, decm)) + if not vel >= 0: + err_msg = 'vel < 0; z, topo, zbot, vbot, vtop:', + depth, topo, zbot[index], vbot[index], vtop[index] + raise ValueError(err_msg) + + outfile.write('%10s %10s\n' % (vel, decm)) progress = float(count) / float(nTotal) * 100 self._update_progress(progress) @@ -775,7 +778,7 @@ class SeisArray(object): for traceID in self.getReceiverCoordinates().keys(): count += 1 x, y, z = self.getReceiverCoordinates()[traceID] - recfile_out.writelines('%5s %15s %15s %15s\n' % (traceID, x, y, z)) + recfile_out.write('%5s %15s %15s %15s\n' % (traceID, x, y, z)) print "Exported coordinates for %s traces to file > %s" % (count, filename) recfile_out.close() @@ -895,11 +898,11 @@ class SeisArray(object): # write header print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Surface Points\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' % (nPoints)) + outfile.write('# vtk DataFile Version 3.1\n') + outfile.write('Surface Points\n') + outfile.write('ASCII\n') + outfile.write('DATASET POLYDATA\n') + outfile.write('POINTS %15d float\n' % (nPoints)) # write coordinates print("Writing coordinates to VTK file...") @@ -908,23 +911,23 @@ class SeisArray(object): y = point[1] z = point[2] - outfile.writelines('%10f %10f %10f \n' % (x, y, z)) + outfile.write('%10f %10f %10f \n' % (x, y, z)) - outfile.writelines('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) + outfile.write('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) # write indices print("Writing indices to VTK file...") for index in range(nPoints): - outfile.writelines('%10d %10d\n' % (1, index)) + outfile.write('%10d %10d\n' % (1, index)) - # outfile.writelines('POINT_DATA %15d\n' %(nPoints)) - # outfile.writelines('SCALARS traceIDs int %d\n' %(1)) - # outfile.writelines('LOOKUP_TABLE default\n') + # outfile.write('POINT_DATA %15d\n' %(nPoints)) + # outfile.write('SCALARS traceIDs int %d\n' %(1)) + # outfile.write('LOOKUP_TABLE default\n') # # write traceIDs # print("Writing traceIDs to VTK file...") # for traceID in traceIDs: - # outfile.writelines('%10d\n' %traceID) + # outfile.write('%10d\n' %traceID) outfile.close() print("Wrote %d points to file: %s" % (nPoints, filename)) @@ -944,11 +947,11 @@ class SeisArray(object): # write header print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Receivers with traceIDs\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' % (nPoints)) + outfile.write('# vtk DataFile Version 3.1\n') + outfile.write('Receivers with traceIDs\n') + outfile.write('ASCII\n') + outfile.write('DATASET POLYDATA\n') + outfile.write('POINTS %15d float\n' % (nPoints)) # write coordinates print("Writing coordinates to VTK file...") @@ -957,23 +960,23 @@ class SeisArray(object): y = self._getYreceiver(traceID) z = self._getZreceiver(traceID) - outfile.writelines('%10f %10f %10f \n' % (x, y, z)) + outfile.write('%10f %10f %10f \n' % (x, y, z)) - outfile.writelines('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) + outfile.write('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) # write indices print("Writing indices to VTK file...") for index in range(nPoints): - outfile.writelines('%10d %10d\n' % (1, index)) + outfile.write('%10d %10d\n' % (1, index)) - outfile.writelines('POINT_DATA %15d\n' % (nPoints)) - outfile.writelines('SCALARS traceIDs int %d\n' % (1)) - outfile.writelines('LOOKUP_TABLE default\n') + outfile.write('POINT_DATA %15d\n' % (nPoints)) + outfile.write('SCALARS traceIDs int %d\n' % (1)) + outfile.write('LOOKUP_TABLE default\n') # write traceIDs print("Writing traceIDs to VTK file...") for traceID in traceIDs: - outfile.writelines('%10d\n' % traceID) + outfile.write('%10d\n' % traceID) outfile.close() print("Wrote %d receiver for to file: %s" % (nPoints, filename)) @@ -993,11 +996,11 @@ class SeisArray(object): # write header print("Writing header for VTK file...") - outfile.writelines('# vtk DataFile Version 3.1\n') - outfile.writelines('Shots with shotnumbers\n') - outfile.writelines('ASCII\n') - outfile.writelines('DATASET POLYDATA\n') - outfile.writelines('POINTS %15d float\n' % (nPoints)) + outfile.write('# vtk DataFile Version 3.1\n') + outfile.write('Shots with shotnumbers\n') + outfile.write('ASCII\n') + outfile.write('DATASET POLYDATA\n') + outfile.write('POINTS %15d float\n' % (nPoints)) # write coordinates print("Writing coordinates to VTK file...") @@ -1006,23 +1009,23 @@ class SeisArray(object): y = self._getYshot(shotnumber) z = self._getZshot(shotnumber) - outfile.writelines('%10f %10f %10f \n' % (x, y, z)) + outfile.write('%10f %10f %10f \n' % (x, y, z)) - outfile.writelines('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) + outfile.write('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) # write indices print("Writing indices to VTK file...") for index in range(nPoints): - outfile.writelines('%10d %10d\n' % (1, index)) + outfile.write('%10d %10d\n' % (1, index)) - outfile.writelines('POINT_DATA %15d\n' % (nPoints)) - outfile.writelines('SCALARS shotnumbers int %d\n' % (1)) - outfile.writelines('LOOKUP_TABLE default\n') + outfile.write('POINT_DATA %15d\n' % (nPoints)) + outfile.write('SCALARS shotnumbers int %d\n' % (1)) + outfile.write('LOOKUP_TABLE default\n') # write shotnumber print("Writing shotnumbers to VTK file...") for shotnumber in shotnumbers: - outfile.writelines('%10d\n' % shotnumber) + outfile.write('%10d\n' % shotnumber) outfile.close() print("Wrote %d sources to file: %s" % (nPoints, filename)) From 1cb86d7b479fc31a7953e584050b8aee646665e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 May 2016 13:42:51 +0200 Subject: [PATCH 0895/1144] Cleaned input file, additional parameter apverbose --- inputs/autoPyLoT_regional.in | 38 ++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/inputs/autoPyLoT_regional.in b/inputs/autoPyLoT_regional.in index 4248fd30..14972000 100644 --- a/inputs/autoPyLoT_regional.in +++ b/inputs/autoPyLoT_regional.in @@ -12,23 +12,27 @@ e1412.008.06 #eventID# %event ID for single event pr /DATA/Egelados/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure 0 #iplot# %flag for plotting: 0 none, 1, partly, >1 everything -AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file -AUTOLOC_AIC_HOS4_ARH #locfile# %name of autoPILOT output location file -AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities -HYPOSAT #locrt# %location routine used ("HYPOINVERSE" or "HYPOSAT") -6 #pmin# %minimum required P picks for location -4 #p0min# %minimum required P picks for location if at least - %3 excellent P picks are found -2 #smin# %minimum required S picks for location -/home/ludger/bin/run_HYPOSAT4autoPILOT.csh #cshellp# %path and name of c-shell script to run location routine -7.6 8.5 #blon# %longitude bounding for location map -49 49.4 #blat# %lattitude bounding for location map -#parameters for moment magnitude estimation# -5000 #vp# %average P-wave velocity -2800 #vs# %average S-wave velocity -2200 #rho# %rock density [kg/m^3] -300 #Qp# %quality factor for P waves -100 #Qs# %quality factor for S waves +True #apverbose# %choose 'True' or 'False' for terminal output +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#NLLoc settings# +/home/ludger/NLLOC #nllocbin# %path to NLLoc executable +/home/ludger/NLLOC/Insheim #nllocroot# %root of NLLoc-processing directory +AUTOPHASES.obs #phasefile# %name of autoPyLoT-output phase file for NLLoc + %(in nllocroot/obs) +Insheim_min1d2015_auto.in #ctrfile# %name of autoPyLoT-output control file for NLLoc + %(in nllocroot/run) +ttime #ttpatter# %pattern of NLLoc ttimes from grid + %(in nllocroot/times) +AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file + %(returns 'eventID_outpatter') +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#parameters for seismic moment estimation# +3530 #vp# %average P-wave velocity +2700 #rho# %average rock density [kg/m^3] +1000f**0.8 #Qp# %quality factor for P waves (Qp*f^a) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing derived polarities +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #common settings picker# 20 #pstart# %start time [s] for calculating CF for P-picking 100 #pstop# %end time [s] for calculating CF for P-picking From 2bb2ddbc7b5027897781ae29c39c9dfba2f1343a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 11 May 2016 13:44:26 +0200 Subject: [PATCH 0896/1144] Cleaned input file --- inputs/autoPyLoT_local.in | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/inputs/autoPyLoT_local.in b/inputs/autoPyLoT_local.in index 725a9dec..d01a9c0d 100644 --- a/inputs/autoPyLoT_local.in +++ b/inputs/autoPyLoT_local.in @@ -6,8 +6,8 @@ #main settings# /DATA/Insheim #rootpath# %project path EVENT_DATA/LOCAL #datapath# %data path -2015.11_Insheim #database# %name of data base - #eventID# %event ID for single event processing +2013.02_Insheim #database# %name of data base +e0019.048.13 #eventID# %event ID for single event processing /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure#%choose data structure 0 #iplot# %flag for plotting: 0 none, 1 partly, >1 everything @@ -30,13 +30,7 @@ AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file 2500 #rho# %average rock density [kg/m^3] 300f**0.8 #Qp# %quality factor for P waves (Qp*f^a) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing polarities -6 #pmin# %minimum required P picks for location -4 #p0min# %minimum required P picks for location if at least - %3 excellent P picks are found -2 #smin# %minimum required S picks for location -7.6 8.5 #blon# %longitude bounding for location map -49 49.4 #blat# %lattitude bounding for location map +AUTOFOCMEC_AIC_HOS4_ARH.in #focmecin# %name of focmec input file containing derived polarities %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #common settings picker# 15.0 #pstart# %start time [s] for calculating CF for P-picking @@ -62,7 +56,6 @@ HOS #algoP# %choose algorithm for P-onset 3 0.1 0.5 0.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise,tsafetey,tsignal,tslope] [s] 3.0 #pickwinP# %for initial AIC pick, length of P-pick window [s] 6.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick) -0 #peps4aic# %for HOS/AR, artificial uplift of samples of AIC-function (P) 0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s] 0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s] 0.001 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P) From 79e429db8d25c67bb0ece6468e4aac053a0e0aed Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 11 May 2016 14:50:52 +0200 Subject: [PATCH 0897/1144] [edit] rearrangement fastens up a bit --- pylot/core/io/phases.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index a46c263b..b5770385 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -234,7 +234,7 @@ def picks_from_picksdict(picks): for station, onsets in picks.items(): print('Reading picks on station %s' % station) for label, phase in onsets.items(): - if not isinstance(phase, dict): + if not isinstance(phase, dict) or len(phase) < 3: continue onset = phase['mpp'] epp = phase['epp'] @@ -276,7 +276,7 @@ def reassess_pilot_db(root_dir, out_dir=None, fn_param=None): reassess_pilot_event(root_dir, evt, out_dir, fn_param) -def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None): +def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbosity=0): from obspy import read from pylot.core.io.inputs import AutoPickParameter @@ -298,12 +298,10 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None): for station in picks_dict.keys(): fn_pattern = os.path.join(search_base, '{0}*'.format(station)) try: - st = read(fn_pattern) + st = read(fn_pattern, format='GSE2') except TypeError as e: print(e.message) - st = read(fn_pattern, format='GSE2') - if not st: - raise RuntimeError('no waveform data found for station {station}'.format(station=station)) + st = read(fn_pattern) for phase in picks_dict[station].keys(): try: mpp = picks_dict[station][phase]['mpp'] @@ -311,6 +309,11 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None): print(e.message, station) continue sel_st = select_for_phase(st, phase) + if not sel_st: + raise warnings.formatwarning( + 'no waveform data found for station {station}'.format( + station=station), category=RuntimeWarning) + print(sel_st) stime, etime = getGlobalTimes(sel_st) rel_pick = mpp - stime epp, lpp, spe = earllatepicker(sel_st, @@ -319,6 +322,8 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None): Pick1=rel_pick, iplot=None, ) + if epp is None or lpp is None: + continue epp = stime + epp lpp = stime + lpp min_diff = 3 * st[0].stats.delta @@ -334,7 +339,7 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None): if not out_dir: fnout_prefix = os.path.join(root_dir, event_id, '{0}.'.format(event_id)) else: - fnout_prefix = os.path.join(out_dir, event_id, '{0}.'.format(event_id)) + fnout_prefix = os.path.join(out_dir, '{0}.'.format(event_id)) evt.write(fnout_prefix + 'xml', format='QUAKEML') #evt.write(fnout_prefix + 'cnv', format='VELEST') From f09af1612066262129480f8bec3677045660526c Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 12 May 2016 10:08:29 +0200 Subject: [PATCH 0898/1144] [task] started to implement data processing step for checking corrupted GSE files --- pylot/core/util/dataprocessing.py | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 pylot/core/util/dataprocessing.py diff --git a/pylot/core/util/dataprocessing.py b/pylot/core/util/dataprocessing.py new file mode 100644 index 00000000..f91e2ba2 --- /dev/null +++ b/pylot/core/util/dataprocessing.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import glob +from obspy import UTCDateTime + +def gse_time_header(lines): + ''' + takes a path FILE to a GSE data file and returns the time header of the file + :param file: path to GSE data file + :type file: str + :return: time header from FILE + :rtype: str + + >>> gse_time_header('test.gse') + "WID2 2005/10/09 20:17:25.000 ATH SHZ NSP CM6 9000 50.000000 0.10E+01 1.000 NSP -1.0 0.0" + ''' + + return lines[1] + +def time_from_header(header): + timeline = header.split(' ') + time = timeline[1].split('/') + timeline[2].split(':') + time = time[:-1] + time[-1].split('.') + time[-1] += '000' + return [int(t) for t in time] + +def check_time(time): + try: + UTCDateTime(time) + return True + except ValueError: + return False \ No newline at end of file From 019b801603e793c1cb1903243827f630ce539cfb Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 12 May 2016 14:01:18 +0200 Subject: [PATCH 0899/1144] code rearrange and minor processing changes --- pylot/core/active/fmtomoUtils.py | 490 ++++++++++++++++--------------- 1 file changed, 255 insertions(+), 235 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 7dfe928b..f70b0b43 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -6,15 +6,28 @@ import datetime import numpy as np class Tomo3d(object): - def __init__(self, nproc, iterations): + def __init__(self, nproc, iterations, citer = 0, overwrite = False): + ''' + Class build from FMTOMO script tomo3d. Can be used to run several instances of FMM code in parallel. + + :param: nproc, number of parallel processes + :type: integer + + :param: iterations, number of iterations + :type: integer + + :param: citer, current iteration (default = 0: start new model) + :type: integer + ''' self.setCWD() self.defParas() self.nproc = nproc self.iter = iterations # number of iterations - self.citer = 0 # current iteration + self.citer = citer # current iteration self.sources = self.readSrcFile() self.traces = self.readTraces() self.directories = [] + self.overwrite = overwrite def defParas(self): self.defFMMParas() @@ -29,6 +42,7 @@ class Tomo3d(object): self.pg = 'propgrid.in' self.rec = 'receivers.in' self.frech = 'frechet.in' + self.frechout = 'frechet.dat' self.ot = 'otimes.dat' self.ttim = 'arrivals.dat' self.mode = 'mode_set.in' @@ -53,8 +67,8 @@ class Tomo3d(object): self.resout = 'residuals.dat' def copyRef(self): - # Attention: Copies reference grids to used grids (e.g. sourcesref.in to sources.in) - os.system('cp %s %s'%(self. ivg, self.cvg)) + # Copies reference grids to used grids (e.g. sourcesref.in to sources.in) + os.system('cp %s %s'%(self.ivg, self.cvg)) os.system('cp %s %s'%(self.iig, self.cig)) os.system('cp %s %s'%(self.isl, self.csl)) @@ -65,6 +79,30 @@ class Tomo3d(object): def runFrech(self): os.system(self.frechgen) + def runTOMO3D(self): + starttime = datetime.datetime.now() + print('Starting TOMO3D on %s parallel processes for %s iteration(s).' + %(self.nproc, self.iter)) + if self.citer == 0: + self.makeInvIterDir() + self.startForward(self.cInvIterDir) + self.raiseIter() + + while self.citer <= self.iter: + self.makeInvIterDir() + self.startInversion() + self.saveVgrid() + self.startForward(self.cInvIterDir) + self.raiseIter() + + if self.citer > self.iter: + self.removeDirectories() + self.unlink(os.path.join(self.cwd, self.frechout)) + self.unlink(os.path.join(self.cwd, self.ttim)) + + tdelta = datetime.datetime.now() - starttime + print('runTOMO3D: Finished %s iterations after %s.'%(self.iter, tdelta)) + def runFmm(self, directory, logfile, processes): os.chdir(directory) fout = open(logfile, 'w') @@ -73,6 +111,47 @@ class Tomo3d(object): os.chdir(self.cwd) return processes + def startForward(self, logdir): + self._printLine() + print('Starting forward simulation for iteration %s.'%(self.citer)) + + if self.citer == 0: + self.copyRef() + self.runFrech() + self.makeDirectories() + + starttime = datetime.datetime.now() + processes = [] + + for procID in range(1, self.nproc + 1): + directory = self.getProcDir(procID) + logfn = 'fm3dlog_' + str(procID) + '.out' + log_out = os.path.join(logdir, logfn) + + self.writeSrcFile(procID, directory) + self.writeTracesFile(procID, directory) + os.system('cp {cvg} {cig} {mode} {pg} {frechin} {dest}' + .format(cvg=self.cvg, cig=self.cig, frechin=self.frech, + mode=self.mode, pg=self.pg, dest=directory)) + processes = self.runFmm(directory, log_out, processes) + + for p in processes: + p.wait() + + self.mergeOutput(self.cInvIterDir) + self.clearDirectories() + self.copyArrivals() + if self.citer == 0: + self.copyArrivals(self.rtrav) + + self.calcRes() + tdelta = datetime.datetime.now() - starttime + print('Finished Forward calculation after %s'%tdelta) + + def startInversion(self): + print('Calling %s...'%self.inv) + os.system(self.inv) + def calcRes(self): resout = os.path.join(self.cwd, self.resout) if self.citer == 0: @@ -85,111 +164,91 @@ class Tomo3d(object): RMS, var, chi2 = residuals[-1].split() print('Residuals: RMS = %s, var = %s, Chi^2 = %s.'%(RMS, var, chi2)) - def runTOMO3D(self): - starttime = datetime.datetime.now() - print('Starting TOMO3D on %s parallel processes for %s iteration(s).' - %(self.nproc, self.iter)) - if self.citer == 0: - self.startForward() - self.raiseIter() - - while self.citer <= self.iter: - self.startInversion() - self.startForward() - self.raiseIter() - - tdelta = datetime.datetime.now() - starttime - print('runTOMO3D: Finished %s iterations after %s.'%(self.iter, tdelta)) - - def _printLine(self): - print('----------------------------------------') - def raiseIter(self): self.citer +=1 self._printLine() + invfile = open(self.cwd + '/inviter.in', 'w') + invfile.write('%s'%self.citer) + invfile.close() - def startInversion(self): - print('Calling %s...'%self.inv) - os.system(self.inv) - - def startForward(self): - self._printLine() - print('Starting forward simulation for iteration %s.'%(self.citer)) - self.makeDirectories() - starttime = datetime.datetime.now() - processes = [] - - if self.citer == 0: - self.copyRef() - self.runFrech() - - for procID in range(1, self.nproc + 1): - directory = self.getProcDir(procID) - log_out = self.cwd + '/fm3dlog_' + str(procID) + '.out' - - self.writeSrcFile(procID, directory) - self.writeTracesFile(procID, directory) - os.system('cp {cvg} {cig} {mode} {pg} {frechout} {dest}' - .format(cvg=self.cvg, cig=self.cig, frechout=self.frech, - mode=self.mode, pg=self.pg, dest=directory)) - processes = self.runFmm(directory, log_out, processes) - - for p in processes: - p.wait() - - self.mergeOutput() - self.clearDirectories() - self.copyArrivals() - if self.citer == 0: - self.copyArrivals(self.rtrav) - - self.calcRes() - tdelta = datetime.datetime.now() - starttime - print('Finished Forward calculation after %s'%tdelta) - - def copyArrivals(self, target = None): - if target == None: - target = self.mtrav - os.system('cp %s %s'%(self.ttim, target)) - - def makeDIR(self, directory): + def makeDir(self, directory): err = os.system('mkdir %s'%directory) + if err is 0: + self.directories.append(directory) + return if err is 256: - response = raw_input('Warning: Directory already existing. Continue (y/n)?\n') - if response == 'y': + if self.overwrite == True: print('Overwriting existing files.') - else: - sys.exit('Aborted') + self.clearDir(directory) + self.directories.append(directory) + return + raise RuntimeError('Could not create directory: %s'%directory) - self.directories.append(directory) - - def clearDIR(self, directory): - err = os.system('rm -r %s'%directory) - def makeDirectories(self): for procID in range(1, self.nproc + 1): directory = self.getProcDir(procID) - self.makeDIR(directory) + self.makeDir(directory) + def makeInvIterDir(self): + invIterDir = self.cwd + '/it_%s'%(self.citer) + err = os.system('mkdir %s'%invIterDir) + if err is 256: + if self.overwrite: + self.clearDir(invIterDir) + elif err is not 0: + raise RuntimeError('Could not create directory: %s'%invIterDir) + self.cInvIterDir = invIterDir + + def clearDir(self, directory): + print('Wiping directory %s...'%directory) + for filename in os.listdir(directory): + filenp = os.path.join(directory, filename) + os.remove(filenp) + def clearDirectories(self): for directory in self.directories: - self.clearDIR(directory) + self.clearDir(directory) + + def rmDir(self, directory): + print('Removing directory %s...'%directory) + return os.rmdir(directory) + + def removeDirectories(self): + for directory in self.directories: + self.rmDir(directory) self.directories = [] - def readNsrc(self): - srcfile = open(self.csl, 'r') - nsrc = int(srcfile.readline()) - srcfile.close() - return nsrc + def getProcDir(self, procID): + return os.path.join(self.cwd, self.folder) + str(procID) - def readNtraces(self): - recfile = open(self.rec, 'r') - nrec = int(recfile.readline()) - recfile.close() - return nrec + def getTraceIDs4Sources(self, sourceIDs): + traceIDs = [] + for traceID in self.traces.keys(): + if self.traces[traceID]['source'] in sourceIDs: + traceIDs.append(traceID) + return traceIDs + + def getTraceIDs4Source(self, sourceID): + traceIDs = [] + for traceID in self.traces.keys(): + if self.traces[traceID]['source'] == sourceID: + traceIDs.append(traceID) + return traceIDs + + def copyArrivals(self, target = None): + if target == None: + target = os.path.join(self.cwd, self.mtrav) + os.system('cp %s %s'%(os.path.join( + self.cInvIterDir, self.ttim), target)) + + def saveVgrid(self): + vgpath = os.path.join(self.cwd, 'vgrids.in') + os.system('cp %s %s'%(vgpath, self.cInvIterDir)) def calcSrcPerKernel(self): nsrc = self.readNsrc() + if self.nproc > nsrc: + raise ValueError('Number of spawned processes higher than number of sources') return nsrc/self.nproc, nsrc%self.nproc def srcIDs4Kernel(self, procID): @@ -207,6 +266,18 @@ class Tomo3d(object): elif proc > remain: start = (srcPK + 1) * remain + srcPK * (proc - remain) + 1 return range(start, start + srcPK) + + def readNsrc(self): + srcfile = open(self.csl, 'r') + nsrc = int(srcfile.readline()) + srcfile.close() + return nsrc + + def readNtraces(self): + recfile = open(self.rec, 'r') + nrec = int(recfile.readline()) + recfile.close() + return nrec def readSrcFile(self): nsrc = self.readNsrc() @@ -258,6 +329,62 @@ class Tomo3d(object): return traces + def readArrivals(self, procID): + directory = self.getProcDir(procID) + arrfile = open(directory + '/arrivals.dat', 'r') + sourceIDs = self.srcIDs4Kernel(procID) + + arrivals = [] + for sourceID in sourceIDs: + traceIDs = self.getTraceIDs4Source(sourceID) + for traceID in traceIDs: + line = arrfile.readline().split() + if line != []: + # recID and srcID for the individual processor will not be needed + recID_proc, srcID_proc, ray, normal, arrtime, diff, head = line + arrivals.append([traceID, sourceID, ray, normal, arrtime, diff, head]) + + return arrivals + + def readRays(self, procID): + directory = self.getProcDir(procID) + raysfile = open(directory + '/rays.dat', 'r') + sourceIDs = self.srcIDs4Kernel(procID) + + rays = {} + for sourceID in sourceIDs: + traceIDs = self.getTraceIDs4Source(sourceID) + for traceID in traceIDs: + line1 = raysfile.readline().split() + if line1 != []: + # recID and srcID for the individual processor will not be needed + recID_proc, srcID_proc, ray, normal, nsec = line1 + raysecs = {} + + for sec in range(int(nsec)): + line2 = raysfile.readline().split() + npoints, region, diff, head = line2 + raypoints = [] + + for j in range(int(npoints)): + raypoints.append(raysfile.readline() + '\n') + + raysecs[sec] = {'npoints': npoints, + 'region': region, + 'diff': diff, + 'head': head, + 'raypoints': raypoints + } + + + rays[traceID] = {'sourceID': sourceID, + 'raypath': ray, + 'normal': normal, + 'nsec': nsec, + 'raysections': raysecs + } + return rays + def writeSrcFile(self, procID, directory): srcfile = open('%s/sources.in'%directory, 'w') sourceIDs = self.srcIDs4Kernel(procID) @@ -289,154 +416,44 @@ class Tomo3d(object): recfile.write('%s\n'%source) recfile.write('%s\n'%trace['path']) - def getTraceIDs4Sources(self, sourceIDs): - traceIDs = [] - for traceID in self.traces.keys(): - if self.traces[traceID]['source'] in sourceIDs: - traceIDs.append(traceID) - return traceIDs - - def getTraceIDs4Source(self, sourceID): - traceIDs = [] - for traceID in self.traces.keys(): - if self.traces[traceID]['source'] == sourceID: - traceIDs.append(traceID) - return traceIDs - - def readArrivals(self, procID): - directory = self.getProcDir(procID) - arrfile = open(directory + '/arrivals.dat', 'r') - sourceIDs = self.srcIDs4Kernel(procID) - - arrivals = [] - for sourceID in sourceIDs: - traceIDs = self.getTraceIDs4Source(sourceID) - for traceID in traceIDs: - line = arrfile.readline().split() - if line != []: - # recID and srcID for the individual processor will not be needed - recID_proc, srcID_proc, ray, normal, arrtime, diff, head = line - arrivals.append([traceID, sourceID, ray, normal, arrtime, diff, head]) - - return arrivals - - def mergeArrivals(self): - arrivalsOut = open(os.path.join(self.cwd, self.ttim), 'w') + def mergeArrivals(self, directory): + arrfn = os.path.join(directory, self.ttim) + arrivalsOut = open(arrfn, 'w') print('Merging arrivals.dat...') for procID in range(1, self.nproc + 1): arrivals = self.readArrivals(procID) for line in arrivals: arrivalsOut.write('%6s %6s %6s %6s %15s %5s %5s\n'%tuple(line)) - # def mergeFrechet(self): - # print('Merging frechet.dat...') - # frechetOut = open(self.cwd + '/frechet.dat', 'w') - # for procID in range(1, self.nproc + 1): - # frechet = self.readFrechet(procID) - # traceIDs = frechet.keys() - # traceIDs.sort() - # for traceID in traceIDs: - # frech = frechet[traceID] - # frechetOut.write('%6s %6s %6s %6s %6s\n'% - # (traceID, - # frech['sourceID'], - # frech['raypath'], - # frech['normal'], - # frech['NPDEV'])) - # for pdev in frech['PDEV']: - # frechetOut.writelines(pdev) + os.system('ln -fs %s %s'%(arrfn, os.path.join(self.cwd, self.ttim))) - def readRays(self, procID): - directory = self.getProcDir(procID) - raysfile = open(directory + '/rays.dat', 'r') - sourceIDs = self.srcIDs4Kernel(procID) - - rays = {} - for sourceID in sourceIDs: - traceIDs = self.getTraceIDs4Source(sourceID) - for traceID in traceIDs: - line1 = raysfile.readline().split() - if line1 != []: - # recID and srcID for the individual processor will not be needed - recID_proc, srcID_proc, ray, normal, nsec = line1 - raysecs = {} - - for sec in range(int(nsec)): - line2 = raysfile.readline.split() - npoints, region, diff, head = line2 - raypoints = [] - - for j in range(int(npoints)): - raypoints.append(raysfile.readline() + '\n') - - raysecs[sec] = {'npoints': npoints, - 'region': region, - 'diff': diff, - 'head': head, - 'raypoints': raypoints - } - - - rays[traceID] = {'sourceID': sourceID, - 'raypath': ray, - 'normal': normal, - 'nsec': nsec, - 'raysections': raysecs - } - return rays - - def mergeRays(self): + def mergeRays(self, directory): print('Merging rays.dat...') - with open(self.cwd + 'rays.dat', 'w') as outfile: - for procID in range(1, self.nproc + 1): - rays = self.readRays(procID) - for traceID in rays: - ray = rays[traceID] - outfile.write('%6s %6s %6s %6s %6s'%(traceID, - ray['sourceID'], - ray['raypath'], - ray['normal'], - ray['nsec'])) - for sec in range(int(ray['nsec'])): - raysec = ray['raysections'][sec] - outfile.write('%6s %6s %6s %6s'%(raysec['npoints'], - raysec['region'], - raysec['diff'], - raysec['head'])) - outfile.writelines(raysec['raypoints']) - - - # def readFrechet(self, procID): - # directory = self.getProcDir(procID) - # frechfile = open(directory + '/frechet.dat', 'r') - # sourceIDs = self.srcIDs4Kernel(procID) - - # frechet = {} - # for sourceID in sourceIDs: - # traceIDs = self.getTraceIDs4Source(sourceID) - # for traceID in traceIDs: - # line = frechfile.readline().split() - # if line != []: - # # recID and srcID for the individual processor will not be needed - # PDEV = [] - # recID_proc, srcID_proc, ray, normal, NPDEV = line - # for i in range(int(NPDEV)): - # PDEV.append(frechfile.readline()) - - # frechet[traceID] = {'sourceID': sourceID, - # 'raypath': ray, - # 'normal': normal, - # 'NPDEV': NPDEV, - # 'PDEV': PDEV - # } - # return frechet - - def mergeFrechet(self): - print('Merging frechet.dat...') - - with open(self.cwd + '/frechet.dat', 'w') as outfile: + with open(directory + '/rays.dat', 'w') as outfile: for procID in range(1, self.nproc + 1): - filename = self.getProcDir(procID) + '/frechet.dat' + rays = self.readRays(procID) + for traceID in rays: + ray = rays[traceID] + outfile.write('%6s %6s %6s %6s %6s\n'%(traceID, + ray['sourceID'], + ray['raypath'], + ray['normal'], + ray['nsec'])) + for sec in range(int(ray['nsec'])): + raysec = ray['raysections'][sec] + outfile.write('%6s %6s %6s %6s\n'%(raysec['npoints'], + raysec['region'], + raysec['diff'], + raysec['head'])) + outfile.writelines(raysec['raypoints']) + + def mergeFrechet(self, directory): + print('Merging frechet.dat...') + + frechfnout = os.path.join(directory, self.frechout) + with open(frechfnout, 'w') as outfile: + for procID in range(1, self.nproc + 1): + filename = os.path.join(self.getProcDir(procID), self.frechout) with open(filename) as infile: for sourceID in self.srcIDs4Kernel(procID): for traceID in self.getTraceIDs4Source(sourceID): @@ -445,15 +462,18 @@ class Tomo3d(object): for index in range(int(NPDEV)): outfile.write(infile.readline()) + os.system('ln -fs %s %s'%(frechfnout, os.path.join(self.cwd, self.frechout))) - def getProcDir(self, procID): - return os.path.join(self.cwd, self.folder) + str(procID) - - def mergeOutput(self): - self.mergeArrivals() - self.mergeFrechet() - self.mergeRays() + def mergeOutput(self, directory): + self.mergeArrivals(directory) + self.mergeFrechet(directory) + self.mergeRays(directory) + def unlink(self, filepath): + return os.system('unlink %s'%filepath) + + def _printLine(self): + print('----------------------------------------') def vgrids2VTK(inputfile='vgrids.in', outputfile='vgrids.vtk', absOrRel='abs', inputfileref='vgridsref.in'): ''' From 4a836fd1f70f81ff811d39784ff67f9110ad84dc Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 12 May 2016 10:08:29 +0200 Subject: [PATCH 0900/1144] [task] started to implement data processing step for checking corrupted GSE files --- pylot/core/util/dataprocessing.py | 118 ++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 pylot/core/util/dataprocessing.py diff --git a/pylot/core/util/dataprocessing.py b/pylot/core/util/dataprocessing.py new file mode 100644 index 00000000..9b0c1fe2 --- /dev/null +++ b/pylot/core/util/dataprocessing.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import glob +from obspy import UTCDateTime +import sys + +def time_from_header(header): + ''' + Function takes in the second line from a .gse file and takes out the date and time from that line. + :param header: second line from .gse file + :type header: string + :return: a list of integers of form [year, month, day, hour, minute, second, microsecond] + ''' + timeline = header.split(' ') + time = timeline[1].split('/') + timeline[2].split(':') + time = time[:-1] + time[-1].split('.') + return [int(t) for t in time] + +def check_time(datetime): + ''' + Function takes in date and time as list and validates it's values by trying to make an UTCDateTime object from it + :param datetime: list of integers [year, month, day, hour, minute, second, microsecond] + :type datetime: list + :return: returns True if Values are in supposed range, returns False otherwise + ''' + try: + UTCDateTime(*datetime) + return True + except ValueError: + return False + +def get_file_list(root_dir): + ''' + Function uses a directorie to get all the *.gse files from it. + :param root_dir: a directorie leading to the .gse files + :type root_dir: string + :return: returns a list of filenames (without path to them) + ''' + file_list = glob.glob1(root_dir, '*.gse') + return file_list + +def checks_station_second(datetime): + ''' + Function uses the given list to check if the parameter 'second' is set to 60 by mistake + and sets the time correctly if so. Can only correct time if no date change would be necessary. + :param datetime: [year, month, day, hour, minute, second, microsecond] + :return: returns the input with the correct value for second + ''' + if datetime[5] == 60: + if datetime[4] == 59: + if datetime[3] == 23: + print 'Date should be next day.'+file + raise ValueError + else: + datetime[3] += 1 + datetime[4] = 0 + datetime[5] = 0 + else: + datetime[4] += 1 + datetime[5] = 0 + return datetime + +def make_time_line(line, datetime): + ''' + Function takes in the original line from a .gse file and a list of date and time values to make a new line with + corrected date and time. + :param line: second line from .gse file. + :type line: string + :param datetime: list of integers [year, month, day, hour, minute, second, microsecond] + :type datetime: list + :return: returns a string to write it into a file. + ''' + insertion = '{:02d}'.format(int(datetime[3])) + ':' + '{:02d}'.format(int(datetime[4])) + ':' + '{:02d}'.format(int(datetime[5])) + '.000' + newline = line[:16]+insertion+line[28:] + return newline + +def evt_head_check(root_dir,out_dir = None): + ''' + A function to make sure that an arbitrary number of .gse files have correct values in their header. + :param root_dir: a directory leading to the .gse files. + :type root_dir: string + :param out_dir: a directory to store the new files somwhere els. + :return: returns nothing + ''' + if not out_dir: + print 'WARNING files are going to be overwritten!' + inp = str(raw_input('Continue? [y/n]')) + if inp == 'y': + pass + else: + sys.exit() + Filelist = get_file_list(root_dir) + debugcounter = 0 + for i in range(len(Filelist)): + inFile = open(root_dir+'/'+Filelist[i], 'r') + lines = inFile.readlines() + datetime = time_from_header(lines[1]) + if check_time(datetime): + continue + else: + debugcounter += 1 + datetime = checks_station_second(datetime) + print 'writing ' + Filelist[i] + timeline = make_time_line(lines[1],datetime) + # write File + lines[1] = timeline + if not out_dir: + out = open(root_dir+Filelist[i], 'w') + out.writelines(lines) + out.close() + else: + out = open(out_dir+Filelist[i], 'w') + out.writelines(lines) + out.close() + inFile.close() + print debugcounter \ No newline at end of file From ca77b06293568244e62a14422ebb7f64e6fa8754 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 17 May 2016 11:53:32 +0200 Subject: [PATCH 0901/1144] Code cleanup --- pylot/core/active/activeSeismoPick.py | 41 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index afcfd200..82a7603d 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -4,11 +4,10 @@ import numpy as np from pylot.core.active import seismicshot from pylot.core.active.surveyUtils import cleanUp - class Survey(object): def __init__(self, path, sourcefile, receiverfile, useDefaultParas=False): ''' - The Survey Class contains all shots [type: seismicshot] of a survey + The Survey Class contains all shots [class: Seismicshot] of a survey as well as the aquisition geometry and the topography. It contains methods to pick all traces of all shots. @@ -24,7 +23,7 @@ class Survey(object): self._generateSurvey() self._initiateFilenames() if useDefaultParas == True: - self.setParametersForShots() + self.setParametersForAllShots() self._removeAllEmptyTraces() self._updateShots() @@ -51,32 +50,36 @@ class Survey(object): print ("Total number of traces: %d \n" % self.countAllTraces()) def _removeAllEmptyTraces(self): - filename = 'removeEmptyTraces.out' + ''' + Removes traces of the dataset that are not found in the input receiver files. + ''' + logfile = 'removeEmptyTraces.out' count = 0 for shot in self.data.values(): removed = shot.removeEmptyTraces() if removed is not None: - if count == 0: outfile = open(filename, 'w') + if count == 0: outfile = open(logfile, 'w') count += 1 outfile.writelines('shot: %s, removed empty traces: %s\n' % (shot.getShotnumber(), removed)) print ("\nremoveEmptyTraces: Finished! Removed %d traces" % count) if count > 0: print ("See %s for more information " - "on removed traces." % (filename)) + "on removed traces." % (logfile)) outfile.close() def _updateShots(self): ''' - Removes traces that do not exist in the dataset for any reason. + Removes traces that do not exist in the dataset for any reason, + but were set in the input files. ''' - filename = 'updateShots.out' + logfile = 'updateShots.out' count = 0; countTraces = 0 for shot in self.data.values(): del_traceIDs = shot.updateTraceList() if len(del_traceIDs) > 0: - if count == 0: outfile = open(filename, 'w') + if count == 0: outfile = open(logfile, 'w') count += 1 countTraces += len(del_traceIDs) outfile.writelines("shot: %s, removed traceID(s) %s because " @@ -87,36 +90,37 @@ class Survey(object): "%d traces" % (count, countTraces)) if count > 0: print ("See %s for more information " - "on removed traces." % (filename)) + "on removed traces." % (logfile)) outfile.close() def setArtificialPick(self, traceID, pick): ''' Sets an artificial pick for a traceID of all shots in the survey object. - (This can be used to create a pick with t = 0 at the source origin) + (Commonly used to generate a travel time t = 0 at the source origin) ''' for shot in self.data.values(): shot.setPick(traceID, pick) - def setParametersForShots(self, cutwindow=(0, 0.2), tmovwind=0.3, tsignal=0.03, tgap=0.0007): + def setParametersForAllShots(self, cutwindow=(0, 0.2), tmovwind=0.3, tsignal=0.03, tgap=0.0007): if (cutwindow == (0, 0.2) and tmovwind == 0.3 and tsignal == 0.03 and tgap == 0.0007): print ("Warning: Standard values used for " "setParamters. This might not be clever.") - # CHANGE this later. Parameters only needed for survey, not for each shot. for shot in self.data.values(): shot.setCut(cutwindow) shot.setTmovwind(tmovwind) shot.setTsignal(tsignal) shot.setTgap(tgap) shot.setOrder(order=4) - print ("setParametersForShots: Parameters set to:\n" + print ("setParametersForAllShots: Parameters set to:\n" "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" % (cutwindow, tmovwind, tsignal, tgap)) def setManualPicksFromFiles(self, directory='picks'): ''' Read manual picks from *.pck files in a directory. + Can be used for comparison of automatic and manual picks. + The * must be identical with the shotnumber. ''' for shot in self.data.values(): @@ -125,6 +129,7 @@ class Survey(object): def getDiffsFromManual(self): ''' Returns a dictionary with the differences between manual and automatic pick for all shots. + Key: Seismicshot [object] ''' diffs = {} for shot in self.data.values(): @@ -136,6 +141,10 @@ class Survey(object): return diffs def plotDiffs(self): + ''' + Creates a plot of all Picks colored by the + difference between automatic and manual pick. + ''' import matplotlib.pyplot as plt diffs = []; dists = []; @@ -163,8 +172,12 @@ class Survey(object): ax.set_xlabel('Distance [m]') ax.set_ylabel('Time [s]') ax.text(0.5, 0.95, 'Plot of all MANUAL picks', transform=ax.transAxes, horizontalalignment='center') + plt.legend() def plotHist(self, nbins=20, ax=None): + ''' + Plot a histogram of the difference between automatic and manual picks. + ''' import matplotlib.pyplot as plt plt.interactive(True) diffs = [] From 30e8f0c96a25fc86f7d327cbf3e5715370878c51 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 17 May 2016 16:19:44 +0200 Subject: [PATCH 0902/1144] code cleanup and commenting --- pylot/core/active/activeSeismoPick.py | 81 +++++++++++++++++++++------ 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 82a7603d..2d02496d 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -193,9 +193,21 @@ class Survey(object): plt.xlabel('Difference in time (auto - manual) [s]') return diffs - def pickAllShots(self, windowsize, HosAic='hos', vmin=333, vmax=5500, folm=0.6): + def pickAllShots(self, vmin=333, vmax=5500, folm=0.6, HosAic='hos', aicwindow=(10, 0)): ''' Automatically pick all traces of all shots of the survey. + + :param: vmin, vmax, minimum (maximum) permitted apparent velocity on direct path between src and rec + :type: real + + :param: folm, fraction of local maximum for HOS pick (0.6 = 60% of the highest maximum) + :type: real + + :param: HosAic, pick with hos only ('hos') or use AIC ('aic') + :type: string + + :param: aicwindow, window around the initial pick to search for local AIC min (samples) + :type: tuple ''' from datetime import datetime starttime = datetime.now() @@ -220,7 +232,7 @@ class Survey(object): pickwin_used = (pwleft, pwright) shot.setPickwindow(traceID, pickwin_used) - shot.pickTraces(traceID, windowsize, folm, HosAic) # picker + shot.pickTraces(traceID, folm, HosAic, aicwindow) # picker shot.setSNR(traceID) # if shot.getSNR(traceID)[0] < snrthreshold: @@ -244,6 +256,9 @@ class Survey(object): % (pickedtraces, ntraces, float(pickedtraces) / float(ntraces) * 100.)) def cleanBySPE(self, maxSPE): + ''' + Sets all picks as invalid if they exceed a certain value of the symmetric pick error. + ''' for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if shot.getPickFlag(traceID) == 1: @@ -251,6 +266,9 @@ class Survey(object): shot.setPickFlag(traceID, 0) def plotSPE(self): + ''' + Plots the symmetric pick error sorted by magnitude. + ''' import matplotlib.pyplot as plt spe = [] for shot in self.data.values(): @@ -264,7 +282,7 @@ class Survey(object): def recover(self): ''' - Recovers all (accidently) removed picks. Still regards SNR threshold. + Recovers all manually removed picks. Still regards SNR threshold. ''' print('Recovering survey...') numpicks = 0 @@ -279,18 +297,28 @@ class Survey(object): print('Recovered %d picks' % numpicks) def setArtificialPick(self, traceID, pick): + ''' + Sets an artificial pick for a certain receiver (traceID) for all shots. + ''' for shot in self.data.values(): shot.setPick(traceID, pick) shot.setPickwindow(traceID, shot.getCut()) def countAllTraces(self): + ''' + Returns the number of traces in total. + ''' numtraces = 0 for shot in self.getShotlist(): - for rec in self.getReceiverlist(): ### shot.getReceiverlist etc. + for rec in self.getReceiverlist(): numtraces += 1 + return numtraces def getShotlist(self): + ''' + Returns a list of all shotnumbers contained in the set Sourcefile. + ''' filename = self.getPath() + self.getSourcefile() srcfile = open(filename, 'r') shotlist = [] @@ -301,6 +329,9 @@ class Survey(object): return shotlist def getReceiverlist(self): + ''' + Returns a list of all trace IDs contained in the set Receiverfile. + ''' filename = self.getPath() + self.getReceiverfile() recfile = open(filename, 'r') reclist = [] @@ -326,6 +357,12 @@ class Survey(object): return self._obsdir def getStats(self): + ''' + Generates and returns a dictionary containing statistical information + of the survey. + + Key: shotnumber + ''' info_dict = {} for shot in self.data.values(): pickedTraces = 0 @@ -347,11 +384,17 @@ class Survey(object): return info_dict def getShotForShotnumber(self, shotnumber): + ''' + Returns Seismicshot [object] of a certain shotnumber if possible. + ''' for shot in self.data.values(): if shot.getShotnumber() == shotnumber: return shot def exportFMTOMO(self, directory='FMTOMO_export', sourcefile='input_sf.in', ttFileExtension='.tt'): + ''' + Exports all picks into a directory as travel time files readable by FMTOMO obsdata. + ''' def getAngle(distance): PI = np.pi R = 6371. @@ -367,13 +410,13 @@ class Survey(object): srcfile.writelines('%10s\n' % len(self.data)) # number of sources for shotnumber in self.getShotlist(): shot = self.getShotForShotnumber(shotnumber) - ttfilename = str(shotnumber) + ttFileExtension + ttfilename = str(shotnumber) + ttFileExtension # filename of travel time file for this shot (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) - srcfile.writelines('%10s %10s %10s\n' % (getAngle(y), getAngle(x), (-1) * z)) # lat, lon, depth + srcfile.writelines('%10s %10s %10s\n' % (getAngle(y), getAngle(x), (-1) * z)) # transform to lat, lon, depth LatAll.append(getAngle(y)); LonAll.append(getAngle(x)); DepthAll.append((-1) * z) - srcfile.writelines('%10s\n' % 1) # + srcfile.writelines('%10s\n' % 1) srcfile.writelines('%10s %10s %10s\n' % (1, 1, ttfilename)) ttfile = open(directory + '/' + ttfilename, 'w') traceIDlist = shot.getTraceIDlist() @@ -406,6 +449,9 @@ class Survey(object): print(msg) def countPickedTraces(self, shot): + ''' + Counts all picked traces of a shot (type Seismicshot). + ''' count = 0 for traceID in shot.getTraceIDlist(): if shot.getPickFlag(traceID) is not 0: @@ -413,6 +459,9 @@ class Survey(object): return count def countAllPickedTraces(self): + ''' + Counts all picked traces of the survey. + ''' count = 0 for shot in self.data.values(): for traceID in shot.getTraceIDlist(): @@ -435,20 +484,9 @@ class Survey(object): figPerSubplot = columns * rows index = 1 - # shotnames = [] - # shotnumbers = [] - - # for shot in self.data.values(): - # shotnames.append(shot.getShotname()) - # shotnumbers.append(shot.getShotnumber()) - - # shotnumbers = [shotnumbers for (shotnumbers, shotnames) in sorted(zip(shotnumbers, shotnames))] for shotnumber in self.getShotlist(): if index <= figPerSubplot: - # ax = fig.add_subplot(3,3,i, projection = '3d', title = 'shot:' - # +str(shot_dict[shotnumber].getShotnumber()), xlabel = 'X', ylabel = 'Y', zlabel = 'traveltime') - # shot_dict[shotnumber].plot3dttc(ax = ax, plotpicks = True) ax = fig.add_subplot(rows, columns, index) if mode == '3d': self.getShot(shotnumber).matshow(ax=ax, colorbar=False, annotations=True, legend=False) @@ -529,6 +567,9 @@ class Survey(object): return ax def createPlot(self, dist, pick, inkByVal, label, ax=None, cbar=None): + ''' + Used by plotAllPicks. + ''' import matplotlib.pyplot as plt plt.interactive(True) cm = plt.cm.jet @@ -560,6 +601,10 @@ class Survey(object): sys.stdout.flush() def saveSurvey(self, filename='survey.pickle'): + ''' + Save Survey object to a file. + Can be loaded by using Survey.from_pickle(filename). + ''' import cPickle cleanUp(self) outfile = open(filename, 'wb') From dbaead4754c0fade758aadf30db3e393e320adc0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 18 May 2016 12:00:45 +0200 Subject: [PATCH 0903/1144] code cleanup + commenting --- pylot/core/active/surveyUtils.py | 72 +++++++++----------------------- 1 file changed, 19 insertions(+), 53 deletions(-) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index ff637612..04c3509e 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -16,26 +16,13 @@ def readParameters(parfile, parameter): return value - -def setArtificialPick(shot_dict, traceID, pick): - """ - - :param shot_dict: - :param traceID: - :param pick: - :return: - """ - for shot in shot_dict.values(): - shot.setPick(traceID, pick) - shot.setPickwindow(traceID, shot.getCut()) - - def fitSNR4dist(shot_dict, shiftdist=30, shiftSNR=100): """ + Approach to fit the decreasing SNR with wave travel distance. - :param shot_dict: - :param shiftdist: - :param shiftSNR: + :param shot_dict: dictionary containing Seismicshot objects (e.g. survey.getShotDict()) + :param shiftdist: shift compensating curve by a certain distance to the left + :param shiftSNR: shift compensating curve by a certain SNR value to the bottom :return: """ import numpy as np @@ -87,6 +74,8 @@ def plotFittedSNR(dists, snrthresholds, snrs, snrBestFit): def setDynamicFittedSNR(shot_dict, shiftdist=30, shiftSNR=100, p1=0.004, p2=-0.0007): """ + Set SNR values for a dictionary containing Seismicshots (e.g. survey.getShotDict()) + by parameters calulated from fitSNR4dist. :param shot_dict: :type shot_dict: dict @@ -119,6 +108,7 @@ def setDynamicFittedSNR(shot_dict, shiftdist=30, shiftSNR=100, p1=0.004, p2=-0.0 def setConstantSNR(shot_dict, snrthreshold=2.5): """ + Set a constant SNR value to all Seismicshots in a dictionary (e.g. survey.getShotDict()). :param shot_dict: :param snrthreshold: @@ -158,42 +148,18 @@ def findTracesInRanges(shot_dict, distancebin, pickbin): def cleanUp(survey): """ - - :param survey: - :return: + Cleans up a Survey object by removing frontend information + which can not be saved in the pickle format. """ for shot in survey.data.values(): shot.traces4plot = {} - -# def plotScatterStats(survey, key, ax = None): -# import matplotlib.pyplot as plt -# x = []; y = []; value = [] -# stats = survey.getStats() -# for shotnumber in stats.keys(): -# if type(value) == list: -# value.append(stats[shotnumber][key][0]) -# else: -# value.append(stats[shotnumber][key]) -# x.append(survey.data[shotnumber].getSrcLoc()[0]) -# y.append(survey.data[shotnumber].getSrcLoc()[1]) - -# if ax == None: -# fig = plt.figure() -# ax = fig.add_subplot(111) - -# sc = ax.scatter(x, y, s = value, c = value) -# plt.xlabel('X') -# plt.ylabel('Y') -# cbar = plt.colorbar(sc) -# cbar.set_label(key) - -def plotScatterStats4Shots(survey, key): +def plotScatterStats4Shots(survey, variable): """ Statistics, scatter plot. - key can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' + :param survey: - :param key: + :param variable: can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' :return: """ import matplotlib.pyplot as plt @@ -225,7 +191,7 @@ def plotScatterStats4Shots(survey, key): for shot in statsShot.keys(): x.append(statsShot[shot]['x']) y.append(statsShot[shot]['y']) - value.append(statsShot[shot][key]) + value.append(statsShot[shot][variable]) fig = plt.figure() ax = fig.add_subplot(111) @@ -239,19 +205,19 @@ def plotScatterStats4Shots(survey, key): plt.xlabel('X') plt.ylabel('Y') cbar = plt.colorbar(sc) - cbar.set_label(key) + cbar.set_label(variable) for shot in statsShot.keys(): ax.annotate(' %s' % shot.getShotnumber(), xy=(shot.getSrcLoc()[0], shot.getSrcLoc()[1]), fontsize='x-small', color='k') -def plotScatterStats4Receivers(survey, key): +def plotScatterStats4Receivers(survey, variable): """ Statistics, scatter plot. - key can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' + :param survey: - :param key: + :param variable: can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' :return: """ import matplotlib.pyplot as plt @@ -283,7 +249,7 @@ def plotScatterStats4Receivers(survey, key): for traceID in statsRec.keys(): x.append(statsRec[traceID]['x']) y.append(statsRec[traceID]['y']) - value.append(statsRec[traceID][key]) + value.append(statsRec[traceID][variable]) fig = plt.figure() ax = fig.add_subplot(111) @@ -297,7 +263,7 @@ def plotScatterStats4Receivers(survey, key): plt.xlabel('X') plt.ylabel('Y') cbar = plt.colorbar(sc) - cbar.set_label(key) + cbar.set_label(variable) shot = survey.data.values()[0] for traceID in shot.getTraceIDlist(): From e62ff9d68c6272c3a5a011095ad1ac8dd9fd5cac Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 May 2016 12:55:04 +0200 Subject: [PATCH 0904/1144] [fix] returning variables now, docstrings corrected to be able to use single quotes in docstrings --- pylot/core/io/phases.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index f3c6c96a..92c327e0 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -193,13 +193,13 @@ def picksdict_from_obs(fn): def picks_to_dict(evt): - ''' + """ Takes an Event object and return the pick dictionary commonly used within PyLoT :param evt: Event object contain all available information :type evt: `~obspy.core.event.Event` :return: pick dictionary - ''' + """ picks = {} for pick in evt.picks: phase = {} @@ -229,6 +229,7 @@ def picks_to_dict(evt): picks[station] = onsets.copy() return picks + def picks_from_dict(picks): firstonset = None for station, onsets in picks.items(): @@ -265,6 +266,7 @@ def picks_from_dict(picks): print('No polarity information found for %s' % phase) if firstonset is None or firstonset > onset: firstonset = onset + return pick, firstonset def reassess_pilot_event(root_dir, event_id): @@ -307,7 +309,7 @@ def reassess_pilot_event(root_dir, event_id): def writephases(arrivals, fformat, filename): - ''' + """ Function of methods to write phases to the following standard file formats used for locating earthquakes: @@ -325,7 +327,7 @@ def writephases(arrivals, fformat, filename): :param: filename, full path and name of phase file :type: string - ''' + """ if fformat == 'NLLoc': print ("Writing phases to %s for NLLoc" % filename) @@ -387,7 +389,6 @@ def writephases(arrivals, fformat, filename): sweight)) fid.close() - elif fformat == 'HYPO71': print ("Writing phases to %s for HYPO71" % filename) fid = open("%s" % filename, 'w') From dc5d76efb86889f21043fed859934858847aee25 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 18 May 2016 13:17:05 +0200 Subject: [PATCH 0905/1144] [fix] fixing docstring problem caused by merge of conflicting files --- pylot/core/io/phases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index bf2ba499..3924ddd3 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -193,7 +193,7 @@ def picksdict_from_obs(fn): def picksdict_from_picks(evt): - ''' + """ Takes an Event object and return the pick dictionary commonly used within PyLoT :param evt: Event object contain all available information From 731acc7117fcf3e476b91bac55a197c715f58e6f Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 19 May 2016 10:51:37 +0200 Subject: [PATCH 0906/1144] [double-checked] code has been double checked before running on data --- pylot/core/util/dataprocessing.py | 85 +++++++++++++++++-------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/pylot/core/util/dataprocessing.py b/pylot/core/util/dataprocessing.py index 9b0c1fe2..ae050ccd 100644 --- a/pylot/core/util/dataprocessing.py +++ b/pylot/core/util/dataprocessing.py @@ -7,52 +7,56 @@ from obspy import UTCDateTime import sys def time_from_header(header): - ''' + """ Function takes in the second line from a .gse file and takes out the date and time from that line. :param header: second line from .gse file :type header: string :return: a list of integers of form [year, month, day, hour, minute, second, microsecond] - ''' + """ timeline = header.split(' ') time = timeline[1].split('/') + timeline[2].split(':') time = time[:-1] + time[-1].split('.') return [int(t) for t in time] + def check_time(datetime): - ''' + """ Function takes in date and time as list and validates it's values by trying to make an UTCDateTime object from it :param datetime: list of integers [year, month, day, hour, minute, second, microsecond] :type datetime: list :return: returns True if Values are in supposed range, returns False otherwise - ''' + """ try: UTCDateTime(*datetime) return True except ValueError: return False + def get_file_list(root_dir): - ''' + """ Function uses a directorie to get all the *.gse files from it. :param root_dir: a directorie leading to the .gse files :type root_dir: string :return: returns a list of filenames (without path to them) - ''' + """ file_list = glob.glob1(root_dir, '*.gse') return file_list -def checks_station_second(datetime): - ''' + +def checks_station_second(datetime, file): + """ Function uses the given list to check if the parameter 'second' is set to 60 by mistake and sets the time correctly if so. Can only correct time if no date change would be necessary. :param datetime: [year, month, day, hour, minute, second, microsecond] :return: returns the input with the correct value for second - ''' + """ if datetime[5] == 60: if datetime[4] == 59: if datetime[3] == 23: - print 'Date should be next day.'+file - raise ValueError + err_msg = 'Date should be next day. ' \ + 'File not changed: {0}'.format(file) + raise ValueError(err_msg) else: datetime[3] += 1 datetime[4] = 0 @@ -62,57 +66,60 @@ def checks_station_second(datetime): datetime[5] = 0 return datetime + def make_time_line(line, datetime): - ''' - Function takes in the original line from a .gse file and a list of date and time values to make a new line with - corrected date and time. + """ + Function takes in the original line from a .gse file and a list of date and + time values to make a new line with corrected date and time. :param line: second line from .gse file. :type line: string :param datetime: list of integers [year, month, day, hour, minute, second, microsecond] :type datetime: list :return: returns a string to write it into a file. - ''' - insertion = '{:02d}'.format(int(datetime[3])) + ':' + '{:02d}'.format(int(datetime[4])) + ':' + '{:02d}'.format(int(datetime[5])) + '.000' - newline = line[:16]+insertion+line[28:] + """ + insertion = '{hour:02d}:{min:02d}:{sec:02d}.{mic:03d}' + insertion.format(hour=int(datetime[3]), + min=int(datetime[4]), + sec=int(datetime[5]), + mic=int(datetime[6]) * 1e-3) + newline = line[:16] + insertion + line[28:] return newline -def evt_head_check(root_dir,out_dir = None): - ''' + +def evt_head_check(root_dir, out_dir = None): + """ A function to make sure that an arbitrary number of .gse files have correct values in their header. :param root_dir: a directory leading to the .gse files. :type root_dir: string :param out_dir: a directory to store the new files somwhere els. :return: returns nothing - ''' + """ if not out_dir: - print 'WARNING files are going to be overwritten!' - inp = str(raw_input('Continue? [y/n]')) - if inp == 'y': - pass - else: + print('WARNING files are going to be overwritten!') + inp = str(raw_input('Continue? [y/N]')) + if not inp == 'y': sys.exit() - Filelist = get_file_list(root_dir) - debugcounter = 0 - for i in range(len(Filelist)): - inFile = open(root_dir+'/'+Filelist[i], 'r') - lines = inFile.readlines() + filelist = get_file_list(root_dir) + nfiles = 0 + for file in filelist: + infile = open(os.path.join(root_dir, file), 'r') + lines = infile.readlines() + infile.close() datetime = time_from_header(lines[1]) if check_time(datetime): continue else: - debugcounter += 1 - datetime = checks_station_second(datetime) - print 'writing ' + Filelist[i] - timeline = make_time_line(lines[1],datetime) + nfiles += 1 + datetime = checks_station_second(datetime, file) + print('writing ' + file) # write File - lines[1] = timeline + lines[1] = make_time_line(lines[1], datetime) if not out_dir: - out = open(root_dir+Filelist[i], 'w') + out = open(os.path.join(root_dir, file), 'w') out.writelines(lines) out.close() else: - out = open(out_dir+Filelist[i], 'w') + out = open(os.path.join(out_dir, file), 'w') out.writelines(lines) out.close() - inFile.close() - print debugcounter \ No newline at end of file + print(nfiles) \ No newline at end of file From 7c5aff0a27d2beba835791b4e9ee268acda3224f Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 19 May 2016 11:16:28 +0200 Subject: [PATCH 0907/1144] [fix] correctly formatting the timestring now --- pylot/core/util/dataprocessing.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/dataprocessing.py b/pylot/core/util/dataprocessing.py index ae050ccd..b1b64d16 100644 --- a/pylot/core/util/dataprocessing.py +++ b/pylot/core/util/dataprocessing.py @@ -77,11 +77,11 @@ def make_time_line(line, datetime): :type datetime: list :return: returns a string to write it into a file. """ - insertion = '{hour:02d}:{min:02d}:{sec:02d}.{mic:03d}' - insertion.format(hour=int(datetime[3]), - min=int(datetime[4]), - sec=int(datetime[5]), - mic=int(datetime[6]) * 1e-3) + ins_form = '{0:02d}:{1:02d}:{2:02d}.{3:03d}' + insertion = ins_form.format(int(datetime[3]), + int(datetime[4]), + int(datetime[5]), + int(datetime[6] * 1e-3)) newline = line[:16] + insertion + line[28:] return newline From 3138bbfa9381d268853c9db704b203ead7ee5506 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 19 May 2016 11:20:37 +0200 Subject: [PATCH 0908/1144] code cleanup and commenting --- pylot/core/active/fmtomoUtils.py | 170 ++++++++++++++++++++++++++----- 1 file changed, 146 insertions(+), 24 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index f70b0b43..086732d2 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -6,23 +6,15 @@ import datetime import numpy as np class Tomo3d(object): - def __init__(self, nproc, iterations, citer = 0, overwrite = False): + def __init__(self, citer = 0, overwrite = False): ''' Class build from FMTOMO script tomo3d. Can be used to run several instances of FMM code in parallel. - - :param: nproc, number of parallel processes - :type: integer - - :param: iterations, number of iterations - :type: integer :param: citer, current iteration (default = 0: start new model) :type: integer ''' self.setCWD() self.defParas() - self.nproc = nproc - self.iter = iterations # number of iterations self.citer = citer # current iteration self.sources = self.readSrcFile() self.traces = self.readTraces() @@ -34,21 +26,37 @@ class Tomo3d(object): self.defInvParas() def defFMMParas(self): + ''' + Initiates parameters for the forward calculation. + ''' + # Name of fast marching program self.fmm = '{0}/fm3d'.format(self.cwd) + # Name of program calculating Frechet derivatives self.frechgen = '{0}/frechgen'.format(self.cwd) + # Name of current velocity/inversion grid self.cvg = 'vgrids.in' + # Name of current interfaces grid self.cig = 'interfaces.in' + # Name of file containing current source locations self.csl = 'sources.in' + # Name of file containing propagation grid self.pg = 'propgrid.in' + # Name of file containing receiver coordinates self.rec = 'receivers.in' self.frech = 'frechet.in' self.frechout = 'frechet.dat' + # Name of file containing measured data self.ot = 'otimes.dat' + # Name of file containing output velocity information self.ttim = 'arrivals.dat' self.mode = 'mode_set.in' + # Name of temporary folders created for each process self.folder = '.proc_' def defInvParas(self): + ''' + Initiates inversion parameters for FMTOMO. + ''' # Name of program for performing inversion self.inv = '{0}/invert3d'.format(self.cwd) # Name of file containing current model traveltimes @@ -67,19 +75,41 @@ class Tomo3d(object): self.resout = 'residuals.dat' def copyRef(self): - # Copies reference grids to used grids (e.g. sourcesref.in to sources.in) + ''' + Copies reference grids to used grids (e.g. sourcesref.in to sources.in) + ''' os.system('cp %s %s'%(self.ivg, self.cvg)) os.system('cp %s %s'%(self.iig, self.cig)) os.system('cp %s %s'%(self.isl, self.csl)) - def setCWD(self): - self.cwd = subprocess.check_output(['pwd'])[0:-1] - print('Working directory is pwd: %s'%self.cwd) + def setCWD(self, directory = None): + ''' + Set working directory containing all necessary files. + + Default: pwd + ''' + if directory == None: + directory = subprocess.check_output(['pwd'])[0:-1] + + self.cwd = directory + print('Working directory is: %s'%self.cwd) def runFrech(self): os.system(self.frechgen) - def runTOMO3D(self): + def runTOMO3D(self, nproc, iterations): + ''' + Starts up the FMTOMO code for the set number of iterations on nproc parallel processes. + + :param: nproc, number of parallel processes + :type: integer + + :param: iterations, number of iterations + :type: integer + ''' + self.nproc = nproc + self.iter = iterations # number of iterations + starttime = datetime.datetime.now() print('Starting TOMO3D on %s parallel processes for %s iteration(s).' %(self.nproc, self.iter)) @@ -104,6 +134,10 @@ class Tomo3d(object): print('runTOMO3D: Finished %s iterations after %s.'%(self.iter, tdelta)) def runFmm(self, directory, logfile, processes): + ''' + Calls an instance of the FMM code in the process directory. + Requires a list of all active processes and returns an updated list. + ''' os.chdir(directory) fout = open(logfile, 'w') processes.append(subprocess.Popen(self.fmm, stdout = fout)) @@ -112,6 +146,9 @@ class Tomo3d(object): return processes def startForward(self, logdir): + ''' + Runs an instance of the FMM code in the process directory. + ''' self._printLine() print('Starting forward simulation for iteration %s.'%(self.citer)) @@ -128,8 +165,8 @@ class Tomo3d(object): logfn = 'fm3dlog_' + str(procID) + '.out' log_out = os.path.join(logdir, logfn) - self.writeSrcFile(procID, directory) - self.writeTracesFile(procID, directory) + self.writeSrcFile(procID) + self.writeTracesFile(procID) os.system('cp {cvg} {cig} {mode} {pg} {frechin} {dest}' .format(cvg=self.cvg, cig=self.cig, frechin=self.frech, mode=self.mode, pg=self.pg, dest=directory)) @@ -149,10 +186,16 @@ class Tomo3d(object): print('Finished Forward calculation after %s'%tdelta) def startInversion(self): + ''' + Simply calls the inversion program. + ''' print('Calling %s...'%self.inv) os.system(self.inv) def calcRes(self): + ''' + Calls residual calculation program. + ''' resout = os.path.join(self.cwd, self.resout) if self.citer == 0: os.system('%s > %s'%(self.resid, resout)) @@ -185,11 +228,17 @@ class Tomo3d(object): raise RuntimeError('Could not create directory: %s'%directory) def makeDirectories(self): + ''' + Makes temporary directories for all processes. + ''' for procID in range(1, self.nproc + 1): directory = self.getProcDir(procID) self.makeDir(directory) def makeInvIterDir(self): + ''' + Makes directories for each iteration step for the output. + ''' invIterDir = self.cwd + '/it_%s'%(self.citer) err = os.system('mkdir %s'%invIterDir) if err is 256: @@ -200,12 +249,18 @@ class Tomo3d(object): self.cInvIterDir = invIterDir def clearDir(self, directory): + ''' + Wipes a certain directory. + ''' print('Wiping directory %s...'%directory) for filename in os.listdir(directory): filenp = os.path.join(directory, filename) os.remove(filenp) def clearDirectories(self): + ''' + Wipes all generated temporary directories. + ''' for directory in self.directories: self.clearDir(directory) @@ -214,14 +269,24 @@ class Tomo3d(object): return os.rmdir(directory) def removeDirectories(self): + ''' + Removes all generated temporary directories. + ''' for directory in self.directories: self.rmDir(directory) self.directories = [] def getProcDir(self, procID): + ''' + Returns the temporary directory for a certain process + with procID = process number. + ''' return os.path.join(self.cwd, self.folder) + str(procID) def getTraceIDs4Sources(self, sourceIDs): + ''' + Returns corresponding trace IDs for a set of given source IDs. + ''' traceIDs = [] for traceID in self.traces.keys(): if self.traces[traceID]['source'] in sourceIDs: @@ -229,6 +294,9 @@ class Tomo3d(object): return traceIDs def getTraceIDs4Source(self, sourceID): + ''' + Returns corresponding trace IDs for a source ID. + ''' traceIDs = [] for traceID in self.traces.keys(): if self.traces[traceID]['source'] == sourceID: @@ -236,22 +304,38 @@ class Tomo3d(object): return traceIDs def copyArrivals(self, target = None): + ''' + Copies the FMM output file (self.ttim) to a specific target file. + Default target is self.mtrav (model travel times). + ''' if target == None: target = os.path.join(self.cwd, self.mtrav) os.system('cp %s %s'%(os.path.join( self.cInvIterDir, self.ttim), target)) def saveVgrid(self): - vgpath = os.path.join(self.cwd, 'vgrids.in') + ''' + Saves the current velocity grid for the current iteration step. + ''' + vgpath = os.path.join(self.cwd, self.cvg) os.system('cp %s %s'%(vgpath, self.cInvIterDir)) def calcSrcPerKernel(self): + ''' + (Equally) distributes all sources depending on the number of processes (kernels). + Returns two integer values. + First: minimum number of sources for each process + Second: remaining sources (always less than number of processes) + ''' nsrc = self.readNsrc() if self.nproc > nsrc: raise ValueError('Number of spawned processes higher than number of sources') return nsrc/self.nproc, nsrc%self.nproc def srcIDs4Kernel(self, procID): + ''' + Calculates and returns all source IDs for a given process ID. + ''' proc = procID - 1 nsrc = self.readNsrc() srcPK, remain = self.calcSrcPerKernel() @@ -274,12 +358,18 @@ class Tomo3d(object): return nsrc def readNtraces(self): + ''' + Reads the total number of traces from self.rec header. + ''' recfile = open(self.rec, 'r') nrec = int(recfile.readline()) recfile.close() return nrec def readSrcFile(self): + ''' + Reads the whole sourcefile and returns structured information in a dictionary. + ''' nsrc = self.readNsrc() srcfile = open(self.csl, 'r') @@ -309,6 +399,10 @@ class Tomo3d(object): return sources def readTraces(self): + ''' + Reads the receiver input file and returns the information + in a structured dictionary. + ''' recfile = open(self.rec, 'r') ntraces = self.readNtraces() @@ -330,8 +424,13 @@ class Tomo3d(object): return traces def readArrivals(self, procID): + ''' + Reads the arrival times from a temporary process directory, + changes local to global sourceIDs and traceIDs and returns + a list of arrival times. + ''' directory = self.getProcDir(procID) - arrfile = open(directory + '/arrivals.dat', 'r') + arrfile = open(os.path.join(directory, self.ttime), 'r') sourceIDs = self.srcIDs4Kernel(procID) arrivals = [] @@ -347,6 +446,10 @@ class Tomo3d(object): return arrivals def readRays(self, procID): + ''' + Reads rays output from a temporary process directory and returns + the information in a structured dictionary. + ''' directory = self.getProcDir(procID) raysfile = open(directory + '/rays.dat', 'r') sourceIDs = self.srcIDs4Kernel(procID) @@ -385,8 +488,12 @@ class Tomo3d(object): } return rays - def writeSrcFile(self, procID, directory): - srcfile = open('%s/sources.in'%directory, 'w') + def writeSrcFile(self, procID): + ''' + Writes a source input file for a process with ID = procID. + ''' + directory = self.getProcDir(procID) + srcfile = open(os.path,join(directory, self.csl), 'w') sourceIDs = self.srcIDs4Kernel(procID) srcfile.write('%s\n'%len(sourceIDs)) @@ -401,7 +508,11 @@ class Tomo3d(object): srcfile.write('%s %s\n'%(int(interactions[0]), int(interactions[1]))) srcfile.write('%s\n'%source['veltype']) - def writeTracesFile(self, procID, directory): + def writeTracesFile(self, procID): + ''' + Writes a receiver input file for a process with ID = procID. + ''' + directory = self.getProcDir(procID) recfile = open('%s/receivers.in'%directory, 'w') sourceIDs = self.srcIDs4Kernel(procID) traceIDs = self.getTraceIDs4Sources(sourceIDs) @@ -417,9 +528,12 @@ class Tomo3d(object): recfile.write('%s\n'%trace['path']) def mergeArrivals(self, directory): + ''' + Merges the arrival times for all processes to self.cInvIterDir. + ''' arrfn = os.path.join(directory, self.ttim) arrivalsOut = open(arrfn, 'w') - print('Merging arrivals.dat...') + print('Merging %s...'%self.ttim) for procID in range(1, self.nproc + 1): arrivals = self.readArrivals(procID) for line in arrivals: @@ -428,6 +542,9 @@ class Tomo3d(object): os.system('ln -fs %s %s'%(arrfn, os.path.join(self.cwd, self.ttim))) def mergeRays(self, directory): + ''' + Merges the ray paths for all processes to self.cInvIterDir. + ''' print('Merging rays.dat...') with open(directory + '/rays.dat', 'w') as outfile: for procID in range(1, self.nproc + 1): @@ -448,9 +565,11 @@ class Tomo3d(object): outfile.writelines(raysec['raypoints']) def mergeFrechet(self, directory): - print('Merging frechet.dat...') - + ''' + Merges the frechet derivatives for all processes to self.cInvIterDir. + ''' frechfnout = os.path.join(directory, self.frechout) + print('Merging %s...'%self.frechout) with open(frechfnout, 'w') as outfile: for procID in range(1, self.nproc + 1): filename = os.path.join(self.getProcDir(procID), self.frechout) @@ -465,6 +584,9 @@ class Tomo3d(object): os.system('ln -fs %s %s'%(frechfnout, os.path.join(self.cwd, self.frechout))) def mergeOutput(self, directory): + ''' + Calls self.mergeArrivals, self.mergeFrechet and self.mergeRays. + ''' self.mergeArrivals(directory) self.mergeFrechet(directory) self.mergeRays(directory) From 4edd5f8e52eb461bc28846bcb6f490aa10fda55d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 19 May 2016 11:21:24 +0200 Subject: [PATCH 0909/1144] code cleanup --- pylot/core/active/seismicshot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 68087561..20b8fb38 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -283,7 +283,7 @@ class SeismicShot(object): # raise ValueError('ambigious or empty traceID: %s' % traceID) - def pickTraces(self, traceID, windowsize, folm, HosAic='hos'): ########## input variables ########## + def pickTraces(self, traceID, folm, HosAic='hos', windowsize = (10, 0)): ########## input variables ########## # LOCALMAX NOT IMPLEMENTED! ''' Intitiate picking for a trace. From db17cb4f8d96924ef1b8e4dfbb8d3884f02e451c Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 19 May 2016 14:24:48 +0200 Subject: [PATCH 0910/1144] code cleanup --- pylot/core/active/seismicshot.py | 66 ++------------------------------ 1 file changed, 3 insertions(+), 63 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 20b8fb38..a5c0d26b 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -64,11 +64,6 @@ class SeismicShot(object): if traceID == trace.stats.channel: self.traces.remove(trace) - # for traceID in TraceIDs: - # traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] - # if len(traces) is not 1: - # self.traces.remove(trace) - def updateTraceList(self): ''' Looks for empty traces, returns a list of deleted traceIDs. @@ -151,7 +146,6 @@ class SeismicShot(object): if not self.getPickFlag(traceID) == 0: return self.picks[traceID]['mpp'] if returnRemoved == True: - # print('getPick: Returned removed pick for shot %d, traceID %d' %(self.getShotnumber(), traceID)) return self.picks[traceID]['mpp'] def getPickIncludeRemoved(self, traceID): @@ -281,10 +275,7 @@ class SeismicShot(object): self.setPick(traceID, None) print 'Warning: ambigious or empty traceID: %s' % traceID - # raise ValueError('ambigious or empty traceID: %s' % traceID) - def pickTraces(self, traceID, folm, HosAic='hos', windowsize = (10, 0)): ########## input variables ########## - # LOCALMAX NOT IMPLEMENTED! ''' Intitiate picking for a trace. @@ -335,11 +326,6 @@ class SeismicShot(object): if self.picks[traceID]['epp'] < 0: self.picks[traceID]['epp'] - # print('setEarllatepick: Set epp to 0 because it was < 0') - - # TEST OF 1/2 PICKERROR - # self.picks[traceID]['spe'] *= 0.5 - # TEST OF 1/2 PICKERROR def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm): ''' @@ -368,12 +354,8 @@ class SeismicShot(object): leftb = int(pickwindow[0] / self.getCut()[1] * len(hoscflist)) rightb = int(pickwindow[1] / self.getCut()[1] * len(hoscflist)) - # threshold = folm * max(hoscflist[leftb : rightb]) # combination of local maximum and threshold - - ### TEST TEST threshold = folm * (max(hoscflist[leftb: rightb]) - min(hoscflist[leftb: rightb])) + min( hoscflist[leftb: rightb]) # combination of local maximum and threshold - ### TEST TEST m = leftb @@ -411,7 +393,6 @@ class SeismicShot(object): raise ValueError("Distance is NaN for traceID %s" % traceID) return dist - # return abs(float(self.getSrcLoc(traceID))-float(self.getRecLoc(traceID))) def getRecLoc(self, traceID): ########## input FILENAME ########## ''' @@ -432,9 +413,7 @@ class SeismicShot(object): z = coordlist[i].split()[3] return float(x), float(y), float(z) - # print "WARNING: traceID %s not found" % traceID raise ValueError("traceID %s not found" % traceID) - # return float(self.getSingleStream(traceID)[0].stats.seg2['RECEIVER_LOCATION']) def getSrcLoc(self): ########## input FILENAME ########## ''' @@ -477,26 +456,6 @@ class SeismicShot(object): if len(traceID_list) > 0: return traceID_list - # def setManualPicks(self, traceID, picklist): ########## picklist momentan nicht allgemein, nur testweise benutzt ########## - # ''' - # Sets the manual picks for a receiver with the ID == traceID for comparison. - - # :param: traceID - # :type: int - - # :param: picklist, list containing the manual picks (mostlikely, earliest, latest). - # :type: list - # ''' - # picks = picklist[traceID - 1].split() - # mostlikely = float(picks[1]) - # earliest = float(picks[2]) - # latest = float(picks[3]) - - # if not self.manualpicks.has_key(traceID): - # self.manualpicks[traceID] = (mostlikely, earliest, latest) - # else: - # raise KeyError('MANUAL pick to be set more than once for traceID %s' % traceID) - def setManualPicksFromFile(self, directory='picks'): ''' Read manual picks from *.pck file. @@ -565,7 +524,6 @@ class SeismicShot(object): :param: (tnoise, tgap, tsignal), as used in pylot SNR ''' - from pylot.core.pick.utils import getSNR tgap = self.getTgap() @@ -591,7 +549,7 @@ class SeismicShot(object): return distancearray - def plot2dttc(self, ax=None): ########## 2D ########## + def plot2dttc(self, ax=None): ''' Function to plot the traveltime curve for automated picks of a shot. 2d only! ATM: X DIRECTION!! ''' @@ -617,7 +575,7 @@ class SeismicShot(object): ax.text(0.5, 0.9, 'shot: %s' % self.getShotnumber(), transform=ax.transAxes , horizontalalignment='center') - def plotmanual2dttc(self, ax=None): ########## 2D ########## + def plotmanual2dttc(self, ax=None): ''' Function to plot the traveltime curve for manual picks of a shot. 2D only! ''' @@ -643,24 +601,6 @@ class SeismicShot(object): y.append(point[1]) ax.plot(x, y, 'b', label="Manual Picks") - # def plotpickwindow(self): ########## 2D ########## - # ''' - # Plots the pickwindow of a shot for the 2nd iteration step of picking. 2D only! - # ''' - # import matplotlib.pyplot as plt - # plt.interactive('True') - # pickwindowarray_lowerb = [] - # pickwindowarray_upperb = [] - - # i = 1 - # for traceID in self.pwindow.keys(): - # pickwindowarray_lowerb.append(self.pwindow[traceID][0]) - # pickwindowarray_upperb.append(self.pwindow[traceID][1]) - # i += 1 - - # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_lowerb, ':k') - # plt.plot(self.getDistArray4ttcPlot(), pickwindowarray_upperb, ':k') - def plotTrace(self, traceID, plotSNR=True, lw=1): fig = plt.figure() ax = fig.add_subplot(111) @@ -679,7 +619,7 @@ class SeismicShot(object): ax.legend() ax.text(0.05, 0.9, 'SNR: %s' % snr, transform=ax.transAxes) - def plot_traces(self, traceID): ########## 2D, muss noch mehr verbessert werden ########## + def plot_traces(self, traceID): from matplotlib.widgets import Button def onclick(event): From 9b7db91037040cb19970efb6f16da31d148437f6 Mon Sep 17 00:00:00 2001 From: sebastianp Date: Thu, 19 May 2016 15:35:11 +0200 Subject: [PATCH 0911/1144] [task] started to implement data processing step for checking corrupted GSE files --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/phases.py | 37 ++++++++++++++++++++++++------- pylot/core/util/dataprocessing.py | 31 +++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index c35d452c..671949c9 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -1508-dirty +7c5a-dirty diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 3924ddd3..b2c5d4ef 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -232,7 +232,7 @@ def picksdict_from_picks(evt): def picks_from_picksdict(picks): picks_list = list() for station, onsets in picks.items(): - print('Reading picks on station %s' % station) + #print('Reading picks on station %s' % station) for label, phase in onsets.items(): if not isinstance(phase, dict) or len(phase) < 3: continue @@ -276,6 +276,7 @@ def reassess_pilot_db(root_dir, out_dir=None, fn_param=None): reassess_pilot_event(root_dir, evt, out_dir, fn_param) + def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbosity=0): from obspy import read @@ -292,9 +293,11 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat')) if not phases_file: return - print('Opening PILOT phases file: {fn}'.format(fn=phases_file[0])) + #print('Opening PILOT phases file: {fn}'.format(fn=phases_file[0])) picks_dict = picks_from_pilot(phases_file[0]) - print('Dictionary read from PHASES.mat:\n{0}'.format(picks_dict)) + #print('Dictionary read from PHASES.mat:\n{0}'.format(picks_dict)) + datacheck = list() + info = None for station in picks_dict.keys(): fn_pattern = os.path.join(search_base, '{0}*'.format(station)) try: @@ -302,6 +305,20 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos except TypeError as e: print(e.message) st = read(fn_pattern) + except ValueError as e: + if e.message == 'second must be in 0..59': + info = 'A known Error was raised. Please find the list of corrupted files and double-check these files.' + datacheck.append(fn_pattern + ' (time info)\n') + continue + else: + raise ValueError(e.message) + except Exception as e: + if 'No file matching file pattern:' in e.message: + warnings.warn('no waveform data found for station {station}'.format(station=station), RuntimeWarning) + datacheck.append(fn_pattern + ' (no data)\n') + continue + else: + raise e for phase in picks_dict[station].keys(): try: mpp = picks_dict[station][phase]['mpp'] @@ -310,10 +327,7 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos continue sel_st = select_for_phase(st, phase) if not sel_st: - raise warnings.formatwarning( - 'no waveform data found for station {station}'.format( - station=station), category=RuntimeWarning) - print(sel_st) + warnings.warn('no waveform data found for station {station}'.format(station=station), RuntimeWarning) stime, etime = getGlobalTimes(sel_st) rel_pick = mpp - stime epp, lpp, spe = earllatepicker(sel_st, @@ -321,7 +335,7 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos default.get('tsnrz' if phase == 'P' else 'tsnrh'), Pick1=rel_pick, iplot=None, - ) + stealthMode=True) if epp is None or lpp is None: continue epp = stime + epp @@ -332,6 +346,13 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos if mpp - epp < min_diff: epp = mpp - min_diff picks_dict[station][phase] = dict(epp=epp, mpp=mpp, lpp=lpp, spe=spe) + if datacheck: + if info: + print(info + ': {0}'.format(search_base)) + fncheck = open(os.path.join(search_base, 'datacheck_list'), 'w') + fncheck.writelines(datacheck) + fncheck.close() + del datacheck # create Event object for export evt = ope.Event(resource_id=event_id) evt.picks = picks_from_picksdict(picks_dict) diff --git a/pylot/core/util/dataprocessing.py b/pylot/core/util/dataprocessing.py index b1b64d16..1a4eff92 100644 --- a/pylot/core/util/dataprocessing.py +++ b/pylot/core/util/dataprocessing.py @@ -25,6 +25,29 @@ def check_time(datetime): :param datetime: list of integers [year, month, day, hour, minute, second, microsecond] :type datetime: list :return: returns True if Values are in supposed range, returns False otherwise + + >>> check_time([1999, 01, 01, 23, 59, 59, 999000]) + True + >>> check_time([1999, 01, 01, 23, 59, 60, 999000]) + False + >>> check_time([1999, 01, 01, 23, 59, 59, 1000000]) + False + >>> check_time([1999, 01, 01, 23, 60, 59, 999000]) + False + >>> check_time([1999, 01, 01, 23, 60, 59, 999000]) + False + >>> check_time([1999, 01, 01, 24, 59, 59, 999000]) + False + >>> check_time([1999, 01, 31, 23, 59, 59, 999000]) + True + >>> check_time([1999, 02, 30, 23, 59, 59, 999000]) + False + >>> check_time([1999, 02, 29, 23, 59, 59, 999000]) + False + >>> check_time([2000, 02, 29, 23, 59, 59, 999000]) + True + >>> check_time([2000, 13, 29, 23, 59, 59, 999000]) + False """ try: UTCDateTime(*datetime) @@ -122,4 +145,10 @@ def evt_head_check(root_dir, out_dir = None): out = open(os.path.join(out_dir, file), 'w') out.writelines(lines) out.close() - print(nfiles) \ No newline at end of file + print(nfiles) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() \ No newline at end of file From 54b557930fd50a4969a78edbd5c5656ea62f3e29 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 20 May 2016 09:58:10 +0200 Subject: [PATCH 0912/1144] [edit] inserted verbosity option to control the amount of output --- pylot/core/io/inputs.py | 5 +++-- pylot/core/io/phases.py | 25 +++++++++++++++++-------- scripts/pylot-reasses-pilot-db.py | 12 +++++++++--- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index 431ac731..a200e056 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -36,7 +36,7 @@ class AutoPickParameter(object): ========== ========== ======================================= ''' - def __init__(self, fnin=None, fnout=None, **kwargs): + def __init__(self, fnin=None, fnout=None, verbosity=0, **kwargs): ''' Initialize parameter object: @@ -60,7 +60,8 @@ class AutoPickParameter(object): parspl = line.split('\t')[:2] parFileCont[parspl[0].strip()] = parspl[1] except IndexError as e: - self._printParameterError(e) + if verbosity > 0: + self._printParameterError(e) inputFile.seek(0) lines = inputFile.readlines() for line in lines: diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index b2c5d4ef..d3a9f1fc 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -262,18 +262,23 @@ def picks_from_picksdict(picks): else: pick.polarity = 'undecidable' except KeyError as e: - print(e.message, 'No polarity information found for %s' % phase) + if 'fm' in e.message: # no polarity information found for this phase + pass + else: + raise e picks_list.append(pick) return picks_list -def reassess_pilot_db(root_dir, out_dir=None, fn_param=None): +def reassess_pilot_db(root_dir, out_dir=None, fn_param=None, verbosity=0): import glob evt_list = glob.glob1(root_dir,'e????.???.??') for evt in evt_list: - reassess_pilot_event(root_dir, evt, out_dir, fn_param) + if verbosity > 0: + print('Reassessing event {0}'.format(evt)) + reassess_pilot_event(root_dir, evt, out_dir, fn_param, verbosity) @@ -287,15 +292,17 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos import pylot.core.util.defaults as defaults fn_param = defaults.AUTOMATIC_DEFAULTS - default = AutoPickParameter(fn_param) + default = AutoPickParameter(fn_param, verbosity) search_base = os.path.join(root_dir, event_id) phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat')) if not phases_file: return - #print('Opening PILOT phases file: {fn}'.format(fn=phases_file[0])) + if verbosity > 1: + print('Opening PILOT phases file: {fn}'.format(fn=phases_file[0])) picks_dict = picks_from_pilot(phases_file[0]) - #print('Dictionary read from PHASES.mat:\n{0}'.format(picks_dict)) + if verbosity > 0: + print('Dictionary read from PHASES.mat:\n{0}'.format(picks_dict)) datacheck = list() info = None for station in picks_dict.keys(): @@ -314,7 +321,8 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos raise ValueError(e.message) except Exception as e: if 'No file matching file pattern:' in e.message: - warnings.warn('no waveform data found for station {station}'.format(station=station), RuntimeWarning) + if verbosity > 0: + warnings.warn('no waveform data found for station {station}'.format(station=station), RuntimeWarning) datacheck.append(fn_pattern + ' (no data)\n') continue else: @@ -348,7 +356,8 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos picks_dict[station][phase] = dict(epp=epp, mpp=mpp, lpp=lpp, spe=spe) if datacheck: if info: - print(info + ': {0}'.format(search_base)) + if verbosity > 0: + print(info + ': {0}'.format(search_base)) fncheck = open(os.path.join(search_base, 'datacheck_list'), 'w') fncheck.writelines(datacheck) fncheck.close() diff --git a/scripts/pylot-reasses-pilot-db.py b/scripts/pylot-reasses-pilot-db.py index 5bb4441d..83cdf254 100755 --- a/scripts/pylot-reasses-pilot-db.py +++ b/scripts/pylot-reasses-pilot-db.py @@ -23,11 +23,17 @@ if __name__ == '__main__': 'most cases PILOT database folder)' ) parser.add_argument( - '--output', '-o', type=str, help='path to the output directory', dest='output' + '--output', '-o', type=str, help='path to the output directory', + dest='output' ) parser.add_argument( - '--parameterfile', '-p', type=str, help='full path to the parameterfile', dest='parfile' + '--parameterfile', '-p', type=str, + help='full path to the parameterfile', dest='parfile' + ) + parser.add_argument( + '--verbosity', '-v', action='count', help='increase output verbosity', + default=0, dest='verbosity' ) args = parser.parse_args() - reassess_pilot_db(args.dbroot, args.output, args.parfile) + reassess_pilot_db(args.dbroot, args.output, args.parfile, args.verbosity) From 08df7c33048932398bfcc6fe2ecc9714f0574e80 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 20 May 2016 10:11:40 +0200 Subject: [PATCH 0913/1144] [edit] decreased unwanted verbosity --- pylot/core/active/seismicshot.py | 2 +- pylot/core/io/phases.py | 2 +- pylot/core/pick/utils.py | 17 ++++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 68087561..dca3ddf3 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -331,7 +331,7 @@ class SeismicShot(object): self.picks[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), nfac, (tnoise, tgap, tsignal), self.getPickIncludeRemoved(traceID), - stealthMode=True) + stealth_mode=True) if self.picks[traceID]['epp'] < 0: self.picks[traceID]['epp'] diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index d3a9f1fc..604ea940 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -343,7 +343,7 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos default.get('tsnrz' if phase == 'P' else 'tsnrh'), Pick1=rel_pick, iplot=None, - stealthMode=True) + stealth_mode=True) if epp is None or lpp is None: continue epp = stime + epp diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index e603cfa4..09405fb1 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -16,7 +16,7 @@ import numpy as np from obspy.core import Stream, UTCDateTime -def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode=False): +def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealth_mode=False): ''' Function to derive earliest and latest possible pick after Diehl & Kissling (2009) as reasonable uncertainties. Latest possible pick is based on noise level, @@ -45,8 +45,9 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode=False): LPick = None EPick = None PickError = None - if stealthMode is False: - print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' + if stealth_mode is False: + print('earllatepicker: Get earliest and latest possible pick' + ' relative to most likely pick ...') x = X[0].data t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, @@ -62,8 +63,9 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode=False): ilup, = np.where(x[isignal] > nlevel) ildown, = np.where(x[isignal] < -nlevel) if not ilup.size and not ildown.size: - print ("earllatepicker: Signal lower than noise level!") - print ("Skip this trace!") + if stealth_mode is False: + print ("earllatepicker: Signal lower than noise level!\n" + "Skip this trace!") return LPick, EPick, PickError il = min(np.min(ilup) if ilup.size else float('inf'), np.min(ildown) if ildown.size else float('inf')) @@ -78,7 +80,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode=False): # if EPick stays NaN the signal window size will be doubled while np.isnan(EPick): if count > 0: - if stealthMode is False: + if stealth_mode is False: print("\nearllatepicker: Doubled signal window size %s time(s) " "because of NaN for earliest pick." % count) isigDoubleWinStart = pis[-1] + 1 @@ -87,7 +89,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None, stealthMode=False): if (isigDoubleWinStart + len(pis)) < X[0].data.size: pis = np.concatenate((pis, isignalDoubleWin)) else: - print("Could not double signal window. Index out of bounds.") + if stealth_mode is False: + print("Could not double signal window. Index out of bounds.") break count += 1 # determine all zero crossings in signal window (demeaned) From 392a556ac4594f9eb21b38ac06be7a9b09111307 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 20 May 2016 14:46:10 +0200 Subject: [PATCH 0914/1144] [restructure] Wall-E wasting time --- QtPyLoT.py | 7 +- pylot/core/io/location.py | 222 +++++++++++++++++++++++++++++++++++++ pylot/core/io/phases.py | 26 +++-- pylot/core/util/utils.py | 227 +------------------------------------- 4 files changed, 244 insertions(+), 238 deletions(-) create mode 100644 pylot/core/io/location.py diff --git a/QtPyLoT.py b/QtPyLoT.py index 6daaffa6..6deb6f16 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -51,7 +51,8 @@ from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError from pylot.core.util.connection import checkurl from pylot.core.util.utils import fnConstructor, createEvent, getLogin, \ - createCreationInfo, getGlobalTimes + getGlobalTimes +from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg from pylot.core.util.structure import DATASTRUCTURE @@ -829,8 +830,8 @@ class MainWindow(QMainWindow): new = NewEventDlg() if new.exec_() != QDialog.Rejected: evtpar = new.getValues() - cinfo = createCreationInfo(agency_id=self.agency) - event = createEvent(evtpar['origintime'], cinfo) + cinfo = create_creation_info(agency_id=self.agency) + event = create_event(evtpar['origintime'], cinfo) self.data = Data(self, evtdata=event) self.setDirty(True) diff --git a/pylot/core/io/location.py b/pylot/core/io/location.py new file mode 100644 index 00000000..667b7f91 --- /dev/null +++ b/pylot/core/io/location.py @@ -0,0 +1,222 @@ +from obspy import UTCDateTime +from obspy.core import event as ope + +from pylot.core.util.utils import getLogin, getHash + + +def create_amplitude(pickID, amp, unit, category, cinfo): + ''' + + :param pickID: + :param amp: + :param unit: + :param category: + :param cinfo: + :return: + ''' + amplitude = ope.Amplitude() + amplitude.creation_info = cinfo + amplitude.generic_amplitude = amp + amplitude.unit = ope.AmplitudeUnit(unit) + amplitude.type = ope.AmplitudeCategory(category) + amplitude.pick_id = pickID + return amplitude + + +def create_arrival(pickresID, cinfo, phase, azimuth=None, dist=None): + ''' + create_arrival - function to create an Obspy Arrival + + :param pickresID: Resource identifier of the created pick + :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + :param azimuth: azimuth between source and receiver + :type azimuth: float or int, optional + :param dist: distance between source and receiver + :type dist: float or int, optional + :return: An ObsPy :class: `~obspy.core.event.Arrival` object + ''' + arrival = ope.Arrival() + arrival.creation_info = cinfo + arrival.pick_id = pickresID + arrival.phase = phase + if azimuth is not None: + arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360. + else: + arrival.azimuth = azimuth + arrival.distance = dist + return arrival + + +def create_creation_info(agency_id=None, creation_time=None, author=None): + ''' + + :param agency_id: + :param creation_time: + :param author: + :return: + ''' + if author is None: + author = getLogin() + if creation_time is None: + creation_time = UTCDateTime() + return ope.CreationInfo(agency_id=agency_id, author=author, + creation_time=creation_time) + + +def create_event(origintime, cinfo, originloc=None, etype=None, resID=None, + authority_id=None): + ''' + create_event - funtion to create an ObsPy Event + + :param origintime: the events origintime + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param originloc: tuple containing the location of the origin + (LAT, LON, DEP) affiliated with the event which is created + :type originloc: tuple, list + :param etype: Event type str object. converted via ObsPy to a valid event + type string. + :type etype: str + :param resID: Resource identifier of the created event + :type resID: :class: `~obspy.core.event.ResourceIdentifier` object, str + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + :return: An ObsPy :class: `~obspy.core.event.Event` object + ''' + etype = ope.EventType(etype) + if originloc is not None: + o = create_origin(origintime, cinfo, + originloc[0], originloc[1], originloc[2]) + else: + o = None + if etype is None: + etype = ope.EventType('earthquake') # defaults to 'earthquake' + if not resID: + resID = create_resourceID(origintime, etype, authority_id) + elif isinstance(resID, str): + resID = create_resourceID(origintime, etype, authority_id, resID) + elif not isinstance(resID, ope.ResourceIdentifier): + raise TypeError("unsupported type(resID) for resource identifier " + "generation: %s" % type(resID)) + event = ope.Event(resource_id=resID) + event.creation_info = cinfo + event.event_type = etype + if o: + event.origins = [o] + return event + + +def create_magnitude(originID, cinfo): + ''' + create_magnitude - function to create an ObsPy Magnitude object + :param originID: + :type originID: + :param cinfo: + :type cinfo: + :return: + ''' + magnitude = ope.Magnitude() + magnitude.creation_info = cinfo + magnitude.origin_id = originID + return magnitude + + +def create_origin(origintime, cinfo, latitude, longitude, depth): + ''' + create_origin - function to create an ObsPy Origin + :param origintime: the origins time of occurence + :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object + :param cinfo: + :type cinfo: + :param latitude: latitude in decimal degree of the origins location + :type latitude: float + :param longitude: longitude in decimal degree of the origins location + :type longitude: float + :param depth: hypocentral depth of the origin + :type depth: float + :return: An ObsPy :class: `~obspy.core.event.Origin` object + ''' + + assert isinstance(origintime, UTCDateTime), "origintime has to be " \ + "a UTCDateTime object, but " \ + "actually is of type " \ + "'%s'" % type(origintime) + + origin = ope.Origin() + origin.time = origintime + origin.creation_info = cinfo + origin.latitude = latitude + origin.longitude = longitude + origin.depth = depth + return origin + + +def create_pick(origintime, picknum, picktime, eventnum, cinfo, phase, station, + wfseedstr, authority_id): + ''' + create_pick - function to create an ObsPy Pick + + :param origintime: + :type origintime: + :param picknum: number of the created pick + :type picknum: int + :param picktime: + :type picktime: + :param eventnum: human-readable event identifier + :type eventnum: str + :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object + holding information on the creation of the returned object + :type cinfo: :class: `~obspy.core.event.CreationInfo` object + :param phase: name of the arrivals seismic phase + :type phase: str + :param station: name of the station at which the seismic phase has been + picked + :type station: str + :param wfseedstr: A SEED formatted string of the form + network.station.location.channel in order to set a referenced waveform + :type wfseedstr: str, SEED formatted + :param authority_id: name of the institution carrying out the processing + :type authority_id: str + :return: An ObsPy :class: `~obspy.core.event.Pick` object + ''' + pickID = eventnum + '_' + station.strip() + '/{0:03d}'.format(picknum) + pickresID = create_resourceID(origintime, 'pick', authority_id, pickID) + pick = ope.Pick() + pick.resource_id = pickresID + pick.time = picktime + pick.creation_info = cinfo + pick.phase_hint = phase + pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') + return pick + + +def create_resourceID(timetohash, restype, authority_id=None, hrstr=None): + ''' + + :param timetohash: + :type timetohash + :param restype: type of the resource, e.g. 'orig', 'earthquake' ... + :type restype: str + :param authority_id: name of the institution carrying out the processing + :type authority_id: str, optional + :param hrstr: + :type hrstr: + :return: + ''' + assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ + "UTCDateTime object" + hid = getHash(timetohash) + if hrstr is None: + resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) + else: + resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) + if authority_id is not None: + resID.convertIDToQuakeMLURI(authority_id=authority_id) + return resID \ No newline at end of file diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 604ea940..cf2e48b6 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -1,16 +1,18 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import os import glob -import warnings -import scipy.io as sio import obspy.core.event as ope +import os +import scipy.io as sio +import warnings from obspy.core import UTCDateTime +from pylot.core.io.location import create_arrival, create_event, \ + create_magnitude, create_origin, create_pick from pylot.core.pick.utils import select_for_phase -from pylot.core.util.utils import getOwner, createPick, createArrival, \ - createEvent, createOrigin, createMagnitude, getGlobalTimes +from pylot.core.util.utils import getOwner, getGlobalTimes + def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): """ @@ -75,14 +77,14 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): stations = [stat for stat in phases['stat'][0:-1:3]] - event = createEvent(eventDate, loccinfo, etype='earthquake', resID=eventNum, - authority_id=authority_id) + event = create_event(eventDate, loccinfo, etype='earthquake', resID=eventNum, + authority_id=authority_id) lat = float(loc['LAT']) lon = float(loc['LON']) dep = float(loc['DEP']) - origin = createOrigin(eventDate, loccinfo, lat, lon, dep) + origin = create_origin(eventDate, loccinfo, lat, lon, dep) for n, pick in enumerate(phases['Ptime']): if pick[0] > 0: kwargs = {'year': int(pick[0]), @@ -115,15 +117,15 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): wffn = os.path.join(sdir, '{0}*{1}*'.format( stations[n].strip(), '[ne]')) print(wffn) - pick = createPick(eventDate, np, picktime, eventNum, pickcinfo, - phase, stations[n], wffn, authority_id) + pick = create_pick(eventDate, np, picktime, eventNum, pickcinfo, + phase, stations[n], wffn, authority_id) event.picks.append(pick) pickID = pick.get('id') - arrival = createArrival(pickID, pickcinfo, phase) + arrival = create_arrival(pickID, pickcinfo, phase) origin.arrivals.append(arrival) np += 1 - magnitude = createMagnitude(origin.get('id'), loccinfo) + magnitude = create_magnitude(origin.get('id'), loccinfo) magnitude.mag = float(loc['Mnet']) magnitude.magnitude_type = 'Ml' diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index c0eb0185..f892c6f6 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -1,232 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import os -import subprocess -import pwd -import re import hashlib import numpy as np +import os +import pwd +import re +import subprocess from obspy.core import UTCDateTime -import obspy.core.event as ope - - -def createAmplitude(pickID, amp, unit, category, cinfo): - ''' - - :param pickID: - :param amp: - :param unit: - :param category: - :param cinfo: - :return: - ''' - amplitude = ope.Amplitude() - amplitude.creation_info = cinfo - amplitude.generic_amplitude = amp - amplitude.unit = ope.AmplitudeUnit(unit) - amplitude.type = ope.AmplitudeCategory(category) - amplitude.pick_id = pickID - return amplitude - - -def createArrival(pickresID, cinfo, phase, azimuth=None, dist=None): - ''' - createArrival - function to create an Obspy Arrival - - :param pickresID: Resource identifier of the created pick - :type pickresID: :class: `~obspy.core.event.ResourceIdentifier` object - :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object - holding information on the creation of the returned object - :type cinfo: :class: `~obspy.core.event.CreationInfo` object - :param phase: name of the arrivals seismic phase - :type phase: str - :param azimuth: azimuth between source and receiver - :type azimuth: float or int, optional - :param dist: distance between source and receiver - :type dist: float or int, optional - :return: An ObsPy :class: `~obspy.core.event.Arrival` object - ''' - arrival = ope.Arrival() - arrival.creation_info = cinfo - arrival.pick_id = pickresID - arrival.phase = phase - if azimuth is not None: - arrival.azimuth = float(azimuth) if azimuth > -180 else azimuth + 360. - else: - arrival.azimuth = azimuth - arrival.distance = dist - return arrival - - -def createCreationInfo(agency_id=None, creation_time=None, author=None): - ''' - - :param agency_id: - :param creation_time: - :param author: - :return: - ''' - if author is None: - author = getLogin() - if creation_time is None: - creation_time = UTCDateTime() - return ope.CreationInfo(agency_id=agency_id, author=author, - creation_time=creation_time) - - -def createEvent(origintime, cinfo, originloc=None, etype=None, resID=None, - authority_id=None): - ''' - createEvent - funtion to create an ObsPy Event - - :param origintime: the events origintime - :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object - :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object - holding information on the creation of the returned object - :type cinfo: :class: `~obspy.core.event.CreationInfo` object - :param originloc: tuple containing the location of the origin - (LAT, LON, DEP) affiliated with the event which is created - :type originloc: tuple, list - :param etype: Event type str object. converted via ObsPy to a valid event - type string. - :type etype: str - :param resID: Resource identifier of the created event - :type resID: :class: `~obspy.core.event.ResourceIdentifier` object, str - :param authority_id: name of the institution carrying out the processing - :type authority_id: str - :return: An ObsPy :class: `~obspy.core.event.Event` object - ''' - etype = ope.EventType(etype) - if originloc is not None: - o = createOrigin(origintime, cinfo, - originloc[0], originloc[1], originloc[2]) - else: - o = None - if etype is None: - etype = ope.EventType('earthquake') # defaults to 'earthquake' - if not resID: - resID = createResourceID(origintime, etype, authority_id) - elif isinstance(resID, str): - resID = createResourceID(origintime, etype, authority_id, resID) - elif not isinstance(resID, ope.ResourceIdentifier): - raise TypeError("unsupported type(resID) for resource identifier " - "generation: %s" % type(resID)) - event = ope.Event(resource_id=resID) - event.creation_info = cinfo - event.event_type = etype - if o: - event.origins = [o] - return event - - -def createMagnitude(originID, cinfo): - ''' - createMagnitude - function to create an ObsPy Magnitude object - :param originID: - :type originID: - :param cinfo: - :type cinfo: - :return: - ''' - magnitude = ope.Magnitude() - magnitude.creation_info = cinfo - magnitude.origin_id = originID - return magnitude - - -def createOrigin(origintime, cinfo, latitude, longitude, depth): - ''' - createOrigin - function to create an ObsPy Origin - :param origintime: the origins time of occurence - :type origintime: :class: `~obspy.core.utcdatetime.UTCDateTime` object - :param cinfo: - :type cinfo: - :param latitude: latitude in decimal degree of the origins location - :type latitude: float - :param longitude: longitude in decimal degree of the origins location - :type longitude: float - :param depth: hypocentral depth of the origin - :type depth: float - :return: An ObsPy :class: `~obspy.core.event.Origin` object - ''' - - assert isinstance(origintime, UTCDateTime), "origintime has to be " \ - "a UTCDateTime object, but " \ - "actually is of type " \ - "'%s'" % type(origintime) - - origin = ope.Origin() - origin.time = origintime - origin.creation_info = cinfo - origin.latitude = latitude - origin.longitude = longitude - origin.depth = depth - return origin - - -def createPick(origintime, picknum, picktime, eventnum, cinfo, phase, station, - wfseedstr, authority_id): - ''' - createPick - function to create an ObsPy Pick - - :param origintime: - :type origintime: - :param picknum: number of the created pick - :type picknum: int - :param picktime: - :type picktime: - :param eventnum: human-readable event identifier - :type eventnum: str - :param cinfo: An ObsPy :class: `~obspy.core.event.CreationInfo` object - holding information on the creation of the returned object - :type cinfo: :class: `~obspy.core.event.CreationInfo` object - :param phase: name of the arrivals seismic phase - :type phase: str - :param station: name of the station at which the seismic phase has been - picked - :type station: str - :param wfseedstr: A SEED formatted string of the form - network.station.location.channel in order to set a referenced waveform - :type wfseedstr: str, SEED formatted - :param authority_id: name of the institution carrying out the processing - :type authority_id: str - :return: An ObsPy :class: `~obspy.core.event.Pick` object - ''' - pickID = eventnum + '_' + station.strip() + '/{0:03d}'.format(picknum) - pickresID = createResourceID(origintime, 'pick', authority_id, pickID) - pick = ope.Pick() - pick.resource_id = pickresID - pick.time = picktime - pick.creation_info = cinfo - pick.phase_hint = phase - pick.waveform_id = ope.ResourceIdentifier(id=wfseedstr, prefix='file:/') - return pick - - -def createResourceID(timetohash, restype, authority_id=None, hrstr=None): - ''' - - :param timetohash: - :type timetohash - :param restype: type of the resource, e.g. 'orig', 'earthquake' ... - :type restype: str - :param authority_id: name of the institution carrying out the processing - :type authority_id: str, optional - :param hrstr: - :type hrstr: - :return: - ''' - assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \ - "UTCDateTime object" - hid = getHash(timetohash) - if hrstr is None: - resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) - else: - resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) - if authority_id is not None: - resID.convertIDToQuakeMLURI(authority_id=authority_id) - return resID def demeanTrace(trace, window): From 1f47f3dd850114b2ecd3aec6f7a7e098ba57757f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 11:22:39 +0200 Subject: [PATCH 0915/1144] parallization of picking algorithm --- pylot/core/active/seismicshot.py | 73 +++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index a5c0d26b..827b12ac 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -78,6 +78,12 @@ class SeismicShot(object): def setParameters(self, name, value): self.paras[name] = value + def setVmin(self, vmin): + self.setParameters('vmin', vmin) + + def setVmax(self, vmax): + self.setParameters('vmax', vmax) + def setCut(self, cut): self.setParameters('cut', cut) @@ -102,6 +108,43 @@ class SeismicShot(object): def setSourcefile(self, sourcefile): self.setParameters('sourcefile', sourcefile) + def setMethod(self, method): + self.setParameters('method', method) + + def setAicwindow(self, aicwindow): + self.setParameters('aicwindow', aicwindow) + + def setFolm(self, folm): + self.setParameters('folm', folm) + + def setDynPickwindow(self, traceID, cutdist = 5.): + distance = self.getDistance(traceID) # receive distance + + vmin = self.getVmin() + vmax = self.getVmax() + + pickwin_used = self.getCut() + cutwindow = self.getCut() + + # for higher distances use a linear vmin/vmax to cut out late/early regions with high noise + if distance > cutdist: + pwleft = distance / vmax + pwright = distance / vmin + if pwright > cutwindow[1]: + pwright = cutwindow[1] + pickwin_used = (pwleft, pwright) + + self.setPickwindow(traceID, pickwin_used) + + def getMethod(self): + return self.paras['method'] + + def getAicwindow(self): + return self.paras['aicwindow'] + + def getFolm(self): + return self.paras['folm'] + def getParas(self): return self.paras @@ -132,6 +175,12 @@ class SeismicShot(object): def getSourcefile(self): return self.paras['sourcefile'] + def getVmin(self): + return self.paras['vmin'] + + def getVmax(self): + return self.paras['vmax'] + def getManualPick(self, traceID): if not self.getManualPickFlag(traceID) == 0: return self.manualpicks[traceID]['mpp'] @@ -275,7 +324,21 @@ class SeismicShot(object): self.setPick(traceID, None) print 'Warning: ambigious or empty traceID: %s' % traceID - def pickTraces(self, traceID, folm, HosAic='hos', windowsize = (10, 0)): ########## input variables ########## + def pickParallel(self, folm, method = 'hos', aicwindow = (10, 0)): + import multiprocessing + + self.setFolm(folm) + self.setMethod(method) + self.setAicwindow(aicwindow) + + maxthreads = multiprocessing.cpu_count() + pool = multiprocessing.Pool(maxthreads) + + traceIDs = self.getTraceIDlist() + + pool.map(self.pickTrace, traceIDs) + + def pickTrace(self, traceID): ''' Intitiate picking for a trace. @@ -300,17 +363,17 @@ class SeismicShot(object): :param: HosAic, get hos or aic pick (can be 'hos'(default) or 'aic') :type: 'string' ''' + self.setDynPickwindow(traceID) + hoscf = self.getHOScf(traceID) ### determination of both, HOS and AIC (need to change threshold-picker) ### aiccf = self.getAICcf(traceID) - self.folm = folm - self.timeArray[traceID] = hoscf.getTimeArray() - aiccftime, hoscftime = self.threshold(hoscf, aiccf, windowsize, self.getPickwindow(traceID), folm) + aiccftime, hoscftime = self.threshold(hoscf, aiccf, self.getAicwindow(), self.getPickwindow(traceID), self.getFolm()) setHosAic = {'hos': hoscftime, 'aic': aiccftime} - self.setPick(traceID, setHosAic[HosAic]) + self.setPick(traceID, setHosAic[self.getMethod()]) def setEarllatepick(self, traceID, nfac=1.5): tgap = self.getTgap() From 73d71a61d5058edaf970bc26520b2ffa08b44d1a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 11:23:23 +0200 Subject: [PATCH 0916/1144] restructuring for parallization --- pylot/core/active/activeSeismoPick.py | 55 +++++++++++++-------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 2d02496d..44381d5b 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -216,45 +216,42 @@ class Survey(object): for shot in self.data.values(): tstartpick = datetime.now(); + shot.setVmin(vmin) + shot.setVmax(vmax) count += 1 - for traceID in shot.getTraceIDlist(): - distance = shot.getDistance(traceID) # receive distance + shot.pickParallel(folm) - pickwin_used = shot.getCut() - cutwindow = shot.getCut() + # tpicksum += (datetime.now() - tstartpick); + # tpick = tpicksum / count + # tremain = (tpick * (len(self.getShotDict()) - count)) + # tend = datetime.now() + tremain + # progress = float(count) / float(len(self.getShotDict())) * 100 + # self._update_progress(shot.getShotname(), tend, progress) - # for higher distances use a linear vmin/vmax to cut out late/early regions with high noise - if distance > 5.: - pwleft = distance / vmax ################## TEST - pwright = distance / vmin - if pwright > cutwindow[1]: - pwright = cutwindow[1] - pickwin_used = (pwleft, pwright) + self.setSNR() + self.setEarllate() - shot.setPickwindow(traceID, pickwin_used) - shot.pickTraces(traceID, folm, HosAic, aicwindow) # picker - - shot.setSNR(traceID) - # if shot.getSNR(traceID)[0] < snrthreshold: - if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): - shot.removePick(traceID) - - # set epp and lpp if SNR > 1 (else earllatepicker cant set values) - if shot.getSNR(traceID)[0] > 1: - shot.setEarllatepick(traceID) - - tpicksum += (datetime.now() - tstartpick); - tpick = tpicksum / count - tremain = (tpick * (len(self.getShotDict()) - count)) - tend = datetime.now() + tremain - progress = float(count) / float(len(self.getShotDict())) * 100 - self._update_progress(shot.getShotname(), tend, progress) print('\npickAllShots: Finished\n') ntraces = self.countAllTraces() pickedtraces = self.countAllPickedTraces() print('Picked %s / %s traces (%d %%)\n' % (pickedtraces, ntraces, float(pickedtraces) / float(ntraces) * 100.)) + def setSNR(self): + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + shot.setSNR(traceID) + # if shot.getSNR(traceID)[0] < snrthreshold: + if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): + shot.removePick(traceID) + + def setEarllate(self): + for shot in self.data.values(): + for traceID in shot.getTraceIDlist(): + # set epp and lpp if SNR > 1 (else earllatepicker cant set values) + if shot.getSNR(traceID)[0] > 1: + shot.setEarllatepick(traceID) + def cleanBySPE(self, maxSPE): ''' Sets all picks as invalid if they exceed a certain value of the symmetric pick error. From 3cc77f4868d9284e79814b557cf7dcaae5d51191 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 11:24:01 +0200 Subject: [PATCH 0917/1144] bugfixes --- pylot/core/active/fmtomoUtils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 086732d2..6db9705d 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -430,7 +430,7 @@ class Tomo3d(object): a list of arrival times. ''' directory = self.getProcDir(procID) - arrfile = open(os.path.join(directory, self.ttime), 'r') + arrfile = open(os.path.join(directory, self.ttim), 'r') sourceIDs = self.srcIDs4Kernel(procID) arrivals = [] @@ -493,7 +493,7 @@ class Tomo3d(object): Writes a source input file for a process with ID = procID. ''' directory = self.getProcDir(procID) - srcfile = open(os.path,join(directory, self.csl), 'w') + srcfile = open(os.path.join(directory, self.csl), 'w') sourceIDs = self.srcIDs4Kernel(procID) srcfile.write('%s\n'%len(sourceIDs)) From 41b7ca6968a77bca06d8ca386277dabaeec0a6ba Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 23 May 2016 11:53:22 +0200 Subject: [PATCH 0918/1144] [task] reformatting activeSeismoPick and editing pool mapping to work properly --- pylot/core/active/activeSeismoPick.py | 140 +++++++++++++++----------- pylot/core/active/seismicshot.py | 11 +- 2 files changed, 92 insertions(+), 59 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 44381d5b..f2c18d83 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -4,6 +4,7 @@ import numpy as np from pylot.core.active import seismicshot from pylot.core.active.surveyUtils import cleanUp + class Survey(object): def __init__(self, path, sourcefile, receiverfile, useDefaultParas=False): ''' @@ -21,13 +22,13 @@ class Survey(object): self._sourcefile = sourcefile self._obsdir = path self._generateSurvey() - self._initiateFilenames() + self._initiate_fnames() if useDefaultParas == True: self.setParametersForAllShots() self._removeAllEmptyTraces() self._updateShots() - def _initiateFilenames(self): + def _initiate_fnames(self): for shot in self.data.values(): shot.setRecfile(self.getPath() + self.getReceiverfile()) shot.setSourcefile(self.getPath() + self.getSourcefile()) @@ -74,7 +75,7 @@ class Survey(object): but were set in the input files. ''' logfile = 'updateShots.out' - count = 0; + count = 0 countTraces = 0 for shot in self.data.values(): del_traceIDs = shot.updateTraceList() @@ -93,15 +94,8 @@ class Survey(object): "on removed traces." % (logfile)) outfile.close() - def setArtificialPick(self, traceID, pick): - ''' - Sets an artificial pick for a traceID of all shots in the survey object. - (Commonly used to generate a travel time t = 0 at the source origin) - ''' - for shot in self.data.values(): - shot.setPick(traceID, pick) - - def setParametersForAllShots(self, cutwindow=(0, 0.2), tmovwind=0.3, tsignal=0.03, tgap=0.0007): + def setParametersForAllShots(self, cutwindow=(0, 0.2), tmovwind=0.3, + tsignal=0.03, tgap=0.0007): if (cutwindow == (0, 0.2) and tmovwind == 0.3 and tsignal == 0.03 and tgap == 0.0007): print ("Warning: Standard values used for " @@ -136,8 +130,10 @@ class Survey(object): if not shot in diffs.keys(): diffs[shot] = {} for traceID in shot.getTraceIDlist(): - if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: - diffs[shot][traceID] = shot.getPick(traceID) - shot.getManualPick(traceID) + if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag( + traceID) == 1: + diffs[shot][traceID] = shot.getPick( + traceID) - shot.getManualPick(traceID) return diffs def plotDiffs(self): @@ -146,14 +142,15 @@ class Survey(object): difference between automatic and manual pick. ''' import matplotlib.pyplot as plt - diffs = []; - dists = []; - mpicks = []; + diffs = [] + dists = [] + mpicks = [] picks = [] diffsDic = self.getDiffsFromManual() for shot in self.data.values(): for traceID in shot.getTraceIDlist(): - if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: + if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag( + traceID) == 1: dists.append(shot.getDistance(traceID)) mpicks.append(shot.getManualPick(traceID)) picks.append(shot.getPick(traceID)) @@ -165,13 +162,16 @@ class Survey(object): fig = plt.figure() ax = fig.add_subplot(111) - sc_a = ax.scatter(dists, picks, c='0.5', s=10, edgecolors='none', label=labela, alpha=0.3) - sc = ax.scatter(dists, mpicks, c=diffs, s=5, edgecolors='none', label=labelm) + sc_a = ax.scatter(dists, picks, c='0.5', s=10, edgecolors='none', + label=labela, alpha=0.3) + sc = ax.scatter(dists, mpicks, c=diffs, s=5, edgecolors='none', + label=labelm) cbar = plt.colorbar(sc, fraction=0.05) cbar.set_label(labelm) ax.set_xlabel('Distance [m]') ax.set_ylabel('Time [s]') - ax.text(0.5, 0.95, 'Plot of all MANUAL picks', transform=ax.transAxes, horizontalalignment='center') + ax.text(0.5, 0.95, 'Plot of all MANUAL picks', transform=ax.transAxes, + horizontalalignment='center') plt.legend() def plotHist(self, nbins=20, ax=None): @@ -186,14 +186,18 @@ class Survey(object): ax = fig.add_subplot(111) for shot in self.data.values(): for traceID in shot.getTraceIDlist(): - if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag(traceID) == 1: + if shot.getPickFlag(traceID) == 1 and shot.getManualPickFlag( + traceID) == 1: diffs.append(self.getDiffsFromManual()[shot][traceID]) - hist = plt.hist(diffs, nbins, histtype='step', normed=True, stacked=True) - plt.title('Histogram of the differences between automatic and manual pick') + hist = plt.hist(diffs, nbins, histtype='step', normed=True, + stacked=True) + plt.title( + 'Histogram of the differences between automatic and manual pick') plt.xlabel('Difference in time (auto - manual) [s]') return diffs - def pickAllShots(self, vmin=333, vmax=5500, folm=0.6, HosAic='hos', aicwindow=(10, 0)): + def pickAllShots(self, vmin=333, vmax=5500, folm=0.6, HosAic='hos', + aicwindow=(10, 0)): ''' Automatically pick all traces of all shots of the survey. @@ -211,11 +215,11 @@ class Survey(object): ''' from datetime import datetime starttime = datetime.now() - count = 0; + count = 0 tpicksum = starttime - starttime for shot in self.data.values(): - tstartpick = datetime.now(); + tstartpick = datetime.now() shot.setVmin(vmin) shot.setVmax(vmax) count += 1 @@ -235,7 +239,8 @@ class Survey(object): ntraces = self.countAllTraces() pickedtraces = self.countAllPickedTraces() print('Picked %s / %s traces (%d %%)\n' - % (pickedtraces, ntraces, float(pickedtraces) / float(ntraces) * 100.)) + % (pickedtraces, ntraces, + float(pickedtraces) / float(ntraces) * 100.)) def setSNR(self): for shot in self.data.values(): @@ -373,8 +378,11 @@ class Survey(object): pickedTraces += 1 info_dict[shot.getShotnumber()] = {'numtraces': numtraces, 'picked traces': [pickedTraces, - '%2.2f %%' % (float(pickedTraces) / - float(numtraces) * 100)], + '%2.2f %%' % ( + float( + pickedTraces) / + float( + numtraces) * 100)], 'mean SNR': np.mean(snrlist), 'mean distance': np.mean(dist)} @@ -388,10 +396,12 @@ class Survey(object): if shot.getShotnumber() == shotnumber: return shot - def exportFMTOMO(self, directory='FMTOMO_export', sourcefile='input_sf.in', ttFileExtension='.tt'): + def exportFMTOMO(self, directory='FMTOMO_export', sourcefile='input_sf.in', + ttFileExtension='.tt'): ''' Exports all picks into a directory as travel time files readable by FMTOMO obsdata. ''' + def getAngle(distance): PI = np.pi R = 6371. @@ -400,18 +410,20 @@ class Survey(object): count = 0 fmtomo_factor = 1000 # transforming [m/s] -> [km/s] - LatAll = []; - LonAll = []; + LatAll = [] + LonAll = [] DepthAll = [] srcfile = open(directory + '/' + sourcefile, 'w') srcfile.writelines('%10s\n' % len(self.data)) # number of sources for shotnumber in self.getShotlist(): shot = self.getShotForShotnumber(shotnumber) - ttfilename = str(shotnumber) + ttFileExtension # filename of travel time file for this shot + ttfilename = str( + shotnumber) + ttFileExtension # filename of travel time file for this shot (x, y, z) = shot.getSrcLoc() # getSrcLoc returns (x, y, z) - srcfile.writelines('%10s %10s %10s\n' % (getAngle(y), getAngle(x), (-1) * z)) # transform to lat, lon, depth - LatAll.append(getAngle(y)); - LonAll.append(getAngle(x)); + srcfile.writelines('%10s %10s %10s\n' % ( + getAngle(y), getAngle(x), (-1) * z)) # transform to lat, lon, depth + LatAll.append(getAngle(y)) + LonAll.append(getAngle(x)) DepthAll.append((-1) * z) srcfile.writelines('%10s\n' % 1) srcfile.writelines('%10s %10s %10s\n' % (1, 1, ttfilename)) @@ -424,9 +436,10 @@ class Survey(object): pick = shot.getPick(traceID) * fmtomo_factor delta = shot.getSymmetricPickError(traceID) * fmtomo_factor (x, y, z) = shot.getRecLoc(traceID) - ttfile.writelines('%20s %20s %20s %10s %10s\n' % (getAngle(y), getAngle(x), (-1) * z, pick, delta)) - LatAll.append(getAngle(y)); - LonAll.append(getAngle(x)); + ttfile.writelines('%20s %20s %20s %10s %10s\n' % ( + getAngle(y), getAngle(x), (-1) * z, pick, delta)) + LatAll.append(getAngle(y)) + LonAll.append(getAngle(x)) DepthAll.append((-1) * z) count += 1 ttfile.close() @@ -486,19 +499,24 @@ class Survey(object): if index <= figPerSubplot: ax = fig.add_subplot(rows, columns, index) if mode == '3d': - self.getShot(shotnumber).matshow(ax=ax, colorbar=False, annotations=True, legend=False) + self.getShot(shotnumber).matshow(ax=ax, colorbar=False, + annotations=True, + legend=False) elif mode == '2d': self.getShot(shotnumber).plot2dttc(ax) self.getShot(shotnumber).plotmanual2dttc(ax) index += 1 if index > figPerSubplot: - fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) + fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, + hspace=0) fig = plt.figure() index = 1 - fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) + fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, + hspace=0) - def plotAllPicks(self, plotRemoved=False, colorByVal='log10SNR', ax=None, cbar=None, refreshPlot=False): + def plotAllPicks(self, plotRemoved=False, colorByVal='log10SNR', ax=None, + cbar=None, refreshPlot=False): ''' Plots all picks over the distance between source and receiver. Returns (ax, region). Picks can be checked and removed by using region class (pylot.core.active.surveyPlotTools.regions) @@ -542,7 +560,8 @@ class Survey(object): for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if plotRemoved == False: - if shot.getPickFlag(traceID) is not 0 or plotRemoved == True: + if shot.getPickFlag( + traceID) is not 0 or plotRemoved == True: dist.append(shot.getDistance(traceID)) pick.append(shot.getPick(traceID)) snrlog.append(math.log10(shot.getSNR(traceID)[0])) @@ -554,12 +573,15 @@ class Survey(object): 'spe': spe} self.color = color if refreshPlot is False: - ax, cbar = self.createPlot(dist, pick, color[colorByVal], label='%s' % colorByVal) + ax, cbar = self.createPlot(dist, pick, color[colorByVal], + label='%s' % colorByVal) region = regions(ax, cbar, self) ax.legend() return (ax, region) if refreshPlot is True: - ax, cbar = self.createPlot(dist, pick, color[colorByVal], label='%s' % colorByVal, ax=ax, cbar=cbar) + ax, cbar = self.createPlot(dist, pick, color[colorByVal], + label='%s' % colorByVal, ax=ax, + cbar=cbar) ax.legend() return ax @@ -574,27 +596,33 @@ class Survey(object): print('Generating new plot...') fig = plt.figure() ax = fig.add_subplot(111) - sc = ax.scatter(dist, pick, cmap=cm, c=inkByVal, s=5, edgecolors='none', label=label) + sc = ax.scatter(dist, pick, cmap=cm, c=inkByVal, s=5, + edgecolors='none', label=label) cbar = plt.colorbar(sc, fraction=0.05) cbar.set_label(label) ax.set_xlabel('Distance [m]') ax.set_ylabel('Time [s]') - ax.text(0.5, 0.95, 'Plot of all picks', transform=ax.transAxes, horizontalalignment='center') + ax.text(0.5, 0.95, 'Plot of all picks', transform=ax.transAxes, + horizontalalignment='center') else: - sc = ax.scatter(dist, pick, cmap=cm, c=inkByVal, s=5, edgecolors='none', label=label) + sc = ax.scatter(dist, pick, cmap=cm, c=inkByVal, s=5, + edgecolors='none', label=label) cbar = plt.colorbar(sc, cax=cbar.ax) cbar.set_label(label) ax.set_xlabel('Distance [m]') ax.set_ylabel('Time [s]') - ax.text(0.5, 0.95, 'Plot of all picks', transform=ax.transAxes, horizontalalignment='center') + ax.text(0.5, 0.95, 'Plot of all picks', transform=ax.transAxes, + horizontalalignment='center') return (ax, cbar) def _update_progress(self, shotname, tend, progress): - sys.stdout.write('Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r' % (shotname, - tend.hour, - tend.minute, - tend.second, - progress)) + sys.stdout.write( + 'Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r' % ( + shotname, + tend.hour, + tend.minute, + tend.second, + progress)) sys.stdout.flush() def saveSurvey(self, filename='survey.pickle'): diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 827b12ac..3195af4a 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -11,6 +11,7 @@ from pylot.core.pick.charfuns import AICcf from pylot.core.pick.utils import getSNR from pylot.core.pick.utils import earllatepicker import matplotlib.pyplot as plt +import warnings plt.interactive('True') @@ -322,7 +323,7 @@ class SeismicShot(object): if len(traces) == 1: return Stream(traces) self.setPick(traceID, None) - print 'Warning: ambigious or empty traceID: %s' % traceID + warnings.warn('ambigious or empty traceID: %s' % traceID) def pickParallel(self, folm, method = 'hos', aicwindow = (10, 0)): import multiprocessing @@ -336,7 +337,11 @@ class SeismicShot(object): traceIDs = self.getTraceIDlist() - pool.map(self.pickTrace, traceIDs) + picks = pool.map(self.pickTrace, traceIDs) + + for traceID, pick in picks: + self.setPick(traceID, pick) + def pickTrace(self, traceID): ''' @@ -373,7 +378,7 @@ class SeismicShot(object): setHosAic = {'hos': hoscftime, 'aic': aiccftime} - self.setPick(traceID, setHosAic[self.getMethod()]) + return traceID, setHosAic[self.getMethod()] def setEarllatepick(self, traceID, nfac=1.5): tgap = self.getTgap() From 093f750aa1ea5234dbe4f85207740a841b6a8770 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 12:06:55 +0200 Subject: [PATCH 0919/1144] tried worker function --- pylot/core/active/seismicshot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 3195af4a..63f1911c 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -327,6 +327,7 @@ class SeismicShot(object): def pickParallel(self, folm, method = 'hos', aicwindow = (10, 0)): import multiprocessing + from pylot.core.util.utils import worker self.setFolm(folm) self.setMethod(method) @@ -337,11 +338,10 @@ class SeismicShot(object): traceIDs = self.getTraceIDlist() - picks = pool.map(self.pickTrace, traceIDs) + picks = worker(self.pickTrace, traceIDs, maxthreads) for traceID, pick in picks: self.setPick(traceID, pick) - def pickTrace(self, traceID): ''' From 25ca11f5724e8fdacc4bae98e52f17f3d36dc640 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 14:22:52 +0200 Subject: [PATCH 0920/1144] minor tweaks --- pylot/core/active/fmtomoUtils.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 6db9705d..eac6e8c4 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -6,15 +6,19 @@ import datetime import numpy as np class Tomo3d(object): - def __init__(self, citer = 0, overwrite = False): + def __init__(self, fmtomodir, simuldir = 'fmtomo_simulation', citer = 0, overwrite = False): ''' Class build from FMTOMO script tomo3d. Can be used to run several instances of FMM code in parallel. :param: citer, current iteration (default = 0: start new model) :type: integer ''' + self.simuldir = simuldir self.setCWD() + self.buildFmtomodir(fmtomodir) + self.buildObsdata() self.defParas() + self.copyRef() self.citer = citer # current iteration self.sources = self.readSrcFile() self.traces = self.readTraces() @@ -25,6 +29,28 @@ class Tomo3d(object): self.defFMMParas() self.defInvParas() + def buildFmtomodir(self, directory): + tomo_files = ['fm3d', + 'frechgen', + 'frechgen.in', + 'invert3d', + 'invert3d.in', + 'mode_set.in', + 'obsdata', + 'obsdata.in', + 'residuals', + 'residuals.in', + 'tomo3d', + 'tomo3d.in'] + + for name in tomo_files: + filename = os.path.join(directory, name) + os.system('cp %s %s'%(filename, self.cwd)) + + def buildObsdata(self): + os.system('obsdata') + os.system('mv sources.in sourcesref.in') + def defFMMParas(self): ''' Initiates parameters for the forward calculation. @@ -89,8 +115,9 @@ class Tomo3d(object): Default: pwd ''' if directory == None: - directory = subprocess.check_output(['pwd'])[0:-1] + directory = os.path.join(os.getcwd(), self.simuldir) + os.chdir(directory) self.cwd = directory print('Working directory is: %s'%self.cwd) From 42cbfeb7872160b2467566da869579fabd632c75 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 14:24:02 +0200 Subject: [PATCH 0921/1144] temporary retaining of parallel tests --- pylot/core/active/seismicshot.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 63f1911c..3d2e7bc6 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -338,11 +338,15 @@ class SeismicShot(object): traceIDs = self.getTraceIDlist() - picks = worker(self.pickTrace, traceIDs, maxthreads) + # picks = worker(self.pickTrace, traceIDs, maxthreads) - for traceID, pick in picks: + # for traceID, pick in picks: + # self.setPick(traceID, pick) + + for traceID in traceIDs: + trID, pick = self.pickTrace(traceID) self.setPick(traceID, pick) - + def pickTrace(self, traceID): ''' Intitiate picking for a trace. From d9844fff1752986ec7806f4f688f84f3391f063d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 14:25:06 +0200 Subject: [PATCH 0922/1144] added worker --- pylot/core/util/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index c0eb0185..24282319 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -450,6 +450,12 @@ def runProgram(cmd, parameter=None): output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), shell=True) +def worker(func, input, cores): + from multiprocessing import Pool + pool = Pool(cores) + result = pool.map(func, input) + pool.close() + return result if __name__ == "__main__": import doctest From 46ccd44e16c61e82970729270c522c5c9972550d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 23 May 2016 14:25:24 +0200 Subject: [PATCH 0923/1144] changed to generation of reference grids --- pylot/core/active/seismicArrayPreparation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 3fc10b9b..2e836f7d 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -401,7 +401,7 @@ class SeisArray(object): customgrid='mygrid.in', writeVTK=True): ''' Generate FMTOMO input files from the SeisArray dimensions. - Generates: vgrids.in, interfaces.in, propgrid.in + Generates: vgridsref.in, interfacesref.in, propgrid.in :param: nPointsPropgrid, number of points in each direction of the propagation grid (z, y, x) :type: tuple @@ -473,10 +473,10 @@ class SeisArray(object): outfile.close() def generateInterfaces(self, nTheta, nPhi, depthmax, cushionfactor=0.1, - outfilename='interfaces.in', method='linear', + outfilename='interfacesref.in', method='linear', returnInterfaces=False): ''' - Create an interfaces.in file for FMTOMO from the SeisArray boundaries. + Create an interfacesref.in file for FMTOMO from the SeisArray boundaries. :param: nTheta, number of points in Theta type: int @@ -610,7 +610,7 @@ class SeisArray(object): def generateVgrid(self, nTheta, nPhi, nR, Rbt, thetaSN=None, phiWE=None, cushionfactor=0.1, - outfilename='vgrids.in', method='linear', + outfilename='vgridsref.in', method='linear', infilename='mygrid.in', returnTopo=False): ''' Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. From 4962561d6ca63025bac6dc1b4062687d5fed19a4 Mon Sep 17 00:00:00 2001 From: sebastianp Date: Tue, 24 May 2016 13:21:38 +0200 Subject: [PATCH 0924/1144] changes made to have better control of the output directory --- pylot/core/io/phases.py | 20 +++++++++++++------- scripts/pylot-reasses-pilot-db.py | 8 +++++--- scripts/pylot-reasses-pilot-event.py | 8 +++++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 604ea940..0893222f 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -270,19 +270,20 @@ def picks_from_picksdict(picks): return picks_list -def reassess_pilot_db(root_dir, out_dir=None, fn_param=None, verbosity=0): +def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0): import glob - evt_list = glob.glob1(root_dir,'e????.???.??') + db_root = os.path.join(root_dir, db_dir) + evt_list = glob.glob1(db_root,'e????.???.??') for evt in evt_list: if verbosity > 0: print('Reassessing event {0}'.format(evt)) - reassess_pilot_event(root_dir, evt, out_dir, fn_param, verbosity) + reassess_pilot_event(root_dir, db_dir, evt, out_dir, fn_param, verbosity) -def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbosity=0): +def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None, verbosity=0): from obspy import read from pylot.core.io.inputs import AutoPickParameter @@ -294,7 +295,7 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos default = AutoPickParameter(fn_param, verbosity) - search_base = os.path.join(root_dir, event_id) + search_base = os.path.join(root_dir, db_dir, event_id) phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat')) if not phases_file: return @@ -335,7 +336,9 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos continue sel_st = select_for_phase(st, phase) if not sel_st: - warnings.warn('no waveform data found for station {station}'.format(station=station), RuntimeWarning) + msg = 'no waveform data found for station {station}'.format(station=station) + warnings.warn(msg, RuntimeWarning) + continue stime, etime = getGlobalTimes(sel_st) rel_pick = mpp - stime epp, lpp, spe = earllatepicker(sel_st, @@ -367,8 +370,11 @@ def reassess_pilot_event(root_dir, event_id, out_dir=None, fn_param=None, verbos evt.picks = picks_from_picksdict(picks_dict) # write phase information to file if not out_dir: - fnout_prefix = os.path.join(root_dir, event_id, '{0}.'.format(event_id)) + fnout_prefix = os.path.join(root_dir, db_dir, event_id, '{0}.'.format(event_id)) else: + out_dir = os.path.join(out_dir, db_dir) + if not os.path.isdir(out_dir): + os.makedirs(out_dir) fnout_prefix = os.path.join(out_dir, '{0}.'.format(event_id)) evt.write(fnout_prefix + 'xml', format='QUAKEML') #evt.write(fnout_prefix + 'cnv', format='VELEST') diff --git a/scripts/pylot-reasses-pilot-db.py b/scripts/pylot-reasses-pilot-db.py index 83cdf254..7b55ce6a 100755 --- a/scripts/pylot-reasses-pilot-db.py +++ b/scripts/pylot-reasses-pilot-db.py @@ -19,8 +19,10 @@ if __name__ == '__main__': ) parser.add_argument( - 'dbroot', type=str, help='specifies the root directory (in ' - 'most cases PILOT database folder)' + 'root', type=str, help='specifies the root directory' + ) + parser.add_argument( + 'db', type=str, help='specifies the database name' ) parser.add_argument( '--output', '-o', type=str, help='path to the output directory', @@ -36,4 +38,4 @@ if __name__ == '__main__': ) args = parser.parse_args() - reassess_pilot_db(args.dbroot, args.output, args.parfile, args.verbosity) + reassess_pilot_db(args.root, args.db, args.output, args.parfile, args.verbosity) diff --git a/scripts/pylot-reasses-pilot-event.py b/scripts/pylot-reasses-pilot-event.py index 1852e680..b9fd3a39 100755 --- a/scripts/pylot-reasses-pilot-event.py +++ b/scripts/pylot-reasses-pilot-event.py @@ -19,8 +19,10 @@ if __name__ == '__main__': ) parser.add_argument( - 'dbroot', type=str, help='specifies the root directory (in ' - 'most cases PILOT database folder)' + 'root', type=str, help='specifies the root directory' + ) + parser.add_argument( + 'db', type=str, help='specifies the database name' ) parser.add_argument( 'id', type=str, help='PILOT event identifier' @@ -33,4 +35,4 @@ if __name__ == '__main__': ) args = parser.parse_args() - reassess_pilot_event(args.dbroot, args.id, args.output, args.parfile) + reassess_pilot_event(args.root, args.db, args.id, args.output, args.parfile) From a6eaac6c339b67ed3b2494438b8eb97743f11f94 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 24 May 2016 14:19:37 +0200 Subject: [PATCH 0925/1144] Changes during parallelization tests of autopicker --- pylot/core/active/fmtomoUtils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index eac6e8c4..d1c065bf 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -45,7 +45,8 @@ class Tomo3d(object): for name in tomo_files: filename = os.path.join(directory, name) - os.system('cp %s %s'%(filename, self.cwd)) + linkname = os.path.join(self.cwd, name) + os.system('ln -s %s %s'%(filename, linkname)) def buildObsdata(self): os.system('obsdata') From 8ca87bc7772625196c15db17c398f0ff926c41d3 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 24 May 2016 14:20:59 +0200 Subject: [PATCH 0926/1144] changes while testing parallelization of autopicker --- pylot/core/active/activeSeismoPick.py | 24 +++++++++++++++++-- pylot/core/active/fmtomoUtils.py | 2 +- pylot/core/active/seismicshot.py | 33 ++++++++++++++------------- pylot/core/util/utils.py | 26 +++++++++++++++------ 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index f2c18d83..3cd098b5 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -3,7 +3,11 @@ import sys import numpy as np from pylot.core.active import seismicshot from pylot.core.active.surveyUtils import cleanUp +import copy_reg +import types +from pylot.core.util.utils import worker, _pickle_method +copy_reg.pickle(types.MethodType, _pickle_method) class Survey(object): def __init__(self, path, sourcefile, receiverfile, useDefaultParas=False): @@ -196,6 +200,12 @@ class Survey(object): plt.xlabel('Difference in time (auto - manual) [s]') return diffs + def pickShot(self, shTr): + shotnumber, traceID = shTr + shot = self.getShotForShotnumber(shotnumber) + traceID, pick = shot.pickTrace(traceID) + return shotnumber, traceID, pick + def pickAllShots(self, vmin=333, vmax=5500, folm=0.6, HosAic='hos', aicwindow=(10, 0)): ''' @@ -218,20 +228,30 @@ class Survey(object): count = 0 tpicksum = starttime - starttime + shTr = [] + for shot in self.data.values(): tstartpick = datetime.now() shot.setVmin(vmin) shot.setVmax(vmax) count += 1 - shot.pickParallel(folm) + #shot.pickParallel(folm) + shot.setPickParameters(folm = folm, method = HosAic, aicwindow = aicwindow) + for traceID in shot.getTraceIDlist(): + shTr.append((shot.getShotnumber(), traceID)) + picks = worker(self.pickShot, shTr, async = True) + + for shotnumber, traceID, pick in picks.get(): + self.getShotForShotnumber(shotnumber).setPick(traceID, pick) + # tpicksum += (datetime.now() - tstartpick); # tpick = tpicksum / count # tremain = (tpick * (len(self.getShotDict()) - count)) # tend = datetime.now() + tremain # progress = float(count) / float(len(self.getShotDict())) * 100 # self._update_progress(shot.getShotname(), tend, progress) - + self.setSNR() self.setEarllate() diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index d1c065bf..94465a34 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -280,7 +280,7 @@ class Tomo3d(object): ''' Wipes a certain directory. ''' - print('Wiping directory %s...'%directory) + #print('Wiping directory %s...'%directory) for filename in os.listdir(directory): filenp = os.path.join(directory, filename) os.remove(filenp) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 3d2e7bc6..cbf1deb8 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -12,10 +12,14 @@ from pylot.core.pick.utils import getSNR from pylot.core.pick.utils import earllatepicker import matplotlib.pyplot as plt import warnings +import copy_reg +import types +from pylot.core.util.utils import worker, _pickle_method + +copy_reg.pickle(types.MethodType, _pickle_method) plt.interactive('True') - class SeismicShot(object): ''' SuperClass for a seismic shot object. @@ -325,27 +329,24 @@ class SeismicShot(object): self.setPick(traceID, None) warnings.warn('ambigious or empty traceID: %s' % traceID) - def pickParallel(self, folm, method = 'hos', aicwindow = (10, 0)): - import multiprocessing - from pylot.core.util.utils import worker - + def setPickParameters(self, folm, method = 'hos', aicwindow = (10, 0)): self.setFolm(folm) self.setMethod(method) self.setAicwindow(aicwindow) - maxthreads = multiprocessing.cpu_count() - pool = multiprocessing.Pool(maxthreads) - - traceIDs = self.getTraceIDlist() + # def pickParallel(self): + # traceIDs = self.getTraceIDlist() + # picks = [] + # #picks = worker(self.pickTrace, traceIDs) - # picks = worker(self.pickTrace, traceIDs, maxthreads) + # # for traceID, pick in picks: + # # self.setPick(traceID, pick) - # for traceID, pick in picks: - # self.setPick(traceID, pick) - - for traceID in traceIDs: - trID, pick = self.pickTrace(traceID) - self.setPick(traceID, pick) + # for traceID in traceIDs: + # trID, pick = self.pickTrace(traceID) + # picks.append([trID, pick]) + # #self.setPick(traceID, pick) + # return picks def pickTrace(self, traceID): ''' diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 24282319..f2b5aae5 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,6 +10,25 @@ import numpy as np from obspy.core import UTCDateTime import obspy.core.event as ope +def _pickle_method(m): + if m.im_self is None: + return getattr, (m.im_class, m.im_func.func_name) + else: + return getattr, (m.im_self, m.im_func.func_name) + +def worker(func, input, cores = 'max', async = False): + import multiprocessing + + if cores == 'max': + cores = multiprocessing.cpu_count() + + pool = multiprocessing.Pool(cores) + if async == True: + result = pool.map_async(func, input) + else: + result = pool.map(func, input) + pool.close() + return result def createAmplitude(pickID, amp, unit, category, cinfo): ''' @@ -450,13 +469,6 @@ def runProgram(cmd, parameter=None): output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), shell=True) -def worker(func, input, cores): - from multiprocessing import Pool - pool = Pool(cores) - result = pool.map(func, input) - pool.close() - return result - if __name__ == "__main__": import doctest From e46e8cb71b18bd4a58c49ee5516adc4c7bd9eb4b Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 25 May 2016 14:22:08 +0200 Subject: [PATCH 0927/1144] [reformat] corrected formatting after merge --- pylot/core/util/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index c4b98b3a..a7167c43 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -9,13 +9,15 @@ import re import subprocess from obspy.core import UTCDateTime + def _pickle_method(m): if m.im_self is None: return getattr, (m.im_class, m.im_func.func_name) else: return getattr, (m.im_self, m.im_func.func_name) -def worker(func, input, cores = 'max', async = False): + +def worker(func, input, cores='max', async=False): return result import multiprocessing @@ -28,6 +30,8 @@ def worker(func, input, cores = 'max', async = False): else: result = pool.map(func, input) pool.close() + + def demeanTrace(trace, window): """ returns the DATA where each trace is demean by the average value within @@ -249,6 +253,7 @@ def runProgram(cmd, parameter=None): output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), shell=True) + if __name__ == "__main__": import doctest From 5057664b1dae8452cbcd118e9cfcaafc00d1bf1e Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 25 May 2016 14:24:19 +0200 Subject: [PATCH 0928/1144] code changes for parallelization --- pylot/core/active/activeSeismoPick.py | 29 +++++++++++++++++---------- pylot/core/active/seismicshot.py | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 3cd098b5..aec7e19f 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -9,6 +9,12 @@ from pylot.core.util.utils import worker, _pickle_method copy_reg.pickle(types.MethodType, _pickle_method) +def ppick(shot): + picks = [] + for traceID in shot.getTraceIDlist(): + picks.append((shot.getShotnumber(), traceID, shot.pickTrace(traceID))) + return picks + class Survey(object): def __init__(self, path, sourcefile, receiverfile, useDefaultParas=False): ''' @@ -200,11 +206,11 @@ class Survey(object): plt.xlabel('Difference in time (auto - manual) [s]') return diffs - def pickShot(self, shTr): - shotnumber, traceID = shTr - shot = self.getShotForShotnumber(shotnumber) - traceID, pick = shot.pickTrace(traceID) - return shotnumber, traceID, pick + # def pickShot(self, shTr): + # shotnumber, traceID = shTr + # shot = self.getShotForShotnumber(shotnumber) + # traceID, pick = shot.pickTrace(traceID) + # return shotnumber, traceID, pick def pickAllShots(self, vmin=333, vmax=5500, folm=0.6, HosAic='hos', aicwindow=(10, 0)): @@ -228,7 +234,7 @@ class Survey(object): count = 0 tpicksum = starttime - starttime - shTr = [] + shotlist = [] for shot in self.data.values(): tstartpick = datetime.now() @@ -237,13 +243,14 @@ class Survey(object): count += 1 #shot.pickParallel(folm) shot.setPickParameters(folm = folm, method = HosAic, aicwindow = aicwindow) - for traceID in shot.getTraceIDlist(): - shTr.append((shot.getShotnumber(), traceID)) + shotlist.append(shot) - picks = worker(self.pickShot, shTr, async = True) + picks = worker(ppick, shotlist, cores = 16) - for shotnumber, traceID, pick in picks.get(): - self.getShotForShotnumber(shotnumber).setPick(traceID, pick) + for item in picks: + for it in item: + shotnumber, traceID, pick = it + self.getShotForShotnumber(shotnumber).setPick(traceID, pick) # tpicksum += (datetime.now() - tstartpick); # tpick = tpicksum / count diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index cbf1deb8..27470fe0 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -383,7 +383,7 @@ class SeismicShot(object): setHosAic = {'hos': hoscftime, 'aic': aiccftime} - return traceID, setHosAic[self.getMethod()] + return setHosAic[self.getMethod()] def setEarllatepick(self, traceID, nfac=1.5): tgap = self.getTgap() From 0dd69a0e1976e815e97a26495a99054e243fa575 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 25 May 2016 14:28:25 +0200 Subject: [PATCH 0929/1144] added default value for cores --- pylot/core/active/activeSeismoPick.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index aec7e19f..5ef93ed0 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -213,7 +213,7 @@ class Survey(object): # return shotnumber, traceID, pick def pickAllShots(self, vmin=333, vmax=5500, folm=0.6, HosAic='hos', - aicwindow=(10, 0)): + aicwindow=(10, 0), cores = 1): ''' Automatically pick all traces of all shots of the survey. @@ -245,7 +245,7 @@ class Survey(object): shot.setPickParameters(folm = folm, method = HosAic, aicwindow = aicwindow) shotlist.append(shot) - picks = worker(ppick, shotlist, cores = 16) + picks = worker(ppick, shotlist, cores) for item in picks: for it in item: From 53614bb5b95af27bbcb037dde07c879906cfeea5 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 27 May 2016 06:49:20 +0200 Subject: [PATCH 0930/1144] [fix] fixing a bug introduced accidently by merge --- pylot/core/util/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index a7167c43..5f3e2131 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -18,19 +18,19 @@ def _pickle_method(m): def worker(func, input, cores='max', async=False): - return result import multiprocessing if cores == 'max': cores = multiprocessing.cpu_count() - pool = multiprocessing.Pool(cores) + pool = multiprocessing.Pool(cores) if async == True: result = pool.map_async(func, input) else: result = pool.map(func, input) pool.close() - + return result + def demeanTrace(trace, window): """ From 163a501ae91ba6713ab041eebb7d0ed388ff71e3 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 27 May 2016 07:43:54 +0200 Subject: [PATCH 0931/1144] introducing automatic data into QtPyLoT --- QtPyLoT.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6deb6f16..2cb2f961 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -488,7 +488,9 @@ class MainWindow(QMainWindow): def setComponent(self, component): self.dispComponent = component - def getData(self): + def getData(self, type='manual'): + if type == 'auto': + return self.autodata return self.data def getPicks(self, type='manual'): @@ -736,7 +738,7 @@ class MainWindow(QMainWindow): return rval def updatePicks(self, type='manual'): - picks = picksdict_from_picks(evt=self.getData().getEvtData()) + picks = picksdict_from_picks(evt=self.getData(type).getEvtData()) if type == 'manual': self.picks.update(picks) elif type == 'auto': From 618dd10c23055e2d8b13ce19483da8e4f81f7b92 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 27 May 2016 11:25:47 +0200 Subject: [PATCH 0932/1144] [task] further steps to implement pick comparison from QtPyLoT --- QtPyLoT.py | 176 ++++++++++++++++++++++++++------------- pylot/core/io/data.py | 47 +++++++---- pylot/core/io/phases.py | 15 ++-- pylot/core/util/utils.py | 8 ++ 4 files changed, 167 insertions(+), 79 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 2cb2f961..2d19d38d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -23,11 +23,10 @@ https://www.iconfinder.com/iconsets/flavour (http://www.gnu.org/copyleft/lesser.html) """ +import matplotlib import os import sys -import matplotlib - matplotlib.use('Qt4Agg') matplotlib.rcParams['backend.qt4'] = 'PySide' @@ -45,12 +44,12 @@ from pylot.core.io.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent from pylot.core.io.phases import picksdict_from_picks from pylot.core.loc.nll import locate as locateNll -from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP,\ +from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP, \ AUTOMATIC_DEFAULTS from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError from pylot.core.util.connection import checkurl -from pylot.core.util.utils import fnConstructor, createEvent, getLogin, \ +from pylot.core.util.utils import fnConstructor, getLogin, \ getGlobalTimes from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ @@ -81,8 +80,8 @@ class MainWindow(QMainWindow): "Enter authority name (e.g. BUG):", "Authority") settings.setValue("agency_id", agency) - self.recentEvents = settings.value("data/recentEvents", []) - self.fname = None + self.recentfiles = settings.value("data/recentEvents", []) + self.fname = dict(manual=None, auto=None, loc=None) self.fnames = None structure_setting = settings.value("data/Structure", "PILOT") self.dataStructure = DATASTRUCTURE[structure_setting]() @@ -104,7 +103,7 @@ class MainWindow(QMainWindow): self.setupUi() # initialize event data - if self.recentEvents: + if self.recentfiles: lastEvent = self.getLastEvent() self.data = Data(self, lastEvent) else: @@ -112,7 +111,7 @@ class MainWindow(QMainWindow): # load and display waveform data self.dirty = False - self.loadData() + self.load_data() if self.loadWaveformData(): self.updateFilterOptions() else: @@ -149,7 +148,10 @@ class MainWindow(QMainWindow): lambda event: self.tutorUser()) _layout.addWidget(self.DataPlot) - openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon) + manupicksicon = self.style().standardIcon(QStyle.SP_DialogYesButton) + autopicksicon = self.style().standardIcon(QStyle.SP_DialogNoButton) + locactionicon = self.style().standardIcon(QStyle.SP_DirOpenIcon) + loadpiloticon = self.style().standardIcon(QStyle.SP_ComputerIcon) quitIcon = self.style().standardIcon(QStyle.SP_MediaStop) saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) @@ -179,18 +181,40 @@ class MainWindow(QMainWindow): self.createNewEvent, QKeySequence.New, newIcon, "Create a new event.") - openEventAction = self.createAction(self, "&Open event ...", - self.loadData, QKeySequence.Open, - openIcon, "Open an event.") - openEventAction.setData(None) + openmanualpicksaction = self.createAction(self, "Load &picks ...", + self.load_data, + QKeySequence.Open, + manupicksicon, + "Load pick data for " + "the actual event.") + openmanualpicksaction.setData(None) + openautopicksaction = self.createAction(self, "Load &automatic picks " + "...", + self.load_autopicks, + "Ctrl+A", + autopicksicon, + "Load automatic pick data " + "for the actual event.") + openautopicksaction.setData(None) + + loadlocationaction = self.createAction(self, "Load &location ...", + self.load_loc, "Ctrl+L", + locactionicon, + "Load location information on " + "the actual event.") + loadpilotevent = self.createAction(self, "Load PILOT &event ...", + self.load_pilot, "Ctrl+E", + loadpiloticon, + "Load PILOT event from information " + "Matlab binary collections.") saveEventAction = self.createAction(self, "&Save event ...", self.saveData, QKeySequence.Save, saveIcon, "Save actual event data.") openWFDataAction = self.createAction(self, "Open &waveforms ...", self.loadWaveformData, "Ctrl+W", QIcon(":/wfIcon.png"), - """Open waveform data (event will - be closed).""") + "Open waveform data (event will " + "be closed)") prefsEventAction = self.createAction(self, "Preferences", self.PyLoTprefs, QKeySequence.Preferences, @@ -228,7 +252,7 @@ class MainWindow(QMainWindow): homepage (internet connection available), or shipped documentation files.""") self.fileMenu = self.menuBar().addMenu('&File') - self.fileMenuActions = (newEventAction, openEventAction, + self.fileMenuActions = (newEventAction, openmanualpicksaction, saveEventAction, openWFDataAction, None, prefsEventAction, quitAction) self.fileMenu.aboutToShow.connect(self.updateFileMenu) @@ -245,7 +269,9 @@ class MainWindow(QMainWindow): self.addActions(self.helpMenu, helpActions) fileToolBar = self.addToolBar("FileTools") - fileToolActions = (newEventAction, openEventAction, saveEventAction) + fileToolActions = (newEventAction, openmanualpicksaction, + openautopicksaction, loadlocationaction, + loadpilotevent, saveEventAction) fileToolBar.setObjectName("FileTools") self.addActions(fileToolBar, fileToolActions) @@ -299,9 +325,11 @@ class MainWindow(QMainWindow): # self.addActions(pickToolBar, pickToolActions) locateEvent = self.createAction(parent=self, text='locateEvent', - slot=self.locateEvent, shortcut='Alt+Ctrl+L', - icon=locate_icon, tip='Locate the event using ' - 'the picked arrivals.') + slot=self.locateEvent, + shortcut='Alt+Ctrl+L', + icon=locate_icon, + tip='Locate the event using ' + 'the picked arrivals.') locationToolBar = self.addToolBar("LocationTools") locationToolActions = (locateEvent,) @@ -333,12 +361,12 @@ class MainWindow(QMainWindow): except AttributeError: current = None recentEvents = [] - for eventID in self.recentEvents: + for eventID in self.recentfiles: fname = fnConstructor(eventID) if eventID != current and QFile.exists(fname): recentEvents.append(eventID) recentEvents.reverse() - self.recentEvents = recentEvents[0:5] + self.recentfiles = recentEvents[0:5] settings = QSettings() settings.setValue() if recentEvents: @@ -350,7 +378,7 @@ class MainWindow(QMainWindow): self) action.setData(fname) self.connect(action, Signal("triggered()"), - self.loadData) + self.load_data) self.fileMenu.addAction(action) self.fileMenu.addSeparator() self.fileMenu.addAction(self.fileMenuActions[-1]) @@ -359,34 +387,49 @@ class MainWindow(QMainWindow): settings = QSettings() return settings.value("data/dataRoot") - def loadAutoPicks(self): - self.loadData(type='auto') + def load_autopicks(self, fname=None): + self.load_data(fname, type='auto') - def loadData(self, fname=None, type='manual'): + def load_loc(self, fname=None): + self.load_data(fname, type='loc') + + def load_pilotevent(self): + filt = "PILOT location files (*.mat)" + caption = "Select PILOT loaction file" + fn_loc = QFileDialog().getOpenFileName(self, caption=caption, + filter=filt) + filt = "PILOT phases files (*.mat)" + caption = "Select PILOT phases file" + fn_phases = QFileDialog().getOpenFileName(self, caption=caption, + filter=filt) + type = QInputDialog().getItem(self, self.tr("Select phases type"), + self.tr("Type:"), [self.tr("manual"), + self.tr("automatic")]) + + if type.startswith('auto'): + type = 'auto' + + fname_dict = dict(phasfn=fn_phases, locfn=fn_loc) + self.load_data(fname_dict, type=type) + + def load_data(self, fname=None, type='manual'): if not self.okToContinue(): return if fname is None: action = self.sender() if isinstance(action, QAction): - if action.data() is None: - filt = "Supported event formats (*.mat *.qml *.xml *.kor *.evt)" - caption = "Open an event file" - fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) - fname = fname[0] - else: - fname = str(action.data().toString()) - self.setFileName(fname) - self.data += Data(self, evtdata=self.getFileName()) - self.updatePicks(type=type) + fname = self.filename_from_action(action) + self.set_fname(fname, type) + self.data += Data(self, evtdata=fname) + if 'loc' not in type: + self.updatePicks(type=type) self.drawPicks() def getLastEvent(self): - return self.recentEvents[0] + return self.recentfiles[0] - def addRecentEvent(self, event): - self.recentEvents.insert(0, event) + def add_recentfile(self, event): + self.recentfiles.insert(0, event) def getWFFnames(self): try: @@ -422,27 +465,44 @@ class MainWindow(QMainWindow): else: return - def getFileName(self): + def filename_from_action(self, action): + if action.data() is None: + filt = "Supported file formats" \ + " (*.mat *.qml *.xml *.kor *.evt)" + caption = "Open an event file" + fname = QFileDialog().getOpenFileName(self, + caption=caption, + filter=filt) + fname = fname[0] + else: + fname = str(action.data().toString()) + return fname + + def get_fnames(self): return self.fname - def setFileName(self, fname): - if self.getFileName() is not None: - self.addRecentEvent(self.getFileName()) - self.fname = fname + def set_fname(self, fname, type): + if self.get_fnames()[type] is not None: + self.add_recentfile(self.get_fnames()) + self.fname[type] = fname def getEventFileName(self): - if self.getFileName() is None: - self.setFileName(self.getData().getEventFileName()) - return self.getFileName() + if self.get_fnames() is None: + self.set_fname(self.getData().getEventFileName()) + return self.get_fnames() def saveData(self): def getSavePath(e): print('warning: {0}'.format(e)) directory = os.path.join(self.getRoot(), self.getEventFileName()) - file_filter = "QuakeML file (*.xml);;VELEST observation file format (*.cnv);;NonLinLoc observation file (*.obs)" - fname, selected_filter = QFileDialog.getSaveFileName(self, 'Save event data ...', - directory, file_filter) + file_filter = "QuakeML file (*.xml);;VELEST observation file " \ + "format (*.cnv);;NonLinLoc observation file (*.obs)" + title = 'Save event data ...' + fname, selected_filter = QFileDialog.getSaveFileName(self, + title, + directory, + file_filter) fbasename, exform = os.path.splitext(fname) @@ -459,7 +519,8 @@ class MainWindow(QMainWindow): except OverwriteError: msgBox = QMessageBox() msgBox.setText("Picks have been modified!") - msgBox.setInformativeText("Do you want to save the changes and overwrite the picks?") + msgBox.setInformativeText( + "Do you want to save the changes and overwrite the picks?") msgBox.setDetailedText(self.getData().getPicksStr()) msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Save) @@ -689,7 +750,8 @@ class MainWindow(QMainWindow): self.setDirty(True) self.logDockWidget = QDockWidget("AutoPickLog", self) self.logDockWidget.setObjectName("LogDockWidget") - self.logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) + self.logDockWidget.setAllowedAreas( + Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.logDockWidget.setWidget(self.listWidget) self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget) self.addListItem('loading default values for local data ...') @@ -754,8 +816,10 @@ class MainWindow(QMainWindow): plotID = self.getStationID(station) ax = self.getPlotWidget().axes ylims = np.array([-.5, +.5]) + plotID - phase_col = {'P': ('c', 'c--', 'b-', 'bv', 'b^'), - 'S': ('m', 'm--', 'r-', 'rv', 'r^')} + phase_col = { + 'P': ('c', 'c--', 'b-', 'bv', 'b^'), + 'S': ('m', 'm--', 'r-', 'rv', 'r^') + } stat_picks = self.getPicks(type=picktype)[station] diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 4f869932..ca75f7e6 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -1,17 +1,17 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import os import glob - -from obspy.io.xseed import Parser -from obspy.core import read, Stream, UTCDateTime +import os from obspy import read_events, read_inventory -from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID +from obspy.core import read, Stream, UTCDateTime +from obspy.core.event import Event +from obspy.io.xseed import Parser -from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict -from pylot.core.util.utils import fnConstructor, getGlobalTimes +from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ + picks_from_pilot from pylot.core.util.errors import FormatError, OverwriteError +from pylot.core.util.utils import fnConstructor, getGlobalTimes class Data(object): @@ -36,17 +36,36 @@ class Data(object): self.wfdata = Stream() self._new = False if isinstance(evtdata, Event): - self.evtdata = evtdata + pass elif isinstance(evtdata, dict): evt = readPILOTEvent(**evtdata) - self.evtdata = evt - elif evtdata: - cat = read_events(evtdata) - self.evtdata = cat[0] + evtdata = evt + elif isinstance(evtdata, str): + try: + cat = read_events(evtdata) + if len(cat) is not 1: + raise ValueError('ambiguous event information for file: ' + '{file}'.format(file=evtdata)) + evtdata = cat[0] + except TypeError as e: + if 'Unknown format for file' in e.message: + if 'PHASES' in evtdata: + picks = picks_from_pilot(evtdata) + evtdata = Event() + evtdata.picks = picks_from_picksdict(picks) + elif 'LOC' in evtdata: + raise NotImplementedError('PILOT location information ' + 'read support not yet ' + 'implemeted.') + else: + raise e + else: + raise e else: # create an empty Event object self.setNew() - self.evtdata = Event() - self.getEvtData().picks = [] + evtdata = Event() + evtdata.picks = [] + self.evtdata = evtdata self.wforiginal = None self.cuttimes = None self.dirty = False diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 38fa1aaa..3f5e80fc 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -11,10 +11,10 @@ from obspy.core import UTCDateTime from pylot.core.io.location import create_arrival, create_event, \ create_magnitude, create_origin, create_pick from pylot.core.pick.utils import select_for_phase -from pylot.core.util.utils import getOwner, getGlobalTimes +from pylot.core.util.utils import getOwner, getGlobalTimes, four_digits -def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): +def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs): """ readPILOTEvent - function @@ -67,18 +67,15 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): minute = int(loc['mm']) second = int(loc['ss']) - if year + 2000 < UTCDateTime.utcnow().year: - year += 2000 - else: - year += 1900 + year = four_digits(year) eventDate = UTCDateTime(year=year, julday=julday, hour=hour, minute=minute, second=second) stations = [stat for stat in phases['stat'][0:-1:3]] - event = create_event(eventDate, loccinfo, etype='earthquake', resID=eventNum, - authority_id=authority_id) + event = create_event(eventDate, loccinfo, etype='earthquake', + resID=eventNum, authority_id=authority_id) lat = float(loc['LAT']) lon = float(loc['LON']) @@ -123,13 +120,13 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id=None, **kwargs): pickID = pick.get('id') arrival = create_arrival(pickID, pickcinfo, phase) origin.arrivals.append(arrival) + event.picks.append(pick) np += 1 magnitude = create_magnitude(origin.get('id'), loccinfo) magnitude.mag = float(loc['Mnet']) magnitude.magnitude_type = 'Ml' - event.picks.append(pick) event.origins.append(origin) event.magnitudes.append(magnitude) return event diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 5f3e2131..5d7d2b2a 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -93,6 +93,14 @@ def fnConstructor(s): return fn +def four_digits(year): + if year + 2000 < UTCDateTime.utcnow().year: + year += 2000 + else: + year += 1900 + return year + + def getGlobalTimes(stream): ''' From 36b0aea86cbcdae545971845f533622a94330eb1 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 27 May 2016 12:53:34 +0200 Subject: [PATCH 0933/1144] [edit] fixing bugs (in progress) --- QtPyLoT.py | 13 ++-- pylot/core/io/location.py | 10 ++- pylot/core/io/phases.py | 130 +++++++++++++++++++------------------- 3 files changed, 78 insertions(+), 75 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 2d19d38d..955a2e68 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -203,7 +203,7 @@ class MainWindow(QMainWindow): "Load location information on " "the actual event.") loadpilotevent = self.createAction(self, "Load PILOT &event ...", - self.load_pilot, "Ctrl+E", + self.load_pilotevent, "Ctrl+E", loadpiloticon, "Load PILOT event from information " "Matlab binary collections.") @@ -397,17 +397,22 @@ class MainWindow(QMainWindow): filt = "PILOT location files (*.mat)" caption = "Select PILOT loaction file" fn_loc = QFileDialog().getOpenFileName(self, caption=caption, - filter=filt) + filter=filt, dir=self.getRoot()) + fn_loc = fn_loc[0] + loc_dir = os.path.split(fn_loc)[0] filt = "PILOT phases files (*.mat)" caption = "Select PILOT phases file" fn_phases = QFileDialog().getOpenFileName(self, caption=caption, - filter=filt) + filter=filt, dir=loc_dir) + fn_phases = fn_phases[0] type = QInputDialog().getItem(self, self.tr("Select phases type"), self.tr("Type:"), [self.tr("manual"), self.tr("automatic")]) - if type.startswith('auto'): + if type[0].startswith('auto'): type = 'auto' + else: + type = type[0] fname_dict = dict(phasfn=fn_phases, locfn=fn_loc) self.load_data(fname_dict, type=type) diff --git a/pylot/core/io/location.py b/pylot/core/io/location.py index 667b7f91..157321a8 100644 --- a/pylot/core/io/location.py +++ b/pylot/core/io/location.py @@ -68,8 +68,8 @@ def create_creation_info(agency_id=None, creation_time=None, author=None): creation_time=creation_time) -def create_event(origintime, cinfo, originloc=None, etype=None, resID=None, - authority_id=None): +def create_event(origintime, cinfo, originloc=None, etype='earthquake', + resID=None, authority_id=None): ''' create_event - funtion to create an ObsPy Event @@ -90,14 +90,12 @@ def create_event(origintime, cinfo, originloc=None, etype=None, resID=None, :type authority_id: str :return: An ObsPy :class: `~obspy.core.event.Event` object ''' - etype = ope.EventType(etype) + if originloc is not None: o = create_origin(origintime, cinfo, originloc[0], originloc[1], originloc[2]) else: o = None - if etype is None: - etype = ope.EventType('earthquake') # defaults to 'earthquake' if not resID: resID = create_resourceID(origintime, etype, authority_id) elif isinstance(resID, str): @@ -216,7 +214,7 @@ def create_resourceID(timetohash, restype, authority_id=None, hrstr=None): if hrstr is None: resID = ope.ResourceIdentifier(restype + '/' + hid[0:6]) else: - resID = ope.ResourceIdentifier(restype + '/' + hrstr + '_' + hid[0:6]) + resID = ope.ResourceIdentifier(restype + '/' + hrstr) if authority_id is not None: resID.convertIDToQuakeMLURI(authority_id=authority_id) return resID \ No newline at end of file diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 3f5e80fc..2fc03378 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -54,86 +54,86 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs): author=locauthor, creation_time=locctime) np = 0 - try: - eventNum = loc['ID'][0] + #try: + eventNum = str(loc['ID'][0]) - # retrieve eventID for the actual database - idsplit = eventNum.split('.') + # retrieve eventID for the actual database + idsplit = eventNum.split('.') - # retrieve date information - julday = int(idsplit[1]) - year = int(idsplit[2]) - hour = int(loc['hh']) - minute = int(loc['mm']) - second = int(loc['ss']) + # retrieve date information + julday = int(idsplit[1]) + year = int(idsplit[2]) + hour = int(loc['hh']) + minute = int(loc['mm']) + second = int(loc['ss']) - year = four_digits(year) + year = four_digits(year) - eventDate = UTCDateTime(year=year, julday=julday, hour=hour, - minute=minute, second=second) + eventDate = UTCDateTime(year=year, julday=julday, hour=hour, + minute=minute, second=second) - stations = [stat for stat in phases['stat'][0:-1:3]] + stations = [stat for stat in phases['stat'][0:-1:3]] - event = create_event(eventDate, loccinfo, etype='earthquake', - resID=eventNum, authority_id=authority_id) + lat = float(loc['LAT']) + lon = float(loc['LON']) + dep = float(loc['DEP']) - lat = float(loc['LAT']) - lon = float(loc['LON']) - dep = float(loc['DEP']) + event = create_event(eventDate, loccinfo, originloc=(lat, lon, dep), + etype='earthquake', resID=eventNum, + authority_id=authority_id) - origin = create_origin(eventDate, loccinfo, lat, lon, dep) - for n, pick in enumerate(phases['Ptime']): - if pick[0] > 0: - kwargs = {'year': int(pick[0]), - 'month': int(pick[1]), - 'day': int(pick[2]), - 'hour': int(pick[3]), - 'minute': int(pick[4]), - 'second': int(str(pick[5]).split('.')[0]), - 'microsecond': int(str(pick[5]).split('.')[1][0:6])} - spick = phases['Stime'][n] - if spick[0] > 0: - skwargs = {'year': int(spick[0]), - 'month': int(spick[1]), - 'day': int(spick[2]), - 'hour': int(spick[3]), - 'minute': int(spick[4]), - 'second': int(str(spick[5]).split('.')[0]), - 'microsecond': int(str(spick[5]).split('.')[1][0:6])} - spicktime = UTCDateTime(**skwargs) - else: - spicktime = None - ppicktime = UTCDateTime(**kwargs) + for n, pick in enumerate(phases['Ptime']): + if pick[0] > 0: + kwargs = {'year': int(pick[0]), + 'month': int(pick[1]), + 'day': int(pick[2]), + 'hour': int(pick[3]), + 'minute': int(pick[4]), + 'second': int(str(pick[5]).split('.')[0]), + 'microsecond': int(str(pick[5]).split('.')[1][0:6])} + spick = phases['Stime'][n] + if spick[0] > 0: + skwargs = {'year': int(spick[0]), + 'month': int(spick[1]), + 'day': int(spick[2]), + 'hour': int(spick[3]), + 'minute': int(spick[4]), + 'second': int(str(spick[5]).split('.')[0]), + 'microsecond': int(str(spick[5]).split('.')[1][0:6])} + spicktime = UTCDateTime(**skwargs) + else: + spicktime = None + ppicktime = UTCDateTime(**kwargs) - for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: - if picktime is not None: - if phase == 'P': - wffn = os.path.join(sdir, '{0}*{1}*'.format( - stations[n].strip(), 'z')) - else: - wffn = os.path.join(sdir, '{0}*{1}*'.format( - stations[n].strip(), '[ne]')) - print(wffn) - pick = create_pick(eventDate, np, picktime, eventNum, pickcinfo, - phase, stations[n], wffn, authority_id) - event.picks.append(pick) - pickID = pick.get('id') - arrival = create_arrival(pickID, pickcinfo, phase) - origin.arrivals.append(arrival) - event.picks.append(pick) - np += 1 + for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: + if picktime is not None: + if phase == 'P': + wffn = os.path.join(sdir, '{0}*{1}*'.format( + stations[n].strip(), 'z')) + else: + wffn = os.path.join(sdir, '{0}*{1}*'.format( + stations[n].strip(), '[ne]')) + print(wffn) + pick = create_pick(eventDate, np, picktime, eventNum, pickcinfo, + phase, stations[n], wffn, authority_id) + event.picks.append(pick) + pickID = pick.get('id') + arrival = create_arrival(pickID, pickcinfo, phase) + if event.origins: + event.origins[0].arrivals.append(arrival) + event.picks.append(pick) + if event.origins: + origin = event.origins[0] magnitude = create_magnitude(origin.get('id'), loccinfo) magnitude.mag = float(loc['Mnet']) magnitude.magnitude_type = 'Ml' - - event.origins.append(origin) event.magnitudes.append(magnitude) - return event + return event - except AttributeError as e: - raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ - insufficient data!'.format(e, phasfn, locfn)) + #except AttributeError as e: + # raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ + # insufficient data!'.format(e, phasfn, locfn)) def picks_from_pilot(fn): From 51aa949b5673ed708e34e5d2744e7f2522aed2a9 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 30 May 2016 17:08:35 +0200 Subject: [PATCH 0934/1144] debugging in progress --- inputs/PILOT_TimeErrors.in | 3 ++ pylot/core/io/data.py | 4 +- pylot/core/io/phases.py | 93 +++++++++++++------------------------ pylot/core/util/defaults.py | 4 ++ 4 files changed, 42 insertions(+), 62 deletions(-) create mode 100644 inputs/PILOT_TimeErrors.in diff --git a/inputs/PILOT_TimeErrors.in b/inputs/PILOT_TimeErrors.in new file mode 100644 index 00000000..6994a829 --- /dev/null +++ b/inputs/PILOT_TimeErrors.in @@ -0,0 +1,3 @@ +## default time errors for old PILOT phases +0.04 0.08 0.16 0.32 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P +0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S \ No newline at end of file diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index ca75f7e6..8c52741c 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -9,7 +9,7 @@ from obspy.core.event import Event from obspy.io.xseed import Parser from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ - picks_from_pilot + picksdict_from_pilot from pylot.core.util.errors import FormatError, OverwriteError from pylot.core.util.utils import fnConstructor, getGlobalTimes @@ -50,7 +50,7 @@ class Data(object): except TypeError as e: if 'Unknown format for file' in e.message: if 'PHASES' in evtdata: - picks = picks_from_pilot(evtdata) + picks = picksdict_from_pilot(evtdata) evtdata = Event() evtdata.picks = picks_from_picksdict(picks) elif 'LOC' in evtdata: diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 2fc03378..610a663d 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -8,6 +8,7 @@ import scipy.io as sio import warnings from obspy.core import UTCDateTime +from pylot.core.io.inputs import AutoPickParameter from pylot.core.io.location import create_arrival, create_event, \ create_magnitude, create_origin, create_pick from pylot.core.pick.utils import select_for_phase @@ -30,7 +31,6 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs): :param locfn: filename of the old PILOT Matlab LOC file :return event: event object containing event and phase information """ - sdir = os.path.split(phasfn)[0] if phasfn is not None and os.path.isfile(phasfn): phases = sio.loadmat(phasfn) phasctime = UTCDateTime(os.path.getmtime(phasfn)) @@ -53,8 +53,7 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs): loccinfo = ope.CreationInfo(agency_id=authority_id, author=locauthor, creation_time=locctime) - np = 0 - #try: + eventNum = str(loc['ID'][0]) # retrieve eventID for the actual database @@ -82,46 +81,9 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs): etype='earthquake', resID=eventNum, authority_id=authority_id) - for n, pick in enumerate(phases['Ptime']): - if pick[0] > 0: - kwargs = {'year': int(pick[0]), - 'month': int(pick[1]), - 'day': int(pick[2]), - 'hour': int(pick[3]), - 'minute': int(pick[4]), - 'second': int(str(pick[5]).split('.')[0]), - 'microsecond': int(str(pick[5]).split('.')[1][0:6])} - spick = phases['Stime'][n] - if spick[0] > 0: - skwargs = {'year': int(spick[0]), - 'month': int(spick[1]), - 'day': int(spick[2]), - 'hour': int(spick[3]), - 'minute': int(spick[4]), - 'second': int(str(spick[5]).split('.')[0]), - 'microsecond': int(str(spick[5]).split('.')[1][0:6])} - spicktime = UTCDateTime(**skwargs) - else: - spicktime = None - ppicktime = UTCDateTime(**kwargs) + picks = picksdict_from_pilot(phasfn) - for picktime, phase in [(ppicktime, 'P'), (spicktime, 'S')]: - if picktime is not None: - if phase == 'P': - wffn = os.path.join(sdir, '{0}*{1}*'.format( - stations[n].strip(), 'z')) - else: - wffn = os.path.join(sdir, '{0}*{1}*'.format( - stations[n].strip(), '[ne]')) - print(wffn) - pick = create_pick(eventDate, np, picktime, eventNum, pickcinfo, - phase, stations[n], wffn, authority_id) - event.picks.append(pick) - pickID = pick.get('id') - arrival = create_arrival(pickID, pickcinfo, phase) - if event.origins: - event.origins[0].arrivals.append(arrival) - event.picks.append(pick) + event.picks = picks_from_picksdict(picks, creation_info=pickcinfo) if event.origins: origin = event.origins[0] @@ -131,15 +93,15 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs): event.magnitudes.append(magnitude) return event - #except AttributeError as e: - # raise AttributeError('{0} - Matlab LOC files {1} and {2} contains \ - # insufficient data!'.format(e, phasfn, locfn)) - -def picks_from_pilot(fn): +def picksdict_from_pilot(fn): + from pylot.core.util.defaults import TIMEERROR_DEFAULTS picks = dict() phases_pilot = sio.loadmat(fn) stations = stations_from_pilot(phases_pilot['stat']) + params = AutoPickParameter(TIMEERROR_DEFAULTS) + timeerrors = dict(P=params.get('timeerrorsP'), + S=params.get('timeerrorsS')) for n, station in enumerate(stations): phases = dict() for onset_name in 'PS': @@ -148,7 +110,14 @@ def picks_from_pilot(fn): if not pick[0]: continue pick = convert_pilot_times(pick) - phases[onset_name] = dict(mpp=pick) + uncertainty_label = '{0}weight'.format(onset_name.lower()) + ierror = phases_pilot[uncertainty_label][0, n] + try: + spe = timeerrors[onset_name][ierror] + except IndexError as e: + print(e.message + '\ntake two times the largest default error value') + spe = timeerrors[onset_name][-1] * 2 + phases[onset_name] = dict(mpp=pick, spe=spe) picks[station] = phases return picks @@ -228,27 +197,31 @@ def picksdict_from_picks(evt): picks[station] = onsets.copy() return picks -def picks_from_picksdict(picks): +def picks_from_picksdict(picks, creation_info=None): picks_list = list() for station, onsets in picks.items(): - #print('Reading picks on station %s' % station) for label, phase in onsets.items(): - if not isinstance(phase, dict) or len(phase) < 3: + if not isinstance(phase, dict): continue onset = phase['mpp'] - epp = phase['epp'] - lpp = phase['lpp'] + pick = ope.Pick() + if creation_info: + pick.creation_info = creation_info + pick.time = onset error = phase['spe'] + pick.time_errors.uncertainty = error + try: + epp = phase['epp'] + lpp = phase['lpp'] + pick.time_errors.lower_uncertainty = onset - epp + pick.time_errors.upper_uncertainty = lpp - onset + except KeyError as e: + warnings.warn(e.message, RuntimeWarning) try: picker = phase['picker'] except KeyError as e: - warnings.warn(str(e), Warning) + warnings.warn(e.message, RuntimeWarning) picker = 'Unknown' - pick = ope.Pick() - pick.time = onset - pick.time_errors.lower_uncertainty = onset - epp - pick.time_errors.upper_uncertainty = lpp - onset - pick.time_errors.uncertainty = error pick.phase_hint = label pick.method_id = ope.ResourceIdentifier(id=picker) pick.waveform_id = ope.WaveformStreamID(station_code=station) @@ -300,7 +273,7 @@ def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None return if verbosity > 1: print('Opening PILOT phases file: {fn}'.format(fn=phases_file[0])) - picks_dict = picks_from_pilot(phases_file[0]) + picks_dict = picksdict_from_pilot(phases_file[0]) if verbosity > 0: print('Dictionary read from PHASES.mat:\n{0}'.format(picks_dict)) datacheck = list() diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index d42a0786..0e69bb5d 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -47,6 +47,10 @@ AUTOMATIC_DEFAULTS = os.path.join(os.path.expanduser('~'), '.pylot', 'autoPyLoT.in') +TIMEERROR_DEFAULTS = os.path.join(os.path.expanduser('~'), + '.pylot', + 'PILOT_TimeErrors.in') + OUTPUTFORMATS = {'.xml': 'QUAKEML', '.cnv': 'CNV', '.obs': 'NLLOC_OBS'} From ae2fc73d89cf8ebcafe6fcc9df3d11ccbeb6c206 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 31 May 2016 11:14:51 +0200 Subject: [PATCH 0935/1144] parallelization --- pylot/core/active/activeSeismoPick.py | 23 ++++++++++------------- pylot/core/active/seismicshot.py | 6 +++--- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 5ef93ed0..797111e8 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -206,14 +206,8 @@ class Survey(object): plt.xlabel('Difference in time (auto - manual) [s]') return diffs - # def pickShot(self, shTr): - # shotnumber, traceID = shTr - # shot = self.getShotForShotnumber(shotnumber) - # traceID, pick = shot.pickTrace(traceID) - # return shotnumber, traceID, pick - def pickAllShots(self, vmin=333, vmax=5500, folm=0.6, HosAic='hos', - aicwindow=(10, 0), cores = 1): + aicwindow=(15, 0), cores = 1): ''' Automatically pick all traces of all shots of the survey. @@ -241,15 +235,14 @@ class Survey(object): shot.setVmin(vmin) shot.setVmax(vmax) count += 1 - #shot.pickParallel(folm) shot.setPickParameters(folm = folm, method = HosAic, aicwindow = aicwindow) shotlist.append(shot) picks = worker(ppick, shotlist, cores) - for item in picks: - for it in item: - shotnumber, traceID, pick = it + for shot in picks: + for item in shot: + shotnumber, traceID, pick = item self.getShotForShotnumber(shotnumber).setPick(traceID, pick) # tpicksum += (datetime.now() - tstartpick); @@ -259,7 +252,7 @@ class Survey(object): # progress = float(count) / float(len(self.getShotDict())) * 100 # self._update_progress(shot.getShotname(), tend, progress) - self.setSNR() + self.filterSNR() self.setEarllate() print('\npickAllShots: Finished\n') @@ -269,15 +262,19 @@ class Survey(object): % (pickedtraces, ntraces, float(pickedtraces) / float(ntraces) * 100.)) - def setSNR(self): + def filterSNR(self): + print('Starting filterSNR...') for shot in self.data.values(): for traceID in shot.getTraceIDlist(): shot.setSNR(traceID) # if shot.getSNR(traceID)[0] < snrthreshold: + if shot.getPick(traceID) <= 0: + shot.removePick(traceID) if shot.getSNR(traceID)[0] < shot.getSNRthreshold(traceID): shot.removePick(traceID) def setEarllate(self): + print('Starting setEarllate...') for shot in self.data.values(): for traceID in shot.getTraceIDlist(): # set epp and lpp if SNR > 1 (else earllatepicker cant set values) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 16b402ad..a93acd7f 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -383,6 +383,9 @@ class SeismicShot(object): setHosAic = {'hos': hoscftime, 'aic': aiccftime} + if aiccftime < self.getPickwindow(traceID)[0] and 'aic' in self.getMethod(): + return 0 + return setHosAic[self.getMethod()] def setEarllatepick(self, traceID, nfac=1.5): @@ -397,9 +400,6 @@ class SeismicShot(object): self.getPickIncludeRemoved(traceID), stealth_mode=True) - if self.picks[traceID]['epp'] < 0: - self.picks[traceID]['epp'] - def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm): ''' Threshold picker, using the local maximum in a pickwindow to find the time at From 0520df5963bd7165e6b5b7f25135d1e7494c8738 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 31 May 2016 11:15:30 +0200 Subject: [PATCH 0936/1144] bugfix --- pylot/core/util/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index a7167c43..55eb1d96 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -18,7 +18,6 @@ def _pickle_method(m): def worker(func, input, cores='max', async=False): - return result import multiprocessing if cores == 'max': @@ -30,7 +29,8 @@ def worker(func, input, cores='max', async=False): else: result = pool.map(func, input) pool.close() - + return result + def demeanTrace(trace, window): """ From 724032b3f687c927deb6fb285065dc83803ca7e2 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 31 May 2016 13:12:42 +0200 Subject: [PATCH 0937/1144] capturing multiply stored station labels --- pylot/core/io/phases.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 610a663d..3dfef9c1 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -127,10 +127,15 @@ def stations_from_pilot(stat_array): stations = list() cur_stat = None for stat in stat_array: + stat = stat.strip() if stat == cur_stat: continue cur_stat = stat - stations.append(stat.strip()) + if stat not in stations: + stations.append(stat) + else: + warnings.warn('station {0} listed at least twice, might corrupt ' + 'phase times', RuntimeWarning) return stations From 7965239a9296080a60ce5ee3bc81bacb726008c3 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 31 May 2016 15:53:23 +0200 Subject: [PATCH 0938/1144] [closes #124] PILOT event data can be loaded now --- QtPyLoT.py | 10 +++++++++- pylot/core/io/phases.py | 10 ++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 955a2e68..f0ca34d0 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -577,7 +577,13 @@ class MainWindow(QMainWindow): ycoord = gui_event.ydata - statID = int(round(ycoord)) + try: + statID = int(round(ycoord)) + except TypeError as e: + if 'a float is required' in e.message: + return None + else: + raise e return statID @@ -724,6 +730,8 @@ class MainWindow(QMainWindow): wfID = self.getWFID(gui_event) + if not wfID: return + station = self.getStationName(wfID) self.updateStatus('picking on station {0}'.format(station)) data = self.getData().getWFData() diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 3dfef9c1..dfa67b4a 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -183,9 +183,15 @@ def picksdict_from_picks(evt): print(e) onsets = {} mpp = pick.time - lpp = mpp + pick.time_errors.upper_uncertainty - epp = mpp - pick.time_errors.lower_uncertainty spe = pick.time_errors.uncertainty + try: + lpp = mpp + pick.time_errors.upper_uncertainty + epp = mpp - pick.time_errors.lower_uncertainty + except TypeError as e: + msg = e.message + ',\n falling back to symmetric uncertainties' + warnings.warn(msg) + lpp = mpp + spe + epp = mpp - spe phase['mpp'] = mpp phase['epp'] = epp phase['lpp'] = lpp From 73c49d82914ec50704677382cc1790ff36a358d0 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 31 May 2016 17:27:59 +0200 Subject: [PATCH 0939/1144] improved plotting performance especially for large data sets --- QtPyLoT.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f0ca34d0..a673e70f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -83,6 +83,7 @@ class MainWindow(QMainWindow): self.recentfiles = settings.value("data/recentEvents", []) self.fname = dict(manual=None, auto=None, loc=None) self.fnames = None + self._stime = None structure_setting = settings.value("data/Structure", "PILOT") self.dataStructure = DATASTRUCTURE[structure_setting]() self.seismicPhase = str(settings.value("phase", "P")) @@ -429,6 +430,7 @@ class MainWindow(QMainWindow): if 'loc' not in type: self.updatePicks(type=type) self.drawPicks() + self.draw() def getLastEvent(self): return self.recentfiles[0] @@ -594,6 +596,9 @@ class MainWindow(QMainWindow): return wfID return None + def getStime(self): + return self._stime + def addActions(self, target, actions): for action in actions: if action is None: @@ -612,6 +617,7 @@ class MainWindow(QMainWindow): ans = self.data.setWFData(self.fnames) elif self.fnames is None and self.okToContinue(): ans = self.data.setWFData(self.getWFFnames()) + self._stime = getGlobalTimes(self.getData().getWFData())[0] if ans: self.plotWaveformData() return ans @@ -636,16 +642,19 @@ class MainWindow(QMainWindow): self.setComponent('Z') self.plotWaveformData() self.drawPicks() + self.draw() def plotN(self): self.setComponent('N') self.plotWaveformData() self.drawPicks() + self.draw() def plotE(self): self.setComponent('E') self.plotWaveformData() self.drawPicks() + self.draw() def pushFilterWF(self, param_args): self.getData().filterWFData(param_args) @@ -661,6 +670,7 @@ class MainWindow(QMainWindow): self.getData().resetWFData() self.plotWaveformData() self.drawPicks() + self.draw() def adjustFilterOptions(self): fstring = "Filter Options ({0})".format(self.getSeismicPhase()) @@ -745,8 +755,10 @@ class MainWindow(QMainWindow): if replot: self.plotWaveformData() self.drawPicks() + self.draw() else: self.drawPicks(station) + self.draw() else: self.updateStatus('picks discarded ({0})'.format(station)) if not self.getLocflag() and self.check4Loc(): @@ -782,6 +794,7 @@ class MainWindow(QMainWindow): def finalizeAutoPick(self): self.drawPicks(picktype='auto') + self.draw() self.thread.quit() def addPicks(self, station, picks, type='manual'): @@ -836,14 +849,14 @@ class MainWindow(QMainWindow): stat_picks = self.getPicks(type=picktype)[station] + stime = self.getStime() + for phase in stat_picks: picks = stat_picks[phase] if type(stat_picks[phase]) is not dict: return colors = phase_col[phase[0].upper()] - stime = getGlobalTimes(self.getData().getWFData())[0] - mpp = picks['mpp'] - stime epp = picks['epp'] - stime lpp = picks['lpp'] - stime @@ -860,7 +873,6 @@ class MainWindow(QMainWindow): mpp, ylims[0], colors[4]) else: raise TypeError('Unknow picktype {0}'.format(picktype)) - self.draw() def locateEvent(self): settings = QSettings() From 62b1a4e6706b459de8a9c87e540a772926c2abd6 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 31 May 2016 17:36:00 +0200 Subject: [PATCH 0940/1144] making widget reusable --- QtPyLoT.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a673e70f..4d5dfb53 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -388,6 +388,18 @@ class MainWindow(QMainWindow): settings = QSettings() return settings.value("data/dataRoot") + def getType(self): + type = QInputDialog().getItem(self, self.tr("Select phases type"), + self.tr("Type:"), [self.tr("manual"), + self.tr("automatic")]) + + if type[0].startswith('auto'): + type = 'auto' + else: + type = type[0] + + return type + def load_autopicks(self, fname=None): self.load_data(fname, type='auto') @@ -396,7 +408,7 @@ class MainWindow(QMainWindow): def load_pilotevent(self): filt = "PILOT location files (*.mat)" - caption = "Select PILOT loaction file" + caption = "Select PILOT location file" fn_loc = QFileDialog().getOpenFileName(self, caption=caption, filter=filt, dir=self.getRoot()) fn_loc = fn_loc[0] @@ -406,14 +418,8 @@ class MainWindow(QMainWindow): fn_phases = QFileDialog().getOpenFileName(self, caption=caption, filter=filt, dir=loc_dir) fn_phases = fn_phases[0] - type = QInputDialog().getItem(self, self.tr("Select phases type"), - self.tr("Type:"), [self.tr("manual"), - self.tr("automatic")]) - if type[0].startswith('auto'): - type = 'auto' - else: - type = type[0] + type = self.getType() fname_dict = dict(phasfn=fn_phases, locfn=fn_loc) self.load_data(fname_dict, type=type) From 0d7ee9d779ce64465b7b8b93751a92c5c0d81b1d Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 1 Jun 2016 08:55:43 +0200 Subject: [PATCH 0941/1144] [fixed] loading of automatic picks did not plot any result (fixed in this rev) --- QtPyLoT.py | 6 ++++-- pylot/core/io/data.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 4d5dfb53..9337fc53 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -109,6 +109,7 @@ class MainWindow(QMainWindow): self.data = Data(self, lastEvent) else: self.data = Data(self) + self.autodata = Data(self) # load and display waveform data self.dirty = False @@ -432,10 +433,11 @@ class MainWindow(QMainWindow): if isinstance(action, QAction): fname = self.filename_from_action(action) self.set_fname(fname, type) - self.data += Data(self, evtdata=fname) + data = dict(auto=self.autodata, manual=self.data) + data[type] += Data(self, evtdata=fname) if 'loc' not in type: self.updatePicks(type=type) - self.drawPicks() + self.drawPicks(picktype=type) self.draw() def getLastEvent(self): diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 8c52741c..71c6371e 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -40,7 +40,7 @@ class Data(object): elif isinstance(evtdata, dict): evt = readPILOTEvent(**evtdata) evtdata = evt - elif isinstance(evtdata, str): + elif isinstance(evtdata, basestring): try: cat = read_events(evtdata) if len(cat) is not 1: From 9996033ca56e62ad6e2f3891499008db36f0be99 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 1 Jun 2016 14:12:49 +0200 Subject: [PATCH 0942/1144] cleanup (worker) --- pylot/core/active/activeSeismoPick.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 797111e8..d44b07de 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -1,14 +1,11 @@ # -*- coding: utf-8 -*- +import os import sys import numpy as np from pylot.core.active import seismicshot from pylot.core.active.surveyUtils import cleanUp -import copy_reg -import types from pylot.core.util.utils import worker, _pickle_method -copy_reg.pickle(types.MethodType, _pickle_method) - def ppick(shot): picks = [] for traceID in shot.getTraceIDlist(): @@ -120,6 +117,15 @@ class Survey(object): "cutwindow = %s, tMovingWindow = %f, tsignal = %f, tgap = %f" % (cutwindow, tmovwind, tsignal, tgap)) + + def loadArray(self, path, receiverfile, sourcefile): + from pylot.core.active.seismicArrayPreparation import SeisArray + + array = SeisArray(os.path.join(path, receiverfile)) + array.addSourceLocations(os.path.join(path, sourcefile)) + self.seisArray = array + + def setManualPicksFromFiles(self, directory='picks'): ''' Read manual picks from *.pck files in a directory. @@ -230,6 +236,7 @@ class Survey(object): shotlist = [] + print('pickAllShots: Setting pick parameters...') for shot in self.data.values(): tstartpick = datetime.now() shot.setVmin(vmin) @@ -238,7 +245,9 @@ class Survey(object): shot.setPickParameters(folm = folm, method = HosAic, aicwindow = aicwindow) shotlist.append(shot) + print('pickAllShots: Starting to pick...') picks = worker(ppick, shotlist, cores) + print('Done!') for shot in picks: for item in shot: From 02117399b5645d12366d7c9c2e77de1a5950a276 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 1 Jun 2016 14:13:15 +0200 Subject: [PATCH 0943/1144] output change --- pylot/core/active/fmtomoUtils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 94465a34..bf08b1d3 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -293,7 +293,7 @@ class Tomo3d(object): self.clearDir(directory) def rmDir(self, directory): - print('Removing directory %s...'%directory) + #print('Removing directory %s...'%directory) return os.rmdir(directory) def removeDirectories(self): @@ -357,7 +357,7 @@ class Tomo3d(object): ''' nsrc = self.readNsrc() if self.nproc > nsrc: - raise ValueError('Number of spawned processes higher than number of sources') + print('Warning: Number of spawned processes higher than number of sources') return nsrc/self.nproc, nsrc%self.nproc def srcIDs4Kernel(self, procID): From 9c8c5bb842d30294eb51664e7db729e315e0e6d4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 1 Jun 2016 14:14:12 +0200 Subject: [PATCH 0944/1144] - --- pylot/core/active/surveyUtils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 04c3509e..a875ca8b 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -16,6 +16,7 @@ def readParameters(parfile, parameter): return value + def fitSNR4dist(shot_dict, shiftdist=30, shiftSNR=100): """ Approach to fit the decreasing SNR with wave travel distance. From a255718f59cebdd47c912218b997fb5235a51d43 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 1 Jun 2016 14:14:57 +0200 Subject: [PATCH 0945/1144] added script to VC --- pylot/core/active/control_script.py | 128 ++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100755 pylot/core/active/control_script.py diff --git a/pylot/core/active/control_script.py b/pylot/core/active/control_script.py new file mode 100755 index 00000000..871520db --- /dev/null +++ b/pylot/core/active/control_script.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import sys +import os +from obspy import read +from obspy import Stream +from obspy import Trace +from datetime import datetime +import numpy as np + +from pylot.core.active import surveyUtils +from pylot.core.active import seismicshot +from pylot.core.active import activeSeismoPick +from pylot.core.active import fmtomoUtils +from pylot.core.active import seismicArrayPreparation +# reload(seismicshot) +# reload(surveyUtils) +# reload(activeSeismoPick) + +##################################################################################### +# parameter definitions:############################################################# +cutwindow = (0, 0.15) # cut out a part of the trace [seconds] +tmovwind = 0.1 # size of the moving window +windowsize = (30, 0) # windowsize for AIC picker (indices around HOS picks [time/sampling rate] !!!) +folm = 0.6 # fraction of local maximum for threshold picker +tsignal = 0.03 +tgap = 0.0007 + +nproc = 32 + +vmin = 333 +vmax = 5500 + +HosAic = 'aic' # can be 'hos' or 'aic' + +rockeskyll = False +GZB = True +bb1 = False + +# Simulation parameters ############################################################# +simulation = True + +niter = 4 + +bottomBoundary = -50.0 +topBoundary = 5.0 +nPointsPropgrid = (100, 100, 100) +nPointsVgrid = (30, 30, 30) +cushionfactor = 0.1 +interpolationMethod = 'linear' +mygrid = '../mygrid.in' + +cwd = os.getcwd() +simuldir = 'fmtomo_simulation' +pickdir = 'picks' +fmtomopath = '/rscratch/minos22/marcel/flachseismik/fmtomo/GZB_clean/' +###################################################################################### +###################################################################################### +if rockeskyll == True: + receiverfile = "Geophone_interpoliert_rockes" + sourcefile = "Schusspunkte_rockes" + obsdir = "/rscratch/minos22/marcel/flachseismik/rockeskyll_200615_270615/" + filename = 'survey_rockes.pickle' +elif GZB == True: + receiverfile = "Geophone_interpoliert_GZB" + sourcefile = "Schusspunkte_GZB" + #sourcefile = "Schusspunkte_GZB_1shot" + obsdir = "/rscratch/minos22/marcel/flachseismik/GZB_26_06_15_01/" + filename = 'survey_GZB.pickle' +elif bb1 == True: + receiverfile = "Geophone_Marcel" + sourcefile = "Schusspunkte_Marcel" + obsdir = "/rscratch/minos22/marcel/flachseismik/bachelor_bausenberg/" + filename = 'survey_bb1.pickle' +###################################################################################### + +starttime = datetime.now() + +print('\n--------------------Starting Script at %s -------------------\n' %starttime.time()) +print('directory: %s\nsourcefile: %s\nreceiverfile: %s\nsurvey output filename: %s\n' %(obsdir, sourcefile, receiverfile, filename)) +if HosAic == 'aic': print('picking with AIC\n') +if HosAic == 'hos': print('picking with HOS\n') + +survey = activeSeismoPick.Survey(obsdir, sourcefile, receiverfile, useDefaultParas = False) +survey.setParametersForAllShots(cutwindow, tmovwind, tsignal, tgap) +surveyUtils.setDynamicFittedSNR(survey.getShotDict()) +#surveyUtils.setConstantSNR(survey.getShotDict(), 0) +survey.setArtificialPick(0, 0) # artificial pick at source origin +print('\nDone after %s seconds!\n------------------------------------------------------------------------------\n' % (datetime.now() - starttime).seconds) +survey.pickAllShots(vmin, vmax, folm, HosAic, windowsize, cores = nproc) +survey.cleanBySPE(maxSPE = 0.0075) +survey.saveSurvey(filename) +print('\n--- Finished picking ---') +print'Elapsed time:', datetime.now()-starttime + + +###################################################################################### +if simulation == False: + sys.exit() + +survey = activeSeismoPick.Survey.from_pickle(filename) + +if not os.path.isdir(os.path.join(cwd, simuldir)): + err = os.mkdir(os.path.join(cwd, simuldir)) + +picks = os.path.join(simuldir, pickdir) + +if not os.path.isdir(os.path.join(cwd, picks)): + err2 = os.mkdir(os.path.join(cwd, picks)) + +survey.exportFMTOMO(picks) + +####### hard coded +os.chdir(simuldir) +survey.loadArray(obsdir, 'Geophone_eingemessen_GZB', 'Schusspunkte_GZB') +survey.seisArray.generateFMTOMOinputFromArray(nPointsPropgrid, nPointsVgrid, + (bottomBoundary, topBoundary), cushionfactor, + interpolationMethod, customgrid = mygrid, + writeVTK = False) + +os.chdir(cwd) +####### test + +tomo = fmtomoUtils.Tomo3d(fmtomopath, simuldir) +tomo.runTOMO3D(nproc, niter) + +print('\n--- Finished script ---') +print'Elapsed time:', datetime.now()-starttime From de89fc83ce1dbd358de9c487545d4cf3403241b6 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 2 Jun 2016 09:39:01 +0200 Subject: [PATCH 0946/1144] moved widget to utils widgets for consistency and reusability additionally the filter for PHASES and LOC files has been modified to avoid false selection --- QtPyLoT.py | 33 +++++++++++---------------------- pylot/core/util/widgets.py | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9337fc53..5b0f5f31 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -53,7 +53,7 @@ from pylot.core.util.utils import fnConstructor, getLogin, \ getGlobalTimes from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ - MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg + MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg, getDataType from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import AutoPickThread from pylot.core.util.version import get_git_version as _getVersionString @@ -389,43 +389,32 @@ class MainWindow(QMainWindow): settings = QSettings() return settings.value("data/dataRoot") - def getType(self): - type = QInputDialog().getItem(self, self.tr("Select phases type"), - self.tr("Type:"), [self.tr("manual"), - self.tr("automatic")]) - - if type[0].startswith('auto'): - type = 'auto' - else: - type = type[0] - - return type - def load_autopicks(self, fname=None): self.load_data(fname, type='auto') def load_loc(self, fname=None): - self.load_data(fname, type='loc') + type = getDataType(self) + self.load_data(fname, type=type, loc=True) def load_pilotevent(self): - filt = "PILOT location files (*.mat)" + filt = "PILOT location files (*LOC*.mat)" caption = "Select PILOT location file" fn_loc = QFileDialog().getOpenFileName(self, caption=caption, filter=filt, dir=self.getRoot()) fn_loc = fn_loc[0] loc_dir = os.path.split(fn_loc)[0] - filt = "PILOT phases files (*.mat)" + filt = "PILOT phases files (*PHASES*.mat)" caption = "Select PILOT phases file" fn_phases = QFileDialog().getOpenFileName(self, caption=caption, filter=filt, dir=loc_dir) fn_phases = fn_phases[0] - type = self.getType() + type = getDataType(self) fname_dict = dict(phasfn=fn_phases, locfn=fn_loc) self.load_data(fname_dict, type=type) - def load_data(self, fname=None, type='manual'): + def load_data(self, fname=None, type='manual', loc=False): if not self.okToContinue(): return if fname is None: @@ -435,7 +424,7 @@ class MainWindow(QMainWindow): self.set_fname(fname, type) data = dict(auto=self.autodata, manual=self.data) data[type] += Data(self, evtdata=fname) - if 'loc' not in type: + if not loc: self.updatePicks(type=type) self.drawPicks(picktype=type) self.draw() @@ -485,9 +474,9 @@ class MainWindow(QMainWindow): filt = "Supported file formats" \ " (*.mat *.qml *.xml *.kor *.evt)" caption = "Open an event file" - fname = QFileDialog().getOpenFileName(self, - caption=caption, - filter=filt) + fname = QFileDialog().getOpenFileName(self, caption=caption, + filter=filt, + dir=self.getRoot()) fname = fname[0] else: fname = str(action.data().toString()) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8b0bdfaf..bf916aff 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -20,7 +20,8 @@ from matplotlib.widgets import MultiCursor from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout, \ QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, QPixmap, QSpinBox, \ - QTabWidget, QToolBar, QVBoxLayout, QWidget, QPushButton, QFileDialog + QTabWidget, QToolBar, QVBoxLayout, QWidget, QPushButton, QFileDialog, \ + QInputDialog from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime @@ -33,6 +34,18 @@ from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ demeanTrace, isSorted, findComboBoxIndex +def getDataType(parent): + type = QInputDialog().getItem(parent, "Select phases type", "Type:", + ["manual", "automatic"]) + + if type[0].startswith('auto'): + type = 'auto' + else: + type = type[0] + + return type + + def createAction(parent, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False): """ From 536019259e54341011933864a8edb7d62c744671 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 6 Jun 2016 14:10:46 +0200 Subject: [PATCH 0947/1144] [adresses 195] preparing GUI elements for a new dialog widget for interactive comparison --- QtPyLoT.py | 38 ++++++++++++++--- icons.qrc | 1 + icons/compare.png | Bin 0 -> 3108 bytes icons_rc.py | 8 ++-- inputs/autoPyLoT.in | 1 + pylot/core/pick/compare.py | 16 +++++-- pylot/core/util/widgets.py | 84 +++++++++++++++++++++++++++++++++++-- 7 files changed, 131 insertions(+), 17 deletions(-) create mode 100644 icons/compare.png diff --git a/QtPyLoT.py b/QtPyLoT.py index 5b0f5f31..e8709d0c 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -53,7 +53,7 @@ from pylot.core.util.utils import fnConstructor, getLogin, \ getGlobalTimes from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ - MPLWidget, PropertiesDlg, HelpForm, createAction, PickDlg, getDataType + WaveformWidget, PropertiesDlg, HelpForm, createAction, PickDlg, getDataType from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import AutoPickThread from pylot.core.util.version import get_git_version as _getVersionString @@ -142,8 +142,8 @@ class MainWindow(QMainWindow): plottitle = "Overview: {0} components ".format(self.getComponent()) # create central matplotlib figure canvas widget - self.DataPlot = MPLWidget(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) + self.DataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) self.DataPlot.mpl_connect('button_press_event', self.pickOnStation) self.DataPlot.mpl_connect('axes_enter_event', @@ -178,6 +178,8 @@ class MainWindow(QMainWindow): auto_icon.addPixmap(QPixmap(':/icons/sync.png')) locate_icon = QIcon() locate_icon.addPixmap(QPixmap(':/icons/locate.png')) + compare_icon = QIcon() + compare_icon.addPixmap(QPixmap(':/icons/compare.png')) newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, @@ -244,6 +246,12 @@ class MainWindow(QMainWindow): "Alt+S", s_icon, "Toggle S phase", True) + self.compare_action = self.createAction(self, "&Compare picks...", + self.comparePicks, "Alt+C", + compare_icon, "Comparison of " + "manual and " + "automatic pick " + "data.", False) printAction = self.createAction(self, "&Print event ...", self.printEvent, QKeySequence.Print, print_icon, @@ -318,7 +326,7 @@ class MainWindow(QMainWindow): ' displayed!') autoPickToolBar = self.addToolBar("autoPyLoT") - autoPickActions = (auto_pick,) + autoPickActions = (auto_pick, self.compare_action) self.addActions(autoPickToolBar, autoPickActions) # pickToolBar = self.addToolBar("PickTools") @@ -568,6 +576,10 @@ class MainWindow(QMainWindow): except KeyError: return None + def comparePicks(self): + if self.check4Comparison(): + compare_dlg = ComparisonDialog(self) + def getPlotWidget(self): return self.DataPlot @@ -828,6 +840,7 @@ class MainWindow(QMainWindow): self.picks.update(picks) elif type == 'auto': self.autopicks.update(picks) + self.check4Comparison() def drawPicks(self, station=None, picktype='manual'): # if picks to draw not specified, draw all picks available @@ -882,9 +895,22 @@ class MainWindow(QMainWindow): def check4Loc(self): return self.picksNum() > 4 - def picksNum(self): + def check4Comparison(self): + mpicks = self.getPicks() + apicks = self.getPicks('auto') + for station, phases in mpicks.items(): + try: + aphases = apicks[station] + for phase in phases.keys(): + if phase in aphases.keys(): + return True + except KeyError: + continue + return False + + def picksNum(self, type='manual'): num = 0 - for phases in self.getPicks().values(): + for phases in self.getPicks(type).values(): num += len(phases) return num diff --git a/icons.qrc b/icons.qrc index 8b2065fc..96b8126c 100644 --- a/icons.qrc +++ b/icons.qrc @@ -5,6 +5,7 @@ icons/locate.png icons/printer.png icons/delete.png + icons/compare.png icons/key_E.png icons/key_N.png icons/key_P.png diff --git a/icons/compare.png b/icons/compare.png new file mode 100644 index 0000000000000000000000000000000000000000..be28ca746ce1746a285f3c01f03674928775e3e6 GIT binary patch literal 3108 zcmZ`*3p5k#8=pi_m5SnXn~#;n zdFWv*=P_Jj`~quk4h)kCsD!*ocfFr+*68|0FP5UAmt$ab$9q63 z@frAsNp$!2tCf9adA{_Ojf?FNan1;FgWemrLF^T3+8ylufqrz0h6l^2FaipciVV;p zKr1pC**-}BcEkTi=kq=)^WXwtGs%r)!NUnQ#gi|xh%CRrV%g12*z2qNkZHO%mXS|MYkn&5#7uOhI^m z1So@kUOz{%;K}o_+JyR?B3AS1ADs$5OEz%eH;m|6&%NPlR#I}Jy+kXg>{T7402}Jg8|AG`-uX);_9s^HSE|4%XD$;&n`n%3$jGf3(e}ogA*I{cc z_#2(ay)n88v*qn*)`CR#R4eD+>cD!+4Ry5-ru*1h-U~Kfs%|5^iM5ABa;p`0oTy>a zVkFiiLi27$r4Dp))Vf47PW87A-@L|mUF%r{?or&Ug166NT)~VvMB?_~C1P=``0|)^ zOw#3%WP@V~TJW9vyQ4y+&xP}XD3X;3w0q}m0>!|Y^bzLi*z00t?4175Qx!c}M$Yf) zc^^jJex#|)qjNVATYDz)Vnw>D4+VGFQwp!OK;N-c8=BZvE+MWxmkL4X7Um{kQCBW} z5BebO-^355Tv^sNTSKd&8h3VY%}dR@sWUra7Z{36h{Gg$k~4U|bKkVP+p%>%J?06% zmU2OlwM2t_+ho7jiaM*0yulvq4lxehwE6sC&2WPFV|!A8gAI zV9S^b-*e*fngx%=a32)YR*j5#DJ4=_*E^G6C0_bajL#o3G4leQp0*(GT6l=%TAo^b zPI8YUzRfXDKlF{}JzY&2-FuamS#F14e>5Dc$6QQ7Yp)W?V+UUCn1(FHh75T7c1xbcSU;6#UYo2~wv)3D1IlR_#u@@#*ueo7tCbGl>|+&$EI0NCe4T}*iM z`L(LM45O3-<6Q-NmrRu{yJZCUy7Az5&@3cvAnsAI?Dqqi5!7Ig`+u39_%0_2@|w|vg<^*CxxBiVHYJxHPlSHIn`<1Pt}D}L{`v>TMh zi%+rHo$aGS@VGpAnhNoVU+OO^2s-bWKBu=;a!n;L^VY69CQ+{?Cg_h>VcRN? zt1`Gt^BEA=r>7-C0^5mxZ6Yre5TP9`=ro0uArTVBi#;CQR`L6!yx`I6>%>W?8j#P)il(0@f9B>d3&&-9f^NgLdlN?a`%-^i&NoWndi82QUNCkIEBm;OJlJnL^0AFAOXU zK(XhupfM!F6+`Q&ec`Mk)(mNEfBv3kl5ziN#FAH+D-FG=r-x#=1U4o@F+;r|3Mt2C zHms^*IFrsX7T5y1kZ+Dk`nMejvtX&u8k{u;p_sFx^RoQd^!3{|f;!*ld#{1V`=|Ot zzI*Arvb(vinlsy$<$2wJDrq{c(3~2cc-Y$ikGx7fj{93fQ~&wnc3YS#+BtEgV(^G% zTby>pN|@Jx`6p@HQJvl@k%?!$I#kR-QPT&keNnTZzE{;`SR|DHGWnOwN2N$$WkNgG zWE~OGFZojP$9QGO+Yc@}r*vCtph^i%Y+^3d z7=F410e(_mORg9N>pU)0MU5JMiS_j6%`8>#4P*J}y^k@#KaRa0)oanxU&?o8xev7~ z7e59wnj|$YgH%ykXz`+JrVU9^?CoY?tEX?TK%CS^I(AI_^!l7v921&8M@(6527!>$ z2nMT|P`j*J;PxxZ#TDpS?)Ur)l07E$A`3;Y)v&bH6mAptYBRA{F=qVvK*Pd;7$wQ3 z5SvN)Ky32Kz0v%`H;ior(pxrm6N=u2Icrq6HArmIJ94aA2mm;|9Pek+d8F5sTf+R% zpDIAKE!;y(H@f{P*Du3(MGeeQrw-v!F0kf<>u>_fh2Zv7#Z-x1d-s~bt89LCdC(j+ zdJ1~ZYGmkz*v$gkaug4meen!6!j92#EwyIGbv76WPOmqpKmaFl>C(&R8z6E>gejfq@0xn2ZlL8;2bLW=F0e(`tK^FN?^2q0 zJ;WZTatm`)1~9?swux1BHX~ww{(xm++Vdw_E0FwL4VKZkf8ge@IK2R~W0ROG8QT+R zA9}y3zGZ;vm{*tKq~v-nQjV4vn@_O(;Drpx5c+V-ZKlJx>pxWJ#lO?5kEugm%|1 everything AUTOPHASES_AIC_HOS4_ARH #phasefile# %name of autoPILOT output phase file diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 1a828c2a..d246be67 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -29,7 +29,10 @@ class Comparison(object): names = list() self._pdfs = dict() for name, fn in kwargs.items(): - self._pdfs[name] = PDFDictionary.from_quakeml(fn) + if not isinstance(PDFDictionary, fn): + self._pdfs[name] = PDFDictionary.from_quakeml(fn) + else: + self._pdfs[name] = fn names.append(name) if len(names) > 2: raise ValueError('Comparison is only defined for two ' @@ -101,14 +104,19 @@ class Comparison(object): return compare_pdfs - def plot(self): - nstations = self.nstations - stations = self.stations + def plot(self, stations=None): + if stations is None: + nstations = self.nstations + stations = self.stations + else: + nstations = len(stations) istations = range(nstations) fig, axarr = plt.subplots(nstations, 2, sharex='col', sharey='row') for n in istations: station = stations[n] + if station not in self.comparison.keys(): + continue compare_pdf = self.comparison[station] for l, phase in enumerate(compare_pdf.keys()): axarr[n, l].plot(compare_pdf[phase].axis, diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index bf916aff..0b30c0c8 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -64,8 +64,86 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, action.setCheckable(True) return action +class ComparsionDialog(QDialog): + def __init__(self, c, parent=None): + self._data = c + self._stats = c.keys() + self._canvas = PlotWidget(parent) + super(ComparsionDialog, self).__init__(parent) + self -class MPLWidget(FigureCanvas): + def setupUI(self): + pass + + @property + def canvas(self): + return self._canvas + + @property + def stations(self): + return self._stats + + @stations.setter + def stations(self, stations): + self._stats = stations + + @property + def data(self): + return self._data + + @data.setter + def data(self, data): + self.stations = data.keys() + self._data = data + + +class PlotWidget(FigureCanvas): + def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): + self._parent = parent + self._fig = Figure() + self._xl = xlabel + self._yl = ylabel + self._title = title + super(PlotWidget, self).__init__(self.figure) + + @property + def figure(self): + return self._fig + + @figure.setter + def figure(self, fig): + self._fig = fig + + @property + def xlabel(self): + return self._xl + + @xlabel.setter + def xlabel(self, label): + self._xl = label + + @property + def ylabel(self): + return self._yl + + @ylabel.setter + def ylabel(self, label): + self._yl = label + + @property + def title(self): + return self._title + + @title.setter + def title(self, title): + self._title = title + + @property + def parent(self): + return self._parent + + +class WaveformWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): self._parent = None @@ -79,7 +157,7 @@ class MPLWidget(FigureCanvas): # clear axes each time plot is called self.axes.hold(True) # initialize super class - super(MPLWidget, self).__init__(self.figure) + super(WaveformWidget, self).__init__(self.figure) # add an cursor for station selection self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True, @@ -223,7 +301,7 @@ class PickDlg(QDialog): self.stime, self.etime = getGlobalTimes(self.getWFData()) # initialize plotting widget - self.multicompfig = MPLWidget(self) + self.multicompfig = WaveformWidget(self) # setup ui self.setupUi() From 7e33502824057a9df89a820a567cc093d9e94b9b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 7 Jun 2016 13:38:35 +0200 Subject: [PATCH 0948/1144] GUI tests --- pylot/core/active/asp3d_test.py | 264 ++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 pylot/core/active/asp3d_test.py diff --git a/pylot/core/active/asp3d_test.py b/pylot/core/active/asp3d_test.py new file mode 100644 index 00000000..1ef23d06 --- /dev/null +++ b/pylot/core/active/asp3d_test.py @@ -0,0 +1,264 @@ +# -*- coding: utf-8 -*- + +import sys +from PySide import QtCore, QtGui +from pylot.core.active import activeSeismoPick + +class Ui_ActiveSeismoPick3D(object): + def __init__(self): + self.survey = None + + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(550, 350) + + self.centralwidget = QtGui.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.addBrowseButtons() + self.addButtons() + self.addLineEdits() + self.addLabels() + + MainWindow.setCentralWidget(self.centralwidget) + + self.setMenubar(MainWindow) + + self.menuPreferences = QtGui.QMenu(self.menubar) + self.menuPreferences.setObjectName("menuPreferences") + MainWindow.setMenuBar(self.menubar) + + self.statusbar = QtGui.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.menubar.addAction(self.menuPreferences.menuAction()) + + self.retranslateUi(MainWindow) + self.connectButtons() + + def setMenubar(self, window): + self.menubar = QtGui.QMenuBar(window) + self.menubar.setGeometry(QtCore.QRect(0, 0, 603, 21)) + self.menubar.setObjectName("menubar") + + def connectButtons(self): + QtCore.QObject.connect(self.browseButton_rec, QtCore.SIGNAL("clicked()"), self.chooseReceiverfile) + QtCore.QObject.connect(self.browseButton_src, QtCore.SIGNAL("clicked()"), self.chooseSourcefile) + QtCore.QObject.connect(self.browseButton_obsdir, QtCore.SIGNAL("clicked()"), self.chooseObsdir) + QtCore.QObject.connect(self.start_picking, QtCore.SIGNAL("clicked()"), self.callPicker) + QtCore.QObject.connect(self.gen_survey, QtCore.SIGNAL("clicked()"), self.generateSurvey) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def addLabels(self): + self.label = QtGui.QLabel(self.centralwidget) + self.label.setGeometry(QtCore.QRect(40, 70, 131, 20)) + self.label.setObjectName("label") + + self.label_2 = QtGui.QLabel(self.centralwidget) + self.label_2.setGeometry(QtCore.QRect(40, 110, 131, 20)) + self.label_2.setObjectName("label_2") + + self.label_3 = QtGui.QLabel(self.centralwidget) + self.label_3.setGeometry(QtCore.QRect(40, 150, 131, 20)) + self.label_3.setObjectName("label_3") + + def addLineEdits(self): + self.receiverfile_lineEdit = QtGui.QLineEdit(self.centralwidget) + self.receiverfile_lineEdit.setGeometry(QtCore.QRect(192, 70, 231, 20)) + self.receiverfile_lineEdit.setObjectName("receiverfile_lineEdit") + + self.sourcefile_lineEdit = QtGui.QLineEdit(self.centralwidget) + self.sourcefile_lineEdit.setGeometry(QtCore.QRect(192, 110, 231, 20)) + self.sourcefile_lineEdit.setObjectName("sourcefile_lineEdit") + + self.obsdir_lineEdit = QtGui.QLineEdit(self.centralwidget) + self.obsdir_lineEdit.setGeometry(QtCore.QRect(192, 150, 231, 20)) + self.obsdir_lineEdit.setText("") + self.obsdir_lineEdit.setObjectName("obsdir_lineEdit") + + def addBrowseButtons(self): + self.browseButton_rec = QtGui.QPushButton(self.centralwidget) + self.browseButton_rec.setGeometry(QtCore.QRect(440, 70, 75, 23)) + self.browseButton_rec.setObjectName("browseButton_rec") + + self.browseButton_src = QtGui.QPushButton(self.centralwidget) + self.browseButton_src.setGeometry(QtCore.QRect(440, 110, 75, 23)) + self.browseButton_src.setObjectName("browseButton_src") + + self.browseButton_obsdir = QtGui.QPushButton(self.centralwidget) + self.browseButton_obsdir.setGeometry(QtCore.QRect(440, 150, 75, 23)) + self.browseButton_obsdir.setObjectName("browseButton_obsdir") + + def addButtons(self): + self.gen_survey = QtGui.QPushButton(self.centralwidget) + self.gen_survey.setGeometry(QtCore.QRect(80, 230, 61, 61)) + self.gen_survey.setObjectName("gen_survey") + + self.start_picking = QtGui.QPushButton(self.centralwidget) + self.start_picking.setGeometry(QtCore.QRect(160, 230, 61, 61)) + self.start_picking.setObjectName("start_picking") + + def addProgressBar(self): + self.progressBar = QtGui.QProgressBar(self.centralwidget) + self.progressBar.setGeometry(QtCore.QRect(470, 280, 118, 23)) + self.progressBar.setObjectName("progressBar") + + def updateProgressBar(self): + self.progressBar.setProperty("value", 24) + + def browseFile(self): + dialog = QtGui.QFileDialog() + filename = dialog.getOpenFileName() + return filename[0] + + def browseDir(self): + dialog = QtGui.QFileDialog() + directory = dialog.getExistingDirectory() + return directory + + def chooseSourcefile(self): + self.sourcefile_lineEdit.setText(self.browseFile()) + + def chooseReceiverfile(self): + self.receiverfile_lineEdit.setText(self.browseFile()) + + def chooseObsdir(self): + self.obsdir_lineEdit.setText(self.browseDir()) + + def getSourcefile(self): + return self.sourcefile_lineEdit.text() + + def getReceiverfile(self): + return self.receiverfile_lineEdit.text() + + def getObsdir(self): + return self.obsdir_lineEdit.text() + + def retranslateUi(self, MainWindow): + MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "ActiveSeismoPick3D", None, QtGui.QApplication.UnicodeUTF8)) + self.browseButton_rec.setText(QtGui.QApplication.translate("MainWindow", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.browseButton_src.setText(QtGui.QApplication.translate("MainWindow", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("MainWindow", "Receiver File", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("MainWindow", "Source File", None, QtGui.QApplication.UnicodeUTF8)) + self.start_picking.setText(QtGui.QApplication.translate("MainWindow", "Start\n" +"Picking", None, QtGui.QApplication.UnicodeUTF8)) + self.browseButton_obsdir.setText(QtGui.QApplication.translate("MainWindow", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.gen_survey.setText(QtGui.QApplication.translate("MainWindow", "Gemerate\nSurvey", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("MainWindow", "Seismogram Directory", None, QtGui.QApplication.UnicodeUTF8)) + self.menuPreferences.setTitle(QtGui.QApplication.translate("MainWindow", "Preferences", None, QtGui.QApplication.UnicodeUTF8)) + + def generateSurvey(self): + obsdir = self.getObsdir() + self.survey = activeSeismoPick.Survey(self.getObsdir(), self.getSourcefile(), self.getReceiverfile(), + useDefaultParas = True) + + + def callPicker(self): + Picking_parameters = QtGui.QDialog(self.centralwidget) + ui = Ui_Picking_parameters() + ui.setupUi(Picking_parameters) + ncores, vmin, vmax, folm, AIC, aicwindow = ui.getParameters(Picking_parameters) + if AIC == True: + HosAic = 'aic' + else: + HosAic = 'hos' + if self.survey is None: + print('Survey not defined.') + return + + self.survey.pickAllShots(vmin = vmin, vmax = vmax, + folm = folm, HosAic = HosAic, + aicwindow = aicwindow, cores = ncores) + + +class Ui_Picking_parameters(object): + def setupUi(self, Picking_parameters): + Picking_parameters.setObjectName("Picking_parameters") + Picking_parameters.resize(300, 300) + self.gridLayoutWidget = QtGui.QWidget(Picking_parameters) + self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 20, 250, 211)) + self.gridLayoutWidget.setObjectName("gridLayoutWidget") + self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.label_cores = QtGui.QLabel(self.gridLayoutWidget) + self.label_cores.setObjectName("label_cores") + self.gridLayout.addWidget(self.label_cores, 0, 0, 1, 1) + self.label_vmax = QtGui.QLabel(self.gridLayoutWidget) + self.label_vmax.setObjectName("label_vmax") + self.gridLayout.addWidget(self.label_vmax, 2, 0, 1, 1) + self.label_vmin = QtGui.QLabel(self.gridLayoutWidget) + self.label_vmin.setObjectName("label_vmin") + self.gridLayout.addWidget(self.label_vmin, 1, 0, 1, 1) + self.lineEdit_ncores = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_ncores.setObjectName("lineEdit_ncores") + self.gridLayout.addWidget(self.lineEdit_ncores, 0, 1, 1, 1) + self.lineEdit_vmin = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_vmin.setObjectName("lineEdit_vmin") + self.gridLayout.addWidget(self.lineEdit_vmin, 1, 1, 1, 1) + self.lineEdit_vmax = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_vmax.setObjectName("lineEdit_vmax") + self.gridLayout.addWidget(self.lineEdit_vmax, 2, 1, 1, 1) + self.checkBox = QtGui.QCheckBox(self.gridLayoutWidget) + self.checkBox.setObjectName("checkBox") + self.gridLayout.addWidget(self.checkBox, 4, 1, 1, 1) + self.label_folm = QtGui.QLabel(self.gridLayoutWidget) + self.label_folm.setObjectName("label_folm") + self.gridLayout.addWidget(self.label_folm, 3, 0, 1, 1) + self.label_aic = QtGui.QLabel(self.gridLayoutWidget) + self.label_aic.setObjectName("label_aic") + self.gridLayout.addWidget(self.label_aic, 4, 0, 1, 1) + self.lineEdit_folm = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_folm.setObjectName("lineEdit_folm") + self.gridLayout.addWidget(self.lineEdit_folm, 3, 1, 1, 1) + self.label_aicwindow = QtGui.QLabel(self.gridLayoutWidget) + self.label_aicwindow.setObjectName("label_aicwindow") + self.gridLayout.addWidget(self.label_aicwindow, 5, 0, 1, 1) + self.lineEdit_aicwindow = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_aicwindow.setObjectName("lineEdit_aicwindow") + self.gridLayout.addWidget(self.lineEdit_aicwindow, 5, 1, 1, 1) + self.buttonBox = QtGui.QDialogButtonBox(Picking_parameters) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.buttonBox.setGeometry(QtCore.QRect(10, 240, 250, 32)) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Picking_parameters.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Picking_parameters.reject) + self.retranslateUi(Picking_parameters) + QtCore.QMetaObject.connectSlotsByName(Picking_parameters) + + def getParameters(self, Picking_parameters): + if Picking_parameters.exec_(): + ncores = int(self.lineEdit_ncores.text()) + vmin = float(self.lineEdit_vmin.text()) + vmax = float(self.lineEdit_vmax.text()) + folm = float(self.lineEdit_folm.text()) + AIC = self.checkBox.isChecked() + aicwindow = [float(val) for val in self.lineEdit_aicwindow.text().split(',')] + + return ncores, vmin, vmax, folm, AIC, tuple(aicwindow) + + def retranslateUi(self, Picking_parameters): + Picking_parameters.setWindowTitle(QtGui.QApplication.translate("Picking_parameters", "Picking_parameters", None, QtGui.QApplication.UnicodeUTF8)) + self.label_cores.setText(QtGui.QApplication.translate("Picking_parameters", "Number of cores", None, QtGui.QApplication.UnicodeUTF8)) + self.label_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "Vmax (default = 5000m/s)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "Vmin (default = 333 m/s)", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_ncores.setText(QtGui.QApplication.translate("Picking_parameters", "1", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "333", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) + self.label_folm.setText(QtGui.QApplication.translate("Picking_parameters", "Fraction of local maximum\n" +"(default = 0.6)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_aic.setText(QtGui.QApplication.translate("Picking_parameters", "AIC", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_folm.setText(QtGui.QApplication.translate("Picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) + self.label_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "AIC window (only if AIC\n" +" is checked)", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "15, 0", None, QtGui.QApplication.UnicodeUTF8)) + +if __name__ == "__main__": + app = QtGui.QApplication(sys.argv) + MainWindow = QtGui.QMainWindow() + ui = Ui_ActiveSeismoPick3D() + ui.setupUi(MainWindow) + MainWindow.show() + sys.exit(app.exec_()) + From f3769846bacb97e95d5f21a1db1b6c41e7e09656 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 7 Jun 2016 13:39:21 +0200 Subject: [PATCH 0949/1144] GUI testing --- pylot/core/active/activeSeismoPick.py | 10 +++++----- pylot/core/active/control_script.py | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index d44b07de..388690bd 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -37,8 +37,8 @@ class Survey(object): def _initiate_fnames(self): for shot in self.data.values(): - shot.setRecfile(self.getPath() + self.getReceiverfile()) - shot.setSourcefile(self.getPath() + self.getSourcefile()) + shot.setRecfile(self.getReceiverfile()) + shot.setSourcefile(self.getSourcefile()) def _generateSurvey(self): from obspy.core import read @@ -47,7 +47,7 @@ class Survey(object): shotlist = self.getShotlist() for shotnumber in shotlist: # loop over data files # generate filenames and read manual picks to a list - obsfile = self._obsdir + str(shotnumber) + '_pickle.dat' + obsfile = os.path.join(self._obsdir, str(shotnumber)) + '_pickle.dat' if obsfile not in shot_dict.keys(): shot_dict[shotnumber] = [] shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) @@ -354,7 +354,7 @@ class Survey(object): ''' Returns a list of all shotnumbers contained in the set Sourcefile. ''' - filename = self.getPath() + self.getSourcefile() + filename = self.getSourcefile() srcfile = open(filename, 'r') shotlist = [] for line in srcfile.readlines(): @@ -367,7 +367,7 @@ class Survey(object): ''' Returns a list of all trace IDs contained in the set Receiverfile. ''' - filename = self.getPath() + self.getReceiverfile() + filename = self.getReceiverfile() recfile = open(filename, 'r') reclist = [] for line in recfile.readlines(): diff --git a/pylot/core/active/control_script.py b/pylot/core/active/control_script.py index 871520db..421efcf0 100755 --- a/pylot/core/active/control_script.py +++ b/pylot/core/active/control_script.py @@ -26,7 +26,7 @@ folm = 0.6 # fraction of local maximum for threshold pic tsignal = 0.03 tgap = 0.0007 -nproc = 32 +nproc = 16 vmin = 333 vmax = 5500 @@ -81,7 +81,8 @@ print('directory: %s\nsourcefile: %s\nreceiverfile: %s\nsurvey output filename: if HosAic == 'aic': print('picking with AIC\n') if HosAic == 'hos': print('picking with HOS\n') -survey = activeSeismoPick.Survey(obsdir, sourcefile, receiverfile, useDefaultParas = False) +survey = activeSeismoPick.Survey(obsdir, os.path.join(obsdir, sourcefile), + os.path.join(obsdir, receiverfile), useDefaultParas = False) survey.setParametersForAllShots(cutwindow, tmovwind, tsignal, tgap) surveyUtils.setDynamicFittedSNR(survey.getShotDict()) #surveyUtils.setConstantSNR(survey.getShotDict(), 0) From 8e09fd7c4293e842813fe8836031d17727812601 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 7 Jun 2016 13:51:03 +0200 Subject: [PATCH 0950/1144] [refs #195] pushing GUI element implementation forward --- QtPyLoT.py | 8 ++++++-- pylot/core/util/widgets.py | 37 ++++++++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e8709d0c..f3c21ba7 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -42,6 +42,7 @@ from obspy import UTCDateTime from pylot.core.io.data import Data from pylot.core.io.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent +from pylot.core.pick.compare import Comparison from pylot.core.io.phases import picksdict_from_picks from pylot.core.loc.nll import locate as locateNll from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP, \ @@ -53,7 +54,8 @@ from pylot.core.util.utils import fnConstructor, getLogin, \ getGlobalTimes from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ - WaveformWidget, PropertiesDlg, HelpForm, createAction, PickDlg, getDataType + WaveformWidget, PropertiesDlg, HelpForm, createAction, PickDlg, \ + getDataType, ComparisonDialog from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import AutoPickThread from pylot.core.util.version import get_git_version as _getVersionString @@ -578,7 +580,9 @@ class MainWindow(QMainWindow): def comparePicks(self): if self.check4Comparison(): - compare_dlg = ComparisonDialog(self) + co = Comparison(auto=self.getPicks('auto'), manu=self.getPicks()) + compare_dlg = ComparisonDialog(co, self) + compare_dlg.exec_() def getPlotWidget(self): return self.DataPlot diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0b30c0c8..16beb544 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -64,16 +64,43 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, action.setCheckable(True) return action -class ComparsionDialog(QDialog): +class ComparisonDialog(QDialog): def __init__(self, c, parent=None): self._data = c self._stats = c.keys() - self._canvas = PlotWidget(parent) - super(ComparsionDialog, self).__init__(parent) - self + self._canvas = PlotWidget(self) + super(ComparisonDialog, self).__init__(parent) + self.setupUI() def setupUI(self): - pass + + _outerlayout = QVBoxLayout(self) + _innerlayout = QVBoxLayout(self) + + _stats_combobox = QComboBox(self) + _stats_combobox.setEditable(True) + _stats_combobox.addItems(self.stations) + _stats_combobox.textChanged.connect(self.plotcomparison) + + _phases_combobox = QComboBox(self) + _phases_combobox.addItems(['P', 'S']) + + _toolbar = QToolBar(self) + _toolbar.addWidget(_stats_combobox) + _toolbar.addWidget(_phases_combobox) + + _buttonbox = QDialogButtonBox(QDialogButtonBox.Close) + + _innerlayout.addWidget(self.canvas) + _innerlayout.addWidget(_buttonbox) + + _outerlayout.addWidget(_toolbar) + _outerlayout.addLayout(_innerlayout) + + _buttonbox.rejected.connect(self.reject) + + # finally layout the entire dialog + self.setLayout(_outerlayout) @property def canvas(self): From b96366e321eeacab67ae46f548b78d6f530e2b18 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Wed, 8 Jun 2016 13:14:38 +0200 Subject: [PATCH 0951/1144] [ref #195] implementation done; some bugs have to be fixed --- pylot/core/pick/compare.py | 8 ++-- pylot/core/util/widgets.py | 93 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index d246be67..ed769fc9 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -29,10 +29,12 @@ class Comparison(object): names = list() self._pdfs = dict() for name, fn in kwargs.items(): - if not isinstance(PDFDictionary, fn): - self._pdfs[name] = PDFDictionary.from_quakeml(fn) - else: + if isinstance(fn, PDFDictionary): self._pdfs[name] = fn + elif isinstance(fn, dict): + self._pdfs[name] = PDFDictionary(fn) + else: + self._pdfs[name] = PDFDictionary.from_quakeml(fn) names.append(name) if len(names) > 2: raise ValueError('Comparison is only defined for two ' diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 16beb544..d31132cf 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -28,6 +28,7 @@ from obspy import Stream, UTCDateTime from pylot.core.io.inputs import FilterOptions from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \ getResolutionWindow +from pylot.core.pick.compare import Comparison from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS, \ COMPPOSITION_MAP from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ @@ -67,10 +68,15 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, class ComparisonDialog(QDialog): def __init__(self, c, parent=None): self._data = c - self._stats = c.keys() + self._stats = c.stations self._canvas = PlotWidget(self) + self._widgets = dict(stationsComboBox=None, + phasesComboBox=None) + self._phases = 'PS' + self._plotprops = dict(station=self.stations[0], phase=self.phases[0]) super(ComparisonDialog, self).__init__(parent) self.setupUI() + self.plotcomparison() def setupUI(self): @@ -78,12 +84,18 @@ class ComparisonDialog(QDialog): _innerlayout = QVBoxLayout(self) _stats_combobox = QComboBox(self) + _stats_combobox.setObjectName('stationsComboBox') _stats_combobox.setEditable(True) + _stats_combobox.setInsertPolicy(QComboBox.NoInsert) _stats_combobox.addItems(self.stations) - _stats_combobox.textChanged.connect(self.plotcomparison) + _stats_combobox.editTextChanged.connect(self.prepareplot) + self.widgets = _stats_combobox _phases_combobox = QComboBox(self) + _phases_combobox.setObjectName('phasesComboBox') _phases_combobox.addItems(['P', 'S']) + _phases_combobox.currentIndexChanged.connect(self.prepareplot) + self.widgets = _phases_combobox _toolbar = QToolBar(self) _toolbar.addWidget(_stats_combobox) @@ -106,6 +118,10 @@ class ComparisonDialog(QDialog): def canvas(self): return self._canvas + @canvas.setter + def canvas(self, canvas_obj): + self._canvas = canvas_obj + @property def stations(self): return self._stats @@ -114,15 +130,86 @@ class ComparisonDialog(QDialog): def stations(self, stations): self._stats = stations + @property + def phases(self): + return self._phases + + @phases.setter + def phases(self, value): + self._phases = value + + @property + def plotprops(self): + return self._plotprops + + @plotprops.setter + def plotprops(self, values): + try: + key, value = values + if key not in self.plotprops.keys(): + raise KeyError("'key' {0} not found in " + "ComparisonDialog.plotprops keys.".format(key)) + except ValueError: + raise ValueError("Pass an iterable with two items") + else: + self._plotprops[key] = value + @property def data(self): return self._data @data.setter def data(self, data): - self.stations = data.keys() + assert not isinstance(data, Comparison) + self.stations = data.stations self._data = data + @property + def widgets(self): + return self._widgets + + @widgets.setter + def widgets(self, widget): + name = widget.objectName + if name in self.widgets.keys(): + self._widgets[name] = widget + + def hasvalue(self, sender): + text = sender.currentText() + index = sender.findText(text.upper()) + return index + + def prepareplot(self): + try: + _widget = self.sender() + name = _widget.objectName() + text = _widget.currentText().upper() + index = self.hasvalue(_widget) + if name == 'stationsComboBox' and index is not -1: + _widget.setCurrentIndex(index) + self.plotprops = ('station', text) + elif name == 'phasesComboBox': + self.plotprops = ('phase', text) + except ValueError: + raise ValueError('No sender widget given!') + finally: + self.plotcomparison() + + def plotcomparison(self): + _axes = self.canvas.figure.add_subplot(111) + station = self.plotprops['station'] + phase = self.plotprops['phase'] + pdf = self.data.comparison[station][phase] + x, y = pdf.axis, pdf.data + _axes.plot(x, y) + _axes.set_title(phase) + _axes.set_ylabel('propability density (qual.)') + _axes.set_xlabel('time difference [s]') + _anno = _axes.annotate(station, xy=(.05, .5), xycoords='axes fraction') + bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) + _anno.set_bbox(bbox_props) + self.canvas.draw() + class PlotWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): From b5d94eed61c3691dafffffecd89e7ae279d59d61 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 8 Jun 2016 15:00:47 +0200 Subject: [PATCH 0952/1144] [closes #195] implementation of comparison within GUI ready for testing --- pylot/core/util/widgets.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d31132cf..1b8631a4 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -197,15 +197,22 @@ class ComparisonDialog(QDialog): def plotcomparison(self): _axes = self.canvas.figure.add_subplot(111) + _axes.cla() station = self.plotprops['station'] phase = self.plotprops['phase'] pdf = self.data.comparison[station][phase] - x, y = pdf.axis, pdf.data + x, y, std, exp = pdf.axis, pdf.data, pdf.standard_deviation(), \ + pdf.expectation() _axes.plot(x, y) _axes.set_title(phase) _axes.set_ylabel('propability density (qual.)') _axes.set_xlabel('time difference [s]') - _anno = _axes.annotate(station, xy=(.05, .5), xycoords='axes fraction') + annotation = "{phase} difference on {station}\n" \ + "expectation: {exp}\n" \ + "std: {std}".format(station=station, phase=phase, + std=std, exp=exp) + _anno = _axes.annotate(annotation, xy=(.05, .5), xycoords='axes ' + 'fraction') bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) _anno.set_bbox(bbox_props) self.canvas.draw() From 14cd175297268fb34a73893ad21b4144e9d98976 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 10 Jun 2016 09:01:37 +0200 Subject: [PATCH 0953/1144] [bugfix] it was not possible to pick the very first station manually --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f3c21ba7..8209d464 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -753,7 +753,7 @@ class MainWindow(QMainWindow): wfID = self.getWFID(gui_event) - if not wfID: return + if wfID is None: return station = self.getStationName(wfID) self.updateStatus('picking on station {0}'.format(station)) From be326cba7b5130c10b87884063c0578acdae52a8 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 10 Jun 2016 09:03:16 +0200 Subject: [PATCH 0954/1144] [bugfix] automatic data with the S-P time saved in the pickdict caused Exception in the ProbabilityDensityFunction constructor --- pylot/core/pick/compare.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index ed769fc9..63a20192 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -270,6 +270,8 @@ class PDFDictionary(object): for station, phases in pdf_picks.items(): for phase, values in phases.items(): + if phase not in 'PS': + continue phases[phase] = ProbabilityDensityFunction.from_pick( values['epp'], values['mpp'], From 280f8544e43a4d43bf97a0f267edf65e260e6e77 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Fri, 10 Jun 2016 09:04:10 +0200 Subject: [PATCH 0955/1144] [prepare] starting implementation of a histogram overview plot from pick comparison dialog --- pylot/core/util/widgets.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1b8631a4..8261ec26 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -205,7 +205,7 @@ class ComparisonDialog(QDialog): pdf.expectation() _axes.plot(x, y) _axes.set_title(phase) - _axes.set_ylabel('propability density (qual.)') + _axes.set_ylabel('propability density [-]') _axes.set_xlabel('time difference [s]') annotation = "{phase} difference on {station}\n" \ "expectation: {exp}\n" \ @@ -217,6 +217,38 @@ class ComparisonDialog(QDialog): _anno.set_bbox(bbox_props) self.canvas.draw() + def plothist(self): + self.canvas = PlotWidget(self) + _axPstd, _axPexp = self.canvas.figure.add_subplot(221), self.canvas.figure.add_subplot(223) + _axSstd, _axSexp = self.canvas.figure.add_subplot(222), self.canvas.figure.add_subplot(224) + axes_dict = dict(P=dict(std=_axPstd, exp=_axPexp), + S=dict(std=_axSstd, exp=_axSexp)) + bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) + for phase in self.phases: + std = self.data.get_std_array(phase) + stdxlims = [0., 1.2 * max(std)] + exp = self.data.get_expectation_array(phase) + eps_exp = 0.05 * (max(exp) - min(exp)) + expxlims = [min(exp) - eps_exp, max(exp) + eps_exp] + axes_dict[phase][std].hist(std, range=stdxlims, bins=20, normed=False) + axes_dict[phase][exp].hist(std, range=expxlims, bins=20, normed=False) + std_annotation = "Distribution curve for {phase} differences'\n" \ + "standard deviations (all stations)\n" \ + "number of samples: {nsamples}".format(phase=phase, nsamples=len(std)) + _anno_std = axes_dict[phase][std].annotate(std_annotation, xy=(.05, .8), xycoords='axes fraction') + _anno_std.set_bbox(bbox_props) + exp_annotation = "Distribution curve for {phase} differences'\n" \ + "expectations (all stations)\n" \ + "number of samples: {nsamples}".format(phase=phase, nsamples=len(exp)) + _anno_exp = axes_dict[phase][exp].annotate(exp_annotation, xy=(.05, .8), xycoords='axes fraction') + _anno_exp.set_bbox(bbox_props) + axes_dict[phase]['exp'].set_xlabel('expectation [s]') + axes_dict[phase]['std'].set_xlabel('standard deviation [s]') + + for ax in axes_dict['P'].values(): + ax.set_ylabel('frequency [-]') + + self.canvas.draw() class PlotWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): From 467f0ae79b15440404bd41764c334702da32f832 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Fri, 10 Jun 2016 14:37:33 +0200 Subject: [PATCH 0956/1144] [new] histogram plot added; debugging in progress --- pylot/core/pick/compare.py | 12 +++++ pylot/core/util/widgets.py | 92 +++++++++++++++++++++++--------------- 2 files changed, 68 insertions(+), 36 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 63a20192..8c64db05 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -140,6 +140,18 @@ class Comparison(object): plt.show() + def get_array(self, phase, method): + pdf_dict = self.comparison + exp_array = list() + for station, phases in pdf_dict.items(): + try: + exp_array.append(phases[phase].expectation()) + except KeyError as e: + print('{err_msg}; station = {station}, phase = {phase}'.format( + err_msg=str(e), station=station, phase=phase)) + continue + return exp_array + def get_expectation_array(self, phase): pdf_dict = self.comparison exp_array = list() diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8261ec26..12ebbbca 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -17,11 +17,11 @@ except ImportError: from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT from matplotlib.widgets import MultiCursor -from PySide.QtGui import QAction, QApplication, QComboBox, QDateTimeEdit, \ - QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout, \ - QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, QPixmap, QSpinBox, \ - QTabWidget, QToolBar, QVBoxLayout, QWidget, QPushButton, QFileDialog, \ - QInputDialog +from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \ + QDateTimeEdit, QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, \ + QGridLayout, QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, \ + QPixmap, QSpinBox, QTabWidget, QToolBar, QVBoxLayout, QWidget, \ + QPushButton, QFileDialog, QInputDialog from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime @@ -71,7 +71,8 @@ class ComparisonDialog(QDialog): self._stats = c.stations self._canvas = PlotWidget(self) self._widgets = dict(stationsComboBox=None, - phasesComboBox=None) + phasesComboBox=None, + histCheckBox=None) self._phases = 'PS' self._plotprops = dict(station=self.stations[0], phase=self.phases[0]) super(ComparisonDialog, self).__init__(parent) @@ -97,9 +98,15 @@ class ComparisonDialog(QDialog): _phases_combobox.currentIndexChanged.connect(self.prepareplot) self.widgets = _phases_combobox + _hist_checkbox = QCheckBox('Show histograms', self) + _hist_checkbox.setObjectName('histCheckBox') + _hist_checkbox.stateChanged.connect(self.plothist) + self.widgets = _hist_checkbox + _toolbar = QToolBar(self) _toolbar.addWidget(_stats_combobox) _toolbar.addWidget(_phases_combobox) + _toolbar.addWidget(_hist_checkbox) _buttonbox = QDialogButtonBox(QDialogButtonBox.Close) @@ -170,7 +177,7 @@ class ComparisonDialog(QDialog): @widgets.setter def widgets(self, widget): - name = widget.objectName + name = widget.objectName() if name in self.widgets.keys(): self._widgets[name] = widget @@ -218,37 +225,50 @@ class ComparisonDialog(QDialog): self.canvas.draw() def plothist(self): - self.canvas = PlotWidget(self) - _axPstd, _axPexp = self.canvas.figure.add_subplot(221), self.canvas.figure.add_subplot(223) - _axSstd, _axSexp = self.canvas.figure.add_subplot(222), self.canvas.figure.add_subplot(224) - axes_dict = dict(P=dict(std=_axPstd, exp=_axPexp), - S=dict(std=_axSstd, exp=_axSexp)) - bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) - for phase in self.phases: - std = self.data.get_std_array(phase) - stdxlims = [0., 1.2 * max(std)] - exp = self.data.get_expectation_array(phase) - eps_exp = 0.05 * (max(exp) - min(exp)) - expxlims = [min(exp) - eps_exp, max(exp) + eps_exp] - axes_dict[phase][std].hist(std, range=stdxlims, bins=20, normed=False) - axes_dict[phase][exp].hist(std, range=expxlims, bins=20, normed=False) - std_annotation = "Distribution curve for {phase} differences'\n" \ - "standard deviations (all stations)\n" \ - "number of samples: {nsamples}".format(phase=phase, nsamples=len(std)) - _anno_std = axes_dict[phase][std].annotate(std_annotation, xy=(.05, .8), xycoords='axes fraction') - _anno_std.set_bbox(bbox_props) - exp_annotation = "Distribution curve for {phase} differences'\n" \ - "expectations (all stations)\n" \ - "number of samples: {nsamples}".format(phase=phase, nsamples=len(exp)) - _anno_exp = axes_dict[phase][exp].annotate(exp_annotation, xy=(.05, .8), xycoords='axes fraction') - _anno_exp.set_bbox(bbox_props) - axes_dict[phase]['exp'].set_xlabel('expectation [s]') - axes_dict[phase]['std'].set_xlabel('standard deviation [s]') + name = self.sender().objectName() + if self.widgets[name].isChecked(): + for wname, widget in self.widgets.items(): + if wname != name: + self.widgets[wname].setEnabled(False) + self.canvas.figure.clf() + _axPstd, _axPexp = self.canvas.figure.add_subplot(221), self.canvas.figure.add_subplot(223) + _axSstd, _axSexp = self.canvas.figure.add_subplot(222), self.canvas.figure.add_subplot(224) + axes_dict = dict(P=dict(std=_axPstd, exp=_axPexp), + S=dict(std=_axSstd, exp=_axSexp)) + bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) + for phase in self.phases: + std = self.data.get_std_array(phase) + stdxlims = [0., 1.2 * max(std)] + exp = self.data.get_expectation_array(phase) + eps_exp = 0.05 * (max(exp) - min(exp)) + expxlims = [min(exp) - eps_exp, max(exp) + eps_exp] + axes_dict[phase]['std'].hist(std, range=stdxlims, bins=20, normed=False) + axes_dict[phase]['exp'].hist(exp, range=expxlims, bins=20, + normed=False) + std_annotation = "Distribution curve for {phase} differences'\n" \ + "standard deviations (all stations)\n" \ + "number of samples: {nsamples}".format(phase=phase, nsamples=len(std)) + _anno_std = axes_dict[phase]['std'].annotate(std_annotation, xy=(.05, .8), xycoords='axes fraction') + _anno_std.set_bbox(bbox_props) + exp_annotation = "Distribution curve for {phase} differences'\n" \ + "expectations (all stations)\n" \ + "number of samples: {nsamples}".format(phase=phase, nsamples=len(exp)) + _anno_exp = axes_dict[phase]['exp'].annotate(exp_annotation, xy=(.05, .8), xycoords='axes fraction') + _anno_exp.set_bbox(bbox_props) + axes_dict[phase]['exp'].set_xlabel('expectation [s]') + axes_dict[phase]['std'].set_xlabel('standard deviation [s]') - for ax in axes_dict['P'].values(): - ax.set_ylabel('frequency [-]') + for ax in axes_dict['P'].values(): + ax.set_ylabel('frequency [-]') + + self.canvas.draw() + else: + for wname, widget in self.widgets.items(): + if wname != name: + self.widgets[wname].setEnabled(True) + self.canvas.figure.clf() + self.plotcomparison() - self.canvas.draw() class PlotWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): From 3568a8a59a613357db775db89402006232a8cb86 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Sat, 11 Jun 2016 08:03:16 +0200 Subject: [PATCH 0957/1144] [bugfix] changed the way the the expectation and the std array are calculated; not sure how to deal with values 'inf' --- pylot/core/pick/compare.py | 43 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 8c64db05..2d7b55da 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -140,41 +140,28 @@ class Comparison(object): plt.show() - def get_array(self, phase, method): + def get_all(self, phasename): pdf_dict = self.comparison - exp_array = list() - for station, phases in pdf_dict.items(): + rlist = list() + for phases in pdf_dict.values(): try: - exp_array.append(phases[phase].expectation()) - except KeyError as e: - print('{err_msg}; station = {station}, phase = {phase}'.format( - err_msg=str(e), station=station, phase=phase)) + rlist.append(phases[phasename]) + except KeyError: continue - return exp_array + return rlist + + def get_array(self, phase, method_name): + import operator + method = operator.methodcaller(method_name) + pdf_list = self.get_all(phase) + rarray = map(method, pdf_list) + return rarray def get_expectation_array(self, phase): - pdf_dict = self.comparison - exp_array = list() - for station, phases in pdf_dict.items(): - try: - exp_array.append(phases[phase].expectation()) - except KeyError as e: - print('{err_msg}; station = {station}, phase = {phase}'.format( - err_msg=str(e), station=station, phase=phase)) - continue - return exp_array + return self.get_array(phase, 'expectation') def get_std_array(self, phase): - pdf_dict = self.comparison - std_array = list() - for station, phases in pdf_dict.items(): - try: - std_array.append(phases[phase].standard_deviation()) - except KeyError as e: - print('{err_msg}; station = {station}, phase = {phase}'.format( - err_msg=str(e), station=station, phase=phase)) - continue - return std_array + return self.get_array(phase, 'standard_deviation') def hist_expectation(self, phases='all', bins=20, normed=False): phases.strip() From 79f0982558279cdc1992c2143e68b16b528340b8 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 13 Jun 2016 11:27:16 +0200 Subject: [PATCH 0958/1144] [bugfix] use only vaild values for plotting --- pylot/core/pick/compare.py | 2 +- pylot/core/util/widgets.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 2d7b55da..2718a39f 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -155,7 +155,7 @@ class Comparison(object): method = operator.methodcaller(method_name) pdf_list = self.get_all(phase) rarray = map(method, pdf_list) - return rarray + return np.array(rarray) def get_expectation_array(self, phase): return self.get_array(phase, 'expectation') diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 12ebbbca..71e668b6 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -238,8 +238,10 @@ class ComparisonDialog(QDialog): bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) for phase in self.phases: std = self.data.get_std_array(phase) + std = std[np.isfinite(std)] stdxlims = [0., 1.2 * max(std)] exp = self.data.get_expectation_array(phase) + exp = exp[np.isfinite(exp)] eps_exp = 0.05 * (max(exp) - min(exp)) expxlims = [min(exp) - eps_exp, max(exp) + eps_exp] axes_dict[phase]['std'].hist(std, range=stdxlims, bins=20, normed=False) From 3cee0e6df945272cbe00cb8e5be47aad9b3e345d Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 13 Jun 2016 14:23:42 +0200 Subject: [PATCH 0959/1144] [change] added plots for debugging --- pylot/core/pick/compare.py | 3 +++ pylot/core/util/widgets.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 2718a39f..b39f8ef8 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -223,6 +223,9 @@ class PDFDictionary(object): else: return True + def __getitem__(self, item): + return self.pdf_data[item] + @property def pdf_data(self): return self._pdfdata diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 71e668b6..5548746b 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -203,7 +203,14 @@ class ComparisonDialog(QDialog): self.plotcomparison() def plotcomparison(self): + from matplotlib import gridspec _axes = self.canvas.figure.add_subplot(111) + + _gs = gridspec.GridSpec(3, 2) + _axes = self.canvas.figure.add_subplot(_gs[0:2, :]) + _ax1 = self.canvas.figure.add_subplot(_gs[2, 0]) + _ax2 = self.canvas.figure.add_subplot(_gs[2, 1]) + _axes.cla() station = self.plotprops['station'] phase = self.plotprops['phase'] @@ -222,6 +229,22 @@ class ComparisonDialog(QDialog): 'fraction') bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) _anno.set_bbox(bbox_props) + + pdf_a = self.data.get('auto')[station][phase] + pdf_m = self.data.get('manu')[station][phase] + xauto, yauto, stdauto, expauto = pdf_a.axis, pdf_a.data, \ + pdf_a.standard_deviation(), \ + pdf_a.expectation() + xmanu, ymanu, stdmanu, expmanu = pdf_m.axis, pdf_m.data, \ + pdf_m.standard_deviation(), \ + pdf_m.expectation() + + _ax1.plot(xauto, yauto) + + _ax2.plot(xmanu, ymanu) + + _gs.update(wspace=0.5, hspace=0.5) + self.canvas.draw() def plothist(self): From 69b5b410f0d2ea08f018d53909452b8dc53e1b2e Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 13 Jun 2016 14:35:29 +0200 Subject: [PATCH 0960/1144] minor changes for GUI --- pylot/core/active/ActiveSeismoPick3D_GUI | 245 +++++++++++++++++++++++ pylot/core/active/activeSeismoPick.py | 25 ++- pylot/core/active/control_script.py | 2 +- 3 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 pylot/core/active/ActiveSeismoPick3D_GUI diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI b/pylot/core/active/ActiveSeismoPick3D_GUI new file mode 100644 index 00000000..a809f5fc --- /dev/null +++ b/pylot/core/active/ActiveSeismoPick3D_GUI @@ -0,0 +1,245 @@ +import os +from PySide import QtCore, QtGui, QtCore +from asp3d_designer_layout import * +from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils + +class gui_control(object): + def __init__(self): + self.mainwindow = MainWindow + self.mainUI = ui + self.connectButtons() + self.survey = None + + def connectButtons(self): + QtCore.QObject.connect(self.mainUI.gen_new_survey, QtCore.SIGNAL("clicked()"), self.gen_survey) + QtCore.QObject.connect(self.mainUI.load_survey, QtCore.SIGNAL("clicked()"), self.load_survey) + QtCore.QObject.connect(self.mainUI.save_survey, QtCore.SIGNAL("clicked()"), self.save_survey) + QtCore.QObject.connect(self.mainUI.picker, QtCore.SIGNAL("clicked()"), self.callPicker) + QtCore.QObject.connect(self.mainUI.postprocessing, QtCore.SIGNAL("clicked()"), self.postprocessing) + QtCore.QObject.connect(self.mainUI.fmtomo, QtCore.SIGNAL("clicked()"), self.startFMTOMO) + + def gen_survey(self): + if self.checkSurveyState(): + if not self.continueSurveyDialog(): + return + qdialog = QtGui.QDialog(self.mainwindow) + ui = Ui_Dialog() + ui.setupUi(qdialog) + self.gen_new_survey = ui + self.connectButtons_gen_survey() + if qdialog.exec_(): + srcfile = self.gen_new_survey.lineEdit_src.text() + recfile = self.gen_new_survey.lineEdit_rec.text() + obsdir = self.gen_new_survey.lineEdit_obs.text() + self.survey = activeSeismoPick.Survey(obsdir, srcfile, recfile, + useDefaultParas = True) + self.survey.setArtificialPick(0, 0) # artificial pick at source origin + surveyUtils.setDynamicFittedSNR(self.survey.getShotDict()) + self.setSurveyState('active') + + def getPickParameters(self, ui, Picking_parameters): + if Picking_parameters.exec_(): + ncores = int(ui.lineEdit_ncores.text()) + vmin = float(ui.lineEdit_vmin.text()) + vmax = float(ui.lineEdit_vmax.text()) + folm = float(ui.lineEdit_folm.text()) + AIC = ui.checkBox.isChecked() + aicwindow = [int(val) for val in ui.lineEdit_aicwindow.text().split(',')] + return ncores, vmin, vmax, folm, AIC, tuple(aicwindow) + + def callPicker(self): + if self.survey is None: + print('Survey not defined.') + return + Picking_parameters = QtGui.QDialog(self.mainwindow) + ui = Ui_Picking_parameters() + ui.setupUi(Picking_parameters) + try: + ncores, vmin, vmax, folm, AIC, aicwindow = self.getPickParameters(ui, Picking_parameters) + except TypeError: + return + + if AIC == True: + HosAic = 'aic' + else: + HosAic = 'hos' + + self.survey.pickAllShots(vmin = vmin, vmax = vmax, + folm = folm, HosAic = HosAic, + aicwindow = aicwindow, cores = ncores) + self.setPickState('active') + + def startFMTOMO(self): + if self.survey is None: + print('Survey not defined.') + return + fmtomo_parameters = QtGui.QDialog(self.mainwindow) + ui = Ui_fmtomo_parameters() + ui.setupUi(fmtomo_parameters) + self.fmtomo_parameters_ui = ui + self.connectButtons_startFMTOMO() + self.getFMTOMOparameters(ui, fmtomo_parameters) + + def getFMTOMOparameters(self, ui, fmtomo_parameters): + if fmtomo_parameters.exec_(): + fmtomo_dir = ui.fmtomo_dir.text() + nIter = int(ui.nIter.value()) + nproc = int(ui.nproc.text()) + btop = float(ui.btop.text()) + bbot = float(ui.bbot.text()) + propgrid = (int(ui.pgrid_x.text()), int(ui.pgrid_y.text()), int(ui.pgrid_z.text())) + vgrid = (int(ui.invgrid_x.text()), int(ui.invgrid_y.text()), int(ui.invgrid_z.text())) + cushionfactor = float(ui.cushion.text())/100. + customgrid = ui.customgrid.text() + simuldir = ui.simuldir.text() + picks_dir = os.path.join(simuldir, 'picks') + seisarray_loc = ui.seisarray.text() + + if not os.path.isdir(picks_dir): + err = os.mkdir(picks_dir) + + self.survey.exportFMTOMO(picks_dir) + self.survey.loadArrayFromPickle(seisarray_loc) + + cwd = os.getcwd() + interpolationMethod = 'linear' + os.chdir(simuldir) + self.survey.seisArray.generateFMTOMOinputFromArray(propgrid, vgrid, (bbot, btop), cushionfactor, + interpolationMethod, customgrid = customgrid, + writeVTK = False) + os.chdir(cwd) + + tomo = fmtomoUtils.Tomo3d(fmtomo_dir, simuldir) + tomo.runTOMO3D(nproc, nIter) + + def connectButtons_startFMTOMO(self): + QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_tomodir, QtCore.SIGNAL("clicked()"), self.chooseFMTOMOdir) + QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_customgrid, QtCore.SIGNAL("clicked()"), self.chooseCustomgrid) + QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_simuldir, QtCore.SIGNAL("clicked()"), self.chooseSimuldir) + QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_seisarray, QtCore.SIGNAL("clicked()"), self.chooseSeisarray) + + def chooseFMTOMOdir(self): + self.fmtomo_parameters_ui.fmtomo_dir.setText(self.browseDir()) + + def chooseCustomgrid(self): + self.fmtomo_parameters_ui.customgrid.setText(self.browseFile()) + + def chooseSimuldir(self): + self.fmtomo_parameters_ui.simuldir.setText(self.browseDir()) + + def chooseSeisarray(self): + self.fmtomo_parameters_ui.seisarray.setText(self.browseFile()) + + def postprocessing(self): + if self.survey is None: + print('Survey not defined.') + return + self.survey.plotAllPicks() + + def load_survey(self): + if self.checkSurveyState(): + if not self.continueSurveyDialog(): + return + filename = self.browseFile() + if filename is None: + return + self.survey = activeSeismoPick.Survey.from_pickle(filename) + self.setSurveyState('active') + if self.survey.picked: + self.setPickState('active') + + def save_survey(self): + if not self.checkSurveyState(): + print('No Survey defined.') + return + if self.checkSurveyState: + filename = self.browseFile() + if filename is None: + return + self.survey.saveSurvey(filename) + else: + print('No active Survey.') + + def setSurveyState(self, state): + if state == 'active': + self.mainUI.survey_active.setCheckable(True) + self.mainUI.survey_active.setChecked(True) + self.mainUI.survey_active.setCheckable(True) + elif state == 'inactive': + self.mainUI.survey_active.setCheckable(True) + self.mainUI.survey_active.setChecked(False) + self.mainUI.survey_active.setCheckable(True) + + def checkSurveyState(self): + if self.mainUI.survey_active.checkState() == QtCore.Qt.CheckState.Checked: + return True + if self.mainUI.survey_active.checkState() == QtCore.Qt.CheckState.Unchecked: + return False + + def setPickState(self, state): + if state == 'active' and self.checkSurveyState(): + self.mainUI.picked_active.setCheckable(True) + self.mainUI.picked_active.setChecked(True) + self.mainUI.picked_active.setCheckable(True) + elif self.checkSurveyState() is False: + print('No Survey defined.') + return + elif state == 'inactive': + self.mainUI.picked_active.setCheckable(True) + self.mainUI.picked_active.setChecked(False) + self.mainUI.picked_active.setCheckable(True) + + def checkPickState(self): + if self.mainUI.picked_active.checkState() == QtCore.Qt.CheckState.Checked: + return True + if self.mainUI.picked_active.checkState() == QtCore.Qt.CheckState.Unchecked: + return False + + def continueSurveyDialog(self): + qmb = QtGui.QMessageBox() + qmb.setText('Survey already exists. Overwrite?') + qmb.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) + qmb.setIcon(QtGui.QMessageBox.Warning) + answer = qmb.exec_() + if answer == 1024: + return True + else: + return False + + def connectButtons_gen_survey(self): + QtCore.QObject.connect(self.gen_new_survey.pushButton_rec, QtCore.SIGNAL("clicked()"), self.chooseReceiverfile) + QtCore.QObject.connect(self.gen_new_survey.pushButton_src, QtCore.SIGNAL("clicked()"), self.chooseSourcefile) + QtCore.QObject.connect(self.gen_new_survey.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseObsdir) + + def chooseSourcefile(self): + self.gen_new_survey.lineEdit_src.setText(self.browseFile()) + + def chooseReceiverfile(self): + self.gen_new_survey.lineEdit_rec.setText(self.browseFile()) + + def chooseObsdir(self): + self.gen_new_survey.lineEdit_obs.setText(self.browseDir()) + + def browseFile(self): + dialog = QtGui.QFileDialog() + filename = dialog.getOpenFileName() + if len(filename[0]) > 0: + return filename[0] + + def browseDir(self): + dialog = QtGui.QFileDialog() + directory = dialog.getExistingDirectory() + if len(directory) > 0: + return directory + +if __name__ == "__main__": + import sys + app = QtGui.QApplication(sys.argv) + MainWindow = QtGui.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + gui = gui_control() + sys.exit(app.exec_()) + + diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 388690bd..33736841 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -6,7 +6,7 @@ from pylot.core.active import seismicshot from pylot.core.active.surveyUtils import cleanUp from pylot.core.util.utils import worker, _pickle_method -def ppick(shot): +def picker(shot): picks = [] for traceID in shot.getTraceIDlist(): picks.append((shot.getShotnumber(), traceID, shot.pickTrace(traceID))) @@ -24,6 +24,7 @@ class Survey(object): creating plots for all shots. ''' self.data = {} + self.seisArray = None self._topography = None self._recfile = receiverfile self._sourcefile = sourcefile @@ -34,6 +35,7 @@ class Survey(object): self.setParametersForAllShots() self._removeAllEmptyTraces() self._updateShots() + self.picked = False def _initiate_fnames(self): for shot in self.data.values(): @@ -118,6 +120,11 @@ class Survey(object): % (cutwindow, tmovwind, tsignal, tgap)) + def loadArrayFromPickle(self, filename): + from pylot.core.active.seismicArrayPreparation import SeisArray + array = SeisArray.from_pickle(filename) + self.seisArray = array + def loadArray(self, path, receiverfile, sourcefile): from pylot.core.active.seismicArrayPreparation import SeisArray @@ -246,7 +253,16 @@ class Survey(object): shotlist.append(shot) print('pickAllShots: Starting to pick...') - picks = worker(ppick, shotlist, cores) + if cores > 1: + print('Picking parallel on %s cores.'%cores) + picks = worker(picker, shotlist, cores) + elif cores == 1: + print('Picking serial on one core.') + picks = [] + for shot in shotlist: + picks.append(picker(shot)) + else: + raise ValueError('cores must be >= 1') print('Done!') for shot in picks: @@ -270,6 +286,7 @@ class Survey(object): print('Picked %s / %s traces (%d %%)\n' % (pickedtraces, ntraces, float(pickedtraces) / float(ntraces) * 100.)) + self.picked = True def filterSNR(self): print('Starting filterSNR...') @@ -674,4 +691,6 @@ class Survey(object): def from_pickle(filename): import cPickle infile = open(filename, 'rb') - return cPickle.load(infile) + survey = cPickle.load(infile) + print('Loaded %s'%filename) + return survey diff --git a/pylot/core/active/control_script.py b/pylot/core/active/control_script.py index 421efcf0..0b7202e1 100755 --- a/pylot/core/active/control_script.py +++ b/pylot/core/active/control_script.py @@ -26,7 +26,7 @@ folm = 0.6 # fraction of local maximum for threshold pic tsignal = 0.03 tgap = 0.0007 -nproc = 16 +nproc = 1 vmin = 333 vmax = 5500 From 410b3c2f5532fd45361a62a7c181662b3794d7f6 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 13 Jun 2016 14:37:12 +0200 Subject: [PATCH 0961/1144] added GUI --- ...moPick3D_GUI => ActiveSeismoPick3D_GUI.py} | 0 pylot/core/active/asp3d_layout.py | 491 ++++++++++++++++++ 2 files changed, 491 insertions(+) rename pylot/core/active/{ActiveSeismoPick3D_GUI => ActiveSeismoPick3D_GUI.py} (100%) create mode 100644 pylot/core/active/asp3d_layout.py diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI b/pylot/core/active/ActiveSeismoPick3D_GUI.py similarity index 100% rename from pylot/core/active/ActiveSeismoPick3D_GUI rename to pylot/core/active/ActiveSeismoPick3D_GUI.py diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py new file mode 100644 index 00000000..e10f04be --- /dev/null +++ b/pylot/core/active/asp3d_layout.py @@ -0,0 +1,491 @@ +from PySide import QtCore, QtGui + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("ActiveSeismoPick3D") + MainWindow.resize(252, 350) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) + MainWindow.setSizePolicy(sizePolicy) + MainWindow.setMinimumSize(QtCore.QSize(252, 350)) + MainWindow.setMaximumSize(QtCore.QSize(252, 350)) + MainWindow.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) + self.centralwidget = QtGui.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.postprocessing = QtGui.QPushButton(self.centralwidget) + self.postprocessing.setGeometry(QtCore.QRect(0, 220, 251, 27)) + self.postprocessing.setObjectName("postprocessing") + self.verticalLayoutWidget = QtGui.QWidget(self.centralwidget) + self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 0, 231, 41)) + self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") + self.horizontalLayout = QtGui.QHBoxLayout(self.verticalLayoutWidget) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.survey_active = QtGui.QCheckBox(self.verticalLayoutWidget) + self.survey_active.setEnabled(True) + self.survey_active.setMouseTracking(False) + self.survey_active.setStatusTip("") + self.survey_active.setWhatsThis("") + self.survey_active.setAccessibleName("") + self.survey_active.setAccessibleDescription("") + self.survey_active.setCheckable(False) + self.survey_active.setObjectName("survey_active") + self.horizontalLayout.addWidget(self.survey_active) + self.picked_active = QtGui.QCheckBox(self.verticalLayoutWidget) + self.picked_active.setEnabled(True) + self.picked_active.setMouseTracking(False) + self.picked_active.setStatusTip("") + self.picked_active.setWhatsThis("") + self.picked_active.setAccessibleName("") + self.picked_active.setAccessibleDescription("") + self.picked_active.setCheckable(False) + self.picked_active.setObjectName("picked_active") + self.horizontalLayout.addWidget(self.picked_active) + self.gen_new_survey = QtGui.QPushButton(self.centralwidget) + self.gen_new_survey.setGeometry(QtCore.QRect(1, 47, 251, 27)) + self.gen_new_survey.setObjectName("gen_new_survey") + self.line = QtGui.QFrame(self.centralwidget) + self.line.setGeometry(QtCore.QRect(1, 160, 249, 16)) + self.line.setFrameShape(QtGui.QFrame.HLine) + self.line.setFrameShadow(QtGui.QFrame.Sunken) + self.line.setObjectName("line") + self.load_survey = QtGui.QPushButton(self.centralwidget) + self.load_survey.setGeometry(QtCore.QRect(1, 86, 251, 27)) + self.load_survey.setObjectName("load_survey") + self.save_survey = QtGui.QPushButton(self.centralwidget) + self.save_survey.setGeometry(QtCore.QRect(1, 125, 251, 27)) + self.save_survey.setObjectName("save_survey") + self.picker = QtGui.QPushButton(self.centralwidget) + self.picker.setGeometry(QtCore.QRect(1, 179, 251, 27)) + self.picker.setObjectName("picker") + self.line_2 = QtGui.QFrame(self.centralwidget) + self.line_2.setGeometry(QtCore.QRect(1, 250, 249, 16)) + self.line_2.setFrameShape(QtGui.QFrame.HLine) + self.line_2.setFrameShadow(QtGui.QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.fmtomo = QtGui.QPushButton(self.centralwidget) + self.fmtomo.setGeometry(QtCore.QRect(1, 270, 251, 27)) + self.fmtomo.setObjectName("fmtomo") + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtGui.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 252, 23)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtGui.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) + self.postprocessing.setText(QtGui.QApplication.translate("MainWindow", "Postprocessing", None, QtGui.QApplication.UnicodeUTF8)) + self.survey_active.setText(QtGui.QApplication.translate("MainWindow", "Survey active", None, QtGui.QApplication.UnicodeUTF8)) + self.picked_active.setText(QtGui.QApplication.translate("MainWindow", "picked", None, QtGui.QApplication.UnicodeUTF8)) + self.gen_new_survey.setText(QtGui.QApplication.translate("MainWindow", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.load_survey.setText(QtGui.QApplication.translate("MainWindow", "Load Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.save_survey.setText(QtGui.QApplication.translate("MainWindow", "Save Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.picker.setText(QtGui.QApplication.translate("MainWindow", "Autopicker", None, QtGui.QApplication.UnicodeUTF8)) + self.fmtomo.setText(QtGui.QApplication.translate("MainWindow", "FMTOMO Simulation", None, QtGui.QApplication.UnicodeUTF8)) + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(380, 160) + self.verticalLayout = QtGui.QVBoxLayout(Dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.lineEdit_rec = QtGui.QLineEdit(Dialog) + self.lineEdit_rec.setObjectName("lineEdit_rec") + self.gridLayout.addWidget(self.lineEdit_rec, 0, 1, 1, 1) + self.pushButton_rec = QtGui.QPushButton(Dialog) + self.pushButton_rec.setObjectName("pushButton_rec") + self.gridLayout.addWidget(self.pushButton_rec, 0, 2, 1, 1) + self.label_rec = QtGui.QLabel(Dialog) + self.label_rec.setObjectName("label_rec") + self.gridLayout.addWidget(self.label_rec, 0, 0, 1, 1) + self.lineEdit_obs = QtGui.QLineEdit(Dialog) + self.lineEdit_obs.setObjectName("lineEdit_obs") + self.gridLayout.addWidget(self.lineEdit_obs, 2, 1, 1, 1) + self.label_obs = QtGui.QLabel(Dialog) + self.label_obs.setObjectName("label_obs") + self.gridLayout.addWidget(self.label_obs, 2, 0, 1, 1) + self.pushButton_obs = QtGui.QPushButton(Dialog) + self.pushButton_obs.setObjectName("pushButton_obs") + self.gridLayout.addWidget(self.pushButton_obs, 2, 2, 1, 1) + self.label_src = QtGui.QLabel(Dialog) + self.label_src.setObjectName("label_src") + self.gridLayout.addWidget(self.label_src, 1, 0, 1, 1) + self.lineEdit_src = QtGui.QLineEdit(Dialog) + self.lineEdit_src.setObjectName("lineEdit_src") + self.gridLayout.addWidget(self.lineEdit_src, 1, 1, 1, 1) + self.pushButton_src = QtGui.QPushButton(Dialog) + self.pushButton_src.setObjectName("pushButton_src") + self.gridLayout.addWidget(self.pushButton_src, 1, 2, 1, 1) + self.verticalLayout.addLayout(self.gridLayout) + self.buttonBox = QtGui.QDialogButtonBox(Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(Dialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_rec.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_rec.setText(QtGui.QApplication.translate("Dialog", "Receiver\n" +"File", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setText(QtGui.QApplication.translate("Dialog", "Seismogram\n" +"Directory", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_obs.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setText(QtGui.QApplication.translate("Dialog", "Source\n" +"File", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_src.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + +class Ui_Picking_parameters(object): + def setupUi(self, Picking_parameters): + Picking_parameters.setObjectName("Picking_parameters") + Picking_parameters.resize(300, 300) + self.gridLayoutWidget = QtGui.QWidget(Picking_parameters) + self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 20, 250, 211)) + self.gridLayoutWidget.setObjectName("gridLayoutWidget") + self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.label_cores = QtGui.QLabel(self.gridLayoutWidget) + self.label_cores.setObjectName("label_cores") + self.gridLayout.addWidget(self.label_cores, 0, 0, 1, 1) + self.label_vmax = QtGui.QLabel(self.gridLayoutWidget) + self.label_vmax.setObjectName("label_vmax") + self.gridLayout.addWidget(self.label_vmax, 2, 0, 1, 1) + self.label_vmin = QtGui.QLabel(self.gridLayoutWidget) + self.label_vmin.setObjectName("label_vmin") + self.gridLayout.addWidget(self.label_vmin, 1, 0, 1, 1) + self.lineEdit_ncores = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_ncores.setObjectName("lineEdit_ncores") + self.gridLayout.addWidget(self.lineEdit_ncores, 0, 1, 1, 1) + self.lineEdit_vmin = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_vmin.setObjectName("lineEdit_vmin") + self.gridLayout.addWidget(self.lineEdit_vmin, 1, 1, 1, 1) + self.lineEdit_vmax = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_vmax.setObjectName("lineEdit_vmax") + self.gridLayout.addWidget(self.lineEdit_vmax, 2, 1, 1, 1) + self.checkBox = QtGui.QCheckBox(self.gridLayoutWidget) + self.checkBox.setObjectName("checkBox") + self.gridLayout.addWidget(self.checkBox, 4, 1, 1, 1) + self.label_folm = QtGui.QLabel(self.gridLayoutWidget) + self.label_folm.setObjectName("label_folm") + self.gridLayout.addWidget(self.label_folm, 3, 0, 1, 1) + self.label_aic = QtGui.QLabel(self.gridLayoutWidget) + self.label_aic.setObjectName("label_aic") + self.gridLayout.addWidget(self.label_aic, 4, 0, 1, 1) + self.lineEdit_folm = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_folm.setObjectName("lineEdit_folm") + self.gridLayout.addWidget(self.lineEdit_folm, 3, 1, 1, 1) + self.label_aicwindow = QtGui.QLabel(self.gridLayoutWidget) + self.label_aicwindow.setObjectName("label_aicwindow") + self.gridLayout.addWidget(self.label_aicwindow, 5, 0, 1, 1) + self.lineEdit_aicwindow = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_aicwindow.setObjectName("lineEdit_aicwindow") + self.gridLayout.addWidget(self.lineEdit_aicwindow, 5, 1, 1, 1) + self.buttonBox = QtGui.QDialogButtonBox(Picking_parameters) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.buttonBox.setGeometry(QtCore.QRect(10, 240, 250, 32)) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Picking_parameters.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Picking_parameters.reject) + self.retranslateUi(Picking_parameters) + QtCore.QMetaObject.connectSlotsByName(Picking_parameters) + + def retranslateUi(self, Picking_parameters): + Picking_parameters.setWindowTitle(QtGui.QApplication.translate("Picking_parameters", "Picking_parameters", None, QtGui.QApplication.UnicodeUTF8)) + self.label_cores.setText(QtGui.QApplication.translate("Picking_parameters", "Number of cores", None, QtGui.QApplication.UnicodeUTF8)) + self.label_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "Vmax (default = 5000m/s)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "Vmin (default = 333 m/s)", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_ncores.setText(QtGui.QApplication.translate("Picking_parameters", "1", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "333", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) + self.label_folm.setText(QtGui.QApplication.translate("Picking_parameters", "Fraction of local maximum\n" +"(default = 0.6)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_aic.setText(QtGui.QApplication.translate("Picking_parameters", "AIC", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_folm.setText(QtGui.QApplication.translate("Picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) + self.label_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "AIC window (only if AIC\n" +" is checked)", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "15, 0", None, QtGui.QApplication.UnicodeUTF8)) + +class Ui_fmtomo_parameters(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(320, 520) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) + Dialog.setSizePolicy(sizePolicy) + Dialog.setMinimumSize(QtCore.QSize(320, 520)) + Dialog.setMaximumSize(QtCore.QSize(320, 520)) + self.buttonBox = QtGui.QDialogButtonBox(Dialog) + self.buttonBox.setGeometry(QtCore.QRect(10, 480, 301, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayoutWidget = QtGui.QWidget(Dialog) + self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 240, 281, 79)) + self.gridLayoutWidget.setObjectName("gridLayoutWidget") + self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.label_4 = QtGui.QLabel(self.gridLayoutWidget) + self.label_4.setObjectName("label_4") + self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) + self.label_5 = QtGui.QLabel(self.gridLayoutWidget) + self.label_5.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_5.setAlignment(QtCore.Qt.AlignCenter) + self.label_5.setObjectName("label_5") + self.gridLayout.addWidget(self.label_5, 0, 1, 1, 1) + self.label_6 = QtGui.QLabel(self.gridLayoutWidget) + self.label_6.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_6.setAlignment(QtCore.Qt.AlignCenter) + self.label_6.setObjectName("label_6") + self.gridLayout.addWidget(self.label_6, 0, 2, 1, 1) + self.label_7 = QtGui.QLabel(self.gridLayoutWidget) + self.label_7.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_7.setAlignment(QtCore.Qt.AlignCenter) + self.label_7.setObjectName("label_7") + self.gridLayout.addWidget(self.label_7, 0, 3, 1, 1) + self.label_8 = QtGui.QLabel(self.gridLayoutWidget) + self.label_8.setObjectName("label_8") + self.gridLayout.addWidget(self.label_8, 1, 0, 1, 1) + self.pgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) + self.pgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_x.setObjectName("pgrid_x") + self.gridLayout.addWidget(self.pgrid_x, 1, 1, 1, 1) + self.pgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) + self.pgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_y.setObjectName("pgrid_y") + self.gridLayout.addWidget(self.pgrid_y, 1, 2, 1, 1) + self.pgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) + self.pgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_z.setObjectName("pgrid_z") + self.gridLayout.addWidget(self.pgrid_z, 1, 3, 1, 1) + self.label_9 = QtGui.QLabel(self.gridLayoutWidget) + self.label_9.setObjectName("label_9") + self.gridLayout.addWidget(self.label_9, 2, 0, 1, 1) + self.invgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) + self.invgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_x.setObjectName("invgrid_x") + self.gridLayout.addWidget(self.invgrid_x, 2, 1, 1, 1) + self.invgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) + self.invgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_y.setObjectName("invgrid_y") + self.gridLayout.addWidget(self.invgrid_y, 2, 2, 1, 1) + self.invgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) + self.invgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_z.setObjectName("invgrid_z") + self.gridLayout.addWidget(self.invgrid_z, 2, 3, 1, 1) + self.gridLayoutWidget_2 = QtGui.QWidget(Dialog) + self.gridLayoutWidget_2.setGeometry(QtCore.QRect(10, 160, 201, 61)) + self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2") + self.gridLayout_2 = QtGui.QGridLayout(self.gridLayoutWidget_2) + self.gridLayout_2.setContentsMargins(0, 0, 0, 0) + self.gridLayout_2.setObjectName("gridLayout_2") + self.label_3 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_3.setObjectName("label_3") + self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) + self.bbot = QtGui.QLineEdit(self.gridLayoutWidget_2) + self.bbot.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.bbot.setObjectName("bbot") + self.gridLayout_2.addWidget(self.bbot, 1, 1, 1, 1) + self.label_11 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_11.setObjectName("label_11") + self.gridLayout_2.addWidget(self.label_11, 1, 2, 1, 1) + self.label_2 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_2.setObjectName("label_2") + self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) + self.btop = QtGui.QLineEdit(self.gridLayoutWidget_2) + self.btop.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.btop.setObjectName("btop") + self.gridLayout_2.addWidget(self.btop, 0, 1, 1, 1) + self.label_10 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_10.setObjectName("label_10") + self.gridLayout_2.addWidget(self.label_10, 0, 2, 1, 1) + self.gridLayoutWidget_3 = QtGui.QWidget(Dialog) + self.gridLayoutWidget_3.setGeometry(QtCore.QRect(10, 60, 201, 31)) + self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3") + self.gridLayout_3 = QtGui.QGridLayout(self.gridLayoutWidget_3) + self.gridLayout_3.setContentsMargins(0, 0, 0, 0) + self.gridLayout_3.setObjectName("gridLayout_3") + self.nIter = QtGui.QSpinBox(self.gridLayoutWidget_3) + self.nIter.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.nIter.setProperty("value", 1) + self.nIter.setObjectName("nIter") + self.gridLayout_3.addWidget(self.nIter, 0, 1, 1, 1) + self.label = QtGui.QLabel(self.gridLayoutWidget_3) + self.label.setObjectName("label") + self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1) + self.horizontalLayoutWidget = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 340, 161, 31)) + self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget") + self.horizontalLayout = QtGui.QHBoxLayout(self.horizontalLayoutWidget) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_13 = QtGui.QLabel(self.horizontalLayoutWidget) + self.label_13.setObjectName("label_13") + self.horizontalLayout.addWidget(self.label_13) + self.cushion = QtGui.QLineEdit(self.horizontalLayoutWidget) + self.cushion.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.cushion.setObjectName("cushion") + self.horizontalLayout.addWidget(self.cushion) + self.label_12 = QtGui.QLabel(self.horizontalLayoutWidget) + self.label_12.setObjectName("label_12") + self.horizontalLayout.addWidget(self.label_12) + self.horizontalLayoutWidget_4 = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(10, 10, 291, 32)) + self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4") + self.horizontalLayout_4 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_4) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_17 = QtGui.QLabel(self.horizontalLayoutWidget_4) + self.label_17.setObjectName("label_17") + self.horizontalLayout_4.addWidget(self.label_17) + self.fmtomo_dir = QtGui.QLineEdit(self.horizontalLayoutWidget_4) + self.fmtomo_dir.setObjectName("fmtomo_dir") + self.horizontalLayout_4.addWidget(self.fmtomo_dir) + self.browse_tomodir = QtGui.QPushButton(self.horizontalLayoutWidget_4) + self.browse_tomodir.setObjectName("browse_tomodir") + self.horizontalLayout_4.addWidget(self.browse_tomodir) + self.line = QtGui.QFrame(Dialog) + self.line.setGeometry(QtCore.QRect(7, 220, 301, 20)) + self.line.setFrameShape(QtGui.QFrame.HLine) + self.line.setFrameShadow(QtGui.QFrame.Sunken) + self.line.setObjectName("line") + self.line_2 = QtGui.QFrame(Dialog) + self.line_2.setGeometry(QtCore.QRect(10, 320, 301, 20)) + self.line_2.setFrameShape(QtGui.QFrame.HLine) + self.line_2.setFrameShadow(QtGui.QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.gridLayoutWidget_4 = QtGui.QWidget(Dialog) + self.gridLayoutWidget_4.setGeometry(QtCore.QRect(10, 390, 291, 80)) + self.gridLayoutWidget_4.setObjectName("gridLayoutWidget_4") + self.gridLayout_4 = QtGui.QGridLayout(self.gridLayoutWidget_4) + self.gridLayout_4.setContentsMargins(0, 0, 0, 0) + self.gridLayout_4.setObjectName("gridLayout_4") + self.label_15 = QtGui.QLabel(self.gridLayoutWidget_4) + self.label_15.setObjectName("label_15") + self.gridLayout_4.addWidget(self.label_15, 0, 0, 1, 1) + self.customgrid = QtGui.QLineEdit(self.gridLayoutWidget_4) + self.customgrid.setObjectName("customgrid") + self.gridLayout_4.addWidget(self.customgrid, 0, 1, 1, 1) + self.browse_customgrid = QtGui.QPushButton(self.gridLayoutWidget_4) + self.browse_customgrid.setObjectName("browse_customgrid") + self.gridLayout_4.addWidget(self.browse_customgrid, 0, 2, 1, 1) + self.label_16 = QtGui.QLabel(self.gridLayoutWidget_4) + self.label_16.setObjectName("label_16") + self.gridLayout_4.addWidget(self.label_16, 1, 0, 1, 1) + self.simuldir = QtGui.QLineEdit(self.gridLayoutWidget_4) + self.simuldir.setObjectName("simuldir") + self.gridLayout_4.addWidget(self.simuldir, 1, 1, 1, 1) + self.browse_simuldir = QtGui.QPushButton(self.gridLayoutWidget_4) + self.browse_simuldir.setObjectName("browse_simuldir") + self.gridLayout_4.addWidget(self.browse_simuldir, 1, 2, 1, 1) + self.line_3 = QtGui.QFrame(Dialog) + self.line_3.setGeometry(QtCore.QRect(10, 370, 301, 20)) + self.line_3.setFrameShape(QtGui.QFrame.HLine) + self.line_3.setFrameShadow(QtGui.QFrame.Sunken) + self.line_3.setObjectName("line_3") + self.line_4 = QtGui.QFrame(Dialog) + self.line_4.setGeometry(QtCore.QRect(10, 90, 301, 20)) + self.line_4.setFrameShape(QtGui.QFrame.HLine) + self.line_4.setFrameShadow(QtGui.QFrame.Sunken) + self.line_4.setObjectName("line_4") + self.line_5 = QtGui.QFrame(Dialog) + self.line_5.setGeometry(QtCore.QRect(10, 40, 301, 20)) + self.line_5.setFrameShape(QtGui.QFrame.HLine) + self.line_5.setFrameShadow(QtGui.QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.horizontalLayoutWidget_2 = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(220, 60, 81, 31)) + self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2") + self.horizontalLayout_2 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_2) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.label_14 = QtGui.QLabel(self.horizontalLayoutWidget_2) + self.label_14.setObjectName("label_14") + self.horizontalLayout_2.addWidget(self.label_14) + self.nproc = QtGui.QLineEdit(self.horizontalLayoutWidget_2) + self.nproc.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.nproc.setObjectName("nproc") + self.horizontalLayout_2.addWidget(self.nproc) + self.label_18 = QtGui.QLabel(Dialog) + self.label_18.setGeometry(QtCore.QRect(10, 102, 271, 16)) + self.label_18.setObjectName("label_18") + self.horizontalLayoutWidget_5 = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget_5.setGeometry(QtCore.QRect(10, 120, 291, 32)) + self.horizontalLayoutWidget_5.setObjectName("horizontalLayoutWidget_5") + self.horizontalLayout_5 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_5) + self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.label_19 = QtGui.QLabel(self.horizontalLayoutWidget_5) + self.label_19.setObjectName("label_19") + self.horizontalLayout_5.addWidget(self.label_19) + self.seisarray = QtGui.QLineEdit(self.horizontalLayoutWidget_5) + self.seisarray.setObjectName("seisarray") + self.horizontalLayout_5.addWidget(self.seisarray) + self.browse_seisarray = QtGui.QPushButton(self.horizontalLayoutWidget_5) + self.browse_seisarray.setObjectName("browse_seisarray") + self.horizontalLayout_5.addWidget(self.browse_seisarray) + + self.retranslateUi(Dialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("Dialog", "Number of Ponts", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("Dialog", "X", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setText(QtGui.QApplication.translate("Dialog", "Y", None, QtGui.QApplication.UnicodeUTF8)) + self.label_7.setText(QtGui.QApplication.translate("Dialog", "Z", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setText(QtGui.QApplication.translate("Dialog", "Propagation Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_x.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_y.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_z.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.label_9.setText(QtGui.QApplication.translate("Dialog", "Inversion Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_x.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_y.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_z.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("Dialog", "Bottom boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.bbot.setText(QtGui.QApplication.translate("Dialog", "-50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_11.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("Dialog", "Top boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.btop.setText(QtGui.QApplication.translate("Dialog", "5", None, QtGui.QApplication.UnicodeUTF8)) + self.label_10.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("Dialog", "Iterations", None, QtGui.QApplication.UnicodeUTF8)) + self.label_13.setText(QtGui.QApplication.translate("Dialog", "Cushion factor", None, QtGui.QApplication.UnicodeUTF8)) + self.cushion.setText(QtGui.QApplication.translate("Dialog", "10", None, QtGui.QApplication.UnicodeUTF8)) + self.label_12.setText(QtGui.QApplication.translate("Dialog", "%", None, QtGui.QApplication.UnicodeUTF8)) + self.label_17.setText(QtGui.QApplication.translate("Dialog", "FMTOMO\n" +"installation", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_tomodir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_15.setText(QtGui.QApplication.translate("Dialog", "Custom velocity\n" +"grid (earthmodel)", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_customgrid.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_16.setText(QtGui.QApplication.translate("Dialog", "Simulation\n" +"Directory", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_simuldir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_14.setText(QtGui.QApplication.translate("Dialog", "nproc", None, QtGui.QApplication.UnicodeUTF8)) + self.nproc.setText(QtGui.QApplication.translate("Dialog", "1", None, QtGui.QApplication.UnicodeUTF8)) + self.label_18.setText(QtGui.QApplication.translate("Dialog", "Grid definitions from seismic Array:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_19.setText(QtGui.QApplication.translate("Dialog", "Seismic\n" +"Array", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_seisarray.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) From 4093dd0ce78d1d368b908aad34dbaf65946f13ac Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 13 Jun 2016 14:37:56 +0200 Subject: [PATCH 0962/1144] name change --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index a809f5fc..7dd1c34c 100644 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -1,6 +1,6 @@ import os from PySide import QtCore, QtGui, QtCore -from asp3d_designer_layout import * +from asp3d_layout import * from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils class gui_control(object): From 073ed658b3f48fd726622f2f23f168b0cd2ab39e Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 14 Jun 2016 14:28:46 +0200 Subject: [PATCH 0963/1144] [edit] changed calculation of difference pdf to include large differences also --- pylot/core/util/pdf.py | 4 +++- pylot/core/util/widgets.py | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 1a233aed..090da81c 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -142,7 +142,9 @@ class ProbabilityDensityFunction(object): x0, incr, npts, pdf_self, pdf_other = self.rearrange(other) - pdf = np.correlate(pdf_self, pdf_other, 'same') * incr + pdf = np.correlate(pdf_self, pdf_other, 'full') * incr + + npts = len(pdf) # shift axis values for correct plotting midpoint = npts / 2 diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 5548746b..0004801c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -181,6 +181,9 @@ class ComparisonDialog(QDialog): if name in self.widgets.keys(): self._widgets[name] = widget + def clf(self): + self.canvas.figure.clf() + def hasvalue(self, sender): text = sender.currentText() index = sender.findText(text.upper()) @@ -204,9 +207,9 @@ class ComparisonDialog(QDialog): def plotcomparison(self): from matplotlib import gridspec - _axes = self.canvas.figure.add_subplot(111) _gs = gridspec.GridSpec(3, 2) + self.clf() _axes = self.canvas.figure.add_subplot(_gs[0:2, :]) _ax1 = self.canvas.figure.add_subplot(_gs[2, 0]) _ax2 = self.canvas.figure.add_subplot(_gs[2, 1]) @@ -232,6 +235,7 @@ class ComparisonDialog(QDialog): pdf_a = self.data.get('auto')[station][phase] pdf_m = self.data.get('manu')[station][phase] + xauto, yauto, stdauto, expauto = pdf_a.axis, pdf_a.data, \ pdf_a.standard_deviation(), \ pdf_a.expectation() @@ -240,8 +244,23 @@ class ComparisonDialog(QDialog): pdf_m.expectation() _ax1.plot(xauto, yauto) + mannotation = "probability density for manual pick\n" \ + "expectation: {exp}\n" \ + "std: {std}".format(std=stdmanu, exp=expmanu) + _anno = _ax1.annotate(mannotation, xy=(.05, .5), xycoords='axes ' + 'fraction') + bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) + _anno.set_bbox(bbox_props) + _ax2.plot(xmanu, ymanu) + aannotation = "probability density for automatic pick\n" \ + "expectation: {exp}\n" \ + "std: {std}".format(std=stdauto, exp=expauto) + _anno = _ax2.annotate(aannotation, xy=(.05, .5), xycoords='axes ' + 'fraction') + bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) + _anno.set_bbox(bbox_props) _gs.update(wspace=0.5, hspace=0.5) From 78ebb1f8db0a042de51e8f45430d5efc2bcfff60 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 14 Jun 2016 14:55:18 +0200 Subject: [PATCH 0964/1144] [change] decreased verbosity --- pylot/core/util/pdf.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 090da81c..f199e52f 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -229,8 +229,7 @@ class ProbabilityDensityFunction(object): # calculate pdf values try: pdf = branches[type](create_axis(x0, incr, npts), barycentre, *params) - except TypeError as e: - print('Warning:\n' + e.message + '\n' + 'trying timestamp instead') + except TypeError: assert isinstance(barycentre, UTCDateTime), 'object not capable of' \ ' timestamp representation' pdf = branches[type](create_axis(x0, incr, npts), From 3c4cbff9f3ca9378bdbcb010790c8428947c9825 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 14 Jun 2016 14:56:15 +0200 Subject: [PATCH 0965/1144] [fix] do not try to add a Layout to a QWidget which already has one --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0004801c..a3c06869 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -82,7 +82,7 @@ class ComparisonDialog(QDialog): def setupUI(self): _outerlayout = QVBoxLayout(self) - _innerlayout = QVBoxLayout(self) + _innerlayout = QVBoxLayout() _stats_combobox = QComboBox(self) _stats_combobox.setObjectName('stationsComboBox') From e554330154ddf143a5c4ee396767a1f77c080cde Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 14 Jun 2016 15:03:13 +0200 Subject: [PATCH 0966/1144] [change] now the station selector entries are sorted for convience --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a3c06869..a9ddecfa 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -88,7 +88,7 @@ class ComparisonDialog(QDialog): _stats_combobox.setObjectName('stationsComboBox') _stats_combobox.setEditable(True) _stats_combobox.setInsertPolicy(QComboBox.NoInsert) - _stats_combobox.addItems(self.stations) + _stats_combobox.addItems(sorted(self.stations)) _stats_combobox.editTextChanged.connect(self.prepareplot) self.widgets = _stats_combobox From 3583f70e2b9cd2b0fd877b9ba56df9a660fd0f5e Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 14 Jun 2016 15:26:16 +0200 Subject: [PATCH 0967/1144] [CAUTION] this rev is not working, implementation of the individaul pdf plots for the picks pending --- pylot/core/util/pdf.py | 12 ++++++++---- pylot/core/util/widgets.py | 8 +++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index f199e52f..2882dec4 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -311,6 +311,12 @@ class ProbabilityDensityFunction(object): plt.title(title_str) plt.show() + def limits(self): + l1 = self.x0 + r1 = l1 + self.incr * self.npts + + return l1, r1 + def commonlimits(self, incr, other, max_npts=1e5): ''' Takes an increment incr and two left and two right limits and returns @@ -330,10 +336,8 @@ class ProbabilityDensityFunction(object): # >>> manu.commonlimits(0.01, auto) # ( - l1 = self.x0 - r1 = l1 + self.incr * self.npts - l2 = other.x0 - r2 = l2 + other.incr * other.npts + l1, r1 = self.limits() + l2, r2 = other.limits() if l1 < l2: x0 = l1 diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a9ddecfa..2b0dcaee 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -243,7 +243,8 @@ class ComparisonDialog(QDialog): pdf_m.standard_deviation(), \ pdf_m.expectation() - _ax1.plot(xauto, yauto) + _ax1.plot(xmanu, ymanu) + _ax1.set_xlim(pdf_m.limits()) mannotation = "probability density for manual pick\n" \ "expectation: {exp}\n" \ "std: {std}".format(std=stdmanu, exp=expmanu) @@ -252,8 +253,9 @@ class ComparisonDialog(QDialog): bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) _anno.set_bbox(bbox_props) - - _ax2.plot(xmanu, ymanu) + _ax2.plot(xauto, yauto) + _ax2.set_xlim(pdf_a.limits()) + _ax2.set_ylabel('time [s]') aannotation = "probability density for automatic pick\n" \ "expectation: {exp}\n" \ "std: {std}".format(std=stdauto, exp=expauto) From 1ec468a586a266c1478932d9045e0a053276a652 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 15 Jun 2016 10:01:28 +0200 Subject: [PATCH 0968/1144] bugfix, boxes clickable --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) mode change 100644 => 100755 pylot/core/active/ActiveSeismoPick3D_GUI.py diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py old mode 100644 new mode 100755 index 7dd1c34c..ce0ecba9 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- import os from PySide import QtCore, QtGui, QtCore from asp3d_layout import * @@ -164,11 +166,11 @@ class gui_control(object): if state == 'active': self.mainUI.survey_active.setCheckable(True) self.mainUI.survey_active.setChecked(True) - self.mainUI.survey_active.setCheckable(True) + self.mainUI.survey_active.setCheckable(False) elif state == 'inactive': self.mainUI.survey_active.setCheckable(True) self.mainUI.survey_active.setChecked(False) - self.mainUI.survey_active.setCheckable(True) + self.mainUI.survey_active.setCheckable(False) def checkSurveyState(self): if self.mainUI.survey_active.checkState() == QtCore.Qt.CheckState.Checked: From c3d134e6cae73abfbcf79f0a709bb4292b55d278 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 15 Jun 2016 12:19:11 +0200 Subject: [PATCH 0969/1144] changed GUI organization (single files for each window) --- pylot/core/active/asp3d_layout.py | 451 ++---------------- pylot/core/active/fmtomo_parameters_layout.py | 281 +++++++++++ pylot/core/active/generate_survey_layout.py | 73 +++ .../core/active/picking_parameters_layout.py | 72 +++ 4 files changed, 458 insertions(+), 419 deletions(-) create mode 100644 pylot/core/active/fmtomo_parameters_layout.py create mode 100644 pylot/core/active/generate_survey_layout.py create mode 100644 pylot/core/active/picking_parameters_layout.py diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index e10f04be..abbb1cb8 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -1,8 +1,18 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'asp3d_layout.ui' +# +# Created: Wed Jun 15 11:55:58 2016 +# by: pyside-uic 0.2.15 running on PySide 1.2.2 +# +# WARNING! All changes made in this file will be lost! + from PySide import QtCore, QtGui class Ui_MainWindow(object): def setupUi(self, MainWindow): - MainWindow.setObjectName("ActiveSeismoPick3D") + MainWindow.setObjectName("MainWindow") + MainWindow.setEnabled(True) MainWindow.resize(252, 350) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -12,37 +22,38 @@ class Ui_MainWindow(object): MainWindow.setMinimumSize(QtCore.QSize(252, 350)) MainWindow.setMaximumSize(QtCore.QSize(252, 350)) MainWindow.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + MainWindow.setWindowIcon(icon) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.postprocessing = QtGui.QPushButton(self.centralwidget) self.postprocessing.setGeometry(QtCore.QRect(0, 220, 251, 27)) self.postprocessing.setObjectName("postprocessing") self.verticalLayoutWidget = QtGui.QWidget(self.centralwidget) - self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 0, 231, 41)) + self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 251, 41)) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.horizontalLayout = QtGui.QHBoxLayout(self.verticalLayoutWidget) self.horizontalLayout.setContentsMargins(0, 0, 0, 0) self.horizontalLayout.setObjectName("horizontalLayout") - self.survey_active = QtGui.QCheckBox(self.verticalLayoutWidget) - self.survey_active.setEnabled(True) - self.survey_active.setMouseTracking(False) - self.survey_active.setStatusTip("") - self.survey_active.setWhatsThis("") - self.survey_active.setAccessibleName("") - self.survey_active.setAccessibleDescription("") - self.survey_active.setCheckable(False) + self.survey_active = QtGui.QLabel(self.verticalLayoutWidget) + self.survey_active.setMaximumSize(QtCore.QSize(20, 20)) + self.survey_active.setMidLineWidth(0) + self.survey_active.setText("") self.survey_active.setObjectName("survey_active") self.horizontalLayout.addWidget(self.survey_active) - self.picked_active = QtGui.QCheckBox(self.verticalLayoutWidget) - self.picked_active.setEnabled(True) - self.picked_active.setMouseTracking(False) - self.picked_active.setStatusTip("") - self.picked_active.setWhatsThis("") - self.picked_active.setAccessibleName("") - self.picked_active.setAccessibleDescription("") - self.picked_active.setCheckable(False) + self.label_4 = QtGui.QLabel(self.verticalLayoutWidget) + self.label_4.setObjectName("label_4") + self.horizontalLayout.addWidget(self.label_4) + self.picked_active = QtGui.QLabel(self.verticalLayoutWidget) + self.picked_active.setMaximumSize(QtCore.QSize(20, 20)) + self.picked_active.setMidLineWidth(0) + self.picked_active.setText("") self.picked_active.setObjectName("picked_active") self.horizontalLayout.addWidget(self.picked_active) + self.label = QtGui.QLabel(self.verticalLayoutWidget) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) self.gen_new_survey = QtGui.QPushButton(self.centralwidget) self.gen_new_survey.setGeometry(QtCore.QRect(1, 47, 251, 27)) self.gen_new_survey.setObjectName("gen_new_survey") @@ -81,411 +92,13 @@ class Ui_MainWindow(object): QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): - MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) + MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "ActiveSeismoPick 3D", None, QtGui.QApplication.UnicodeUTF8)) self.postprocessing.setText(QtGui.QApplication.translate("MainWindow", "Postprocessing", None, QtGui.QApplication.UnicodeUTF8)) - self.survey_active.setText(QtGui.QApplication.translate("MainWindow", "Survey active", None, QtGui.QApplication.UnicodeUTF8)) - self.picked_active.setText(QtGui.QApplication.translate("MainWindow", "picked", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("MainWindow", "Survey active", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("MainWindow", "Survey picked", None, QtGui.QApplication.UnicodeUTF8)) self.gen_new_survey.setText(QtGui.QApplication.translate("MainWindow", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) self.load_survey.setText(QtGui.QApplication.translate("MainWindow", "Load Survey", None, QtGui.QApplication.UnicodeUTF8)) self.save_survey.setText(QtGui.QApplication.translate("MainWindow", "Save Survey", None, QtGui.QApplication.UnicodeUTF8)) self.picker.setText(QtGui.QApplication.translate("MainWindow", "Autopicker", None, QtGui.QApplication.UnicodeUTF8)) self.fmtomo.setText(QtGui.QApplication.translate("MainWindow", "FMTOMO Simulation", None, QtGui.QApplication.UnicodeUTF8)) -class Ui_Dialog(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(380, 160) - self.verticalLayout = QtGui.QVBoxLayout(Dialog) - self.verticalLayout.setObjectName("verticalLayout") - self.gridLayout = QtGui.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.lineEdit_rec = QtGui.QLineEdit(Dialog) - self.lineEdit_rec.setObjectName("lineEdit_rec") - self.gridLayout.addWidget(self.lineEdit_rec, 0, 1, 1, 1) - self.pushButton_rec = QtGui.QPushButton(Dialog) - self.pushButton_rec.setObjectName("pushButton_rec") - self.gridLayout.addWidget(self.pushButton_rec, 0, 2, 1, 1) - self.label_rec = QtGui.QLabel(Dialog) - self.label_rec.setObjectName("label_rec") - self.gridLayout.addWidget(self.label_rec, 0, 0, 1, 1) - self.lineEdit_obs = QtGui.QLineEdit(Dialog) - self.lineEdit_obs.setObjectName("lineEdit_obs") - self.gridLayout.addWidget(self.lineEdit_obs, 2, 1, 1, 1) - self.label_obs = QtGui.QLabel(Dialog) - self.label_obs.setObjectName("label_obs") - self.gridLayout.addWidget(self.label_obs, 2, 0, 1, 1) - self.pushButton_obs = QtGui.QPushButton(Dialog) - self.pushButton_obs.setObjectName("pushButton_obs") - self.gridLayout.addWidget(self.pushButton_obs, 2, 2, 1, 1) - self.label_src = QtGui.QLabel(Dialog) - self.label_src.setObjectName("label_src") - self.gridLayout.addWidget(self.label_src, 1, 0, 1, 1) - self.lineEdit_src = QtGui.QLineEdit(Dialog) - self.lineEdit_src.setObjectName("lineEdit_src") - self.gridLayout.addWidget(self.lineEdit_src, 1, 1, 1, 1) - self.pushButton_src = QtGui.QPushButton(Dialog) - self.pushButton_src.setObjectName("pushButton_src") - self.gridLayout.addWidget(self.pushButton_src, 1, 2, 1, 1) - self.verticalLayout.addLayout(self.gridLayout) - self.buttonBox = QtGui.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout.addWidget(self.buttonBox) - - self.retranslateUi(Dialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) - QtCore.QMetaObject.connectSlotsByName(Dialog) - - def retranslateUi(self, Dialog): - Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_rec.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setText(QtGui.QApplication.translate("Dialog", "Receiver\n" -"File", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setText(QtGui.QApplication.translate("Dialog", "Seismogram\n" -"Directory", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_obs.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setText(QtGui.QApplication.translate("Dialog", "Source\n" -"File", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_src.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - -class Ui_Picking_parameters(object): - def setupUi(self, Picking_parameters): - Picking_parameters.setObjectName("Picking_parameters") - Picking_parameters.resize(300, 300) - self.gridLayoutWidget = QtGui.QWidget(Picking_parameters) - self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 20, 250, 211)) - self.gridLayoutWidget.setObjectName("gridLayoutWidget") - self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setObjectName("gridLayout") - self.label_cores = QtGui.QLabel(self.gridLayoutWidget) - self.label_cores.setObjectName("label_cores") - self.gridLayout.addWidget(self.label_cores, 0, 0, 1, 1) - self.label_vmax = QtGui.QLabel(self.gridLayoutWidget) - self.label_vmax.setObjectName("label_vmax") - self.gridLayout.addWidget(self.label_vmax, 2, 0, 1, 1) - self.label_vmin = QtGui.QLabel(self.gridLayoutWidget) - self.label_vmin.setObjectName("label_vmin") - self.gridLayout.addWidget(self.label_vmin, 1, 0, 1, 1) - self.lineEdit_ncores = QtGui.QLineEdit(self.gridLayoutWidget) - self.lineEdit_ncores.setObjectName("lineEdit_ncores") - self.gridLayout.addWidget(self.lineEdit_ncores, 0, 1, 1, 1) - self.lineEdit_vmin = QtGui.QLineEdit(self.gridLayoutWidget) - self.lineEdit_vmin.setObjectName("lineEdit_vmin") - self.gridLayout.addWidget(self.lineEdit_vmin, 1, 1, 1, 1) - self.lineEdit_vmax = QtGui.QLineEdit(self.gridLayoutWidget) - self.lineEdit_vmax.setObjectName("lineEdit_vmax") - self.gridLayout.addWidget(self.lineEdit_vmax, 2, 1, 1, 1) - self.checkBox = QtGui.QCheckBox(self.gridLayoutWidget) - self.checkBox.setObjectName("checkBox") - self.gridLayout.addWidget(self.checkBox, 4, 1, 1, 1) - self.label_folm = QtGui.QLabel(self.gridLayoutWidget) - self.label_folm.setObjectName("label_folm") - self.gridLayout.addWidget(self.label_folm, 3, 0, 1, 1) - self.label_aic = QtGui.QLabel(self.gridLayoutWidget) - self.label_aic.setObjectName("label_aic") - self.gridLayout.addWidget(self.label_aic, 4, 0, 1, 1) - self.lineEdit_folm = QtGui.QLineEdit(self.gridLayoutWidget) - self.lineEdit_folm.setObjectName("lineEdit_folm") - self.gridLayout.addWidget(self.lineEdit_folm, 3, 1, 1, 1) - self.label_aicwindow = QtGui.QLabel(self.gridLayoutWidget) - self.label_aicwindow.setObjectName("label_aicwindow") - self.gridLayout.addWidget(self.label_aicwindow, 5, 0, 1, 1) - self.lineEdit_aicwindow = QtGui.QLineEdit(self.gridLayoutWidget) - self.lineEdit_aicwindow.setObjectName("lineEdit_aicwindow") - self.gridLayout.addWidget(self.lineEdit_aicwindow, 5, 1, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(Picking_parameters) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.buttonBox.setGeometry(QtCore.QRect(10, 240, 250, 32)) - - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Picking_parameters.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Picking_parameters.reject) - self.retranslateUi(Picking_parameters) - QtCore.QMetaObject.connectSlotsByName(Picking_parameters) - - def retranslateUi(self, Picking_parameters): - Picking_parameters.setWindowTitle(QtGui.QApplication.translate("Picking_parameters", "Picking_parameters", None, QtGui.QApplication.UnicodeUTF8)) - self.label_cores.setText(QtGui.QApplication.translate("Picking_parameters", "Number of cores", None, QtGui.QApplication.UnicodeUTF8)) - self.label_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "Vmax (default = 5000m/s)", None, QtGui.QApplication.UnicodeUTF8)) - self.label_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "Vmin (default = 333 m/s)", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_ncores.setText(QtGui.QApplication.translate("Picking_parameters", "1", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "333", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) - self.label_folm.setText(QtGui.QApplication.translate("Picking_parameters", "Fraction of local maximum\n" -"(default = 0.6)", None, QtGui.QApplication.UnicodeUTF8)) - self.label_aic.setText(QtGui.QApplication.translate("Picking_parameters", "AIC", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_folm.setText(QtGui.QApplication.translate("Picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) - self.label_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "AIC window (only if AIC\n" -" is checked)", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "15, 0", None, QtGui.QApplication.UnicodeUTF8)) - -class Ui_fmtomo_parameters(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(320, 520) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) - Dialog.setSizePolicy(sizePolicy) - Dialog.setMinimumSize(QtCore.QSize(320, 520)) - Dialog.setMaximumSize(QtCore.QSize(320, 520)) - self.buttonBox = QtGui.QDialogButtonBox(Dialog) - self.buttonBox.setGeometry(QtCore.QRect(10, 480, 301, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.gridLayoutWidget = QtGui.QWidget(Dialog) - self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 240, 281, 79)) - self.gridLayoutWidget.setObjectName("gridLayoutWidget") - self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setObjectName("gridLayout") - self.label_4 = QtGui.QLabel(self.gridLayoutWidget) - self.label_4.setObjectName("label_4") - self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) - self.label_5 = QtGui.QLabel(self.gridLayoutWidget) - self.label_5.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_5.setAlignment(QtCore.Qt.AlignCenter) - self.label_5.setObjectName("label_5") - self.gridLayout.addWidget(self.label_5, 0, 1, 1, 1) - self.label_6 = QtGui.QLabel(self.gridLayoutWidget) - self.label_6.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_6.setAlignment(QtCore.Qt.AlignCenter) - self.label_6.setObjectName("label_6") - self.gridLayout.addWidget(self.label_6, 0, 2, 1, 1) - self.label_7 = QtGui.QLabel(self.gridLayoutWidget) - self.label_7.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_7.setAlignment(QtCore.Qt.AlignCenter) - self.label_7.setObjectName("label_7") - self.gridLayout.addWidget(self.label_7, 0, 3, 1, 1) - self.label_8 = QtGui.QLabel(self.gridLayoutWidget) - self.label_8.setObjectName("label_8") - self.gridLayout.addWidget(self.label_8, 1, 0, 1, 1) - self.pgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) - self.pgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_x.setObjectName("pgrid_x") - self.gridLayout.addWidget(self.pgrid_x, 1, 1, 1, 1) - self.pgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) - self.pgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_y.setObjectName("pgrid_y") - self.gridLayout.addWidget(self.pgrid_y, 1, 2, 1, 1) - self.pgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) - self.pgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_z.setObjectName("pgrid_z") - self.gridLayout.addWidget(self.pgrid_z, 1, 3, 1, 1) - self.label_9 = QtGui.QLabel(self.gridLayoutWidget) - self.label_9.setObjectName("label_9") - self.gridLayout.addWidget(self.label_9, 2, 0, 1, 1) - self.invgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) - self.invgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.invgrid_x.setObjectName("invgrid_x") - self.gridLayout.addWidget(self.invgrid_x, 2, 1, 1, 1) - self.invgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) - self.invgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.invgrid_y.setObjectName("invgrid_y") - self.gridLayout.addWidget(self.invgrid_y, 2, 2, 1, 1) - self.invgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) - self.invgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.invgrid_z.setObjectName("invgrid_z") - self.gridLayout.addWidget(self.invgrid_z, 2, 3, 1, 1) - self.gridLayoutWidget_2 = QtGui.QWidget(Dialog) - self.gridLayoutWidget_2.setGeometry(QtCore.QRect(10, 160, 201, 61)) - self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2") - self.gridLayout_2 = QtGui.QGridLayout(self.gridLayoutWidget_2) - self.gridLayout_2.setContentsMargins(0, 0, 0, 0) - self.gridLayout_2.setObjectName("gridLayout_2") - self.label_3 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_3.setObjectName("label_3") - self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) - self.bbot = QtGui.QLineEdit(self.gridLayoutWidget_2) - self.bbot.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.bbot.setObjectName("bbot") - self.gridLayout_2.addWidget(self.bbot, 1, 1, 1, 1) - self.label_11 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_11.setObjectName("label_11") - self.gridLayout_2.addWidget(self.label_11, 1, 2, 1, 1) - self.label_2 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_2.setObjectName("label_2") - self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) - self.btop = QtGui.QLineEdit(self.gridLayoutWidget_2) - self.btop.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.btop.setObjectName("btop") - self.gridLayout_2.addWidget(self.btop, 0, 1, 1, 1) - self.label_10 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_10.setObjectName("label_10") - self.gridLayout_2.addWidget(self.label_10, 0, 2, 1, 1) - self.gridLayoutWidget_3 = QtGui.QWidget(Dialog) - self.gridLayoutWidget_3.setGeometry(QtCore.QRect(10, 60, 201, 31)) - self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3") - self.gridLayout_3 = QtGui.QGridLayout(self.gridLayoutWidget_3) - self.gridLayout_3.setContentsMargins(0, 0, 0, 0) - self.gridLayout_3.setObjectName("gridLayout_3") - self.nIter = QtGui.QSpinBox(self.gridLayoutWidget_3) - self.nIter.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.nIter.setProperty("value", 1) - self.nIter.setObjectName("nIter") - self.gridLayout_3.addWidget(self.nIter, 0, 1, 1, 1) - self.label = QtGui.QLabel(self.gridLayoutWidget_3) - self.label.setObjectName("label") - self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1) - self.horizontalLayoutWidget = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 340, 161, 31)) - self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget") - self.horizontalLayout = QtGui.QHBoxLayout(self.horizontalLayoutWidget) - self.horizontalLayout.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout.setObjectName("horizontalLayout") - self.label_13 = QtGui.QLabel(self.horizontalLayoutWidget) - self.label_13.setObjectName("label_13") - self.horizontalLayout.addWidget(self.label_13) - self.cushion = QtGui.QLineEdit(self.horizontalLayoutWidget) - self.cushion.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.cushion.setObjectName("cushion") - self.horizontalLayout.addWidget(self.cushion) - self.label_12 = QtGui.QLabel(self.horizontalLayoutWidget) - self.label_12.setObjectName("label_12") - self.horizontalLayout.addWidget(self.label_12) - self.horizontalLayoutWidget_4 = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(10, 10, 291, 32)) - self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4") - self.horizontalLayout_4 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_4) - self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.label_17 = QtGui.QLabel(self.horizontalLayoutWidget_4) - self.label_17.setObjectName("label_17") - self.horizontalLayout_4.addWidget(self.label_17) - self.fmtomo_dir = QtGui.QLineEdit(self.horizontalLayoutWidget_4) - self.fmtomo_dir.setObjectName("fmtomo_dir") - self.horizontalLayout_4.addWidget(self.fmtomo_dir) - self.browse_tomodir = QtGui.QPushButton(self.horizontalLayoutWidget_4) - self.browse_tomodir.setObjectName("browse_tomodir") - self.horizontalLayout_4.addWidget(self.browse_tomodir) - self.line = QtGui.QFrame(Dialog) - self.line.setGeometry(QtCore.QRect(7, 220, 301, 20)) - self.line.setFrameShape(QtGui.QFrame.HLine) - self.line.setFrameShadow(QtGui.QFrame.Sunken) - self.line.setObjectName("line") - self.line_2 = QtGui.QFrame(Dialog) - self.line_2.setGeometry(QtCore.QRect(10, 320, 301, 20)) - self.line_2.setFrameShape(QtGui.QFrame.HLine) - self.line_2.setFrameShadow(QtGui.QFrame.Sunken) - self.line_2.setObjectName("line_2") - self.gridLayoutWidget_4 = QtGui.QWidget(Dialog) - self.gridLayoutWidget_4.setGeometry(QtCore.QRect(10, 390, 291, 80)) - self.gridLayoutWidget_4.setObjectName("gridLayoutWidget_4") - self.gridLayout_4 = QtGui.QGridLayout(self.gridLayoutWidget_4) - self.gridLayout_4.setContentsMargins(0, 0, 0, 0) - self.gridLayout_4.setObjectName("gridLayout_4") - self.label_15 = QtGui.QLabel(self.gridLayoutWidget_4) - self.label_15.setObjectName("label_15") - self.gridLayout_4.addWidget(self.label_15, 0, 0, 1, 1) - self.customgrid = QtGui.QLineEdit(self.gridLayoutWidget_4) - self.customgrid.setObjectName("customgrid") - self.gridLayout_4.addWidget(self.customgrid, 0, 1, 1, 1) - self.browse_customgrid = QtGui.QPushButton(self.gridLayoutWidget_4) - self.browse_customgrid.setObjectName("browse_customgrid") - self.gridLayout_4.addWidget(self.browse_customgrid, 0, 2, 1, 1) - self.label_16 = QtGui.QLabel(self.gridLayoutWidget_4) - self.label_16.setObjectName("label_16") - self.gridLayout_4.addWidget(self.label_16, 1, 0, 1, 1) - self.simuldir = QtGui.QLineEdit(self.gridLayoutWidget_4) - self.simuldir.setObjectName("simuldir") - self.gridLayout_4.addWidget(self.simuldir, 1, 1, 1, 1) - self.browse_simuldir = QtGui.QPushButton(self.gridLayoutWidget_4) - self.browse_simuldir.setObjectName("browse_simuldir") - self.gridLayout_4.addWidget(self.browse_simuldir, 1, 2, 1, 1) - self.line_3 = QtGui.QFrame(Dialog) - self.line_3.setGeometry(QtCore.QRect(10, 370, 301, 20)) - self.line_3.setFrameShape(QtGui.QFrame.HLine) - self.line_3.setFrameShadow(QtGui.QFrame.Sunken) - self.line_3.setObjectName("line_3") - self.line_4 = QtGui.QFrame(Dialog) - self.line_4.setGeometry(QtCore.QRect(10, 90, 301, 20)) - self.line_4.setFrameShape(QtGui.QFrame.HLine) - self.line_4.setFrameShadow(QtGui.QFrame.Sunken) - self.line_4.setObjectName("line_4") - self.line_5 = QtGui.QFrame(Dialog) - self.line_5.setGeometry(QtCore.QRect(10, 40, 301, 20)) - self.line_5.setFrameShape(QtGui.QFrame.HLine) - self.line_5.setFrameShadow(QtGui.QFrame.Sunken) - self.line_5.setObjectName("line_5") - self.horizontalLayoutWidget_2 = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(220, 60, 81, 31)) - self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2") - self.horizontalLayout_2 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_2) - self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.label_14 = QtGui.QLabel(self.horizontalLayoutWidget_2) - self.label_14.setObjectName("label_14") - self.horizontalLayout_2.addWidget(self.label_14) - self.nproc = QtGui.QLineEdit(self.horizontalLayoutWidget_2) - self.nproc.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.nproc.setObjectName("nproc") - self.horizontalLayout_2.addWidget(self.nproc) - self.label_18 = QtGui.QLabel(Dialog) - self.label_18.setGeometry(QtCore.QRect(10, 102, 271, 16)) - self.label_18.setObjectName("label_18") - self.horizontalLayoutWidget_5 = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget_5.setGeometry(QtCore.QRect(10, 120, 291, 32)) - self.horizontalLayoutWidget_5.setObjectName("horizontalLayoutWidget_5") - self.horizontalLayout_5 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_5) - self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.label_19 = QtGui.QLabel(self.horizontalLayoutWidget_5) - self.label_19.setObjectName("label_19") - self.horizontalLayout_5.addWidget(self.label_19) - self.seisarray = QtGui.QLineEdit(self.horizontalLayoutWidget_5) - self.seisarray.setObjectName("seisarray") - self.horizontalLayout_5.addWidget(self.seisarray) - self.browse_seisarray = QtGui.QPushButton(self.horizontalLayoutWidget_5) - self.browse_seisarray.setObjectName("browse_seisarray") - self.horizontalLayout_5.addWidget(self.browse_seisarray) - - self.retranslateUi(Dialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) - QtCore.QMetaObject.connectSlotsByName(Dialog) - - def retranslateUi(self, Dialog): - Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setText(QtGui.QApplication.translate("Dialog", "Number of Ponts", None, QtGui.QApplication.UnicodeUTF8)) - self.label_5.setText(QtGui.QApplication.translate("Dialog", "X", None, QtGui.QApplication.UnicodeUTF8)) - self.label_6.setText(QtGui.QApplication.translate("Dialog", "Y", None, QtGui.QApplication.UnicodeUTF8)) - self.label_7.setText(QtGui.QApplication.translate("Dialog", "Z", None, QtGui.QApplication.UnicodeUTF8)) - self.label_8.setText(QtGui.QApplication.translate("Dialog", "Propagation Grid", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_x.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_y.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_z.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.label_9.setText(QtGui.QApplication.translate("Dialog", "Inversion Grid", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_x.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_y.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_z.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setText(QtGui.QApplication.translate("Dialog", "Bottom boundary", None, QtGui.QApplication.UnicodeUTF8)) - self.bbot.setText(QtGui.QApplication.translate("Dialog", "-50", None, QtGui.QApplication.UnicodeUTF8)) - self.label_11.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("Dialog", "Top boundary", None, QtGui.QApplication.UnicodeUTF8)) - self.btop.setText(QtGui.QApplication.translate("Dialog", "5", None, QtGui.QApplication.UnicodeUTF8)) - self.label_10.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("Dialog", "Iterations", None, QtGui.QApplication.UnicodeUTF8)) - self.label_13.setText(QtGui.QApplication.translate("Dialog", "Cushion factor", None, QtGui.QApplication.UnicodeUTF8)) - self.cushion.setText(QtGui.QApplication.translate("Dialog", "10", None, QtGui.QApplication.UnicodeUTF8)) - self.label_12.setText(QtGui.QApplication.translate("Dialog", "%", None, QtGui.QApplication.UnicodeUTF8)) - self.label_17.setText(QtGui.QApplication.translate("Dialog", "FMTOMO\n" -"installation", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_tomodir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_15.setText(QtGui.QApplication.translate("Dialog", "Custom velocity\n" -"grid (earthmodel)", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_customgrid.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_16.setText(QtGui.QApplication.translate("Dialog", "Simulation\n" -"Directory", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_simuldir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_14.setText(QtGui.QApplication.translate("Dialog", "nproc", None, QtGui.QApplication.UnicodeUTF8)) - self.nproc.setText(QtGui.QApplication.translate("Dialog", "1", None, QtGui.QApplication.UnicodeUTF8)) - self.label_18.setText(QtGui.QApplication.translate("Dialog", "Grid definitions from seismic Array:", None, QtGui.QApplication.UnicodeUTF8)) - self.label_19.setText(QtGui.QApplication.translate("Dialog", "Seismic\n" -"Array", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_seisarray.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py new file mode 100644 index 00000000..99173ff5 --- /dev/null +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -0,0 +1,281 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'fmtomo_parameters.ui' +# +# Created: Wed Jun 15 11:56:04 2016 +# by: pyside-uic 0.2.15 running on PySide 1.2.2 +# +# WARNING! All changes made in this file will be lost! + +from PySide import QtCore, QtGui + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(320, 520) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) + Dialog.setSizePolicy(sizePolicy) + Dialog.setMinimumSize(QtCore.QSize(320, 520)) + Dialog.setMaximumSize(QtCore.QSize(320, 520)) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + Dialog.setWindowIcon(icon) + self.buttonBox = QtGui.QDialogButtonBox(Dialog) + self.buttonBox.setGeometry(QtCore.QRect(10, 480, 301, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayoutWidget = QtGui.QWidget(Dialog) + self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 240, 281, 79)) + self.gridLayoutWidget.setObjectName("gridLayoutWidget") + self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.label_4 = QtGui.QLabel(self.gridLayoutWidget) + self.label_4.setObjectName("label_4") + self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) + self.label_5 = QtGui.QLabel(self.gridLayoutWidget) + self.label_5.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_5.setAlignment(QtCore.Qt.AlignCenter) + self.label_5.setObjectName("label_5") + self.gridLayout.addWidget(self.label_5, 0, 1, 1, 1) + self.label_6 = QtGui.QLabel(self.gridLayoutWidget) + self.label_6.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_6.setAlignment(QtCore.Qt.AlignCenter) + self.label_6.setObjectName("label_6") + self.gridLayout.addWidget(self.label_6, 0, 2, 1, 1) + self.label_7 = QtGui.QLabel(self.gridLayoutWidget) + self.label_7.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_7.setAlignment(QtCore.Qt.AlignCenter) + self.label_7.setObjectName("label_7") + self.gridLayout.addWidget(self.label_7, 0, 3, 1, 1) + self.label_8 = QtGui.QLabel(self.gridLayoutWidget) + self.label_8.setObjectName("label_8") + self.gridLayout.addWidget(self.label_8, 1, 0, 1, 1) + self.pgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) + self.pgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_x.setObjectName("pgrid_x") + self.gridLayout.addWidget(self.pgrid_x, 1, 1, 1, 1) + self.pgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) + self.pgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_y.setObjectName("pgrid_y") + self.gridLayout.addWidget(self.pgrid_y, 1, 2, 1, 1) + self.pgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) + self.pgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_z.setObjectName("pgrid_z") + self.gridLayout.addWidget(self.pgrid_z, 1, 3, 1, 1) + self.label_9 = QtGui.QLabel(self.gridLayoutWidget) + self.label_9.setObjectName("label_9") + self.gridLayout.addWidget(self.label_9, 2, 0, 1, 1) + self.invgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) + self.invgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_x.setObjectName("invgrid_x") + self.gridLayout.addWidget(self.invgrid_x, 2, 1, 1, 1) + self.invgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) + self.invgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_y.setObjectName("invgrid_y") + self.gridLayout.addWidget(self.invgrid_y, 2, 2, 1, 1) + self.invgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) + self.invgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_z.setObjectName("invgrid_z") + self.gridLayout.addWidget(self.invgrid_z, 2, 3, 1, 1) + self.gridLayoutWidget_2 = QtGui.QWidget(Dialog) + self.gridLayoutWidget_2.setGeometry(QtCore.QRect(10, 160, 201, 61)) + self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2") + self.gridLayout_2 = QtGui.QGridLayout(self.gridLayoutWidget_2) + self.gridLayout_2.setContentsMargins(0, 0, 0, 0) + self.gridLayout_2.setObjectName("gridLayout_2") + self.label_3 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_3.setObjectName("label_3") + self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) + self.bbot = QtGui.QLineEdit(self.gridLayoutWidget_2) + self.bbot.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.bbot.setObjectName("bbot") + self.gridLayout_2.addWidget(self.bbot, 1, 1, 1, 1) + self.label_11 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_11.setObjectName("label_11") + self.gridLayout_2.addWidget(self.label_11, 1, 2, 1, 1) + self.label_2 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_2.setObjectName("label_2") + self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) + self.btop = QtGui.QLineEdit(self.gridLayoutWidget_2) + self.btop.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.btop.setObjectName("btop") + self.gridLayout_2.addWidget(self.btop, 0, 1, 1, 1) + self.label_10 = QtGui.QLabel(self.gridLayoutWidget_2) + self.label_10.setObjectName("label_10") + self.gridLayout_2.addWidget(self.label_10, 0, 2, 1, 1) + self.gridLayoutWidget_3 = QtGui.QWidget(Dialog) + self.gridLayoutWidget_3.setGeometry(QtCore.QRect(10, 60, 201, 31)) + self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3") + self.gridLayout_3 = QtGui.QGridLayout(self.gridLayoutWidget_3) + self.gridLayout_3.setContentsMargins(0, 0, 0, 0) + self.gridLayout_3.setObjectName("gridLayout_3") + self.nIter = QtGui.QSpinBox(self.gridLayoutWidget_3) + self.nIter.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.nIter.setProperty("value", 1) + self.nIter.setObjectName("nIter") + self.gridLayout_3.addWidget(self.nIter, 0, 1, 1, 1) + self.label = QtGui.QLabel(self.gridLayoutWidget_3) + self.label.setObjectName("label") + self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1) + self.horizontalLayoutWidget = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 340, 161, 31)) + self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget") + self.horizontalLayout = QtGui.QHBoxLayout(self.horizontalLayoutWidget) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_13 = QtGui.QLabel(self.horizontalLayoutWidget) + self.label_13.setObjectName("label_13") + self.horizontalLayout.addWidget(self.label_13) + self.cushion = QtGui.QLineEdit(self.horizontalLayoutWidget) + self.cushion.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.cushion.setObjectName("cushion") + self.horizontalLayout.addWidget(self.cushion) + self.label_12 = QtGui.QLabel(self.horizontalLayoutWidget) + self.label_12.setObjectName("label_12") + self.horizontalLayout.addWidget(self.label_12) + self.horizontalLayoutWidget_4 = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(10, 10, 291, 32)) + self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4") + self.horizontalLayout_4 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_4) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_17 = QtGui.QLabel(self.horizontalLayoutWidget_4) + self.label_17.setObjectName("label_17") + self.horizontalLayout_4.addWidget(self.label_17) + self.fmtomo_dir = QtGui.QLineEdit(self.horizontalLayoutWidget_4) + self.fmtomo_dir.setObjectName("fmtomo_dir") + self.horizontalLayout_4.addWidget(self.fmtomo_dir) + self.browse_tomodir = QtGui.QPushButton(self.horizontalLayoutWidget_4) + self.browse_tomodir.setObjectName("browse_tomodir") + self.horizontalLayout_4.addWidget(self.browse_tomodir) + self.line = QtGui.QFrame(Dialog) + self.line.setGeometry(QtCore.QRect(7, 220, 301, 20)) + self.line.setFrameShape(QtGui.QFrame.HLine) + self.line.setFrameShadow(QtGui.QFrame.Sunken) + self.line.setObjectName("line") + self.line_2 = QtGui.QFrame(Dialog) + self.line_2.setGeometry(QtCore.QRect(10, 320, 301, 20)) + self.line_2.setFrameShape(QtGui.QFrame.HLine) + self.line_2.setFrameShadow(QtGui.QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.gridLayoutWidget_4 = QtGui.QWidget(Dialog) + self.gridLayoutWidget_4.setGeometry(QtCore.QRect(10, 390, 291, 80)) + self.gridLayoutWidget_4.setObjectName("gridLayoutWidget_4") + self.gridLayout_4 = QtGui.QGridLayout(self.gridLayoutWidget_4) + self.gridLayout_4.setContentsMargins(0, 0, 0, 0) + self.gridLayout_4.setObjectName("gridLayout_4") + self.label_15 = QtGui.QLabel(self.gridLayoutWidget_4) + self.label_15.setObjectName("label_15") + self.gridLayout_4.addWidget(self.label_15, 0, 0, 1, 1) + self.customgrid = QtGui.QLineEdit(self.gridLayoutWidget_4) + self.customgrid.setObjectName("customgrid") + self.gridLayout_4.addWidget(self.customgrid, 0, 1, 1, 1) + self.browse_customgrid = QtGui.QPushButton(self.gridLayoutWidget_4) + self.browse_customgrid.setObjectName("browse_customgrid") + self.gridLayout_4.addWidget(self.browse_customgrid, 0, 2, 1, 1) + self.label_16 = QtGui.QLabel(self.gridLayoutWidget_4) + self.label_16.setObjectName("label_16") + self.gridLayout_4.addWidget(self.label_16, 1, 0, 1, 1) + self.simuldir = QtGui.QLineEdit(self.gridLayoutWidget_4) + self.simuldir.setObjectName("simuldir") + self.gridLayout_4.addWidget(self.simuldir, 1, 1, 1, 1) + self.browse_simuldir = QtGui.QPushButton(self.gridLayoutWidget_4) + self.browse_simuldir.setObjectName("browse_simuldir") + self.gridLayout_4.addWidget(self.browse_simuldir, 1, 2, 1, 1) + self.line_3 = QtGui.QFrame(Dialog) + self.line_3.setGeometry(QtCore.QRect(10, 370, 301, 20)) + self.line_3.setFrameShape(QtGui.QFrame.HLine) + self.line_3.setFrameShadow(QtGui.QFrame.Sunken) + self.line_3.setObjectName("line_3") + self.line_4 = QtGui.QFrame(Dialog) + self.line_4.setGeometry(QtCore.QRect(10, 90, 301, 20)) + self.line_4.setFrameShape(QtGui.QFrame.HLine) + self.line_4.setFrameShadow(QtGui.QFrame.Sunken) + self.line_4.setObjectName("line_4") + self.line_5 = QtGui.QFrame(Dialog) + self.line_5.setGeometry(QtCore.QRect(10, 40, 301, 20)) + self.line_5.setFrameShape(QtGui.QFrame.HLine) + self.line_5.setFrameShadow(QtGui.QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.horizontalLayoutWidget_2 = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(220, 60, 81, 31)) + self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2") + self.horizontalLayout_2 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_2) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.label_14 = QtGui.QLabel(self.horizontalLayoutWidget_2) + self.label_14.setObjectName("label_14") + self.horizontalLayout_2.addWidget(self.label_14) + self.nproc = QtGui.QLineEdit(self.horizontalLayoutWidget_2) + self.nproc.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.nproc.setObjectName("nproc") + self.horizontalLayout_2.addWidget(self.nproc) + self.label_18 = QtGui.QLabel(Dialog) + self.label_18.setGeometry(QtCore.QRect(10, 102, 271, 16)) + self.label_18.setObjectName("label_18") + self.horizontalLayoutWidget_5 = QtGui.QWidget(Dialog) + self.horizontalLayoutWidget_5.setGeometry(QtCore.QRect(10, 120, 291, 32)) + self.horizontalLayoutWidget_5.setObjectName("horizontalLayoutWidget_5") + self.horizontalLayout_5 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_5) + self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.label_19 = QtGui.QLabel(self.horizontalLayoutWidget_5) + self.label_19.setObjectName("label_19") + self.horizontalLayout_5.addWidget(self.label_19) + self.seisarray = QtGui.QLineEdit(self.horizontalLayoutWidget_5) + self.seisarray.setObjectName("seisarray") + self.horizontalLayout_5.addWidget(self.seisarray) + self.browse_seisarray = QtGui.QPushButton(self.horizontalLayoutWidget_5) + self.browse_seisarray.setObjectName("browse_seisarray") + self.horizontalLayout_5.addWidget(self.browse_seisarray) + + self.retranslateUi(Dialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Choose FMTOMO parameters", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("Dialog", "Number of Ponts", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("Dialog", "X", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setText(QtGui.QApplication.translate("Dialog", "Y", None, QtGui.QApplication.UnicodeUTF8)) + self.label_7.setText(QtGui.QApplication.translate("Dialog", "Z", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setText(QtGui.QApplication.translate("Dialog", "Propagation Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_x.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_y.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_z.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.label_9.setText(QtGui.QApplication.translate("Dialog", "Inversion Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_x.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_y.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_z.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("Dialog", "Bottom boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.bbot.setText(QtGui.QApplication.translate("Dialog", "-50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_11.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("Dialog", "Top boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.btop.setText(QtGui.QApplication.translate("Dialog", "5", None, QtGui.QApplication.UnicodeUTF8)) + self.label_10.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("Dialog", "Iterations", None, QtGui.QApplication.UnicodeUTF8)) + self.label_13.setText(QtGui.QApplication.translate("Dialog", "Cushion factor", None, QtGui.QApplication.UnicodeUTF8)) + self.cushion.setText(QtGui.QApplication.translate("Dialog", "10", None, QtGui.QApplication.UnicodeUTF8)) + self.label_12.setText(QtGui.QApplication.translate("Dialog", "%", None, QtGui.QApplication.UnicodeUTF8)) + self.label_17.setText(QtGui.QApplication.translate("Dialog", "FMTOMO\n" +"installation", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_tomodir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_15.setText(QtGui.QApplication.translate("Dialog", "Custom velocity\n" +"grid (earthmodel)", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_customgrid.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_16.setText(QtGui.QApplication.translate("Dialog", "Simulation\n" +"Directory", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_simuldir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_14.setText(QtGui.QApplication.translate("Dialog", "nproc", None, QtGui.QApplication.UnicodeUTF8)) + self.nproc.setText(QtGui.QApplication.translate("Dialog", "1", None, QtGui.QApplication.UnicodeUTF8)) + self.label_18.setText(QtGui.QApplication.translate("Dialog", "Grid definitions from seismic Array:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_19.setText(QtGui.QApplication.translate("Dialog", "Seismic\n" +"Array", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_seisarray.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + diff --git a/pylot/core/active/generate_survey_layout.py b/pylot/core/active/generate_survey_layout.py new file mode 100644 index 00000000..7358d086 --- /dev/null +++ b/pylot/core/active/generate_survey_layout.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'generate_survey.ui' +# +# Created: Wed Jun 15 11:56:01 2016 +# by: pyside-uic 0.2.15 running on PySide 1.2.2 +# +# WARNING! All changes made in this file will be lost! + +from PySide import QtCore, QtGui + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(380, 160) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + Dialog.setWindowIcon(icon) + self.verticalLayout = QtGui.QVBoxLayout(Dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.lineEdit_rec = QtGui.QLineEdit(Dialog) + self.lineEdit_rec.setObjectName("lineEdit_rec") + self.gridLayout.addWidget(self.lineEdit_rec, 0, 1, 1, 1) + self.pushButton_rec = QtGui.QPushButton(Dialog) + self.pushButton_rec.setObjectName("pushButton_rec") + self.gridLayout.addWidget(self.pushButton_rec, 0, 2, 1, 1) + self.label_rec = QtGui.QLabel(Dialog) + self.label_rec.setObjectName("label_rec") + self.gridLayout.addWidget(self.label_rec, 0, 0, 1, 1) + self.lineEdit_obs = QtGui.QLineEdit(Dialog) + self.lineEdit_obs.setObjectName("lineEdit_obs") + self.gridLayout.addWidget(self.lineEdit_obs, 2, 1, 1, 1) + self.label_obs = QtGui.QLabel(Dialog) + self.label_obs.setObjectName("label_obs") + self.gridLayout.addWidget(self.label_obs, 2, 0, 1, 1) + self.pushButton_obs = QtGui.QPushButton(Dialog) + self.pushButton_obs.setObjectName("pushButton_obs") + self.gridLayout.addWidget(self.pushButton_obs, 2, 2, 1, 1) + self.label_src = QtGui.QLabel(Dialog) + self.label_src.setObjectName("label_src") + self.gridLayout.addWidget(self.label_src, 1, 0, 1, 1) + self.lineEdit_src = QtGui.QLineEdit(Dialog) + self.lineEdit_src.setObjectName("lineEdit_src") + self.gridLayout.addWidget(self.lineEdit_src, 1, 1, 1, 1) + self.pushButton_src = QtGui.QPushButton(Dialog) + self.pushButton_src.setObjectName("pushButton_src") + self.gridLayout.addWidget(self.pushButton_src, 1, 2, 1, 1) + self.verticalLayout.addLayout(self.gridLayout) + self.buttonBox = QtGui.QDialogButtonBox(Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(Dialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_rec.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_rec.setText(QtGui.QApplication.translate("Dialog", "Receiver\n" +"File", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setText(QtGui.QApplication.translate("Dialog", "Seismogram\n" +"Directory", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_obs.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setText(QtGui.QApplication.translate("Dialog", "Source\n" +"File", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_src.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py new file mode 100644 index 00000000..e14c0bf4 --- /dev/null +++ b/pylot/core/active/picking_parameters_layout.py @@ -0,0 +1,72 @@ +class Ui_Picking_parameters(object): + def setupUi(self, Picking_parameters): + Picking_parameters.setObjectName("Picking_parameters") + Picking_parameters.resize(300, 300) + self.gridLayoutWidget = QtGui.QWidget(Picking_parameters) + self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 20, 250, 211)) + self.gridLayoutWidget.setObjectName("gridLayoutWidget") + self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.label_cores = QtGui.QLabel(self.gridLayoutWidget) + self.label_cores.setObjectName("label_cores") + self.gridLayout.addWidget(self.label_cores, 0, 0, 1, 1) + self.label_vmax = QtGui.QLabel(self.gridLayoutWidget) + self.label_vmax.setObjectName("label_vmax") + self.gridLayout.addWidget(self.label_vmax, 2, 0, 1, 1) + self.label_vmin = QtGui.QLabel(self.gridLayoutWidget) + self.label_vmin.setObjectName("label_vmin") + self.gridLayout.addWidget(self.label_vmin, 1, 0, 1, 1) + self.lineEdit_ncores = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_ncores.setObjectName("lineEdit_ncores") + self.gridLayout.addWidget(self.lineEdit_ncores, 0, 1, 1, 1) + self.lineEdit_vmin = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_vmin.setObjectName("lineEdit_vmin") + self.gridLayout.addWidget(self.lineEdit_vmin, 1, 1, 1, 1) + self.lineEdit_vmax = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_vmax.setObjectName("lineEdit_vmax") + self.gridLayout.addWidget(self.lineEdit_vmax, 2, 1, 1, 1) + self.checkBox = QtGui.QCheckBox(self.gridLayoutWidget) + self.checkBox.setObjectName("checkBox") + self.gridLayout.addWidget(self.checkBox, 4, 1, 1, 1) + self.label_folm = QtGui.QLabel(self.gridLayoutWidget) + self.label_folm.setObjectName("label_folm") + self.gridLayout.addWidget(self.label_folm, 3, 0, 1, 1) + self.label_aic = QtGui.QLabel(self.gridLayoutWidget) + self.label_aic.setObjectName("label_aic") + self.gridLayout.addWidget(self.label_aic, 4, 0, 1, 1) + self.lineEdit_folm = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_folm.setObjectName("lineEdit_folm") + self.gridLayout.addWidget(self.lineEdit_folm, 3, 1, 1, 1) + self.label_aicwindow = QtGui.QLabel(self.gridLayoutWidget) + self.label_aicwindow.setObjectName("label_aicwindow") + self.gridLayout.addWidget(self.label_aicwindow, 5, 0, 1, 1) + self.lineEdit_aicwindow = QtGui.QLineEdit(self.gridLayoutWidget) + self.lineEdit_aicwindow.setObjectName("lineEdit_aicwindow") + self.gridLayout.addWidget(self.lineEdit_aicwindow, 5, 1, 1, 1) + self.buttonBox = QtGui.QDialogButtonBox(Picking_parameters) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.buttonBox.setGeometry(QtCore.QRect(10, 240, 250, 32)) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Picking_parameters.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Picking_parameters.reject) + self.retranslateUi(Picking_parameters) + QtCore.QMetaObject.connectSlotsByName(Picking_parameters) + + def retranslateUi(self, Picking_parameters): + Picking_parameters.setWindowTitle(QtGui.QApplication.translate("Picking_parameters", "Picking_parameters", None, QtGui.QApplication.UnicodeUTF8)) + self.label_cores.setText(QtGui.QApplication.translate("Picking_parameters", "Number of cores", None, QtGui.QApplication.UnicodeUTF8)) + self.label_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "Vmax (default = 5000m/s)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "Vmin (default = 333 m/s)", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_ncores.setText(QtGui.QApplication.translate("Picking_parameters", "1", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "333", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) + self.label_folm.setText(QtGui.QApplication.translate("Picking_parameters", "Fraction of local maximum\n" +"(default = 0.6)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_aic.setText(QtGui.QApplication.translate("Picking_parameters", "AIC", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_folm.setText(QtGui.QApplication.translate("Picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) + self.label_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "AIC window (only if AIC\n" +" is checked)", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "15, 0", None, QtGui.QApplication.UnicodeUTF8)) From b3acef0bcd4bd98423578c5e332ec17d4412e619 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Wed, 15 Jun 2016 14:52:42 +0200 Subject: [PATCH 0970/1144] [closes #198] comparison dialog now available from QtPyLoT --- pylot/core/util/pdf.py | 20 ++------- pylot/core/util/utils.py | 37 +++++++++++++++ pylot/core/util/widgets.py | 92 +++++++++++++++++++++++--------------- 3 files changed, 97 insertions(+), 52 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 2882dec4..c2956e90 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -4,7 +4,7 @@ import warnings import numpy as np from obspy import UTCDateTime -from pylot.core.util.utils import find_nearest +from pylot.core.util.utils import find_nearest, clims from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -329,28 +329,14 @@ class ProbabilityDensityFunction(object): :param r2: :param max_npts: :return: - ''' - # >>> manu = ProbabilityDensityFunction.from_pick(0.01, 0.3, 0.5, 0.54) - # >>> auto = ProbabilityDensityFunction.from_pick(0.01, 0.3, 0.34, 0.54) - # >>> manu.commonlimits(0.01, auto) - # ( - l1, r1 = self.limits() - l2, r2 = other.limits() - - if l1 < l2: - x0 = l1 - else: - x0 = l2 + x0, r = clims(self.limits(), other.limits()) # calculate index for rounding ri = self.precision(incr) - if r1 < r2: - npts = int(round(r2 - x0, ri) // incr) - else: - npts = int(round(r1 - x0, ri) // incr) + npts = int(round(r - x0, ri) // incr) if npts > max_npts: raise ValueError('Maximum number of points exceeded:\n' diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 5d7d2b2a..21976607 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -31,6 +31,43 @@ def worker(func, input, cores='max', async=False): pool.close() return result +def clims(lim1, lim2): + """ + takes two pairs of limits and returns one pair of common limts + :param lim1: + :param lim2: + :return: + + >>> clims([0, 4], [1, 3]) + [0, 4] + >>> clims([1, 4], [0, 3]) + [0, 4] + >>> clims([1, 3], [0, 4]) + [0, 4] + >>> clims([0, 3], [1, 4]) + [0, 4] + >>> clims([0, 3], [0, 4]) + [0, 4] + >>> clims([1, 4], [0, 4]) + [0, 4] + >>> clims([0, 4], [0, 4]) + [0, 4] + >>> clims([0, 4], [1, 4]) + [0, 4] + >>> clims([0, 4], [0, 3]) + [0, 4] + """ + lim = [None, None] + if lim1[0] < lim2[0]: + lim[0] = lim1[0] + else: + lim[0] = lim2[0] + if lim1[1] > lim2[1]: + lim[1] = lim1[1] + else: + lim[1] = lim2[1] + return lim + def demeanTrace(trace, window): """ diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 2b0dcaee..28cc1e85 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -6,6 +6,7 @@ Created on Wed Mar 19 11:27:35 2014 """ import warnings +import copy import datetime import numpy as np @@ -32,7 +33,7 @@ from pylot.core.pick.compare import Comparison from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS, \ COMPPOSITION_MAP from pylot.core.util.utils import prepTimeAxis, getGlobalTimes, scaleWFData, \ - demeanTrace, isSorted, findComboBoxIndex + demeanTrace, isSorted, findComboBoxIndex, clims def getDataType(parent): @@ -46,6 +47,19 @@ def getDataType(parent): return type +def plot_pdf(_axes, x, y, annotation, bbox_props, xlabel=None, ylabel=None, + title=None): + _axes.plot(x, y) + if title: + _axes.set_title(title) + if xlabel: + _axes.set_xlabel(xlabel) + if ylabel: + _axes.set_ylabel(ylabel) + _anno = _axes.annotate(annotation, xy=(.05, .5), xycoords='axes fraction') + _anno.set_bbox(bbox_props) + + return _axes def createAction(parent, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False): @@ -214,55 +228,63 @@ class ComparisonDialog(QDialog): _ax1 = self.canvas.figure.add_subplot(_gs[2, 0]) _ax2 = self.canvas.figure.add_subplot(_gs[2, 1]) - _axes.cla() + #_axes.cla() station = self.plotprops['station'] phase = self.plotprops['phase'] pdf = self.data.comparison[station][phase] x, y, std, exp = pdf.axis, pdf.data, pdf.standard_deviation(), \ pdf.expectation() - _axes.plot(x, y) - _axes.set_title(phase) - _axes.set_ylabel('propability density [-]') - _axes.set_xlabel('time difference [s]') + annotation = "{phase} difference on {station}\n" \ - "expectation: {exp}\n" \ - "std: {std}".format(station=station, phase=phase, - std=std, exp=exp) - _anno = _axes.annotate(annotation, xy=(.05, .5), xycoords='axes ' - 'fraction') + "expectation: {exp}\n" \ + "std: {std}".format(station=station, phase=phase, + std=std, exp=exp) bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) - _anno.set_bbox(bbox_props) - pdf_a = self.data.get('auto')[station][phase] - pdf_m = self.data.get('manu')[station][phase] + plot_pdf(_axes, x, y, annotation, bbox_props, 'time difference [s]', + 'propability density [-]', phase) - xauto, yauto, stdauto, expauto = pdf_a.axis, pdf_a.data, \ - pdf_a.standard_deviation(), \ - pdf_a.expectation() - xmanu, ymanu, stdmanu, expmanu = pdf_m.axis, pdf_m.data, \ - pdf_m.standard_deviation(), \ - pdf_m.expectation() + pdf_a = copy.deepcopy(self.data.get('auto')[station][phase]) + pdf_m = copy.deepcopy(self.data.get('manu')[station][phase]) - _ax1.plot(xmanu, ymanu) - _ax1.set_xlim(pdf_m.limits()) + xauto, yauto, stdauto, expauto, alim = pdf_a.axis, pdf_a.data, \ + pdf_a.standard_deviation(), \ + pdf_a.expectation(), \ + pdf_a.limits() + xmanu, ymanu, stdmanu, expmanu, mlim = pdf_m.axis, pdf_m.data, \ + pdf_m.standard_deviation(), \ + pdf_m.expectation(), \ + pdf_m.limits() + # find common limits + lims = clims(alim, mlim) + # relative x axis + x0 = lims[0] + xmanu -= x0 + xauto -= x0 + lims = [lim - x0 for lim in lims] + x0 = UTCDateTime(x0) + + # set annotation text mannotation = "probability density for manual pick\n" \ "expectation: {exp}\n" \ - "std: {std}".format(std=stdmanu, exp=expmanu) - _anno = _ax1.annotate(mannotation, xy=(.05, .5), xycoords='axes ' - 'fraction') - bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) - _anno.set_bbox(bbox_props) + "std: {std}".format(std=stdmanu, + exp=expmanu-x0.timestamp) - _ax2.plot(xauto, yauto) - _ax2.set_xlim(pdf_a.limits()) - _ax2.set_ylabel('time [s]') aannotation = "probability density for automatic pick\n" \ "expectation: {exp}\n" \ - "std: {std}".format(std=stdauto, exp=expauto) - _anno = _ax2.annotate(aannotation, xy=(.05, .5), xycoords='axes ' - 'fraction') - bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7) - _anno.set_bbox(bbox_props) + "std: {std}".format(std=stdauto, + exp=expauto-x0.timestamp) + + _ax1 = plot_pdf(_ax1, xmanu, ymanu, mannotation, + bbox_props=bbox_props, xlabel='seconds since ' + '{0}'.format(x0), + ylabel='probability density [-]') + _ax1.set_xlim(lims) + + _ax2 = plot_pdf(_ax2, xauto, yauto, aannotation, + bbox_props=bbox_props, xlabel='seconds since ' + '{0}'.format(x0)) + _ax2.set_xlim(lims) _gs.update(wspace=0.5, hspace=0.5) From 9f71bf808273b166ef4ab2ddcf461d616fe1599b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 16 Jun 2016 13:57:15 +0200 Subject: [PATCH 0971/1144] restructured GUI and added first Seismic Array options --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 249 ++++++--- pylot/core/active/activeSeismoPick.py | 7 +- pylot/core/active/asp3d_layout.py | 239 +++++++-- pylot/core/active/fmtomo_parameters_layout.py | 488 +++++++++--------- .../core/active/generate_seisarray_layout.py | 101 ++++ pylot/core/active/generate_survey_layout.py | 2 +- .../core/active/picking_parameters_layout.py | 11 + pylot/core/active/seismicArrayPreparation.py | 1 + 8 files changed, 747 insertions(+), 351 deletions(-) create mode 100644 pylot/core/active/generate_seisarray_layout.py diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index ce0ecba9..bc941ad6 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -3,7 +3,11 @@ import os from PySide import QtCore, QtGui, QtCore from asp3d_layout import * -from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils +from fmtomo_parameters_layout import * +from generate_survey_layout import * +from generate_seisarray_layout import * +from picking_parameters_layout import * +from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils, seismicArrayPreparation class gui_control(object): def __init__(self): @@ -11,8 +15,23 @@ class gui_control(object): self.mainUI = ui self.connectButtons() self.survey = None - + self.seisarray = None + self.cancelpixmap = self.mainwindow.style().standardPixmap(QtGui.QStyle.SP_DialogCancelButton) + self.applypixmap = self.mainwindow.style().standardPixmap(QtGui.QStyle.SP_DialogApplyButton) + self.setInitStates() + + def setInitStates(self): + self.setPickState(False) + self.setSurveyState(False) + self.setSeisArrayState(False) + self.setConnected2SurveyState(False) + def connectButtons(self): + QtCore.QObject.connect(self.mainUI.gen_new_seisarray, QtCore.SIGNAL("clicked()"), self.gen_seisarray) + QtCore.QObject.connect(self.mainUI.load_seisarray, QtCore.SIGNAL("clicked()"), self.load_seisarray) + QtCore.QObject.connect(self.mainUI.save_seisarray, QtCore.SIGNAL("clicked()"), self.save_seisarray) + QtCore.QObject.connect(self.mainUI.connect_to_survey, QtCore.SIGNAL("clicked()"), self.connect2Survey) + QtCore.QObject.connect(self.mainUI.interpolate_receivers, QtCore.SIGNAL("clicked()"), self.interpolate_receivers) QtCore.QObject.connect(self.mainUI.gen_new_survey, QtCore.SIGNAL("clicked()"), self.gen_survey) QtCore.QObject.connect(self.mainUI.load_survey, QtCore.SIGNAL("clicked()"), self.load_survey) QtCore.QObject.connect(self.mainUI.save_survey, QtCore.SIGNAL("clicked()"), self.save_survey) @@ -20,12 +39,32 @@ class gui_control(object): QtCore.QObject.connect(self.mainUI.postprocessing, QtCore.SIGNAL("clicked()"), self.postprocessing) QtCore.QObject.connect(self.mainUI.fmtomo, QtCore.SIGNAL("clicked()"), self.startFMTOMO) - def gen_survey(self): - if self.checkSurveyState(): - if not self.continueSurveyDialog(): + def gen_seisarray(self): + if self.checkSeisArrayState(): + if not self.continueDialogExists('Seismic Array'): return qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_Dialog() + ui = Ui_generate_seisarray() + ui.setupUi(qdialog) + self.gen_new_seisarray = ui + self.connectButtons_gen_seisarray() + if qdialog.exec_(): + srcfile = self.gen_new_seisarray.lineEdit_src.text() + recfile = self.gen_new_seisarray.lineEdit_rec.text() + ptsfile = self.gen_new_seisarray.lineEdit_pts.text() + self.seisarray = seismicArrayPreparation.SeisArray(recfile) + if len(srcfile) > 0: + self.seisarray.addSourceLocations(srcfile) + if len(ptsfile) > 0: + self.seisarray.addMeasuredTopographyPoints(ptsfile) + self.setSeisArrayState(True) + + def gen_survey(self): + if self.checkSurveyState(): + if not self.continueDialogExists('Survey'): + return + qdialog = QtGui.QDialog(self.mainwindow) + ui = Ui_generate_survey() ui.setupUi(qdialog) self.gen_new_survey = ui self.connectButtons_gen_survey() @@ -37,7 +76,12 @@ class gui_control(object): useDefaultParas = True) self.survey.setArtificialPick(0, 0) # artificial pick at source origin surveyUtils.setDynamicFittedSNR(self.survey.getShotDict()) - self.setSurveyState('active') + self.setSurveyState(True) + + def interpolate_receivers(self): + if not self.seisarray: + print('No Seismic Array defined.') + self.seisarray.interpolateAll() def getPickParameters(self, ui, Picking_parameters): if Picking_parameters.exec_(): @@ -48,11 +92,28 @@ class gui_control(object): AIC = ui.checkBox.isChecked() aicwindow = [int(val) for val in ui.lineEdit_aicwindow.text().split(',')] return ncores, vmin, vmax, folm, AIC, tuple(aicwindow) - - def callPicker(self): - if self.survey is None: + + def connect2Survey(self): + if not self.checkSurveyState(): print('Survey not defined.') return + if not self.checkSeisArrayState(): + print('Got no Seismic Array.') + return + if self.checkConnected2SurveyState(): + if self.continueDialogMessage('Existing Survey already got Seismic Array object. Continue?'): + pass + self.survey.seisarray = self.seisarray + self.setConnected2SurveyState(True) + print('Connected Seismic Array to active Survey object.') + + def callPicker(self): + if not self.checkSurveyState(): + print('Survey not defined.') + return + if self.checkPickState(): + if not self.continueDialogMessage('Survey already picked. Continue?'): + return Picking_parameters = QtGui.QDialog(self.mainwindow) ui = Ui_Picking_parameters() ui.setupUi(Picking_parameters) @@ -69,10 +130,10 @@ class gui_control(object): self.survey.pickAllShots(vmin = vmin, vmax = vmax, folm = folm, HosAic = HosAic, aicwindow = aicwindow, cores = ncores) - self.setPickState('active') + self.setPickState(True) def startFMTOMO(self): - if self.survey is None: + if not self.checkSurveyState(): print('Survey not defined.') return fmtomo_parameters = QtGui.QDialog(self.mainwindow) @@ -95,18 +156,16 @@ class gui_control(object): customgrid = ui.customgrid.text() simuldir = ui.simuldir.text() picks_dir = os.path.join(simuldir, 'picks') - seisarray_loc = ui.seisarray.text() if not os.path.isdir(picks_dir): err = os.mkdir(picks_dir) self.survey.exportFMTOMO(picks_dir) - self.survey.loadArrayFromPickle(seisarray_loc) cwd = os.getcwd() interpolationMethod = 'linear' os.chdir(simuldir) - self.survey.seisArray.generateFMTOMOinputFromArray(propgrid, vgrid, (bbot, btop), cushionfactor, + self.survey.seisarray.generateFMTOMOinputFromArray(propgrid, vgrid, (bbot, btop), cushionfactor, interpolationMethod, customgrid = customgrid, writeVTK = False) os.chdir(cwd) @@ -124,82 +183,136 @@ class gui_control(object): self.fmtomo_parameters_ui.fmtomo_dir.setText(self.browseDir()) def chooseCustomgrid(self): - self.fmtomo_parameters_ui.customgrid.setText(self.browseFile()) + self.fmtomo_parameters_ui.customgrid.setText(self.openFile()) def chooseSimuldir(self): self.fmtomo_parameters_ui.simuldir.setText(self.browseDir()) def chooseSeisarray(self): - self.fmtomo_parameters_ui.seisarray.setText(self.browseFile()) + self.fmtomo_parameters_ui.seisarray.setText(self.openFile()) def postprocessing(self): - if self.survey is None: + if not self.checkSurveyState(): print('Survey not defined.') return self.survey.plotAllPicks() def load_survey(self): if self.checkSurveyState(): - if not self.continueSurveyDialog(): + if not self.continueDialogExists('Survey'): return - filename = self.browseFile() + filename = self.openFile() if filename is None: return self.survey = activeSeismoPick.Survey.from_pickle(filename) - self.setSurveyState('active') + self.setSurveyState(True) if self.survey.picked: - self.setPickState('active') + self.setPickState(True) + else: + self.setPickState(False) + if self.survey.seisarray != None: + self.seisarray = self.survey.seisarray + self.setConnected2SurveyState(True) + self.setSeisArrayState(True) + print('Loaded Survey with active Seismic Array.') + + def load_seisarray(self): + if self.checkSeisArrayState(): + if not self.continueDialogExists('Seismic Array'): + return + filename = self.openFile() + if filename is None: + return + self.seisarray = seismicArrayPreparation.SeisArray.from_pickle(filename) + self.setSeisArrayState(True) + + def save_seisarray(self): + if not self.checkSeisArrayState(): + print('No Seismic Array defined.') + return + filename = self.saveFile() + if filename is None: + return + self.seisarray.saveSeisArray(filename) def save_survey(self): if not self.checkSurveyState(): print('No Survey defined.') return - if self.checkSurveyState: - filename = self.browseFile() - if filename is None: - return - self.survey.saveSurvey(filename) - else: - print('No active Survey.') + filename = self.saveFile() + if filename is None: + return + self.survey.saveSurvey(filename) def setSurveyState(self, state): - if state == 'active': - self.mainUI.survey_active.setCheckable(True) - self.mainUI.survey_active.setChecked(True) - self.mainUI.survey_active.setCheckable(False) - elif state == 'inactive': - self.mainUI.survey_active.setCheckable(True) - self.mainUI.survey_active.setChecked(False) - self.mainUI.survey_active.setCheckable(False) + if state == True: + self.mainUI.survey_active.setPixmap(self.applypixmap) + elif state == False: + self.mainUI.survey_active.setPixmap(self.cancelpixmap) def checkSurveyState(self): - if self.mainUI.survey_active.checkState() == QtCore.Qt.CheckState.Checked: - return True - if self.mainUI.survey_active.checkState() == QtCore.Qt.CheckState.Unchecked: + if self.survey == None: return False + else: + return True + + def checkSeisArrayState(self): + if self.seisarray == None: + return False + else: + return True def setPickState(self, state): - if state == 'active' and self.checkSurveyState(): - self.mainUI.picked_active.setCheckable(True) - self.mainUI.picked_active.setChecked(True) - self.mainUI.picked_active.setCheckable(True) - elif self.checkSurveyState() is False: + if state == True and self.checkSurveyState(): + self.mainUI.picked_active.setPixmap(self.applypixmap) + self.survey.picked = True + elif state == True and self.checkSurveyState() is False: print('No Survey defined.') return - elif state == 'inactive': - self.mainUI.picked_active.setCheckable(True) - self.mainUI.picked_active.setChecked(False) - self.mainUI.picked_active.setCheckable(True) + elif state == False: + self.mainUI.picked_active.setPixmap(self.cancelpixmap) + if self.checkSurveyState(): + self.survey.picked = False - def checkPickState(self): - if self.mainUI.picked_active.checkState() == QtCore.Qt.CheckState.Checked: - return True - if self.mainUI.picked_active.checkState() == QtCore.Qt.CheckState.Unchecked: + def setSeisArrayState(self, state): + if state == True: + self.mainUI.seisarray_active.setPixmap(self.applypixmap) + elif state == False: + self.mainUI.seisarray_active.setPixmap(self.cancelpixmap) + + def setConnected2SurveyState(self, state): + if state == True: + self.mainUI.seisarray_on_survey_active.setPixmap(self.applypixmap) + elif state == False: + self.mainUI.seisarray_on_survey_active.setPixmap(self.cancelpixmap) + + def checkConnected2SurveyState(self): + if self.checkSurveyState(): + if self.survey.seisarray != None: + return True + else: return False - def continueSurveyDialog(self): + def checkPickState(self): + if not self.survey: + print ('No Survey defined.') + return + return self.survey.picked + + def continueDialogExists(self, name): qmb = QtGui.QMessageBox() - qmb.setText('Survey already exists. Overwrite?') + qmb.setText('%s object already exists. Overwrite?'%name) + qmb.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) + qmb.setIcon(QtGui.QMessageBox.Warning) + answer = qmb.exec_() + if answer == 1024: + return True + else: + return False + + def continueDialogMessage(self, message): + qmb = QtGui.QMessageBox() + qmb.setText(message) qmb.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) qmb.setIcon(QtGui.QMessageBox.Warning) answer = qmb.exec_() @@ -213,21 +326,41 @@ class gui_control(object): QtCore.QObject.connect(self.gen_new_survey.pushButton_src, QtCore.SIGNAL("clicked()"), self.chooseSourcefile) QtCore.QObject.connect(self.gen_new_survey.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseObsdir) + def connectButtons_gen_seisarray(self): + QtCore.QObject.connect(self.gen_new_seisarray.pushButton_rec, QtCore.SIGNAL("clicked()"), self.chooseMeasuredRec) + QtCore.QObject.connect(self.gen_new_seisarray.pushButton_src, QtCore.SIGNAL("clicked()"), self.chooseMeasuredSrc) + QtCore.QObject.connect(self.gen_new_seisarray.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseMeasuredPts) + + def chooseMeasuredSrc(self): + self.gen_new_seisarray.lineEdit_src.setText(self.openFile()) + + def chooseMeasuredRec(self): + self.gen_new_seisarray.lineEdit_rec.setText(self.openFile()) + + def chooseMeasuredPts(self): + self.gen_new_seisarray.lineEdit_pts.setText(self.browseDir()) + def chooseSourcefile(self): - self.gen_new_survey.lineEdit_src.setText(self.browseFile()) + self.gen_new_survey.lineEdit_src.setText(self.openFile()) def chooseReceiverfile(self): - self.gen_new_survey.lineEdit_rec.setText(self.browseFile()) + self.gen_new_survey.lineEdit_rec.setText(self.openFile()) def chooseObsdir(self): self.gen_new_survey.lineEdit_obs.setText(self.browseDir()) - def browseFile(self): + def openFile(self): dialog = QtGui.QFileDialog() filename = dialog.getOpenFileName() if len(filename[0]) > 0: return filename[0] + def saveFile(self): + dialog = QtGui.QFileDialog() + filename = dialog.getSaveFileName() + if len(filename[0]) > 0: + return filename[0] + def browseDir(self): dialog = QtGui.QFileDialog() directory = dialog.getExistingDirectory() diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 33736841..3f91c898 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -24,7 +24,7 @@ class Survey(object): creating plots for all shots. ''' self.data = {} - self.seisArray = None + self.seisarray = None self._topography = None self._recfile = receiverfile self._sourcefile = sourcefile @@ -123,16 +123,15 @@ class Survey(object): def loadArrayFromPickle(self, filename): from pylot.core.active.seismicArrayPreparation import SeisArray array = SeisArray.from_pickle(filename) - self.seisArray = array + self.seisarray = array def loadArray(self, path, receiverfile, sourcefile): from pylot.core.active.seismicArrayPreparation import SeisArray array = SeisArray(os.path.join(path, receiverfile)) array.addSourceLocations(os.path.join(path, sourcefile)) - self.seisArray = array + self.seisarray = array - def setManualPicksFromFiles(self, directory='picks'): ''' Read manual picks from *.pck files in a directory. diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index abbb1cb8..1e266f50 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Wed Jun 15 11:55:58 2016 +# Created: Thu Jun 16 12:18:04 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -13,75 +13,227 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.setEnabled(True) - MainWindow.resize(252, 350) + MainWindow.resize(300, 585) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) - MainWindow.setMinimumSize(QtCore.QSize(252, 350)) - MainWindow.setMaximumSize(QtCore.QSize(252, 350)) + MainWindow.setMinimumSize(QtCore.QSize(300, 585)) + MainWindow.setMaximumSize(QtCore.QSize(250000, 350000)) MainWindow.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") - self.postprocessing = QtGui.QPushButton(self.centralwidget) - self.postprocessing.setGeometry(QtCore.QRect(0, 220, 251, 27)) - self.postprocessing.setObjectName("postprocessing") - self.verticalLayoutWidget = QtGui.QWidget(self.centralwidget) - self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 251, 41)) - self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") - self.horizontalLayout = QtGui.QHBoxLayout(self.verticalLayoutWidget) - self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_5 = QtGui.QVBoxLayout(self.centralwidget) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.label_2 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) + self.label_2.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Sans Serif") + font.setPointSize(10) + font.setWeight(75) + font.setBold(True) + self.label_2.setFont(font) + self.label_2.setAlignment(QtCore.Qt.AlignCenter) + self.label_2.setObjectName("label_2") + self.verticalLayout.addWidget(self.label_2) + self.gen_new_seisarray = QtGui.QPushButton(self.centralwidget) + self.gen_new_seisarray.setObjectName("gen_new_seisarray") + self.verticalLayout.addWidget(self.gen_new_seisarray) + self.load_seisarray = QtGui.QPushButton(self.centralwidget) + self.load_seisarray.setObjectName("load_seisarray") + self.verticalLayout.addWidget(self.load_seisarray) + self.save_seisarray = QtGui.QPushButton(self.centralwidget) + self.save_seisarray.setObjectName("save_seisarray") + self.verticalLayout.addWidget(self.save_seisarray) + self.interpolate_receivers = QtGui.QPushButton(self.centralwidget) + self.interpolate_receivers.setObjectName("interpolate_receivers") + self.verticalLayout.addWidget(self.interpolate_receivers) + self.connect_to_survey = QtGui.QPushButton(self.centralwidget) + self.connect_to_survey.setObjectName("connect_to_survey") + self.verticalLayout.addWidget(self.connect_to_survey) + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.seisarray_active = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.seisarray_active.sizePolicy().hasHeightForWidth()) + self.seisarray_active.setSizePolicy(sizePolicy) + self.seisarray_active.setMaximumSize(QtCore.QSize(20, 20)) + self.seisarray_active.setMidLineWidth(0) + self.seisarray_active.setText("") + self.seisarray_active.setObjectName("seisarray_active") + self.horizontalLayout_2.addWidget(self.seisarray_active) + self.label_5 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth()) + self.label_5.setSizePolicy(sizePolicy) + self.label_5.setObjectName("label_5") + self.horizontalLayout_2.addWidget(self.label_5) + self.seisarray_on_survey_active = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.seisarray_on_survey_active.sizePolicy().hasHeightForWidth()) + self.seisarray_on_survey_active.setSizePolicy(sizePolicy) + self.seisarray_on_survey_active.setMaximumSize(QtCore.QSize(20, 20)) + self.seisarray_on_survey_active.setMidLineWidth(0) + self.seisarray_on_survey_active.setText("") + self.seisarray_on_survey_active.setObjectName("seisarray_on_survey_active") + self.horizontalLayout_2.addWidget(self.seisarray_on_survey_active) + self.label_6 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth()) + self.label_6.setSizePolicy(sizePolicy) + self.label_6.setObjectName("label_6") + self.horizontalLayout_2.addWidget(self.label_6) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.verticalLayout_5.addLayout(self.verticalLayout) + self.line = QtGui.QFrame(self.centralwidget) + self.line.setFrameShape(QtGui.QFrame.HLine) + self.line.setFrameShadow(QtGui.QFrame.Sunken) + self.line.setObjectName("line") + self.verticalLayout_5.addWidget(self.line) + self.verticalLayout_2 = QtGui.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label_3 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) + self.label_3.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Sans Serif") + font.setPointSize(10) + font.setWeight(75) + font.setBold(True) + self.label_3.setFont(font) + self.label_3.setAlignment(QtCore.Qt.AlignCenter) + self.label_3.setObjectName("label_3") + self.verticalLayout_2.addWidget(self.label_3) + self.gen_new_survey = QtGui.QPushButton(self.centralwidget) + self.gen_new_survey.setObjectName("gen_new_survey") + self.verticalLayout_2.addWidget(self.gen_new_survey) + self.load_survey = QtGui.QPushButton(self.centralwidget) + self.load_survey.setObjectName("load_survey") + self.verticalLayout_2.addWidget(self.load_survey) + self.save_survey = QtGui.QPushButton(self.centralwidget) + self.save_survey.setObjectName("save_survey") + self.verticalLayout_2.addWidget(self.save_survey) + self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.survey_active = QtGui.QLabel(self.verticalLayoutWidget) + self.survey_active = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.survey_active.sizePolicy().hasHeightForWidth()) + self.survey_active.setSizePolicy(sizePolicy) self.survey_active.setMaximumSize(QtCore.QSize(20, 20)) self.survey_active.setMidLineWidth(0) self.survey_active.setText("") self.survey_active.setObjectName("survey_active") self.horizontalLayout.addWidget(self.survey_active) - self.label_4 = QtGui.QLabel(self.verticalLayoutWidget) + self.label_4 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth()) + self.label_4.setSizePolicy(sizePolicy) self.label_4.setObjectName("label_4") self.horizontalLayout.addWidget(self.label_4) - self.picked_active = QtGui.QLabel(self.verticalLayoutWidget) + self.picked_active = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.picked_active.sizePolicy().hasHeightForWidth()) + self.picked_active.setSizePolicy(sizePolicy) self.picked_active.setMaximumSize(QtCore.QSize(20, 20)) self.picked_active.setMidLineWidth(0) self.picked_active.setText("") self.picked_active.setObjectName("picked_active") self.horizontalLayout.addWidget(self.picked_active) - self.label = QtGui.QLabel(self.verticalLayoutWidget) + self.label = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) + self.label.setSizePolicy(sizePolicy) self.label.setObjectName("label") self.horizontalLayout.addWidget(self.label) - self.gen_new_survey = QtGui.QPushButton(self.centralwidget) - self.gen_new_survey.setGeometry(QtCore.QRect(1, 47, 251, 27)) - self.gen_new_survey.setObjectName("gen_new_survey") - self.line = QtGui.QFrame(self.centralwidget) - self.line.setGeometry(QtCore.QRect(1, 160, 249, 16)) - self.line.setFrameShape(QtGui.QFrame.HLine) - self.line.setFrameShadow(QtGui.QFrame.Sunken) - self.line.setObjectName("line") - self.load_survey = QtGui.QPushButton(self.centralwidget) - self.load_survey.setGeometry(QtCore.QRect(1, 86, 251, 27)) - self.load_survey.setObjectName("load_survey") - self.save_survey = QtGui.QPushButton(self.centralwidget) - self.save_survey.setGeometry(QtCore.QRect(1, 125, 251, 27)) - self.save_survey.setObjectName("save_survey") + self.verticalLayout_2.addLayout(self.horizontalLayout) + self.verticalLayout_5.addLayout(self.verticalLayout_2) + self.line_3 = QtGui.QFrame(self.centralwidget) + self.line_3.setFrameShape(QtGui.QFrame.HLine) + self.line_3.setFrameShadow(QtGui.QFrame.Sunken) + self.line_3.setObjectName("line_3") + self.verticalLayout_5.addWidget(self.line_3) + self.verticalLayout_3 = QtGui.QVBoxLayout() + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.label_7 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth()) + self.label_7.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Sans Serif") + font.setPointSize(10) + font.setWeight(75) + font.setBold(True) + self.label_7.setFont(font) + self.label_7.setAlignment(QtCore.Qt.AlignCenter) + self.label_7.setObjectName("label_7") + self.verticalLayout_3.addWidget(self.label_7) self.picker = QtGui.QPushButton(self.centralwidget) - self.picker.setGeometry(QtCore.QRect(1, 179, 251, 27)) self.picker.setObjectName("picker") + self.verticalLayout_3.addWidget(self.picker) + self.postprocessing = QtGui.QPushButton(self.centralwidget) + self.postprocessing.setObjectName("postprocessing") + self.verticalLayout_3.addWidget(self.postprocessing) + self.verticalLayout_5.addLayout(self.verticalLayout_3) self.line_2 = QtGui.QFrame(self.centralwidget) - self.line_2.setGeometry(QtCore.QRect(1, 250, 249, 16)) self.line_2.setFrameShape(QtGui.QFrame.HLine) self.line_2.setFrameShadow(QtGui.QFrame.Sunken) self.line_2.setObjectName("line_2") + self.verticalLayout_5.addWidget(self.line_2) + self.verticalLayout_4 = QtGui.QVBoxLayout() + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.label_8 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_8.sizePolicy().hasHeightForWidth()) + self.label_8.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Sans Serif") + font.setPointSize(10) + font.setWeight(75) + font.setBold(True) + self.label_8.setFont(font) + self.label_8.setAlignment(QtCore.Qt.AlignCenter) + self.label_8.setObjectName("label_8") + self.verticalLayout_4.addWidget(self.label_8) self.fmtomo = QtGui.QPushButton(self.centralwidget) - self.fmtomo.setGeometry(QtCore.QRect(1, 270, 251, 27)) self.fmtomo.setObjectName("fmtomo") + self.verticalLayout_4.addWidget(self.fmtomo) + self.verticalLayout_5.addLayout(self.verticalLayout_4) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 252, 23)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtGui.QStatusBar(MainWindow) @@ -93,12 +245,23 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "ActiveSeismoPick 3D", None, QtGui.QApplication.UnicodeUTF8)) - self.postprocessing.setText(QtGui.QApplication.translate("MainWindow", "Postprocessing", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setText(QtGui.QApplication.translate("MainWindow", "Survey active", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("MainWindow", "Survey picked", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("MainWindow", "Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.gen_new_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Generate new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.load_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Load Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.save_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Save Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.interpolate_receivers.setText(QtGui.QApplication.translate("MainWindow", "Interpolate Receivers", None, QtGui.QApplication.UnicodeUTF8)) + self.connect_to_survey.setText(QtGui.QApplication.translate("MainWindow", "Connect to Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("MainWindow", "active", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setText(QtGui.QApplication.translate("MainWindow", "connected to Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("MainWindow", "Survey", None, QtGui.QApplication.UnicodeUTF8)) self.gen_new_survey.setText(QtGui.QApplication.translate("MainWindow", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) self.load_survey.setText(QtGui.QApplication.translate("MainWindow", "Load Survey", None, QtGui.QApplication.UnicodeUTF8)) self.save_survey.setText(QtGui.QApplication.translate("MainWindow", "Save Survey", None, QtGui.QApplication.UnicodeUTF8)) - self.picker.setText(QtGui.QApplication.translate("MainWindow", "Autopicker", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("MainWindow", "active", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("MainWindow", "picked", None, QtGui.QApplication.UnicodeUTF8)) + self.label_7.setText(QtGui.QApplication.translate("MainWindow", "Picking", None, QtGui.QApplication.UnicodeUTF8)) + self.picker.setText(QtGui.QApplication.translate("MainWindow", "Automatic Picking", None, QtGui.QApplication.UnicodeUTF8)) + self.postprocessing.setText(QtGui.QApplication.translate("MainWindow", "Postprocessing", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setText(QtGui.QApplication.translate("MainWindow", "Simulation", None, QtGui.QApplication.UnicodeUTF8)) self.fmtomo.setText(QtGui.QApplication.translate("MainWindow", "FMTOMO Simulation", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index 99173ff5..8ce529f7 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -1,281 +1,269 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'fmtomo_parameters.ui' +# Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Wed Jun 15 11:56:04 2016 +# Created: Thu Jun 16 13:20:37 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! from PySide import QtCore, QtGui -class Ui_Dialog(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(320, 520) +class Ui_fmtomo_parameters(object): + def setupUi(self, fmtomo_parameters): + fmtomo_parameters.setObjectName("fmtomo_parameters") + fmtomo_parameters.resize(380, 440) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) - Dialog.setSizePolicy(sizePolicy) - Dialog.setMinimumSize(QtCore.QSize(320, 520)) - Dialog.setMaximumSize(QtCore.QSize(320, 520)) + sizePolicy.setHeightForWidth(fmtomo_parameters.sizePolicy().hasHeightForWidth()) + fmtomo_parameters.setSizePolicy(sizePolicy) + fmtomo_parameters.setMinimumSize(QtCore.QSize(380, 440)) + fmtomo_parameters.setMaximumSize(QtCore.QSize(320000, 520000)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) - Dialog.setWindowIcon(icon) - self.buttonBox = QtGui.QDialogButtonBox(Dialog) - self.buttonBox.setGeometry(QtCore.QRect(10, 480, 301, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.gridLayoutWidget = QtGui.QWidget(Dialog) - self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 240, 281, 79)) - self.gridLayoutWidget.setObjectName("gridLayoutWidget") - self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setObjectName("gridLayout") - self.label_4 = QtGui.QLabel(self.gridLayoutWidget) - self.label_4.setObjectName("label_4") - self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) - self.label_5 = QtGui.QLabel(self.gridLayoutWidget) - self.label_5.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_5.setAlignment(QtCore.Qt.AlignCenter) - self.label_5.setObjectName("label_5") - self.gridLayout.addWidget(self.label_5, 0, 1, 1, 1) - self.label_6 = QtGui.QLabel(self.gridLayoutWidget) - self.label_6.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_6.setAlignment(QtCore.Qt.AlignCenter) - self.label_6.setObjectName("label_6") - self.gridLayout.addWidget(self.label_6, 0, 2, 1, 1) - self.label_7 = QtGui.QLabel(self.gridLayoutWidget) - self.label_7.setLayoutDirection(QtCore.Qt.LeftToRight) - self.label_7.setAlignment(QtCore.Qt.AlignCenter) - self.label_7.setObjectName("label_7") - self.gridLayout.addWidget(self.label_7, 0, 3, 1, 1) - self.label_8 = QtGui.QLabel(self.gridLayoutWidget) - self.label_8.setObjectName("label_8") - self.gridLayout.addWidget(self.label_8, 1, 0, 1, 1) - self.pgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) - self.pgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_x.setObjectName("pgrid_x") - self.gridLayout.addWidget(self.pgrid_x, 1, 1, 1, 1) - self.pgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) - self.pgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_y.setObjectName("pgrid_y") - self.gridLayout.addWidget(self.pgrid_y, 1, 2, 1, 1) - self.pgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) - self.pgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_z.setObjectName("pgrid_z") - self.gridLayout.addWidget(self.pgrid_z, 1, 3, 1, 1) - self.label_9 = QtGui.QLabel(self.gridLayoutWidget) - self.label_9.setObjectName("label_9") - self.gridLayout.addWidget(self.label_9, 2, 0, 1, 1) - self.invgrid_x = QtGui.QLineEdit(self.gridLayoutWidget) - self.invgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.invgrid_x.setObjectName("invgrid_x") - self.gridLayout.addWidget(self.invgrid_x, 2, 1, 1, 1) - self.invgrid_y = QtGui.QLineEdit(self.gridLayoutWidget) - self.invgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.invgrid_y.setObjectName("invgrid_y") - self.gridLayout.addWidget(self.invgrid_y, 2, 2, 1, 1) - self.invgrid_z = QtGui.QLineEdit(self.gridLayoutWidget) - self.invgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.invgrid_z.setObjectName("invgrid_z") - self.gridLayout.addWidget(self.invgrid_z, 2, 3, 1, 1) - self.gridLayoutWidget_2 = QtGui.QWidget(Dialog) - self.gridLayoutWidget_2.setGeometry(QtCore.QRect(10, 160, 201, 61)) - self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2") - self.gridLayout_2 = QtGui.QGridLayout(self.gridLayoutWidget_2) - self.gridLayout_2.setContentsMargins(0, 0, 0, 0) - self.gridLayout_2.setObjectName("gridLayout_2") - self.label_3 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_3.setObjectName("label_3") - self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) - self.bbot = QtGui.QLineEdit(self.gridLayoutWidget_2) - self.bbot.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.bbot.setObjectName("bbot") - self.gridLayout_2.addWidget(self.bbot, 1, 1, 1, 1) - self.label_11 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_11.setObjectName("label_11") - self.gridLayout_2.addWidget(self.label_11, 1, 2, 1, 1) - self.label_2 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_2.setObjectName("label_2") - self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) - self.btop = QtGui.QLineEdit(self.gridLayoutWidget_2) - self.btop.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.btop.setObjectName("btop") - self.gridLayout_2.addWidget(self.btop, 0, 1, 1, 1) - self.label_10 = QtGui.QLabel(self.gridLayoutWidget_2) - self.label_10.setObjectName("label_10") - self.gridLayout_2.addWidget(self.label_10, 0, 2, 1, 1) - self.gridLayoutWidget_3 = QtGui.QWidget(Dialog) - self.gridLayoutWidget_3.setGeometry(QtCore.QRect(10, 60, 201, 31)) - self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3") - self.gridLayout_3 = QtGui.QGridLayout(self.gridLayoutWidget_3) - self.gridLayout_3.setContentsMargins(0, 0, 0, 0) + fmtomo_parameters.setWindowIcon(icon) + self.verticalLayout = QtGui.QVBoxLayout(fmtomo_parameters) + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout_4 = QtGui.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_17 = QtGui.QLabel(fmtomo_parameters) + self.label_17.setObjectName("label_17") + self.horizontalLayout_4.addWidget(self.label_17) + self.fmtomo_dir = QtGui.QLineEdit(fmtomo_parameters) + self.fmtomo_dir.setObjectName("fmtomo_dir") + self.horizontalLayout_4.addWidget(self.fmtomo_dir) + self.browse_tomodir = QtGui.QPushButton(fmtomo_parameters) + self.browse_tomodir.setObjectName("browse_tomodir") + self.horizontalLayout_4.addWidget(self.browse_tomodir) + self.verticalLayout.addLayout(self.horizontalLayout_4) + self.line_5 = QtGui.QFrame(fmtomo_parameters) + self.line_5.setFrameShape(QtGui.QFrame.HLine) + self.line_5.setFrameShadow(QtGui.QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.verticalLayout.addWidget(self.line_5) + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.label_14 = QtGui.QLabel(fmtomo_parameters) + self.label_14.setObjectName("label_14") + self.horizontalLayout_2.addWidget(self.label_14) + self.nproc = QtGui.QLineEdit(fmtomo_parameters) + self.nproc.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.nproc.setObjectName("nproc") + self.horizontalLayout_2.addWidget(self.nproc) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.gridLayout_3 = QtGui.QGridLayout() self.gridLayout_3.setObjectName("gridLayout_3") - self.nIter = QtGui.QSpinBox(self.gridLayoutWidget_3) + self.nIter = QtGui.QSpinBox(fmtomo_parameters) self.nIter.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.nIter.setProperty("value", 1) self.nIter.setObjectName("nIter") self.gridLayout_3.addWidget(self.nIter, 0, 1, 1, 1) - self.label = QtGui.QLabel(self.gridLayoutWidget_3) + self.label = QtGui.QLabel(fmtomo_parameters) self.label.setObjectName("label") self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1) - self.horizontalLayoutWidget = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 340, 161, 31)) - self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget") - self.horizontalLayout = QtGui.QHBoxLayout(self.horizontalLayoutWidget) - self.horizontalLayout.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout.setObjectName("horizontalLayout") - self.label_13 = QtGui.QLabel(self.horizontalLayoutWidget) - self.label_13.setObjectName("label_13") - self.horizontalLayout.addWidget(self.label_13) - self.cushion = QtGui.QLineEdit(self.horizontalLayoutWidget) - self.cushion.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.cushion.setObjectName("cushion") - self.horizontalLayout.addWidget(self.cushion) - self.label_12 = QtGui.QLabel(self.horizontalLayoutWidget) - self.label_12.setObjectName("label_12") - self.horizontalLayout.addWidget(self.label_12) - self.horizontalLayoutWidget_4 = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(10, 10, 291, 32)) - self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4") - self.horizontalLayout_4 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_4) - self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.label_17 = QtGui.QLabel(self.horizontalLayoutWidget_4) - self.label_17.setObjectName("label_17") - self.horizontalLayout_4.addWidget(self.label_17) - self.fmtomo_dir = QtGui.QLineEdit(self.horizontalLayoutWidget_4) - self.fmtomo_dir.setObjectName("fmtomo_dir") - self.horizontalLayout_4.addWidget(self.fmtomo_dir) - self.browse_tomodir = QtGui.QPushButton(self.horizontalLayoutWidget_4) - self.browse_tomodir.setObjectName("browse_tomodir") - self.horizontalLayout_4.addWidget(self.browse_tomodir) - self.line = QtGui.QFrame(Dialog) - self.line.setGeometry(QtCore.QRect(7, 220, 301, 20)) - self.line.setFrameShape(QtGui.QFrame.HLine) - self.line.setFrameShadow(QtGui.QFrame.Sunken) - self.line.setObjectName("line") - self.line_2 = QtGui.QFrame(Dialog) - self.line_2.setGeometry(QtCore.QRect(10, 320, 301, 20)) - self.line_2.setFrameShape(QtGui.QFrame.HLine) - self.line_2.setFrameShadow(QtGui.QFrame.Sunken) - self.line_2.setObjectName("line_2") - self.gridLayoutWidget_4 = QtGui.QWidget(Dialog) - self.gridLayoutWidget_4.setGeometry(QtCore.QRect(10, 390, 291, 80)) - self.gridLayoutWidget_4.setObjectName("gridLayoutWidget_4") - self.gridLayout_4 = QtGui.QGridLayout(self.gridLayoutWidget_4) - self.gridLayout_4.setContentsMargins(0, 0, 0, 0) - self.gridLayout_4.setObjectName("gridLayout_4") - self.label_15 = QtGui.QLabel(self.gridLayoutWidget_4) - self.label_15.setObjectName("label_15") - self.gridLayout_4.addWidget(self.label_15, 0, 0, 1, 1) - self.customgrid = QtGui.QLineEdit(self.gridLayoutWidget_4) - self.customgrid.setObjectName("customgrid") - self.gridLayout_4.addWidget(self.customgrid, 0, 1, 1, 1) - self.browse_customgrid = QtGui.QPushButton(self.gridLayoutWidget_4) - self.browse_customgrid.setObjectName("browse_customgrid") - self.gridLayout_4.addWidget(self.browse_customgrid, 0, 2, 1, 1) - self.label_16 = QtGui.QLabel(self.gridLayoutWidget_4) - self.label_16.setObjectName("label_16") - self.gridLayout_4.addWidget(self.label_16, 1, 0, 1, 1) - self.simuldir = QtGui.QLineEdit(self.gridLayoutWidget_4) - self.simuldir.setObjectName("simuldir") - self.gridLayout_4.addWidget(self.simuldir, 1, 1, 1, 1) - self.browse_simuldir = QtGui.QPushButton(self.gridLayoutWidget_4) - self.browse_simuldir.setObjectName("browse_simuldir") - self.gridLayout_4.addWidget(self.browse_simuldir, 1, 2, 1, 1) - self.line_3 = QtGui.QFrame(Dialog) - self.line_3.setGeometry(QtCore.QRect(10, 370, 301, 20)) - self.line_3.setFrameShape(QtGui.QFrame.HLine) - self.line_3.setFrameShadow(QtGui.QFrame.Sunken) - self.line_3.setObjectName("line_3") - self.line_4 = QtGui.QFrame(Dialog) - self.line_4.setGeometry(QtCore.QRect(10, 90, 301, 20)) + self.verticalLayout.addLayout(self.gridLayout_3) + self.line_4 = QtGui.QFrame(fmtomo_parameters) self.line_4.setFrameShape(QtGui.QFrame.HLine) self.line_4.setFrameShadow(QtGui.QFrame.Sunken) self.line_4.setObjectName("line_4") - self.line_5 = QtGui.QFrame(Dialog) - self.line_5.setGeometry(QtCore.QRect(10, 40, 301, 20)) - self.line_5.setFrameShape(QtGui.QFrame.HLine) - self.line_5.setFrameShadow(QtGui.QFrame.Sunken) - self.line_5.setObjectName("line_5") - self.horizontalLayoutWidget_2 = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(220, 60, 81, 31)) - self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2") - self.horizontalLayout_2 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_2) - self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.label_14 = QtGui.QLabel(self.horizontalLayoutWidget_2) - self.label_14.setObjectName("label_14") - self.horizontalLayout_2.addWidget(self.label_14) - self.nproc = QtGui.QLineEdit(self.horizontalLayoutWidget_2) - self.nproc.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.nproc.setObjectName("nproc") - self.horizontalLayout_2.addWidget(self.nproc) - self.label_18 = QtGui.QLabel(Dialog) - self.label_18.setGeometry(QtCore.QRect(10, 102, 271, 16)) + self.verticalLayout.addWidget(self.line_4) + self.label_18 = QtGui.QLabel(fmtomo_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_18.sizePolicy().hasHeightForWidth()) + self.label_18.setSizePolicy(sizePolicy) self.label_18.setObjectName("label_18") - self.horizontalLayoutWidget_5 = QtGui.QWidget(Dialog) - self.horizontalLayoutWidget_5.setGeometry(QtCore.QRect(10, 120, 291, 32)) - self.horizontalLayoutWidget_5.setObjectName("horizontalLayoutWidget_5") - self.horizontalLayout_5 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_5) - self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.label_19 = QtGui.QLabel(self.horizontalLayoutWidget_5) - self.label_19.setObjectName("label_19") - self.horizontalLayout_5.addWidget(self.label_19) - self.seisarray = QtGui.QLineEdit(self.horizontalLayoutWidget_5) - self.seisarray.setObjectName("seisarray") - self.horizontalLayout_5.addWidget(self.seisarray) - self.browse_seisarray = QtGui.QPushButton(self.horizontalLayoutWidget_5) - self.browse_seisarray.setObjectName("browse_seisarray") - self.horizontalLayout_5.addWidget(self.browse_seisarray) + self.verticalLayout.addWidget(self.label_18) + self.gridLayout_2 = QtGui.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.label_3 = QtGui.QLabel(fmtomo_parameters) + self.label_3.setObjectName("label_3") + self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) + self.bbot = QtGui.QLineEdit(fmtomo_parameters) + self.bbot.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.bbot.setObjectName("bbot") + self.gridLayout_2.addWidget(self.bbot, 1, 1, 1, 1) + self.label_11 = QtGui.QLabel(fmtomo_parameters) + self.label_11.setObjectName("label_11") + self.gridLayout_2.addWidget(self.label_11, 1, 2, 1, 1) + self.label_2 = QtGui.QLabel(fmtomo_parameters) + self.label_2.setObjectName("label_2") + self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1) + self.btop = QtGui.QLineEdit(fmtomo_parameters) + self.btop.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.btop.setObjectName("btop") + self.gridLayout_2.addWidget(self.btop, 0, 1, 1, 1) + self.label_10 = QtGui.QLabel(fmtomo_parameters) + self.label_10.setObjectName("label_10") + self.gridLayout_2.addWidget(self.label_10, 0, 2, 1, 1) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_13 = QtGui.QLabel(fmtomo_parameters) + self.label_13.setObjectName("label_13") + self.horizontalLayout.addWidget(self.label_13) + self.cushion = QtGui.QLineEdit(fmtomo_parameters) + self.cushion.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.cushion.setObjectName("cushion") + self.horizontalLayout.addWidget(self.cushion) + self.label_12 = QtGui.QLabel(fmtomo_parameters) + self.label_12.setObjectName("label_12") + self.horizontalLayout.addWidget(self.label_12) + self.gridLayout_2.addLayout(self.horizontalLayout, 0, 3, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_2) + self.line = QtGui.QFrame(fmtomo_parameters) + self.line.setFrameShape(QtGui.QFrame.HLine) + self.line.setFrameShadow(QtGui.QFrame.Sunken) + self.line.setObjectName("line") + self.verticalLayout.addWidget(self.line) + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.label_4 = QtGui.QLabel(fmtomo_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth()) + self.label_4.setSizePolicy(sizePolicy) + self.label_4.setObjectName("label_4") + self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1) + self.label_5 = QtGui.QLabel(fmtomo_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth()) + self.label_5.setSizePolicy(sizePolicy) + self.label_5.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_5.setAlignment(QtCore.Qt.AlignCenter) + self.label_5.setObjectName("label_5") + self.gridLayout.addWidget(self.label_5, 0, 1, 1, 1) + self.label_6 = QtGui.QLabel(fmtomo_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth()) + self.label_6.setSizePolicy(sizePolicy) + self.label_6.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_6.setAlignment(QtCore.Qt.AlignCenter) + self.label_6.setObjectName("label_6") + self.gridLayout.addWidget(self.label_6, 0, 2, 1, 1) + self.label_7 = QtGui.QLabel(fmtomo_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth()) + self.label_7.setSizePolicy(sizePolicy) + self.label_7.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label_7.setAlignment(QtCore.Qt.AlignCenter) + self.label_7.setObjectName("label_7") + self.gridLayout.addWidget(self.label_7, 0, 3, 1, 1) + self.label_8 = QtGui.QLabel(fmtomo_parameters) + self.label_8.setObjectName("label_8") + self.gridLayout.addWidget(self.label_8, 1, 0, 1, 1) + self.pgrid_x = QtGui.QLineEdit(fmtomo_parameters) + self.pgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_x.setObjectName("pgrid_x") + self.gridLayout.addWidget(self.pgrid_x, 1, 1, 1, 1) + self.pgrid_y = QtGui.QLineEdit(fmtomo_parameters) + self.pgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_y.setObjectName("pgrid_y") + self.gridLayout.addWidget(self.pgrid_y, 1, 2, 1, 1) + self.pgrid_z = QtGui.QLineEdit(fmtomo_parameters) + self.pgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_z.setObjectName("pgrid_z") + self.gridLayout.addWidget(self.pgrid_z, 1, 3, 1, 1) + self.label_9 = QtGui.QLabel(fmtomo_parameters) + self.label_9.setObjectName("label_9") + self.gridLayout.addWidget(self.label_9, 2, 0, 1, 1) + self.invgrid_x = QtGui.QLineEdit(fmtomo_parameters) + self.invgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_x.setObjectName("invgrid_x") + self.gridLayout.addWidget(self.invgrid_x, 2, 1, 1, 1) + self.invgrid_y = QtGui.QLineEdit(fmtomo_parameters) + self.invgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_y.setObjectName("invgrid_y") + self.gridLayout.addWidget(self.invgrid_y, 2, 2, 1, 1) + self.invgrid_z = QtGui.QLineEdit(fmtomo_parameters) + self.invgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_z.setObjectName("invgrid_z") + self.gridLayout.addWidget(self.invgrid_z, 2, 3, 1, 1) + self.verticalLayout.addLayout(self.gridLayout) + self.line_2 = QtGui.QFrame(fmtomo_parameters) + self.line_2.setFrameShape(QtGui.QFrame.HLine) + self.line_2.setFrameShadow(QtGui.QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.verticalLayout.addWidget(self.line_2) + self.line_3 = QtGui.QFrame(fmtomo_parameters) + self.line_3.setFrameShape(QtGui.QFrame.HLine) + self.line_3.setFrameShadow(QtGui.QFrame.Sunken) + self.line_3.setObjectName("line_3") + self.verticalLayout.addWidget(self.line_3) + self.gridLayout_4 = QtGui.QGridLayout() + self.gridLayout_4.setObjectName("gridLayout_4") + self.label_15 = QtGui.QLabel(fmtomo_parameters) + self.label_15.setObjectName("label_15") + self.gridLayout_4.addWidget(self.label_15, 0, 0, 1, 1) + self.customgrid = QtGui.QLineEdit(fmtomo_parameters) + self.customgrid.setObjectName("customgrid") + self.gridLayout_4.addWidget(self.customgrid, 0, 1, 1, 1) + self.browse_customgrid = QtGui.QPushButton(fmtomo_parameters) + self.browse_customgrid.setObjectName("browse_customgrid") + self.gridLayout_4.addWidget(self.browse_customgrid, 0, 2, 1, 1) + self.label_16 = QtGui.QLabel(fmtomo_parameters) + self.label_16.setObjectName("label_16") + self.gridLayout_4.addWidget(self.label_16, 1, 0, 1, 1) + self.simuldir = QtGui.QLineEdit(fmtomo_parameters) + self.simuldir.setObjectName("simuldir") + self.gridLayout_4.addWidget(self.simuldir, 1, 1, 1, 1) + self.browse_simuldir = QtGui.QPushButton(fmtomo_parameters) + self.browse_simuldir.setObjectName("browse_simuldir") + self.gridLayout_4.addWidget(self.browse_simuldir, 1, 2, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_4) + self.buttonBox = QtGui.QDialogButtonBox(fmtomo_parameters) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) - self.retranslateUi(Dialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) - QtCore.QMetaObject.connectSlotsByName(Dialog) + self.retranslateUi(fmtomo_parameters) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), fmtomo_parameters.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), fmtomo_parameters.reject) + QtCore.QMetaObject.connectSlotsByName(fmtomo_parameters) - def retranslateUi(self, Dialog): - Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Choose FMTOMO parameters", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setText(QtGui.QApplication.translate("Dialog", "Number of Ponts", None, QtGui.QApplication.UnicodeUTF8)) - self.label_5.setText(QtGui.QApplication.translate("Dialog", "X", None, QtGui.QApplication.UnicodeUTF8)) - self.label_6.setText(QtGui.QApplication.translate("Dialog", "Y", None, QtGui.QApplication.UnicodeUTF8)) - self.label_7.setText(QtGui.QApplication.translate("Dialog", "Z", None, QtGui.QApplication.UnicodeUTF8)) - self.label_8.setText(QtGui.QApplication.translate("Dialog", "Propagation Grid", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_x.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_y.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_z.setText(QtGui.QApplication.translate("Dialog", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.label_9.setText(QtGui.QApplication.translate("Dialog", "Inversion Grid", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_x.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_y.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_z.setText(QtGui.QApplication.translate("Dialog", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setText(QtGui.QApplication.translate("Dialog", "Bottom boundary", None, QtGui.QApplication.UnicodeUTF8)) - self.bbot.setText(QtGui.QApplication.translate("Dialog", "-50", None, QtGui.QApplication.UnicodeUTF8)) - self.label_11.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("Dialog", "Top boundary", None, QtGui.QApplication.UnicodeUTF8)) - self.btop.setText(QtGui.QApplication.translate("Dialog", "5", None, QtGui.QApplication.UnicodeUTF8)) - self.label_10.setText(QtGui.QApplication.translate("Dialog", "m", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("Dialog", "Iterations", None, QtGui.QApplication.UnicodeUTF8)) - self.label_13.setText(QtGui.QApplication.translate("Dialog", "Cushion factor", None, QtGui.QApplication.UnicodeUTF8)) - self.cushion.setText(QtGui.QApplication.translate("Dialog", "10", None, QtGui.QApplication.UnicodeUTF8)) - self.label_12.setText(QtGui.QApplication.translate("Dialog", "%", None, QtGui.QApplication.UnicodeUTF8)) - self.label_17.setText(QtGui.QApplication.translate("Dialog", "FMTOMO\n" + def retranslateUi(self, fmtomo_parameters): + fmtomo_parameters.setWindowTitle(QtGui.QApplication.translate("fmtomo_parameters", "Choose FMTOMO parameters", None, QtGui.QApplication.UnicodeUTF8)) + self.label_17.setText(QtGui.QApplication.translate("fmtomo_parameters", "FMTOMO\n" "installation", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_tomodir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_15.setText(QtGui.QApplication.translate("Dialog", "Custom velocity\n" + self.browse_tomodir.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_14.setText(QtGui.QApplication.translate("fmtomo_parameters", "nproc", None, QtGui.QApplication.UnicodeUTF8)) + self.nproc.setText(QtGui.QApplication.translate("fmtomo_parameters", "1", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("fmtomo_parameters", "Iterations", None, QtGui.QApplication.UnicodeUTF8)) + self.label_18.setText(QtGui.QApplication.translate("fmtomo_parameters", "Grid definitions from seismic Array:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("fmtomo_parameters", "Bottom boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.bbot.setText(QtGui.QApplication.translate("fmtomo_parameters", "-50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_11.setText(QtGui.QApplication.translate("fmtomo_parameters", "m", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("fmtomo_parameters", "Top boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.btop.setText(QtGui.QApplication.translate("fmtomo_parameters", "5", None, QtGui.QApplication.UnicodeUTF8)) + self.label_10.setText(QtGui.QApplication.translate("fmtomo_parameters", "m", None, QtGui.QApplication.UnicodeUTF8)) + self.label_13.setText(QtGui.QApplication.translate("fmtomo_parameters", "Cushion factor", None, QtGui.QApplication.UnicodeUTF8)) + self.cushion.setText(QtGui.QApplication.translate("fmtomo_parameters", "10", None, QtGui.QApplication.UnicodeUTF8)) + self.label_12.setText(QtGui.QApplication.translate("fmtomo_parameters", "%", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("fmtomo_parameters", "Number of Ponts", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("fmtomo_parameters", "X", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setText(QtGui.QApplication.translate("fmtomo_parameters", "Y", None, QtGui.QApplication.UnicodeUTF8)) + self.label_7.setText(QtGui.QApplication.translate("fmtomo_parameters", "Z", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setText(QtGui.QApplication.translate("fmtomo_parameters", "Propagation Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_x.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_y.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.pgrid_z.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) + self.label_9.setText(QtGui.QApplication.translate("fmtomo_parameters", "Inversion Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_x.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_y.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.invgrid_z.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_15.setText(QtGui.QApplication.translate("fmtomo_parameters", "Custom velocity\n" "grid (earthmodel)", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_customgrid.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_16.setText(QtGui.QApplication.translate("Dialog", "Simulation\n" + self.browse_customgrid.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_16.setText(QtGui.QApplication.translate("fmtomo_parameters", "Simulation\n" "Directory", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_simuldir.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_14.setText(QtGui.QApplication.translate("Dialog", "nproc", None, QtGui.QApplication.UnicodeUTF8)) - self.nproc.setText(QtGui.QApplication.translate("Dialog", "1", None, QtGui.QApplication.UnicodeUTF8)) - self.label_18.setText(QtGui.QApplication.translate("Dialog", "Grid definitions from seismic Array:", None, QtGui.QApplication.UnicodeUTF8)) - self.label_19.setText(QtGui.QApplication.translate("Dialog", "Seismic\n" -"Array", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_seisarray.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.browse_simuldir.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py new file mode 100644 index 00000000..ab13cad8 --- /dev/null +++ b/pylot/core/active/generate_seisarray_layout.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'generate_seisarray_layout.ui' +# +# Created: Thu Jun 16 12:10:38 2016 +# by: pyside-uic 0.2.15 running on PySide 1.2.2 +# +# WARNING! All changes made in this file will be lost! + +from PySide import QtCore, QtGui + +class Ui_generate_seisarray(object): + def setupUi(self, generate_seisarray): + generate_seisarray.setObjectName("generate_seisarray") + generate_seisarray.resize(400, 220) + generate_seisarray.setMinimumSize(QtCore.QSize(400, 220)) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + generate_seisarray.setWindowIcon(icon) + self.verticalLayout = QtGui.QVBoxLayout(generate_seisarray) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.label_rec = QtGui.QLabel(generate_seisarray) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_rec.sizePolicy().hasHeightForWidth()) + self.label_rec.setSizePolicy(sizePolicy) + self.label_rec.setObjectName("label_rec") + self.gridLayout.addWidget(self.label_rec, 0, 0, 1, 1) + self.horizontalLayout_4 = QtGui.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.lineEdit_rec = QtGui.QLineEdit(generate_seisarray) + self.lineEdit_rec.setObjectName("lineEdit_rec") + self.horizontalLayout_4.addWidget(self.lineEdit_rec) + self.pushButton_rec = QtGui.QPushButton(generate_seisarray) + self.pushButton_rec.setObjectName("pushButton_rec") + self.horizontalLayout_4.addWidget(self.pushButton_rec) + self.gridLayout.addLayout(self.horizontalLayout_4, 1, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout) + self.gridLayout_2 = QtGui.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.horizontalLayout_5 = QtGui.QHBoxLayout() + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.lineEdit_src = QtGui.QLineEdit(generate_seisarray) + self.lineEdit_src.setObjectName("lineEdit_src") + self.horizontalLayout_5.addWidget(self.lineEdit_src) + self.pushButton_src = QtGui.QPushButton(generate_seisarray) + self.pushButton_src.setObjectName("pushButton_src") + self.horizontalLayout_5.addWidget(self.pushButton_src) + self.gridLayout_2.addLayout(self.horizontalLayout_5, 1, 0, 1, 1) + self.label_src = QtGui.QLabel(generate_seisarray) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_src.sizePolicy().hasHeightForWidth()) + self.label_src.setSizePolicy(sizePolicy) + self.label_src.setObjectName("label_src") + self.gridLayout_2.addWidget(self.label_src, 0, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_2) + self.gridLayout_3 = QtGui.QGridLayout() + self.gridLayout_3.setObjectName("gridLayout_3") + self.horizontalLayout_6 = QtGui.QHBoxLayout() + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.lineEdit_pts = QtGui.QLineEdit(generate_seisarray) + self.lineEdit_pts.setObjectName("lineEdit_pts") + self.horizontalLayout_6.addWidget(self.lineEdit_pts) + self.pushButton_obs = QtGui.QPushButton(generate_seisarray) + self.pushButton_obs.setObjectName("pushButton_obs") + self.horizontalLayout_6.addWidget(self.pushButton_obs) + self.gridLayout_3.addLayout(self.horizontalLayout_6, 1, 0, 1, 1) + self.label_obs = QtGui.QLabel(generate_seisarray) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_obs.sizePolicy().hasHeightForWidth()) + self.label_obs.setSizePolicy(sizePolicy) + self.label_obs.setObjectName("label_obs") + self.gridLayout_3.addWidget(self.label_obs, 0, 0, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_3) + self.buttonBox = QtGui.QDialogButtonBox(generate_seisarray) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(generate_seisarray) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_seisarray.reject) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), generate_seisarray.accept) + QtCore.QMetaObject.connectSlotsByName(generate_seisarray) + + def retranslateUi(self, generate_seisarray): + generate_seisarray.setWindowTitle(QtGui.QApplication.translate("generate_seisarray", "Generate new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.label_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Receivers", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_src.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Sources (optional)", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Additional measured points (optional)", None, QtGui.QApplication.UnicodeUTF8)) + diff --git a/pylot/core/active/generate_survey_layout.py b/pylot/core/active/generate_survey_layout.py index 7358d086..e04ad4de 100644 --- a/pylot/core/active/generate_survey_layout.py +++ b/pylot/core/active/generate_survey_layout.py @@ -9,7 +9,7 @@ from PySide import QtCore, QtGui -class Ui_Dialog(object): +class Ui_generate_survey(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(380, 160) diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index e14c0bf4..e79c5dab 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'fmtomo_parameters.ui' +# +# Created: Wed Jun 15 11:56:04 2016 +# by: pyside-uic 0.2.15 running on PySide 1.2.2 +# +# WARNING! All changes made in this file will be lost! + +from PySide import QtCore, QtGui + class Ui_Picking_parameters(object): def setupUi(self, Picking_parameters): Picking_parameters.setObjectName("Picking_parameters") diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 2e836f7d..dee47e58 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -316,6 +316,7 @@ class SeisArray(object): def interpolateAll(self): self._interpolateXY4rec() self.interpZcoords4rec() + print('Interpolated receiver locations.') def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, elevation=0.25, method='linear'): ''' From f01187615be5f8f8468922c15071137917a46306 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 16 Jun 2016 14:00:04 +0200 Subject: [PATCH 0972/1144] fix --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index bc941ad6..bf7f3de1 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -79,8 +79,9 @@ class gui_control(object): self.setSurveyState(True) def interpolate_receivers(self): - if not self.seisarray: + if not self.checkSeisArrayState(): print('No Seismic Array defined.') + return self.seisarray.interpolateAll() def getPickParameters(self, ui, Picking_parameters): From 8393f283c798de8d54afe2a620564913638c99e6 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 17 Jun 2016 13:14:20 +0200 Subject: [PATCH 0973/1144] GUI changes and updates --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 71 ++++-- pylot/core/active/fmtomo_parameters_layout.py | 7 +- .../core/active/picking_parameters_layout.py | 210 ++++++++++++------ 3 files changed, 199 insertions(+), 89 deletions(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index bf7f3de1..57079248 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -80,26 +80,26 @@ class gui_control(object): def interpolate_receivers(self): if not self.checkSeisArrayState(): - print('No Seismic Array defined.') + self.printDialogMessage('No Seismic Array defined.') return self.seisarray.interpolateAll() def getPickParameters(self, ui, Picking_parameters): if Picking_parameters.exec_(): - ncores = int(ui.lineEdit_ncores.text()) + ncores = int(ui.ncores.value()) vmin = float(ui.lineEdit_vmin.text()) vmax = float(ui.lineEdit_vmax.text()) folm = float(ui.lineEdit_folm.text()) - AIC = ui.checkBox.isChecked() - aicwindow = [int(val) for val in ui.lineEdit_aicwindow.text().split(',')] - return ncores, vmin, vmax, folm, AIC, tuple(aicwindow) + AIC = ui.checkBox_AIC.isChecked() + aicwindow = (int(ui.lineEdit_aicleft.text()), int(ui.lineEdit_aicright.text())) + return ncores, vmin, vmax, folm, AIC, aicwindow def connect2Survey(self): if not self.checkSurveyState(): - print('Survey not defined.') + self.printDialogMessage('No Survey defined.') return if not self.checkSeisArrayState(): - print('Got no Seismic Array.') + self.printDialogMessage('Got no Seismic Array.') return if self.checkConnected2SurveyState(): if self.continueDialogMessage('Existing Survey already got Seismic Array object. Continue?'): @@ -108,16 +108,21 @@ class gui_control(object): self.setConnected2SurveyState(True) print('Connected Seismic Array to active Survey object.') + def getMaxCPU(self): + import multiprocessing + return multiprocessing.cpu_count() + def callPicker(self): if not self.checkSurveyState(): - print('Survey not defined.') + self.printDialogMessage('No Survey defined.') return if self.checkPickState(): if not self.continueDialogMessage('Survey already picked. Continue?'): return Picking_parameters = QtGui.QDialog(self.mainwindow) - ui = Ui_Picking_parameters() + ui = Ui_picking_parameters() ui.setupUi(Picking_parameters) + ui.ncores.setMaximum(self.getMaxCPU()) try: ncores, vmin, vmax, folm, AIC, aicwindow = self.getPickParameters(ui, Picking_parameters) except TypeError: @@ -135,11 +140,13 @@ class gui_control(object): def startFMTOMO(self): if not self.checkSurveyState(): - print('Survey not defined.') + self.printDialogMessage('No Survey defined.') return fmtomo_parameters = QtGui.QDialog(self.mainwindow) ui = Ui_fmtomo_parameters() ui.setupUi(fmtomo_parameters) + ui.nproc.setMaximum(self.getMaxCPU()) + self.fmtomo_parameters_ui = ui self.connectButtons_startFMTOMO() self.getFMTOMOparameters(ui, fmtomo_parameters) @@ -148,7 +155,7 @@ class gui_control(object): if fmtomo_parameters.exec_(): fmtomo_dir = ui.fmtomo_dir.text() nIter = int(ui.nIter.value()) - nproc = int(ui.nproc.text()) + nproc = int(ui.nproc.value()) btop = float(ui.btop.text()) bbot = float(ui.bbot.text()) propgrid = (int(ui.pgrid_x.text()), int(ui.pgrid_y.text()), int(ui.pgrid_z.text())) @@ -178,7 +185,6 @@ class gui_control(object): QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_tomodir, QtCore.SIGNAL("clicked()"), self.chooseFMTOMOdir) QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_customgrid, QtCore.SIGNAL("clicked()"), self.chooseCustomgrid) QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_simuldir, QtCore.SIGNAL("clicked()"), self.chooseSimuldir) - QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_seisarray, QtCore.SIGNAL("clicked()"), self.chooseSeisarray) def chooseFMTOMOdir(self): self.fmtomo_parameters_ui.fmtomo_dir.setText(self.browseDir()) @@ -194,7 +200,7 @@ class gui_control(object): def postprocessing(self): if not self.checkSurveyState(): - print('Survey not defined.') + self.printDialogMessage('No Survey defined.') return self.survey.plotAllPicks() @@ -205,7 +211,16 @@ class gui_control(object): filename = self.openFile() if filename is None: return - self.survey = activeSeismoPick.Survey.from_pickle(filename) + try: + survey = activeSeismoPick.Survey.from_pickle(filename) + except: + self.printDialogMessage('Could not load object %s.'%filename) + return + if not type(survey) == activeSeismoPick.Survey: + self.printDialogMessage('Wrong input file of type %s, expected %s.' + %(type(survey), activeSeismoPick.Survey)) + return + self.survey = survey self.setSurveyState(True) if self.survey.picked: self.setPickState(True) @@ -215,7 +230,7 @@ class gui_control(object): self.seisarray = self.survey.seisarray self.setConnected2SurveyState(True) self.setSeisArrayState(True) - print('Loaded Survey with active Seismic Array.') + self.printDialogMessage('Loaded Survey with active Seismic Array.') def load_seisarray(self): if self.checkSeisArrayState(): @@ -224,12 +239,21 @@ class gui_control(object): filename = self.openFile() if filename is None: return - self.seisarray = seismicArrayPreparation.SeisArray.from_pickle(filename) + try: + seisarray = seismicArrayPreparation.SeisArray.from_pickle(filename) + except: + self.printDialogMessage('Could not load object %s.'%filename) + return + if not type(seisarray) == seismicArrayPreparation.SeisArray: + self.printDialogMessage('Wrong input file of type %s, expected %s.' + %(type(survey), seismicArrayPreparation.SeisArray)) + return + self.seisarray = seisarray self.setSeisArrayState(True) def save_seisarray(self): if not self.checkSeisArrayState(): - print('No Seismic Array defined.') + self.printDialogMessage('No Seismic Array defined.') return filename = self.saveFile() if filename is None: @@ -238,7 +262,7 @@ class gui_control(object): def save_survey(self): if not self.checkSurveyState(): - print('No Survey defined.') + self.printDialogMessage('No Survey defined.') return filename = self.saveFile() if filename is None: @@ -268,7 +292,7 @@ class gui_control(object): self.mainUI.picked_active.setPixmap(self.applypixmap) self.survey.picked = True elif state == True and self.checkSurveyState() is False: - print('No Survey defined.') + self.printDialogMessage('No Survey defined.') return elif state == False: self.mainUI.picked_active.setPixmap(self.cancelpixmap) @@ -296,10 +320,17 @@ class gui_control(object): def checkPickState(self): if not self.survey: - print ('No Survey defined.') + self.printDialogMessage('No Survey defined.') return return self.survey.picked + def printDialogMessage(self, message): + qmb = QtGui.QMessageBox() + qmb.setText(message) + qmb.setStandardButtons(QtGui.QMessageBox.Ok) + qmb.setIcon(QtGui.QMessageBox.Warning) + qmb.exec_() + def continueDialogExists(self, name): qmb = QtGui.QMessageBox() qmb.setText('%s object already exists. Overwrite?'%name) diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index 8ce529f7..7b607c61 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Thu Jun 16 13:20:37 2016 +# Created: Fri Jun 17 10:24:38 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -47,8 +47,10 @@ class Ui_fmtomo_parameters(object): self.label_14 = QtGui.QLabel(fmtomo_parameters) self.label_14.setObjectName("label_14") self.horizontalLayout_2.addWidget(self.label_14) - self.nproc = QtGui.QLineEdit(fmtomo_parameters) + self.nproc = QtGui.QSpinBox(fmtomo_parameters) self.nproc.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.nproc.setMinimum(1) + self.nproc.setMaximum(10000) self.nproc.setObjectName("nproc") self.horizontalLayout_2.addWidget(self.nproc) self.verticalLayout.addLayout(self.horizontalLayout_2) @@ -236,7 +238,6 @@ class Ui_fmtomo_parameters(object): "installation", None, QtGui.QApplication.UnicodeUTF8)) self.browse_tomodir.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.label_14.setText(QtGui.QApplication.translate("fmtomo_parameters", "nproc", None, QtGui.QApplication.UnicodeUTF8)) - self.nproc.setText(QtGui.QApplication.translate("fmtomo_parameters", "1", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("fmtomo_parameters", "Iterations", None, QtGui.QApplication.UnicodeUTF8)) self.label_18.setText(QtGui.QApplication.translate("fmtomo_parameters", "Grid definitions from seismic Array:", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("fmtomo_parameters", "Bottom boundary", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index e79c5dab..ce6d17da 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -1,83 +1,161 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'fmtomo_parameters.ui' +# Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Wed Jun 15 11:56:04 2016 +# Created: Fri Jun 17 13:06:08 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! from PySide import QtCore, QtGui -class Ui_Picking_parameters(object): - def setupUi(self, Picking_parameters): - Picking_parameters.setObjectName("Picking_parameters") - Picking_parameters.resize(300, 300) - self.gridLayoutWidget = QtGui.QWidget(Picking_parameters) - self.gridLayoutWidget.setGeometry(QtCore.QRect(20, 20, 250, 211)) - self.gridLayoutWidget.setObjectName("gridLayoutWidget") - self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setObjectName("gridLayout") - self.label_cores = QtGui.QLabel(self.gridLayoutWidget) - self.label_cores.setObjectName("label_cores") - self.gridLayout.addWidget(self.label_cores, 0, 0, 1, 1) - self.label_vmax = QtGui.QLabel(self.gridLayoutWidget) - self.label_vmax.setObjectName("label_vmax") - self.gridLayout.addWidget(self.label_vmax, 2, 0, 1, 1) - self.label_vmin = QtGui.QLabel(self.gridLayoutWidget) - self.label_vmin.setObjectName("label_vmin") - self.gridLayout.addWidget(self.label_vmin, 1, 0, 1, 1) - self.lineEdit_ncores = QtGui.QLineEdit(self.gridLayoutWidget) - self.lineEdit_ncores.setObjectName("lineEdit_ncores") - self.gridLayout.addWidget(self.lineEdit_ncores, 0, 1, 1, 1) - self.lineEdit_vmin = QtGui.QLineEdit(self.gridLayoutWidget) +class Ui_picking_parameters(object): + def setupUi(self, picking_parameters): + picking_parameters.setObjectName("picking_parameters") + picking_parameters.resize(325, 400) + picking_parameters.setMinimumSize(QtCore.QSize(325, 400)) + self.verticalLayout_3 = QtGui.QVBoxLayout(picking_parameters) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.label_7 = QtGui.QLabel(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth()) + self.label_7.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Sans Serif") + font.setWeight(75) + font.setBold(True) + self.label_7.setFont(font) + self.label_7.setObjectName("label_7") + self.verticalLayout_3.addWidget(self.label_7) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtGui.QLabel(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) + self.label.setSizePolicy(sizePolicy) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.ncores = QtGui.QSpinBox(picking_parameters) + self.ncores.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.ncores.setMinimum(1) + self.ncores.setMaximum(10000) + self.ncores.setObjectName("ncores") + self.horizontalLayout.addWidget(self.ncores) + self.verticalLayout_3.addLayout(self.horizontalLayout) + self.horizontalLayout_3 = QtGui.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.verticalLayout_2 = QtGui.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label_2 = QtGui.QLabel(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) + self.label_2.setSizePolicy(sizePolicy) + self.label_2.setObjectName("label_2") + self.verticalLayout_2.addWidget(self.label_2) + self.lineEdit_vmin = QtGui.QLineEdit(picking_parameters) + self.lineEdit_vmin.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lineEdit_vmin.setObjectName("lineEdit_vmin") - self.gridLayout.addWidget(self.lineEdit_vmin, 1, 1, 1, 1) - self.lineEdit_vmax = QtGui.QLineEdit(self.gridLayoutWidget) + self.verticalLayout_2.addWidget(self.lineEdit_vmin) + self.horizontalLayout_3.addLayout(self.verticalLayout_2) + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.label_3 = QtGui.QLabel(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) + self.label_3.setSizePolicy(sizePolicy) + self.label_3.setObjectName("label_3") + self.verticalLayout.addWidget(self.label_3) + self.lineEdit_vmax = QtGui.QLineEdit(picking_parameters) + self.lineEdit_vmax.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lineEdit_vmax.setObjectName("lineEdit_vmax") - self.gridLayout.addWidget(self.lineEdit_vmax, 2, 1, 1, 1) - self.checkBox = QtGui.QCheckBox(self.gridLayoutWidget) - self.checkBox.setObjectName("checkBox") - self.gridLayout.addWidget(self.checkBox, 4, 1, 1, 1) - self.label_folm = QtGui.QLabel(self.gridLayoutWidget) - self.label_folm.setObjectName("label_folm") - self.gridLayout.addWidget(self.label_folm, 3, 0, 1, 1) - self.label_aic = QtGui.QLabel(self.gridLayoutWidget) - self.label_aic.setObjectName("label_aic") - self.gridLayout.addWidget(self.label_aic, 4, 0, 1, 1) - self.lineEdit_folm = QtGui.QLineEdit(self.gridLayoutWidget) + self.verticalLayout.addWidget(self.lineEdit_vmax) + self.horizontalLayout_3.addLayout(self.verticalLayout) + self.verticalLayout_3.addLayout(self.horizontalLayout_3) + self.horizontalLayout_4 = QtGui.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_4 = QtGui.QLabel(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth()) + self.label_4.setSizePolicy(sizePolicy) + self.label_4.setObjectName("label_4") + self.horizontalLayout_4.addWidget(self.label_4) + self.lineEdit_folm = QtGui.QLineEdit(picking_parameters) + self.lineEdit_folm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lineEdit_folm.setObjectName("lineEdit_folm") - self.gridLayout.addWidget(self.lineEdit_folm, 3, 1, 1, 1) - self.label_aicwindow = QtGui.QLabel(self.gridLayoutWidget) - self.label_aicwindow.setObjectName("label_aicwindow") - self.gridLayout.addWidget(self.label_aicwindow, 5, 0, 1, 1) - self.lineEdit_aicwindow = QtGui.QLineEdit(self.gridLayoutWidget) - self.lineEdit_aicwindow.setObjectName("lineEdit_aicwindow") - self.gridLayout.addWidget(self.lineEdit_aicwindow, 5, 1, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(Picking_parameters) + self.horizontalLayout_4.addWidget(self.lineEdit_folm) + self.verticalLayout_3.addLayout(self.horizontalLayout_4) + self.horizontalLayout_5 = QtGui.QHBoxLayout() + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.label_5 = QtGui.QLabel(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth()) + self.label_5.setSizePolicy(sizePolicy) + self.label_5.setObjectName("label_5") + self.horizontalLayout_5.addWidget(self.label_5) + self.checkBox_AIC = QtGui.QCheckBox(picking_parameters) + self.checkBox_AIC.setText("") + self.checkBox_AIC.setChecked(True) + self.checkBox_AIC.setObjectName("checkBox_AIC") + self.horizontalLayout_5.addWidget(self.checkBox_AIC) + self.verticalLayout_3.addLayout(self.horizontalLayout_5) + self.horizontalLayout_6 = QtGui.QHBoxLayout() + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.label_6 = QtGui.QLabel(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth()) + self.label_6.setSizePolicy(sizePolicy) + self.label_6.setObjectName("label_6") + self.horizontalLayout_6.addWidget(self.label_6) + self.horizontalLayout_7 = QtGui.QHBoxLayout() + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.lineEdit_aicleft = QtGui.QLineEdit(picking_parameters) + self.lineEdit_aicleft.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lineEdit_aicleft.setObjectName("lineEdit_aicleft") + self.horizontalLayout_7.addWidget(self.lineEdit_aicleft) + self.lineEdit_aicright = QtGui.QLineEdit(picking_parameters) + self.lineEdit_aicright.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lineEdit_aicright.setObjectName("lineEdit_aicright") + self.horizontalLayout_7.addWidget(self.lineEdit_aicright) + self.horizontalLayout_6.addLayout(self.horizontalLayout_7) + self.verticalLayout_3.addLayout(self.horizontalLayout_6) + self.buttonBox = QtGui.QDialogButtonBox(picking_parameters) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.buttonBox.setGeometry(QtCore.QRect(10, 240, 250, 32)) + self.verticalLayout_3.addWidget(self.buttonBox) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Picking_parameters.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Picking_parameters.reject) - self.retranslateUi(Picking_parameters) - QtCore.QMetaObject.connectSlotsByName(Picking_parameters) + self.retranslateUi(picking_parameters) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), picking_parameters.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), picking_parameters.reject) + QtCore.QMetaObject.connectSlotsByName(picking_parameters) + + def retranslateUi(self, picking_parameters): + picking_parameters.setWindowTitle(QtGui.QApplication.translate("picking_parameters", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) + self.label_7.setText(QtGui.QApplication.translate("picking_parameters", "Choose parameters for FMTOMO simulation", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("picking_parameters", "nproc", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("picking_parameters", "vmin [m/s]", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmin.setText(QtGui.QApplication.translate("picking_parameters", "333", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("picking_parameters", "vmax [m/s]", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vmax.setText(QtGui.QApplication.translate("picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("picking_parameters", "Fraction of local maximum", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_folm.setText(QtGui.QApplication.translate("picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("picking_parameters", "AIC", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setText(QtGui.QApplication.translate("picking_parameters", "AIC window indices\n" +"(only if AIC checked)", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_aicleft.setText(QtGui.QApplication.translate("picking_parameters", "15", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_aicright.setText(QtGui.QApplication.translate("picking_parameters", "0", None, QtGui.QApplication.UnicodeUTF8)) - def retranslateUi(self, Picking_parameters): - Picking_parameters.setWindowTitle(QtGui.QApplication.translate("Picking_parameters", "Picking_parameters", None, QtGui.QApplication.UnicodeUTF8)) - self.label_cores.setText(QtGui.QApplication.translate("Picking_parameters", "Number of cores", None, QtGui.QApplication.UnicodeUTF8)) - self.label_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "Vmax (default = 5000m/s)", None, QtGui.QApplication.UnicodeUTF8)) - self.label_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "Vmin (default = 333 m/s)", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_ncores.setText(QtGui.QApplication.translate("Picking_parameters", "1", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_vmin.setText(QtGui.QApplication.translate("Picking_parameters", "333", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_vmax.setText(QtGui.QApplication.translate("Picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) - self.label_folm.setText(QtGui.QApplication.translate("Picking_parameters", "Fraction of local maximum\n" -"(default = 0.6)", None, QtGui.QApplication.UnicodeUTF8)) - self.label_aic.setText(QtGui.QApplication.translate("Picking_parameters", "AIC", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_folm.setText(QtGui.QApplication.translate("Picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) - self.label_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "AIC window (only if AIC\n" -" is checked)", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_aicwindow.setText(QtGui.QApplication.translate("Picking_parameters", "15, 0", None, QtGui.QApplication.UnicodeUTF8)) From 952a2ea5354f5d92ebc3a3245904822e3c6acdc2 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 21 Jun 2016 09:31:26 +0200 Subject: [PATCH 0974/1144] make use of formerly implemented subprocess call routine --- pylot/core/loc/nll.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index 956e8e50..b5a5522a 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -4,7 +4,7 @@ import subprocess import os from pylot.core.io.phases import writephases -from pylot.core.util.utils import getPatternLine +from pylot.core.util.utils import getPatternLine, runProgram from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -79,7 +79,7 @@ def locate(call, fnin): ''' # locate the event - subprocess.call([call, fnin]) + runProgram(call, fnin) def readLocation(fn): From 043f60dc2aa41537e1da3849555a2addbc392dd1 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 21 Jun 2016 09:32:37 +0200 Subject: [PATCH 0975/1144] [change] make Python 3 compatible calls to print --- pylot/core/util/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 21976607..521c548b 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -234,10 +234,10 @@ def prepTimeAxis(stime, trace): etime = stime + nsamp / srate time_ax = np.arange(stime, etime, tincr) if len(time_ax) < nsamp: - print 'elongate time axes by one datum' + print('elongate time axes by one datum') time_ax = np.arange(stime, etime + tincr, tincr) elif len(time_ax) > nsamp: - print 'shorten time axes by one datum' + print('shorten time axes by one datum') time_ax = np.arange(stime, etime - tincr, tincr) if len(time_ax) != nsamp: raise ValueError('{0} samples of data \n ' From 806b974011710439183e82b38dec18f4d5d2e0a2 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 21 Jun 2016 13:29:10 +0200 Subject: [PATCH 0976/1144] further GUI implementations, plot of seismic array in main window --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 22 +++++++++++++++++ pylot/core/active/asp3d_layout.py | 26 ++++++++++++++------ pylot/core/active/seismicArrayPreparation.py | 16 +++++++----- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index 57079248..ab1de3d8 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -1,6 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import os +import matplotlib +matplotlib.use('Qt4Agg') +matplotlib.rcParams['backend.qt4']='PySide' + from PySide import QtCore, QtGui, QtCore from asp3d_layout import * from fmtomo_parameters_layout import * @@ -9,6 +13,10 @@ from generate_seisarray_layout import * from picking_parameters_layout import * from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils, seismicArrayPreparation +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +import matplotlib.pyplot as plt +from matplotlib.figure import Figure + class gui_control(object): def __init__(self): self.mainwindow = MainWindow @@ -19,6 +27,7 @@ class gui_control(object): self.cancelpixmap = self.mainwindow.style().standardPixmap(QtGui.QStyle.SP_DialogCancelButton) self.applypixmap = self.mainwindow.style().standardPixmap(QtGui.QStyle.SP_DialogApplyButton) self.setInitStates() + self.addArrayPlot() def setInitStates(self): self.setPickState(False) @@ -57,6 +66,7 @@ class gui_control(object): self.seisarray.addSourceLocations(srcfile) if len(ptsfile) > 0: self.seisarray.addMeasuredTopographyPoints(ptsfile) + self.reprintArray() self.setSeisArrayState(True) def gen_survey(self): @@ -78,6 +88,12 @@ class gui_control(object): surveyUtils.setDynamicFittedSNR(self.survey.getShotDict()) self.setSurveyState(True) + def addArrayPlot(self): + self.seisArrayFigure = Figure() + self.seisArrayCanvas = FigureCanvas(self.seisArrayFigure) + self.mainUI.verticalLayout_right.addWidget(self.seisArrayCanvas) + self.seisArrayAx = self.seisArrayFigure.add_subplot(111) + def interpolate_receivers(self): if not self.checkSeisArrayState(): self.printDialogMessage('No Seismic Array defined.') @@ -230,8 +246,13 @@ class gui_control(object): self.seisarray = self.survey.seisarray self.setConnected2SurveyState(True) self.setSeisArrayState(True) + self.reprintArray() self.printDialogMessage('Loaded Survey with active Seismic Array.') + def reprintArray(self): + self.seisArrayAx.clear() + self.seisarray.plotArray2D(self.seisArrayAx) + def load_seisarray(self): if self.checkSeisArrayState(): if not self.continueDialogExists('Seismic Array'): @@ -249,6 +270,7 @@ class gui_control(object): %(type(survey), seismicArrayPreparation.SeisArray)) return self.seisarray = seisarray + self.reprintArray() self.setSeisArrayState(True) def save_seisarray(self): diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index 1e266f50..cb4e8d11 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Thu Jun 16 12:18:04 2016 +# Created: Tue Jun 21 13:22:35 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -13,7 +13,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.setEnabled(True) - MainWindow.resize(300, 585) + MainWindow.resize(800, 600) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -27,7 +27,13 @@ class Ui_MainWindow(object): MainWindow.setWindowIcon(icon) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") - self.verticalLayout_5 = QtGui.QVBoxLayout(self.centralwidget) + self.horizontalLayout_4 = QtGui.QHBoxLayout(self.centralwidget) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.horizontalLayout_outer = QtGui.QHBoxLayout() + self.horizontalLayout_outer.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) + self.horizontalLayout_outer.setObjectName("horizontalLayout_outer") + self.verticalLayout_5 = QtGui.QVBoxLayout() + self.verticalLayout_5.setSizeConstraint(QtGui.QLayout.SetMinimumSize) self.verticalLayout_5.setObjectName("verticalLayout_5") self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") @@ -204,12 +210,11 @@ class Ui_MainWindow(object): self.postprocessing = QtGui.QPushButton(self.centralwidget) self.postprocessing.setObjectName("postprocessing") self.verticalLayout_3.addWidget(self.postprocessing) - self.verticalLayout_5.addLayout(self.verticalLayout_3) self.line_2 = QtGui.QFrame(self.centralwidget) self.line_2.setFrameShape(QtGui.QFrame.HLine) self.line_2.setFrameShadow(QtGui.QFrame.Sunken) self.line_2.setObjectName("line_2") - self.verticalLayout_5.addWidget(self.line_2) + self.verticalLayout_3.addWidget(self.line_2) self.verticalLayout_4 = QtGui.QVBoxLayout() self.verticalLayout_4.setObjectName("verticalLayout_4") self.label_8 = QtGui.QLabel(self.centralwidget) @@ -230,10 +235,17 @@ class Ui_MainWindow(object): self.fmtomo = QtGui.QPushButton(self.centralwidget) self.fmtomo.setObjectName("fmtomo") self.verticalLayout_4.addWidget(self.fmtomo) - self.verticalLayout_5.addLayout(self.verticalLayout_4) + self.verticalLayout_3.addLayout(self.verticalLayout_4) + self.verticalLayout_5.addLayout(self.verticalLayout_3) + self.horizontalLayout_outer.addLayout(self.verticalLayout_5) + self.verticalLayout_right = QtGui.QVBoxLayout() + self.verticalLayout_right.setSizeConstraint(QtGui.QLayout.SetMaximumSize) + self.verticalLayout_right.setObjectName("verticalLayout_right") + self.horizontalLayout_outer.addLayout(self.verticalLayout_right) + self.horizontalLayout_4.addLayout(self.horizontalLayout_outer) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 23)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtGui.QStatusBar(MainWindow) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index dee47e58..84372993 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -783,11 +783,12 @@ class SeisArray(object): print "Exported coordinates for %s traces to file > %s" % (count, filename) recfile_out.close() - def plotArray2D(self, plot_topo=False, highlight_measured=False, annotations=True, pointsize=10): + def plotArray2D(self, ax = None, plot_topo=False, highlight_measured=False, annotations=True, pointsize=10): import matplotlib.pyplot as plt - plt.interactive(True) - fig = plt.figure() - ax = plt.axes() + if ax == None: + plt.interactive(True) + fig = plt.figure() + ax = plt.axes() xmt, ymt, zmt = self.getMeasuredTopoLists() xsc, ysc, zsc = self.getSourceLocsLists() xmr, ymr, zmr = self.getMeasuredReceiverLists() @@ -803,11 +804,14 @@ class SeisArray(object): if highlight_measured == True: ax.plot(xmr, ymr, 'r.', markersize=pointsize, label='measured receivers') - plt.title('2D plot of seismic array %s' % self.recfile) + ax.text(0.5, 1.05,'2D plot of seismic array\n %s'%self.recfile, + horizontalalignment='center', verticalalignment='center', + transform=ax.transAxes) + #plt.title('2D plot of seismic array %s' % self.recfile) ax.set_xlabel('X [m]') ax.set_ylabel('Y [m]') ax.set_aspect('equal') - plt.legend() + ax.legend() if annotations == True: for traceID in self.getReceiverCoordinates().keys(): ax.annotate((' ' + str(traceID)), xy=(self._getXreceiver(traceID), self._getYreceiver(traceID)), From 47bd7384f8ef67bb94a9e048bd0c0678a89d33d4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 23 Jun 2016 11:42:09 +0200 Subject: [PATCH 0977/1144] bugfixes, replotting of SeisArray figure --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index ab1de3d8..dbcc5e2c 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -66,7 +66,6 @@ class gui_control(object): self.seisarray.addSourceLocations(srcfile) if len(ptsfile) > 0: self.seisarray.addMeasuredTopographyPoints(ptsfile) - self.reprintArray() self.setSeisArrayState(True) def gen_survey(self): @@ -92,6 +91,9 @@ class gui_control(object): self.seisArrayFigure = Figure() self.seisArrayCanvas = FigureCanvas(self.seisArrayFigure) self.mainUI.verticalLayout_right.addWidget(self.seisArrayCanvas) + self.addArrayAxes() + + def addArrayAxes(self): self.seisArrayAx = self.seisArrayFigure.add_subplot(111) def interpolate_receivers(self): @@ -99,6 +101,7 @@ class gui_control(object): self.printDialogMessage('No Seismic Array defined.') return self.seisarray.interpolateAll() + self.replotArray() def getPickParameters(self, ui, Picking_parameters): if Picking_parameters.exec_(): @@ -246,12 +249,16 @@ class gui_control(object): self.seisarray = self.survey.seisarray self.setConnected2SurveyState(True) self.setSeisArrayState(True) - self.reprintArray() self.printDialogMessage('Loaded Survey with active Seismic Array.') - def reprintArray(self): - self.seisArrayAx.clear() - self.seisarray.plotArray2D(self.seisArrayAx) + def replotArray(self): + self.seisArrayFigure.clf() + self.addArrayAxes() + self.plotArray() + self.seisArrayCanvas.draw() + + def plotArray(self): + self.seisarray.plotArray2D(self.seisArrayAx, highlight_measured = True) def load_seisarray(self): if self.checkSeisArrayState(): @@ -270,7 +277,7 @@ class gui_control(object): %(type(survey), seismicArrayPreparation.SeisArray)) return self.seisarray = seisarray - self.reprintArray() + self.replotArray() self.setSeisArrayState(True) def save_seisarray(self): @@ -324,6 +331,7 @@ class gui_control(object): def setSeisArrayState(self, state): if state == True: self.mainUI.seisarray_active.setPixmap(self.applypixmap) + self.replotArray() elif state == False: self.mainUI.seisarray_active.setPixmap(self.cancelpixmap) From 30d911b5cc56afa755d05e6e57e895f74c85aa57 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 23 Jun 2016 11:55:54 +0200 Subject: [PATCH 0978/1144] window resizing --- pylot/core/active/asp3d_layout.py | 28 ++++----- .../core/active/generate_seisarray_layout.py | 58 +++++++++---------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index cb4e8d11..df75ae3c 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Tue Jun 21 13:22:35 2016 +# Created: Thu Jun 23 11:47:12 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -38,7 +38,7 @@ class Ui_MainWindow(object): self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.label_2 = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) @@ -70,7 +70,7 @@ class Ui_MainWindow(object): self.horizontalLayout_2 = QtGui.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.seisarray_active = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.seisarray_active.sizePolicy().hasHeightForWidth()) @@ -81,7 +81,7 @@ class Ui_MainWindow(object): self.seisarray_active.setObjectName("seisarray_active") self.horizontalLayout_2.addWidget(self.seisarray_active) self.label_5 = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth()) @@ -89,7 +89,7 @@ class Ui_MainWindow(object): self.label_5.setObjectName("label_5") self.horizontalLayout_2.addWidget(self.label_5) self.seisarray_on_survey_active = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.seisarray_on_survey_active.sizePolicy().hasHeightForWidth()) @@ -100,7 +100,7 @@ class Ui_MainWindow(object): self.seisarray_on_survey_active.setObjectName("seisarray_on_survey_active") self.horizontalLayout_2.addWidget(self.seisarray_on_survey_active) self.label_6 = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth()) @@ -117,7 +117,7 @@ class Ui_MainWindow(object): self.verticalLayout_2 = QtGui.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.label_3 = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) @@ -143,7 +143,7 @@ class Ui_MainWindow(object): self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.survey_active = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.survey_active.sizePolicy().hasHeightForWidth()) @@ -154,7 +154,7 @@ class Ui_MainWindow(object): self.survey_active.setObjectName("survey_active") self.horizontalLayout.addWidget(self.survey_active) self.label_4 = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth()) @@ -162,7 +162,7 @@ class Ui_MainWindow(object): self.label_4.setObjectName("label_4") self.horizontalLayout.addWidget(self.label_4) self.picked_active = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.picked_active.sizePolicy().hasHeightForWidth()) @@ -173,7 +173,7 @@ class Ui_MainWindow(object): self.picked_active.setObjectName("picked_active") self.horizontalLayout.addWidget(self.picked_active) self.label = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) @@ -190,7 +190,7 @@ class Ui_MainWindow(object): self.verticalLayout_3 = QtGui.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") self.label_7 = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth()) @@ -218,7 +218,7 @@ class Ui_MainWindow(object): self.verticalLayout_4 = QtGui.QVBoxLayout() self.verticalLayout_4.setObjectName("verticalLayout_4") self.label_8 = QtGui.QLabel(self.centralwidget) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_8.sizePolicy().hasHeightForWidth()) @@ -258,7 +258,7 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "ActiveSeismoPick 3D", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("MainWindow", "Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) - self.gen_new_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Generate new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.gen_new_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Create new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) self.load_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Load Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) self.save_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Save Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) self.interpolate_receivers.setText(QtGui.QApplication.translate("MainWindow", "Interpolate Receivers", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index ab13cad8..d6904455 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Thu Jun 16 12:10:38 2016 +# Created: Thu Jun 23 11:55:22 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -19,8 +19,8 @@ class Ui_generate_seisarray(object): generate_seisarray.setWindowIcon(icon) self.verticalLayout = QtGui.QVBoxLayout(generate_seisarray) self.verticalLayout.setObjectName("verticalLayout") - self.gridLayout = QtGui.QGridLayout() - self.gridLayout.setObjectName("gridLayout") + self.verticalLayout_3 = QtGui.QVBoxLayout() + self.verticalLayout_3.setObjectName("verticalLayout_3") self.label_rec = QtGui.QLabel(generate_seisarray) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -28,7 +28,7 @@ class Ui_generate_seisarray(object): sizePolicy.setHeightForWidth(self.label_rec.sizePolicy().hasHeightForWidth()) self.label_rec.setSizePolicy(sizePolicy) self.label_rec.setObjectName("label_rec") - self.gridLayout.addWidget(self.label_rec, 0, 0, 1, 1) + self.verticalLayout_3.addWidget(self.label_rec) self.horizontalLayout_4 = QtGui.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.lineEdit_rec = QtGui.QLineEdit(generate_seisarray) @@ -37,10 +37,18 @@ class Ui_generate_seisarray(object): self.pushButton_rec = QtGui.QPushButton(generate_seisarray) self.pushButton_rec.setObjectName("pushButton_rec") self.horizontalLayout_4.addWidget(self.pushButton_rec) - self.gridLayout.addLayout(self.horizontalLayout_4, 1, 0, 1, 1) - self.verticalLayout.addLayout(self.gridLayout) - self.gridLayout_2 = QtGui.QGridLayout() - self.gridLayout_2.setObjectName("gridLayout_2") + self.verticalLayout_3.addLayout(self.horizontalLayout_4) + self.verticalLayout.addLayout(self.verticalLayout_3) + self.verticalLayout_2 = QtGui.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label_src = QtGui.QLabel(generate_seisarray) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_src.sizePolicy().hasHeightForWidth()) + self.label_src.setSizePolicy(sizePolicy) + self.label_src.setObjectName("label_src") + self.verticalLayout_2.addWidget(self.label_src) self.horizontalLayout_5 = QtGui.QHBoxLayout() self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.lineEdit_src = QtGui.QLineEdit(generate_seisarray) @@ -49,18 +57,18 @@ class Ui_generate_seisarray(object): self.pushButton_src = QtGui.QPushButton(generate_seisarray) self.pushButton_src.setObjectName("pushButton_src") self.horizontalLayout_5.addWidget(self.pushButton_src) - self.gridLayout_2.addLayout(self.horizontalLayout_5, 1, 0, 1, 1) - self.label_src = QtGui.QLabel(generate_seisarray) + self.verticalLayout_2.addLayout(self.horizontalLayout_5) + self.verticalLayout.addLayout(self.verticalLayout_2) + self.verticalLayout_4 = QtGui.QVBoxLayout() + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.label_obs = QtGui.QLabel(generate_seisarray) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_src.sizePolicy().hasHeightForWidth()) - self.label_src.setSizePolicy(sizePolicy) - self.label_src.setObjectName("label_src") - self.gridLayout_2.addWidget(self.label_src, 0, 0, 1, 1) - self.verticalLayout.addLayout(self.gridLayout_2) - self.gridLayout_3 = QtGui.QGridLayout() - self.gridLayout_3.setObjectName("gridLayout_3") + sizePolicy.setHeightForWidth(self.label_obs.sizePolicy().hasHeightForWidth()) + self.label_obs.setSizePolicy(sizePolicy) + self.label_obs.setObjectName("label_obs") + self.verticalLayout_4.addWidget(self.label_obs) self.horizontalLayout_6 = QtGui.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.lineEdit_pts = QtGui.QLineEdit(generate_seisarray) @@ -69,16 +77,8 @@ class Ui_generate_seisarray(object): self.pushButton_obs = QtGui.QPushButton(generate_seisarray) self.pushButton_obs.setObjectName("pushButton_obs") self.horizontalLayout_6.addWidget(self.pushButton_obs) - self.gridLayout_3.addLayout(self.horizontalLayout_6, 1, 0, 1, 1) - self.label_obs = QtGui.QLabel(generate_seisarray) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_obs.sizePolicy().hasHeightForWidth()) - self.label_obs.setSizePolicy(sizePolicy) - self.label_obs.setObjectName("label_obs") - self.gridLayout_3.addWidget(self.label_obs, 0, 0, 1, 1) - self.verticalLayout.addLayout(self.gridLayout_3) + self.verticalLayout_4.addLayout(self.horizontalLayout_6) + self.verticalLayout.addLayout(self.verticalLayout_4) self.buttonBox = QtGui.QDialogButtonBox(generate_seisarray) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) @@ -94,8 +94,8 @@ class Ui_generate_seisarray(object): generate_seisarray.setWindowTitle(QtGui.QApplication.translate("generate_seisarray", "Generate new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) self.label_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Receivers", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_src.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.label_src.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Sources (optional)", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_src.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.label_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Additional measured points (optional)", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) From 3bec20bd45c0579177988c4a359aa0b01726c1e4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 23 Jun 2016 11:59:12 +0200 Subject: [PATCH 0979/1144] window resizing pt.2 --- pylot/core/active/generate_seisarray_layout.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index d6904455..a38fd1cd 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Thu Jun 23 11:55:22 2016 +# Created: Thu Jun 23 11:58:52 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,12 +12,14 @@ from PySide import QtCore, QtGui class Ui_generate_seisarray(object): def setupUi(self, generate_seisarray): generate_seisarray.setObjectName("generate_seisarray") - generate_seisarray.resize(400, 220) + generate_seisarray.resize(400, 221) generate_seisarray.setMinimumSize(QtCore.QSize(400, 220)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) generate_seisarray.setWindowIcon(icon) - self.verticalLayout = QtGui.QVBoxLayout(generate_seisarray) + self.verticalLayout_5 = QtGui.QVBoxLayout(generate_seisarray) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_3 = QtGui.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") @@ -58,7 +60,6 @@ class Ui_generate_seisarray(object): self.pushButton_src.setObjectName("pushButton_src") self.horizontalLayout_5.addWidget(self.pushButton_src) self.verticalLayout_2.addLayout(self.horizontalLayout_5) - self.verticalLayout.addLayout(self.verticalLayout_2) self.verticalLayout_4 = QtGui.QVBoxLayout() self.verticalLayout_4.setObjectName("verticalLayout_4") self.label_obs = QtGui.QLabel(generate_seisarray) @@ -78,12 +79,14 @@ class Ui_generate_seisarray(object): self.pushButton_obs.setObjectName("pushButton_obs") self.horizontalLayout_6.addWidget(self.pushButton_obs) self.verticalLayout_4.addLayout(self.horizontalLayout_6) - self.verticalLayout.addLayout(self.verticalLayout_4) + self.verticalLayout_2.addLayout(self.verticalLayout_4) + self.verticalLayout.addLayout(self.verticalLayout_2) + self.verticalLayout_5.addLayout(self.verticalLayout) self.buttonBox = QtGui.QDialogButtonBox(generate_seisarray) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.verticalLayout.addWidget(self.buttonBox) + self.verticalLayout_5.addWidget(self.buttonBox) self.retranslateUi(generate_seisarray) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_seisarray.reject) From 4bbb40c8b741c8c92e9bc670c9cfc5e155f8d5b6 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 23 Jun 2016 12:06:59 +0200 Subject: [PATCH 0980/1144] window resizing pt.3 --- .../core/active/generate_seisarray_layout.py | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index a38fd1cd..22bdc1bd 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Thu Jun 23 11:58:52 2016 +# Created: Thu Jun 23 12:06:35 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,17 +12,15 @@ from PySide import QtCore, QtGui class Ui_generate_seisarray(object): def setupUi(self, generate_seisarray): generate_seisarray.setObjectName("generate_seisarray") - generate_seisarray.resize(400, 221) + generate_seisarray.resize(400, 220) generate_seisarray.setMinimumSize(QtCore.QSize(400, 220)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) generate_seisarray.setWindowIcon(icon) - self.verticalLayout_5 = QtGui.QVBoxLayout(generate_seisarray) - self.verticalLayout_5.setObjectName("verticalLayout_5") - self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout = QtGui.QVBoxLayout(generate_seisarray) self.verticalLayout.setObjectName("verticalLayout") - self.verticalLayout_3 = QtGui.QVBoxLayout() - self.verticalLayout_3.setObjectName("verticalLayout_3") + self.verticalLayout_4 = QtGui.QVBoxLayout() + self.verticalLayout_4.setObjectName("verticalLayout_4") self.label_rec = QtGui.QLabel(generate_seisarray) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -30,7 +28,7 @@ class Ui_generate_seisarray(object): sizePolicy.setHeightForWidth(self.label_rec.sizePolicy().hasHeightForWidth()) self.label_rec.setSizePolicy(sizePolicy) self.label_rec.setObjectName("label_rec") - self.verticalLayout_3.addWidget(self.label_rec) + self.verticalLayout_4.addWidget(self.label_rec) self.horizontalLayout_4 = QtGui.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.lineEdit_rec = QtGui.QLineEdit(generate_seisarray) @@ -39,8 +37,8 @@ class Ui_generate_seisarray(object): self.pushButton_rec = QtGui.QPushButton(generate_seisarray) self.pushButton_rec.setObjectName("pushButton_rec") self.horizontalLayout_4.addWidget(self.pushButton_rec) - self.verticalLayout_3.addLayout(self.horizontalLayout_4) - self.verticalLayout.addLayout(self.verticalLayout_3) + self.verticalLayout_4.addLayout(self.horizontalLayout_4) + self.verticalLayout.addLayout(self.verticalLayout_4) self.verticalLayout_2 = QtGui.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.label_src = QtGui.QLabel(generate_seisarray) @@ -60,8 +58,9 @@ class Ui_generate_seisarray(object): self.pushButton_src.setObjectName("pushButton_src") self.horizontalLayout_5.addWidget(self.pushButton_src) self.verticalLayout_2.addLayout(self.horizontalLayout_5) - self.verticalLayout_4 = QtGui.QVBoxLayout() - self.verticalLayout_4.setObjectName("verticalLayout_4") + self.verticalLayout.addLayout(self.verticalLayout_2) + self.verticalLayout_3 = QtGui.QVBoxLayout() + self.verticalLayout_3.setObjectName("verticalLayout_3") self.label_obs = QtGui.QLabel(generate_seisarray) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -69,7 +68,7 @@ class Ui_generate_seisarray(object): sizePolicy.setHeightForWidth(self.label_obs.sizePolicy().hasHeightForWidth()) self.label_obs.setSizePolicy(sizePolicy) self.label_obs.setObjectName("label_obs") - self.verticalLayout_4.addWidget(self.label_obs) + self.verticalLayout_3.addWidget(self.label_obs) self.horizontalLayout_6 = QtGui.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.lineEdit_pts = QtGui.QLineEdit(generate_seisarray) @@ -78,15 +77,13 @@ class Ui_generate_seisarray(object): self.pushButton_obs = QtGui.QPushButton(generate_seisarray) self.pushButton_obs.setObjectName("pushButton_obs") self.horizontalLayout_6.addWidget(self.pushButton_obs) - self.verticalLayout_4.addLayout(self.horizontalLayout_6) - self.verticalLayout_2.addLayout(self.verticalLayout_4) - self.verticalLayout.addLayout(self.verticalLayout_2) - self.verticalLayout_5.addLayout(self.verticalLayout) + self.verticalLayout_3.addLayout(self.horizontalLayout_6) + self.verticalLayout.addLayout(self.verticalLayout_3) self.buttonBox = QtGui.QDialogButtonBox(generate_seisarray) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_5.addWidget(self.buttonBox) + self.verticalLayout.addWidget(self.buttonBox) self.retranslateUi(generate_seisarray) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_seisarray.reject) From 4591b7b1d9731dbe1c1614e07800fbb16c62c0fd Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Mon, 27 Jun 2016 10:59:50 +0200 Subject: [PATCH 0981/1144] [bugfix] changed the order of trying to read seismic data for reassessment --- pylot/core/io/phases.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index dfa67b4a..e3c7e90a 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -292,23 +292,26 @@ def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None for station in picks_dict.keys(): fn_pattern = os.path.join(search_base, '{0}*'.format(station)) try: - st = read(fn_pattern, format='GSE2') - except TypeError as e: - print(e.message) st = read(fn_pattern) - except ValueError as e: - if e.message == 'second must be in 0..59': - info = 'A known Error was raised. Please find the list of corrupted files and double-check these files.' - datacheck.append(fn_pattern + ' (time info)\n') - continue - else: - raise ValueError(e.message) - except Exception as e: - if 'No file matching file pattern:' in e.message: - if verbosity > 0: - warnings.warn('no waveform data found for station {station}'.format(station=station), RuntimeWarning) - datacheck.append(fn_pattern + ' (no data)\n') - continue + except TypeError as e: + if 'Unknown format for file' in e.message: + try: + st = read(fn_pattern, format='GSE2') + except ValueError as e: + if e.message == 'second must be in 0..59': + info = 'A known Error was raised. Please find the list of corrupted files and double-check these files.' + datacheck.append(fn_pattern + ' (time info)\n') + continue + else: + raise ValueError(e.message) + except Exception as e: + if 'No file matching file pattern:' in e.message: + if verbosity > 0: + warnings.warn('no waveform data found for station {station}'.format(station=station), RuntimeWarning) + datacheck.append(fn_pattern + ' (no data)\n') + continue + else: + raise e else: raise e for phase in picks_dict[station].keys(): From a813eb462f2aa6c6461c19a9d514aa00c10a7704 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 27 Jun 2016 14:47:34 +0200 Subject: [PATCH 0982/1144] adding tooltips --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 113 ++++++++++++++---- pylot/core/active/asp3d_layout.py | 48 ++++++-- pylot/core/active/fmtomo_parameters_layout.py | 55 +++++++-- .../core/active/generate_seisarray_layout.py | 59 +++++++-- pylot/core/active/generate_survey_layout.py | 52 +++++++- .../core/active/picking_parameters_layout.py | 2 +- pylot/core/active/surveyUtils.py | 44 ++++--- 7 files changed, 300 insertions(+), 73 deletions(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index dbcc5e2c..cf704f02 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -24,10 +24,12 @@ class gui_control(object): self.connectButtons() self.survey = None self.seisarray = None + self.seisArrayFigure = None self.cancelpixmap = self.mainwindow.style().standardPixmap(QtGui.QStyle.SP_DialogCancelButton) self.applypixmap = self.mainwindow.style().standardPixmap(QtGui.QStyle.SP_DialogApplyButton) self.setInitStates() self.addArrayPlot() + self.addStatPlots() def setInitStates(self): self.setPickState(False) @@ -47,11 +49,20 @@ class gui_control(object): QtCore.QObject.connect(self.mainUI.picker, QtCore.SIGNAL("clicked()"), self.callPicker) QtCore.QObject.connect(self.mainUI.postprocessing, QtCore.SIGNAL("clicked()"), self.postprocessing) QtCore.QObject.connect(self.mainUI.fmtomo, QtCore.SIGNAL("clicked()"), self.startFMTOMO) + QtCore.QObject.connect(self.mainUI.comboBox, QtCore.SIGNAL("activated(int)"), self.replotStat) def gen_seisarray(self): + disconnect = False if self.checkSeisArrayState(): if not self.continueDialogExists('Seismic Array'): return + if self.checkConnected2SurveyState(): + if not self.continueDialogMessage('Seismic Array connected to present Survey.\n' + 'Continuation will disconnect the Seismic Array.'): + return + else: + self.survey.seisarray = None + disconnect = True qdialog = QtGui.QDialog(self.mainwindow) ui = Ui_generate_seisarray() ui.setupUi(qdialog) @@ -66,6 +77,8 @@ class gui_control(object): self.seisarray.addSourceLocations(srcfile) if len(ptsfile) > 0: self.seisarray.addMeasuredTopographyPoints(ptsfile) + if disconnect: + self.setConnected2SurveyState(False) self.setSeisArrayState(True) def gen_survey(self): @@ -86,16 +99,60 @@ class gui_control(object): self.survey.setArtificialPick(0, 0) # artificial pick at source origin surveyUtils.setDynamicFittedSNR(self.survey.getShotDict()) self.setSurveyState(True) + self.setPickState(False) + self.setConnected2SurveyState(False) def addArrayPlot(self): self.seisArrayFigure = Figure() self.seisArrayCanvas = FigureCanvas(self.seisArrayFigure) - self.mainUI.verticalLayout_right.addWidget(self.seisArrayCanvas) - self.addArrayAxes() + self.mainUI.horizontalLayout_tr.addWidget(self.seisArrayCanvas) + + def addStatPlots(self): + self.statFigure_left = Figure() + self.statCanvas_left = FigureCanvas(self.statFigure_left) + self.mainUI.horizontalLayout_br.addWidget(self.statCanvas_left) + self.statFigure_right = Figure() + self.statCanvas_right = FigureCanvas(self.statFigure_right) + self.mainUI.horizontalLayout_br.addWidget(self.statCanvas_right) + self.addItems2ComboBox() + + def addItems2ComboBox(self): + self.mainUI.comboBox.insertItem(0, 'picked traces') + self.mainUI.comboBox.insertItem(1, 'mean SNR') + self.mainUI.comboBox.insertItem(2, 'median SNR') + self.mainUI.comboBox.insertItem(3, 'mean SPE') + self.mainUI.comboBox.insertItem(4, 'median SPE') + self.mainUI.comboBox.setEnabled(False) def addArrayAxes(self): self.seisArrayAx = self.seisArrayFigure.add_subplot(111) + def addStatAxes(self): + self.statAx_left = self.statFigure_left.add_subplot(111) + self.statAx_right = self.statFigure_right.add_subplot(111) + + def replotArray(self): + self.seisArrayFigure.clf() + self.addArrayAxes() + self.plotArray() + self.seisArrayCanvas.draw() + + def plotArray(self): + self.seisarray.plotArray2D(self.seisArrayAx, highlight_measured = True) + + def replotStat(self): + self.statFigure_left.clf() + self.statFigure_right.clf() + self.addStatAxes() + self.plotStat() + self.statCanvas_left.draw() + self.statCanvas_right.draw() + + def plotStat(self): + if self.checkPickState(): + surveyUtils.plotScatterStats4Receivers(self.survey, self.mainUI.comboBox.currentText(), self.statAx_left) + surveyUtils.plotScatterStats4Shots(self.survey, self.mainUI.comboBox.currentText(), self.statAx_right) + def interpolate_receivers(self): if not self.checkSeisArrayState(): self.printDialogMessage('No Seismic Array defined.') @@ -250,20 +307,24 @@ class gui_control(object): self.setConnected2SurveyState(True) self.setSeisArrayState(True) self.printDialogMessage('Loaded Survey with active Seismic Array.') - - def replotArray(self): - self.seisArrayFigure.clf() - self.addArrayAxes() - self.plotArray() - self.seisArrayCanvas.draw() - - def plotArray(self): - self.seisarray.plotArray2D(self.seisArrayAx, highlight_measured = True) + else: + self.setConnected2SurveyState(False) + self.setSeisArrayState(False) + self.printDialogMessage('Loaded Survey.') def load_seisarray(self): + disconnect = False if self.checkSeisArrayState(): if not self.continueDialogExists('Seismic Array'): return + if self.checkConnected2SurveyState(): + if not self.continueDialogMessage('Seismic Array connected to present Survey.\n' + 'Continuation will disconnect the Seismic Array.'): + return + else: + self.survey.seisarray = None + disconnect = True + filename = self.openFile() if filename is None: return @@ -276,6 +337,8 @@ class gui_control(object): self.printDialogMessage('Wrong input file of type %s, expected %s.' %(type(survey), seismicArrayPreparation.SeisArray)) return + if disconnect: + self.setConnected2SurveyState(False) self.seisarray = seisarray self.replotArray() self.setSeisArrayState(True) @@ -319,6 +382,8 @@ class gui_control(object): def setPickState(self, state): if state == True and self.checkSurveyState(): self.mainUI.picked_active.setPixmap(self.applypixmap) + self.replotStat() + self.mainUI.comboBox.setEnabled(True) self.survey.picked = True elif state == True and self.checkSurveyState() is False: self.printDialogMessage('No Survey defined.') @@ -326,6 +391,9 @@ class gui_control(object): elif state == False: self.mainUI.picked_active.setPixmap(self.cancelpixmap) if self.checkSurveyState(): + self.statFigure_left.clf() + self.statFigure_right.clf() + self.mainUI.comboBox.setEnabled(True) self.survey.picked = False def setSeisArrayState(self, state): @@ -334,6 +402,8 @@ class gui_control(object): self.replotArray() elif state == False: self.mainUI.seisarray_active.setPixmap(self.cancelpixmap) + if self.seisArrayFigure is not None: + self.seisArrayFigure.clf() def setConnected2SurveyState(self, state): if state == True: @@ -394,37 +464,40 @@ class gui_control(object): QtCore.QObject.connect(self.gen_new_seisarray.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseMeasuredPts) def chooseMeasuredSrc(self): - self.gen_new_seisarray.lineEdit_src.setText(self.openFile()) + self.gen_new_seisarray.lineEdit_src.setText(self.openFile('Open measured sources file.')) def chooseMeasuredRec(self): - self.gen_new_seisarray.lineEdit_rec.setText(self.openFile()) + self.gen_new_seisarray.lineEdit_rec.setText(self.openFile('Open measured receivers file.')) def chooseMeasuredPts(self): - self.gen_new_seisarray.lineEdit_pts.setText(self.browseDir()) + self.gen_new_seisarray.lineEdit_pts.setText(self.openFile('Open measured points file.')) def chooseSourcefile(self): - self.gen_new_survey.lineEdit_src.setText(self.openFile()) + self.gen_new_survey.lineEdit_src.setText(self.openFile('Open sourcefile.')) def chooseReceiverfile(self): - self.gen_new_survey.lineEdit_rec.setText(self.openFile()) + self.gen_new_survey.lineEdit_rec.setText(self.openFile('Open receiverfile.')) def chooseObsdir(self): - self.gen_new_survey.lineEdit_obs.setText(self.browseDir()) + self.gen_new_survey.lineEdit_obs.setText(self.browseDir('Choose observation directory.')) - def openFile(self): + def openFile(self, name = 'Open'): dialog = QtGui.QFileDialog() + dialog.setWindowTitle(name) #not working yet filename = dialog.getOpenFileName() if len(filename[0]) > 0: return filename[0] - def saveFile(self): + def saveFile(self, name = 'Save'): dialog = QtGui.QFileDialog() + dialog.setWindowTitle(name) filename = dialog.getSaveFileName() if len(filename[0]) > 0: return filename[0] - def browseDir(self): + def browseDir(self, name = 'Open Directory'): dialog = QtGui.QFileDialog() + dialog.setWindowTitle(name) directory = dialog.getExistingDirectory() if len(directory) > 0: return directory diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index df75ae3c..461fb2fd 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Thu Jun 23 11:47:12 2016 +# Created: Mon Jun 27 13:23:32 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -13,13 +13,13 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.setEnabled(True) - MainWindow.resize(800, 600) + MainWindow.resize(905, 707) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) - MainWindow.setMinimumSize(QtCore.QSize(300, 585)) + MainWindow.setMinimumSize(QtCore.QSize(800, 600)) MainWindow.setMaximumSize(QtCore.QSize(250000, 350000)) MainWindow.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) icon = QtGui.QIcon() @@ -29,9 +29,10 @@ class Ui_MainWindow(object): self.centralwidget.setObjectName("centralwidget") self.horizontalLayout_4 = QtGui.QHBoxLayout(self.centralwidget) self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.horizontalLayout_outer = QtGui.QHBoxLayout() - self.horizontalLayout_outer.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) - self.horizontalLayout_outer.setObjectName("horizontalLayout_outer") + self.formLayout = QtGui.QFormLayout() + self.formLayout.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) + self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) + self.formLayout.setObjectName("formLayout") self.verticalLayout_5 = QtGui.QVBoxLayout() self.verticalLayout_5.setSizeConstraint(QtGui.QLayout.SetMinimumSize) self.verticalLayout_5.setObjectName("verticalLayout_5") @@ -53,6 +54,7 @@ class Ui_MainWindow(object): self.label_2.setObjectName("label_2") self.verticalLayout.addWidget(self.label_2) self.gen_new_seisarray = QtGui.QPushButton(self.centralwidget) + self.gen_new_seisarray.setEnabled(True) self.gen_new_seisarray.setObjectName("gen_new_seisarray") self.verticalLayout.addWidget(self.gen_new_seisarray) self.load_seisarray = QtGui.QPushButton(self.centralwidget) @@ -237,15 +239,30 @@ class Ui_MainWindow(object): self.verticalLayout_4.addWidget(self.fmtomo) self.verticalLayout_3.addLayout(self.verticalLayout_4) self.verticalLayout_5.addLayout(self.verticalLayout_3) - self.horizontalLayout_outer.addLayout(self.verticalLayout_5) + self.formLayout.setLayout(0, QtGui.QFormLayout.LabelRole, self.verticalLayout_5) self.verticalLayout_right = QtGui.QVBoxLayout() self.verticalLayout_right.setSizeConstraint(QtGui.QLayout.SetMaximumSize) self.verticalLayout_right.setObjectName("verticalLayout_right") - self.horizontalLayout_outer.addLayout(self.verticalLayout_right) - self.horizontalLayout_4.addLayout(self.horizontalLayout_outer) + self.horizontalLayout_tr = QtGui.QHBoxLayout() + self.horizontalLayout_tr.setObjectName("horizontalLayout_tr") + self.verticalLayout_right.addLayout(self.horizontalLayout_tr) + self.line_4 = QtGui.QFrame(self.centralwidget) + self.line_4.setFrameShape(QtGui.QFrame.HLine) + self.line_4.setFrameShadow(QtGui.QFrame.Sunken) + self.line_4.setObjectName("line_4") + self.verticalLayout_right.addWidget(self.line_4) + self.comboBox = QtGui.QComboBox(self.centralwidget) + self.comboBox.setEnabled(False) + self.comboBox.setObjectName("comboBox") + self.verticalLayout_right.addWidget(self.comboBox) + self.horizontalLayout_br = QtGui.QHBoxLayout() + self.horizontalLayout_br.setObjectName("horizontalLayout_br") + self.verticalLayout_right.addLayout(self.horizontalLayout_br) + self.formLayout.setLayout(0, QtGui.QFormLayout.FieldRole, self.verticalLayout_right) + self.horizontalLayout_4.addLayout(self.formLayout) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 905, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtGui.QStatusBar(MainWindow) @@ -258,22 +275,33 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "ActiveSeismoPick 3D", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("MainWindow", "Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.gen_new_seisarray.setToolTip(QtGui.QApplication.translate("MainWindow", "Generate a new Seismic Array object.", None, QtGui.QApplication.UnicodeUTF8)) self.gen_new_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Create new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.load_seisarray.setToolTip(QtGui.QApplication.translate("MainWindow", "Load an existing Seismic Array object saved as \'.pickle\' file.", None, QtGui.QApplication.UnicodeUTF8)) self.load_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Load Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.save_seisarray.setToolTip(QtGui.QApplication.translate("MainWindow", "Save current Seismic Array object to \'.pickle\' file.", None, QtGui.QApplication.UnicodeUTF8)) self.save_seisarray.setText(QtGui.QApplication.translate("MainWindow", "Save Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) + self.interpolate_receivers.setToolTip(QtGui.QApplication.translate("MainWindow", "Interpolate positions of receivers in between that were not manually measured.", None, QtGui.QApplication.UnicodeUTF8)) self.interpolate_receivers.setText(QtGui.QApplication.translate("MainWindow", "Interpolate Receivers", None, QtGui.QApplication.UnicodeUTF8)) + self.connect_to_survey.setToolTip(QtGui.QApplication.translate("MainWindow", "Save the current Seismic Array to the current Survey object.", None, QtGui.QApplication.UnicodeUTF8)) self.connect_to_survey.setText(QtGui.QApplication.translate("MainWindow", "Connect to Survey", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("MainWindow", "active", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("MainWindow", "connected to Survey", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("MainWindow", "Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.gen_new_survey.setToolTip(QtGui.QApplication.translate("MainWindow", "Generate a new Survey object.", None, QtGui.QApplication.UnicodeUTF8)) self.gen_new_survey.setText(QtGui.QApplication.translate("MainWindow", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.load_survey.setToolTip(QtGui.QApplication.translate("MainWindow", "Load an existing Survey object from a \'.pickle\' file.", None, QtGui.QApplication.UnicodeUTF8)) self.load_survey.setText(QtGui.QApplication.translate("MainWindow", "Load Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.save_survey.setToolTip(QtGui.QApplication.translate("MainWindow", "Save current Survey object to \'.pickle\' file.", None, QtGui.QApplication.UnicodeUTF8)) self.save_survey.setText(QtGui.QApplication.translate("MainWindow", "Save Survey", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("MainWindow", "active", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("MainWindow", "picked", None, QtGui.QApplication.UnicodeUTF8)) self.label_7.setText(QtGui.QApplication.translate("MainWindow", "Picking", None, QtGui.QApplication.UnicodeUTF8)) + self.picker.setToolTip(QtGui.QApplication.translate("MainWindow", "Choose parameters and call automatic picking routines.", None, QtGui.QApplication.UnicodeUTF8)) self.picker.setText(QtGui.QApplication.translate("MainWindow", "Automatic Picking", None, QtGui.QApplication.UnicodeUTF8)) + self.postprocessing.setToolTip(QtGui.QApplication.translate("MainWindow", "Manual control and postprocessing of a picked Survey.", None, QtGui.QApplication.UnicodeUTF8)) self.postprocessing.setText(QtGui.QApplication.translate("MainWindow", "Postprocessing", None, QtGui.QApplication.UnicodeUTF8)) self.label_8.setText(QtGui.QApplication.translate("MainWindow", "Simulation", None, QtGui.QApplication.UnicodeUTF8)) + self.fmtomo.setToolTip(QtGui.QApplication.translate("MainWindow", "Set parameters and call Fast Marching Tomography algorithm.", None, QtGui.QApplication.UnicodeUTF8)) self.fmtomo.setText(QtGui.QApplication.translate("MainWindow", "FMTOMO Simulation", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index 7b607c61..aa6edd3e 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Fri Jun 17 10:24:38 2016 +# Created: Mon Jun 27 13:23:32 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,13 +12,13 @@ from PySide import QtCore, QtGui class Ui_fmtomo_parameters(object): def setupUi(self, fmtomo_parameters): fmtomo_parameters.setObjectName("fmtomo_parameters") - fmtomo_parameters.resize(380, 440) + fmtomo_parameters.resize(400, 440) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(fmtomo_parameters.sizePolicy().hasHeightForWidth()) fmtomo_parameters.setSizePolicy(sizePolicy) - fmtomo_parameters.setMinimumSize(QtCore.QSize(380, 440)) + fmtomo_parameters.setMinimumSize(QtCore.QSize(400, 440)) fmtomo_parameters.setMaximumSize(QtCore.QSize(320000, 520000)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) @@ -234,37 +234,66 @@ class Ui_fmtomo_parameters(object): def retranslateUi(self, fmtomo_parameters): fmtomo_parameters.setWindowTitle(QtGui.QApplication.translate("fmtomo_parameters", "Choose FMTOMO parameters", None, QtGui.QApplication.UnicodeUTF8)) + self.label_17.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Directory containing a clean FMTOMO installation.", None, QtGui.QApplication.UnicodeUTF8)) self.label_17.setText(QtGui.QApplication.translate("fmtomo_parameters", "FMTOMO\n" -"installation", None, QtGui.QApplication.UnicodeUTF8)) +"installation [?]", None, QtGui.QApplication.UnicodeUTF8)) self.browse_tomodir.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_14.setText(QtGui.QApplication.translate("fmtomo_parameters", "nproc", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("fmtomo_parameters", "Iterations", None, QtGui.QApplication.UnicodeUTF8)) + self.label_14.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Number of processes spawned for parallel forward simulation.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_14.setText(QtGui.QApplication.translate("fmtomo_parameters", "Number of processes [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Number of inversion iterations.", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("fmtomo_parameters", "Iterations [?]", None, QtGui.QApplication.UnicodeUTF8)) self.label_18.setText(QtGui.QApplication.translate("fmtomo_parameters", "Grid definitions from seismic Array:", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setText(QtGui.QApplication.translate("fmtomo_parameters", "Bottom boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Bottom boundary of the model.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("fmtomo_parameters", "Bottom boundary [?]", None, QtGui.QApplication.UnicodeUTF8)) self.bbot.setText(QtGui.QApplication.translate("fmtomo_parameters", "-50", None, QtGui.QApplication.UnicodeUTF8)) self.label_11.setText(QtGui.QApplication.translate("fmtomo_parameters", "m", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("fmtomo_parameters", "Top boundary", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Top boundary of the model (must be greater than highest source or receiver position).\n" +"Too low values will cause error.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("fmtomo_parameters", "Top boundary [?]", None, QtGui.QApplication.UnicodeUTF8)) self.btop.setText(QtGui.QApplication.translate("fmtomo_parameters", "5", None, QtGui.QApplication.UnicodeUTF8)) self.label_10.setText(QtGui.QApplication.translate("fmtomo_parameters", "m", None, QtGui.QApplication.UnicodeUTF8)) - self.label_13.setText(QtGui.QApplication.translate("fmtomo_parameters", "Cushion factor", None, QtGui.QApplication.UnicodeUTF8)) + self.label_13.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Extension of the model around the maximum values of the receiver grid (X and Y).\n" +"Too low values will cause error.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_13.setText(QtGui.QApplication.translate("fmtomo_parameters", "Cushion factor [?]", None, QtGui.QApplication.UnicodeUTF8)) self.cushion.setText(QtGui.QApplication.translate("fmtomo_parameters", "10", None, QtGui.QApplication.UnicodeUTF8)) self.label_12.setText(QtGui.QApplication.translate("fmtomo_parameters", "%", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("fmtomo_parameters", "Number of Ponts", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("fmtomo_parameters", "X", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("fmtomo_parameters", "Y", None, QtGui.QApplication.UnicodeUTF8)) self.label_7.setText(QtGui.QApplication.translate("fmtomo_parameters", "Z", None, QtGui.QApplication.UnicodeUTF8)) - self.label_8.setText(QtGui.QApplication.translate("fmtomo_parameters", "Propagation Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Number of points for forward simulation in X, Y, Z.\n" +"Must be higher than number of inversion grid points.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setText(QtGui.QApplication.translate("fmtomo_parameters", "Propagation Grid [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pgrid_x.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) self.pgrid_y.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) self.pgrid_z.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.label_9.setText(QtGui.QApplication.translate("fmtomo_parameters", "Inversion Grid", None, QtGui.QApplication.UnicodeUTF8)) + self.label_9.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Number of inversion grid (velocity grid) points in X, Y, Z.\n" +"Must be lower than for propagation grid.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_9.setText(QtGui.QApplication.translate("fmtomo_parameters", "Inversion Grid [?]", None, QtGui.QApplication.UnicodeUTF8)) self.invgrid_x.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) self.invgrid_y.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) self.invgrid_z.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_15.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "\n" +"\n" +"

Specifiy simple input velocity file from which linear gradients will be evaluated.

\n" +"

\n" +"

For example:

\n" +"

\n" +"

First gradient: Surface velocity: 0.5 km/s rising to 1.0 km/s at a depth of 5 m below the surface.

\n" +"

Second gradient: Continuous change to another gradient from 1.0 km/s to 4.0 km/s at 60 m depth below the surface.

\n" +"

\n" +"

0 0.5

\n" +"

-5 1.0

\n" +"

-5 1.0

\n" +"

-60 4.0

\n" +"

", None, QtGui.QApplication.UnicodeUTF8)) self.label_15.setText(QtGui.QApplication.translate("fmtomo_parameters", "Custom velocity\n" -"grid (earthmodel)", None, QtGui.QApplication.UnicodeUTF8)) +"grid (earthmodel) [?]", None, QtGui.QApplication.UnicodeUTF8)) self.browse_customgrid.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_16.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Specifiy directory for FMTOMO simulation.", None, QtGui.QApplication.UnicodeUTF8)) self.label_16.setText(QtGui.QApplication.translate("fmtomo_parameters", "Simulation\n" -"Directory", None, QtGui.QApplication.UnicodeUTF8)) +"Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) self.browse_simuldir.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index 22bdc1bd..4caeb2a9 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Thu Jun 23 12:06:35 2016 +# Created: Mon Jun 27 13:23:32 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,12 +12,14 @@ from PySide import QtCore, QtGui class Ui_generate_seisarray(object): def setupUi(self, generate_seisarray): generate_seisarray.setObjectName("generate_seisarray") - generate_seisarray.resize(400, 220) + generate_seisarray.resize(400, 221) generate_seisarray.setMinimumSize(QtCore.QSize(400, 220)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) generate_seisarray.setWindowIcon(icon) - self.verticalLayout = QtGui.QVBoxLayout(generate_seisarray) + self.verticalLayout_5 = QtGui.QVBoxLayout(generate_seisarray) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_4 = QtGui.QVBoxLayout() self.verticalLayout_4.setObjectName("verticalLayout_4") @@ -79,11 +81,12 @@ class Ui_generate_seisarray(object): self.horizontalLayout_6.addWidget(self.pushButton_obs) self.verticalLayout_3.addLayout(self.horizontalLayout_6) self.verticalLayout.addLayout(self.verticalLayout_3) + self.verticalLayout_5.addLayout(self.verticalLayout) self.buttonBox = QtGui.QDialogButtonBox(generate_seisarray) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.verticalLayout.addWidget(self.buttonBox) + self.verticalLayout_5.addWidget(self.buttonBox) self.retranslateUi(generate_seisarray) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_seisarray.reject) @@ -92,10 +95,52 @@ class Ui_generate_seisarray(object): def retranslateUi(self, generate_seisarray): generate_seisarray.setWindowTitle(QtGui.QApplication.translate("generate_seisarray", "Generate new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Receivers", None, QtGui.QApplication.UnicodeUTF8)) + self.label_rec.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" +"\n" +"

Load receiver input file. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, seperated by spaces:

\n" +"

\n" +"

[trace ID (int)] [receiver line ID (int)] [number of the geophone on receiver line (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Third geophone on the second receiver line with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" +"

\n" +"

50 2 3 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) + self.label_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Receivers [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Sources (optional)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" +"\n" +"

Load measured sources input file to improve interpolation precision. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, seperated by spaces:

\n" +"

\n" +"

[source ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Shot number 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" +"

\n" +"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Sources (optional) [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_src.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Additional measured points (optional)", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" +"\n" +"

Load measured points input file to improve interpolation precision. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, seperated by spaces:

\n" +"

\n" +"

[point ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Point number 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" +"

\n" +"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Additional measured points (optional) [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/generate_survey_layout.py b/pylot/core/active/generate_survey_layout.py index e04ad4de..6e0bc92a 100644 --- a/pylot/core/active/generate_survey_layout.py +++ b/pylot/core/active/generate_survey_layout.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'generate_survey.ui' +# Form implementation generated from reading ui file 'generate_survey_layout.ui' # -# Created: Wed Jun 15 11:56:01 2016 +# Created: Mon Jun 27 13:23:33 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! from PySide import QtCore, QtGui -class Ui_generate_survey(object): +class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(380, 160) @@ -62,12 +62,52 @@ class Ui_generate_survey(object): def retranslateUi(self, Dialog): Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_rec.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_rec.setToolTip(QtGui.QApplication.translate("Dialog", "\n" +"\n" +"

Load receiver input file. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, seperated by spaces:

\n" +"

\n" +"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Trace ID 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" +"

\n" +"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) self.label_rec.setText(QtGui.QApplication.translate("Dialog", "Receiver\n" -"File", None, QtGui.QApplication.UnicodeUTF8)) +"File [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setToolTip(QtGui.QApplication.translate("Dialog", "\n" +"\n" +"

Specifiy directory containing seismograms for each shot.

\n" +"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" +"

\n" +"

For example:

\n" +"

\n" +"

Shot number 100 containing seismograms for all traces with the name:

\n" +"

\n" +"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) self.label_obs.setText(QtGui.QApplication.translate("Dialog", "Seismogram\n" -"Directory", None, QtGui.QApplication.UnicodeUTF8)) +"Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_obs.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setToolTip(QtGui.QApplication.translate("Dialog", "\n" +"\n" +"

Load sources input file. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, seperated by spaces:

\n" +"

\n" +"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Source number 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" +"

\n" +"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) self.label_src.setText(QtGui.QApplication.translate("Dialog", "Source\n" -"File", None, QtGui.QApplication.UnicodeUTF8)) +"File [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_src.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index ce6d17da..fdecdea1 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Fri Jun 17 13:06:08 2016 +# Created: Mon Jun 27 13:23:33 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index a875ca8b..5edf7af3 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -155,7 +155,7 @@ def cleanUp(survey): for shot in survey.data.values(): shot.traces4plot = {} -def plotScatterStats4Shots(survey, variable): +def plotScatterStats4Shots(survey, variable, ax = None): """ Statistics, scatter plot. @@ -178,7 +178,9 @@ def plotScatterStats4Shots(survey, variable): 'SPE': [], 'picked traces': 0} - statsShot[shot]['SNR'].append(shot.getSNR(traceID)[0]) + SNR = shot.getSNR(traceID)[0] + if not SNR == np.inf: + statsShot[shot]['SNR'].append(SNR) if shot.getPickFlag(traceID) == 1: statsShot[shot]['picked traces'] += 1 statsShot[shot]['SPE'].append(shot.getSymmetricPickError(traceID)) @@ -194,18 +196,22 @@ def plotScatterStats4Shots(survey, variable): y.append(statsShot[shot]['y']) value.append(statsShot[shot][variable]) - fig = plt.figure() - ax = fig.add_subplot(111) + if ax is None: + fig = plt.figure() + ax = fig.add_subplot(111) size = [] for val in value: size.append(100 * val / max(value)) sc = ax.scatter(x, y, s=size, c=value) - plt.title('Plot of all shots') - plt.xlabel('X') - plt.ylabel('Y') - cbar = plt.colorbar(sc) + ax.text(0.5, 1.05,'Plot of all shots', + horizontalalignment='center', verticalalignment='center', + transform=ax.transAxes) + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_aspect('equal') + cbar = ax.figure.colorbar(sc) cbar.set_label(variable) for shot in statsShot.keys(): @@ -213,7 +219,7 @@ def plotScatterStats4Shots(survey, variable): fontsize='x-small', color='k') -def plotScatterStats4Receivers(survey, variable): +def plotScatterStats4Receivers(survey, variable, ax = None): """ Statistics, scatter plot. @@ -236,7 +242,9 @@ def plotScatterStats4Receivers(survey, variable): 'SPE': [], 'picked traces': 0} - statsRec[traceID]['SNR'].append(shot.getSNR(traceID)[0]) + SNR = shot.getSNR(traceID)[0] + if not SNR == np.inf: + statsRec[traceID]['SNR'].append(SNR) if shot.getPickFlag(traceID) == 1: statsRec[traceID]['picked traces'] += 1 statsRec[traceID]['SPE'].append(shot.getSymmetricPickError(traceID)) @@ -252,18 +260,22 @@ def plotScatterStats4Receivers(survey, variable): y.append(statsRec[traceID]['y']) value.append(statsRec[traceID][variable]) - fig = plt.figure() - ax = fig.add_subplot(111) + if ax is None: + fig = plt.figure() + ax = fig.add_subplot(111) size = [] for val in value: size.append(100 * val / max(value)) sc = ax.scatter(x, y, s=size, c=value) - plt.title('Plot of all receivers') - plt.xlabel('X') - plt.ylabel('Y') - cbar = plt.colorbar(sc) + ax.text(0.5, 1.05,'Plot of all receivers', + horizontalalignment='center', verticalalignment='center', + transform=ax.transAxes) + ax.set_xlabel('X') + ax.set_ylabel('Y') + ax.set_aspect('equal') + cbar = ax.figure.colorbar(sc) cbar.set_label(variable) shot = survey.data.values()[0] From 8714616d1baf79f2479147b26c699302d0a25e3b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 27 Jun 2016 15:20:17 +0200 Subject: [PATCH 0983/1144] Working on input file format (SEG2/SEGY) etc. --- pylot/core/active/activeSeismoPick.py | 6 +- pylot/core/active/asp3d_layout.py | 2 +- pylot/core/active/fmtomo_parameters_layout.py | 2 +- .../core/active/generate_seisarray_layout.py | 2 +- pylot/core/active/generate_survey_layout.py | 64 +++++++++---------- .../core/active/picking_parameters_layout.py | 2 +- pylot/core/active/seismicshot.py | 8 ++- 7 files changed, 47 insertions(+), 39 deletions(-) diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 3f91c898..b2815192 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -49,7 +49,9 @@ class Survey(object): shotlist = self.getShotlist() for shotnumber in shotlist: # loop over data files # generate filenames and read manual picks to a list - obsfile = os.path.join(self._obsdir, str(shotnumber)) + '_pickle.dat' + #fileending = '_pickle.dat' + fileending = '.sg2' + obsfile = os.path.join(self._obsdir, str(shotnumber)) + fileending if obsfile not in shot_dict.keys(): shot_dict[shotnumber] = [] shot_dict[shotnumber] = seismicshot.SeismicShot(obsfile) @@ -375,7 +377,7 @@ class Survey(object): shotlist = [] for line in srcfile.readlines(): line = line.split() - shotlist.append(int(line[0])) + shotlist.append(line[0]) return shotlist diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index 461fb2fd..fe85b039 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Mon Jun 27 13:23:32 2016 +# Created: Mon Jun 27 15:18:25 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index aa6edd3e..9ba8f5ec 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Mon Jun 27 13:23:32 2016 +# Created: Mon Jun 27 15:18:25 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index 4caeb2a9..be785a97 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Mon Jun 27 13:23:32 2016 +# Created: Mon Jun 27 15:18:25 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_survey_layout.py b/pylot/core/active/generate_survey_layout.py index 6e0bc92a..f2c8c992 100644 --- a/pylot/core/active/generate_survey_layout.py +++ b/pylot/core/active/generate_survey_layout.py @@ -2,67 +2,67 @@ # Form implementation generated from reading ui file 'generate_survey_layout.ui' # -# Created: Mon Jun 27 13:23:33 2016 +# Created: Mon Jun 27 15:18:25 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! from PySide import QtCore, QtGui -class Ui_Dialog(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(380, 160) +class Ui_generate_survey(object): + def setupUi(self, generate_survey): + generate_survey.setObjectName("generate_survey") + generate_survey.resize(380, 160) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) - Dialog.setWindowIcon(icon) - self.verticalLayout = QtGui.QVBoxLayout(Dialog) + generate_survey.setWindowIcon(icon) + self.verticalLayout = QtGui.QVBoxLayout(generate_survey) self.verticalLayout.setObjectName("verticalLayout") self.gridLayout = QtGui.QGridLayout() self.gridLayout.setObjectName("gridLayout") - self.lineEdit_rec = QtGui.QLineEdit(Dialog) + self.lineEdit_rec = QtGui.QLineEdit(generate_survey) self.lineEdit_rec.setObjectName("lineEdit_rec") self.gridLayout.addWidget(self.lineEdit_rec, 0, 1, 1, 1) - self.pushButton_rec = QtGui.QPushButton(Dialog) + self.pushButton_rec = QtGui.QPushButton(generate_survey) self.pushButton_rec.setObjectName("pushButton_rec") self.gridLayout.addWidget(self.pushButton_rec, 0, 2, 1, 1) - self.label_rec = QtGui.QLabel(Dialog) + self.label_rec = QtGui.QLabel(generate_survey) self.label_rec.setObjectName("label_rec") self.gridLayout.addWidget(self.label_rec, 0, 0, 1, 1) - self.lineEdit_obs = QtGui.QLineEdit(Dialog) + self.lineEdit_obs = QtGui.QLineEdit(generate_survey) self.lineEdit_obs.setObjectName("lineEdit_obs") self.gridLayout.addWidget(self.lineEdit_obs, 2, 1, 1, 1) - self.label_obs = QtGui.QLabel(Dialog) + self.label_obs = QtGui.QLabel(generate_survey) self.label_obs.setObjectName("label_obs") self.gridLayout.addWidget(self.label_obs, 2, 0, 1, 1) - self.pushButton_obs = QtGui.QPushButton(Dialog) + self.pushButton_obs = QtGui.QPushButton(generate_survey) self.pushButton_obs.setObjectName("pushButton_obs") self.gridLayout.addWidget(self.pushButton_obs, 2, 2, 1, 1) - self.label_src = QtGui.QLabel(Dialog) + self.label_src = QtGui.QLabel(generate_survey) self.label_src.setObjectName("label_src") self.gridLayout.addWidget(self.label_src, 1, 0, 1, 1) - self.lineEdit_src = QtGui.QLineEdit(Dialog) + self.lineEdit_src = QtGui.QLineEdit(generate_survey) self.lineEdit_src.setObjectName("lineEdit_src") self.gridLayout.addWidget(self.lineEdit_src, 1, 1, 1, 1) - self.pushButton_src = QtGui.QPushButton(Dialog) + self.pushButton_src = QtGui.QPushButton(generate_survey) self.pushButton_src.setObjectName("pushButton_src") self.gridLayout.addWidget(self.pushButton_src, 1, 2, 1, 1) self.verticalLayout.addLayout(self.gridLayout) - self.buttonBox = QtGui.QDialogButtonBox(Dialog) + self.buttonBox = QtGui.QDialogButtonBox(generate_survey) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout.addWidget(self.buttonBox) - self.retranslateUi(Dialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) - QtCore.QMetaObject.connectSlotsByName(Dialog) + self.retranslateUi(generate_survey) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), generate_survey.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_survey.reject) + QtCore.QMetaObject.connectSlotsByName(generate_survey) - def retranslateUi(self, Dialog): - Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_rec.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setToolTip(QtGui.QApplication.translate("Dialog", "\n" + def retranslateUi(self, generate_survey): + generate_survey.setWindowTitle(QtGui.QApplication.translate("generate_survey", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_rec.setText(QtGui.QApplication.translate("generate_survey", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_rec.setToolTip(QtGui.QApplication.translate("generate_survey", "\n" "\n" @@ -76,9 +76,9 @@ class Ui_Dialog(object): "

Trace ID 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" "

\n" "

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setText(QtGui.QApplication.translate("Dialog", "Receiver\n" + self.label_rec.setText(QtGui.QApplication.translate("generate_survey", "Receiver\n" "File [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setToolTip(QtGui.QApplication.translate("Dialog", "\n" + self.label_obs.setToolTip(QtGui.QApplication.translate("generate_survey", "\n" "\n" @@ -90,10 +90,10 @@ class Ui_Dialog(object): "

Shot number 100 containing seismograms for all traces with the name:

\n" "

\n" "

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setText(QtGui.QApplication.translate("Dialog", "Seismogram\n" + self.label_obs.setText(QtGui.QApplication.translate("generate_survey", "Seismogram\n" "Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_obs.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setToolTip(QtGui.QApplication.translate("Dialog", "\n" + self.pushButton_obs.setText(QtGui.QApplication.translate("generate_survey", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setToolTip(QtGui.QApplication.translate("generate_survey", "\n" "\n" @@ -107,7 +107,7 @@ class Ui_Dialog(object): "

Source number 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" "

\n" "

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setText(QtGui.QApplication.translate("Dialog", "Source\n" + self.label_src.setText(QtGui.QApplication.translate("generate_survey", "Source\n" "File [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_src.setText(QtGui.QApplication.translate("Dialog", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_src.setText(QtGui.QApplication.translate("generate_survey", "Browse", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index fdecdea1..eb6c87eb 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Mon Jun 27 13:23:33 2016 +# Created: Mon Jun 27 15:18:25 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index a93acd7f..bc51b4b4 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -33,6 +33,7 @@ class SeismicShot(object): :type: string ''' self.traces = read(obsfile) + self.renameChannelIDs() self.recCoordlist = None self.srcCoordlist = None self.traceIDs = None @@ -47,6 +48,11 @@ class SeismicShot(object): self.paras['shotname'] = obsfile self.folm = None + def renameChannelIDs(self): + for trace in self.traces: + if trace.stats._format == 'SEG2': + trace.stats.channel = int(trace.stats.seg2['CHANNEL_NUMBER']) + def removeEmptyTraces(self): traceIDs = [] coordlist = self.getRecCoordlist() @@ -495,7 +501,7 @@ class SeismicShot(object): ''' coordlist = self.getSrcCoordlist() for i in range(0, len(coordlist)): - if int(coordlist[i].split()[0]) == self.paras['shotnumber']: + if coordlist[i].split()[0] == self.paras['shotnumber']: x = coordlist[i].split()[1] y = coordlist[i].split()[2] z = coordlist[i].split()[3] From 9e3f3b29d2b974ad0474dc438857c057a72ad316 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 28 Jun 2016 14:57:48 +0200 Subject: [PATCH 0984/1144] changed general structure of seisarray and survey. survey object can now be generated by using a seismic array (also in GUI). --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 43 ++++++++-- pylot/core/active/activeSeismoPick.py | 68 ++++++++++++--- pylot/core/active/asp3d_layout.py | 2 +- pylot/core/active/fmtomo_parameters_layout.py | 2 +- .../core/active/generate_seisarray_layout.py | 10 +-- pylot/core/active/generate_survey_layout.py | 6 +- .../core/active/picking_parameters_layout.py | 2 +- pylot/core/active/seismicArrayPreparation.py | 28 +++--- pylot/core/active/seismicshot.py | 85 +++++++------------ 9 files changed, 154 insertions(+), 92 deletions(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index cf704f02..b6a48568 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -9,6 +9,7 @@ from PySide import QtCore, QtGui, QtCore from asp3d_layout import * from fmtomo_parameters_layout import * from generate_survey_layout import * +from generate_survey_layout_minimal import * from generate_seisarray_layout import * from picking_parameters_layout import * from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils, seismicArrayPreparation @@ -85,6 +86,34 @@ class gui_control(object): if self.checkSurveyState(): if not self.continueDialogExists('Survey'): return + if self.checkSeisArrayState(): + if self.continueDialogMessage('Use geometry information of active Seismic Array?'): + if self.gen_survey_fromSeisArray(): + self.initNewSurvey() + else: + if self.gen_survey_fromSRfiles(): + self.initNewSurvey() + + def initNewSurvey(self): + self.survey.setArtificialPick(0, 0) # artificial pick at source origin + surveyUtils.setDynamicFittedSNR(self.survey.getShotDict()) + self.setSurveyState(True) + self.setPickState(False) + + def gen_survey_fromSeisArray(self): + qdialog = QtGui.QDialog(self.mainwindow) + ui = Ui_generate_survey_minimal() + ui.setupUi(qdialog) + self.gen_new_survey_min = ui + self.connectButtons_gen_survey_min() + if qdialog.exec_(): + obsdir = self.gen_new_survey_min.lineEdit_obs.text() + self.survey = activeSeismoPick.Survey(obsdir, seisArray = self.seisarray, + useDefaultParas = True) + self.setConnected2SurveyState(True) + return True + + def gen_survey_fromSRfiles(self): qdialog = QtGui.QDialog(self.mainwindow) ui = Ui_generate_survey() ui.setupUi(qdialog) @@ -96,11 +125,8 @@ class gui_control(object): obsdir = self.gen_new_survey.lineEdit_obs.text() self.survey = activeSeismoPick.Survey(obsdir, srcfile, recfile, useDefaultParas = True) - self.survey.setArtificialPick(0, 0) # artificial pick at source origin - surveyUtils.setDynamicFittedSNR(self.survey.getShotDict()) - self.setSurveyState(True) - self.setPickState(False) self.setConnected2SurveyState(False) + return True def addArrayPlot(self): self.seisArrayFigure = Figure() @@ -182,6 +208,7 @@ class gui_control(object): pass self.survey.seisarray = self.seisarray self.setConnected2SurveyState(True) + self.survey._initiate_SRfiles() print('Connected Seismic Array to active Survey object.') def getMaxCPU(self): @@ -393,7 +420,7 @@ class gui_control(object): if self.checkSurveyState(): self.statFigure_left.clf() self.statFigure_right.clf() - self.mainUI.comboBox.setEnabled(True) + self.mainUI.comboBox.setEnabled(False) self.survey.picked = False def setSeisArrayState(self, state): @@ -458,6 +485,9 @@ class gui_control(object): QtCore.QObject.connect(self.gen_new_survey.pushButton_src, QtCore.SIGNAL("clicked()"), self.chooseSourcefile) QtCore.QObject.connect(self.gen_new_survey.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseObsdir) + def connectButtons_gen_survey_min(self): + QtCore.QObject.connect(self.gen_new_survey_min.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseObsdir_min) + def connectButtons_gen_seisarray(self): QtCore.QObject.connect(self.gen_new_seisarray.pushButton_rec, QtCore.SIGNAL("clicked()"), self.chooseMeasuredRec) QtCore.QObject.connect(self.gen_new_seisarray.pushButton_src, QtCore.SIGNAL("clicked()"), self.chooseMeasuredSrc) @@ -481,6 +511,9 @@ class gui_control(object): def chooseObsdir(self): self.gen_new_survey.lineEdit_obs.setText(self.browseDir('Choose observation directory.')) + def chooseObsdir_min(self): + self.gen_new_survey_min.lineEdit_obs.setText(self.browseDir('Choose observation directory.')) + def openFile(self, name = 'Open'): dialog = QtGui.QFileDialog() dialog.setWindowTitle(name) #not working yet diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index b2815192..541e8e33 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -13,7 +13,7 @@ def picker(shot): return picks class Survey(object): - def __init__(self, path, sourcefile, receiverfile, useDefaultParas=False): + def __init__(self, path, sourcefile = None, receiverfile = None, seisArray = None, useDefaultParas=False): ''' The Survey Class contains all shots [class: Seismicshot] of a survey as well as the aquisition geometry and the topography. @@ -24,23 +24,61 @@ class Survey(object): creating plots for all shots. ''' self.data = {} - self.seisarray = None + self.seisarray = seisArray self._topography = None self._recfile = receiverfile self._sourcefile = sourcefile self._obsdir = path self._generateSurvey() - self._initiate_fnames() + self._initiate_SRfiles() if useDefaultParas == True: self.setParametersForAllShots() self._removeAllEmptyTraces() self._updateShots() self.picked = False - def _initiate_fnames(self): - for shot in self.data.values(): - shot.setRecfile(self.getReceiverfile()) - shot.setSourcefile(self.getSourcefile()) + def _coordsFromSeisArray(self): + self._receiverCoords = self.seisarray.getReceiverCoordinates() + self._sourceCoords = self.seisarray.getSourceCoordinates() + + def _coordsFromFiles(self): + self._receiversFromFile() + self._sourcesFromFile() + + def _receiversFromFile(self): + self._receiverCoords = {} + reclist = open(self.getReceiverfile(), 'r').readlines() + for line in reclist: + line = line.split() + traceID = int(line[0]) + x = float(line[1]) + y = float(line[2]) + z = float(line[3]) + self._receiverCoords[traceID] = (x, y, z) + + def _sourcesFromFile(self): + self._sourceCoords = {} + reclist = open(self.getSourcefile(), 'r').readlines() + for line in reclist: + line = line.split() + sourceID = int(line[0]) + x = float(line[1]) + y = float(line[2]) + z = float(line[3]) + self._sourceCoords[sourceID] = (x, y, z) + + def _initiate_SRfiles(self): + if self._recfile == None and self._sourcefile == None: + if self.seisarray == None: + raise RuntimeError('No SeisArray defined. No source or receiver file given.') + self._coordsFromSeisArray() + else: + self._coordsFromFiles() + for shotnumber in self.data.keys(): + shot = self.data[shotnumber] + shot.setShotnumber(shotnumber) + shot.setReceiverCoords(self._receiverCoords) + shot.setSourceCoords(self._sourceCoords[shotnumber]) def _generateSurvey(self): from obspy.core import read @@ -49,8 +87,8 @@ class Survey(object): shotlist = self.getShotlist() for shotnumber in shotlist: # loop over data files # generate filenames and read manual picks to a list - #fileending = '_pickle.dat' - fileending = '.sg2' + fileending = '_pickle.dat' + #fileending = '.sg2' obsfile = os.path.join(self._obsdir, str(shotnumber)) + fileending if obsfile not in shot_dict.keys(): shot_dict[shotnumber] = [] @@ -372,12 +410,17 @@ class Survey(object): ''' Returns a list of all shotnumbers contained in the set Sourcefile. ''' + if self._recfile == None and self._sourcefile == None: + if self.seisarray == None: + raise RuntimeError('No SeisArray defined. No source or receiver file given.') + return self.seisarray.getSourceCoordinates().keys() + filename = self.getSourcefile() srcfile = open(filename, 'r') shotlist = [] for line in srcfile.readlines(): line = line.split() - shotlist.append(line[0]) + shotlist.append(int(line[0])) return shotlist @@ -385,6 +428,11 @@ class Survey(object): ''' Returns a list of all trace IDs contained in the set Receiverfile. ''' + if self._recfile == None and self._sourcefile == None: + if self.seisarray == None: + raise RuntimeError('No SeisArray defined. No source or receiver file given.') + return self.seisarray.getReceiverCoordinates().keys() + filename = self.getReceiverfile() recfile = open(filename, 'r') reclist = [] diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index fe85b039..a1830674 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Mon Jun 27 15:18:25 2016 +# Created: Tue Jun 28 14:40:06 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index 9ba8f5ec..1c11112b 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Mon Jun 27 15:18:25 2016 +# Created: Tue Jun 28 14:40:06 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index be785a97..0d6d75e1 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Mon Jun 27 15:18:25 2016 +# Created: Tue Jun 28 14:40:06 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -99,9 +99,9 @@ class Ui_generate_seisarray(object): "\n" -"

Load receiver input file. The input file must be in the following format:

\n" +"

Load measured receiver input file. The input file must be in the following format:

\n" "

\n" -"

Containing in each line, seperated by spaces:

\n" +"

Containing in each line, separated by spaces:

\n" "

\n" "

[trace ID (int)] [receiver line ID (int)] [number of the geophone on receiver line (int)] [X (float)] [Y (float)] [Z (float)]

\n" "

\n" @@ -117,7 +117,7 @@ class Ui_generate_seisarray(object): "\n" "

Load measured sources input file to improve interpolation precision. The input file must be in the following format:

\n" "

\n" -"

Containing in each line, seperated by spaces:

\n" +"

Containing in each line, separated by spaces:

\n" "

\n" "

[source ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" "

\n" @@ -133,7 +133,7 @@ class Ui_generate_seisarray(object): "\n" "

Load measured points input file to improve interpolation precision. The input file must be in the following format:

\n" "

\n" -"

Containing in each line, seperated by spaces:

\n" +"

Containing in each line, separated by spaces:

\n" "

\n" "

[point ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" "

\n" diff --git a/pylot/core/active/generate_survey_layout.py b/pylot/core/active/generate_survey_layout.py index f2c8c992..ad197771 100644 --- a/pylot/core/active/generate_survey_layout.py +++ b/pylot/core/active/generate_survey_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout.ui' # -# Created: Mon Jun 27 15:18:25 2016 +# Created: Tue Jun 28 14:40:06 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -68,7 +68,7 @@ class Ui_generate_survey(object): "\n" "

Load receiver input file. The input file must be in the following format:

\n" "

\n" -"

Containing in each line, seperated by spaces:

\n" +"

Containing in each line, separated by spaces:

\n" "

\n" "

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" "

\n" @@ -99,7 +99,7 @@ class Ui_generate_survey(object): "\n" "

Load sources input file. The input file must be in the following format:

\n" "

\n" -"

Containing in each line, seperated by spaces:

\n" +"

Containing in each line, separated by spaces:

\n" "

\n" "

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" "

\n" diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index eb6c87eb..d3208eab 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Mon Jun 27 15:18:25 2016 +# Created: Tue Jun 28 14:40:07 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 84372993..ba391bb0 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -23,7 +23,7 @@ class SeisArray(object): self._receiverCoords = {} self._measuredReceivers = {} self._measuredTopo = {} - self._sourceLocs = {} + self._sourceCoords = {} self._geophoneNumbers = {} self._receiverlist = open(self.recfile, 'r').readlines() self._generateReceiverlines() @@ -79,13 +79,13 @@ class SeisArray(object): return self._receiverCoords[traceID][2] def _getXshot(self, shotnumber): - return self._sourceLocs[shotnumber][0] + return self._sourceCoords[shotnumber][0] def _getYshot(self, shotnumber): - return self._sourceLocs[shotnumber][1] + return self._sourceCoords[shotnumber][1] def _getZshot(self, shotnumber): - return self._sourceLocs[shotnumber][2] + return self._sourceCoords[shotnumber][2] def _getReceiverValue(self, traceID, coordinate): setCoordinate = {'X': self._getXreceiver, @@ -102,8 +102,8 @@ class SeisArray(object): def getMeasuredTopo(self): return self._measuredTopo - def getSourceLocations(self): - return self._sourceLocs + def getSourceCoordinates(self): + return self._sourceCoords def _setXvalue(self, traceID, value): self._checkKey(traceID) @@ -209,7 +209,7 @@ class SeisArray(object): x = float(line[1]) y = float(line[2]) z = float(line[3]) - self._sourceLocs[pointID] = (x, y, z) + self._sourceCoords[pointID] = (x, y, z) def interpZcoords4rec(self, method='linear'): ''' @@ -274,10 +274,10 @@ class SeisArray(object): x = []; y = []; z = [] - for pointID in self.getSourceLocations().keys(): - x.append(self.getSourceLocations()[pointID][0]) - y.append(self.getSourceLocations()[pointID][1]) - z.append(self.getSourceLocations()[pointID][2]) + for pointID in self.getSourceCoordinates().keys(): + x.append(self.getSourceCoordinates()[pointID][0]) + y.append(self.getSourceCoordinates()[pointID][1]) + z.append(self.getSourceCoordinates()[pointID][2]) return x, y, z def getAllMeasuredPointsLists(self): @@ -457,7 +457,7 @@ class SeisArray(object): outfile = open(outfilename, 'w') recx, recy, recz = self.getReceiverLists() - nsrc = len(self.getSourceLocations()) + nsrc = len(self.getSourceCoordinates()) outfile.write('%s\n' % (len(zip(recx, recy, recz)) * nsrc)) for index in range(nsrc): @@ -816,7 +816,7 @@ class SeisArray(object): for traceID in self.getReceiverCoordinates().keys(): ax.annotate((' ' + str(traceID)), xy=(self._getXreceiver(traceID), self._getYreceiver(traceID)), fontsize='x-small', color='k') - for shotnumber in self.getSourceLocations().keys(): + for shotnumber in self.getSourceCoordinates().keys(): ax.annotate((' ' + str(shotnumber)), xy=(self._getXshot(shotnumber), self._getYshot(shotnumber)), fontsize='x-small', color='b') @@ -994,7 +994,7 @@ class SeisArray(object): outfile = open(filename, 'w') shotnumbers = [] - for shotnumber in self.getSourceLocations(): + for shotnumber in self.getSourceCoordinates(): shotnumbers.append(shotnumber) nPoints = len(shotnumbers) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index bc51b4b4..4f42fade 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -24,7 +24,6 @@ class SeismicShot(object): ''' SuperClass for a seismic shot object. ''' - def __init__(self, obsfile): ''' Initialize seismic shot object giving an inputfile. @@ -34,8 +33,8 @@ class SeismicShot(object): ''' self.traces = read(obsfile) self.renameChannelIDs() - self.recCoordlist = None - self.srcCoordlist = None + # self.recCoordlist = None + # self.srcCoordlist = None self.traceIDs = None self.picks = {} self.pwindow = {} @@ -55,10 +54,8 @@ class SeismicShot(object): def removeEmptyTraces(self): traceIDs = [] - coordlist = self.getRecCoordlist() removed = [] - for i in range(0, len(coordlist)): - traceIDs.append(int(coordlist[i].split()[0])) + traceIDs = self.getReceiverCoords().keys() for trace in self.traces: try: @@ -110,14 +107,14 @@ class SeismicShot(object): def setTgap(self, tgap): self.setParameters('tgap', tgap) - def setShotnumber(self, shotname): - self.setParameters('shotname', shotname) + def setShotnumber(self, shotnumber): + self.setParameters('shotnumber', shotnumber) - def setRecfile(self, recfile): - self.setParameters('recfile', recfile) + def setReceiverCoords(self, receiver): + self.setParameters('receiverLoc', receiver) - def setSourcefile(self, sourcefile): - self.setParameters('sourcefile', sourcefile) + def setSourceCoords(self, source): + self.setParameters('sourceLoc', source) def setMethod(self, method): self.setParameters('method', method) @@ -159,8 +156,14 @@ class SeismicShot(object): def getParas(self): return self.paras - def getShotname(self): - return self.paras['shotname'] + def getShotnumber(self): + return self.paras['shotnumber'] + + def getSourceCoords(self): + return self.paras['sourceLoc'] + + def getReceiverCoords(self): + return self.paras['receiverLoc'] def getCut(self): return self.paras['cut'] @@ -180,12 +183,6 @@ class SeismicShot(object): def getShotnumber(self): return self.paras['shotnumber'] - def getRecfile(self): - return self.paras['recfile'] - - def getSourcefile(self): - return self.paras['sourcefile'] - def getVmin(self): return self.paras['vmin'] @@ -237,15 +234,12 @@ class SeismicShot(object): def getTraceIDlist(self): ''' - Returns a list containing the traceIDs read from the receiver inputfile. + Returns a list containing the traceIDs. ''' traceIDs = [] if self.traceIDs == None: - recCoordlist = self.getRecCoordlist() - for i in range(0, len(recCoordlist)): - traceIDs.append(int(recCoordlist[i].split()[0])) + traceIDs = self.getReceiverCoords().keys() self.traceIDs = traceIDs - return self.traceIDs def getPickwindow(self, traceID): @@ -262,19 +256,19 @@ class SeismicShot(object): def getSNRthreshold(self, traceID): return self.snrthreshold[traceID] - def getRecCoordlist(self): - if self.recCoordlist is None: - coordlist = open(self.getRecfile(), 'r').readlines() - # print 'Reading receiver coordinates from %s' %(self.getRecfile()) - self.recCoordlist = coordlist - return self.recCoordlist + # def getRecCoordlist(self): + # if self.recCoordlist is None: + # coordlist = open(self.getRecfile(), 'r').readlines() + # # print 'Reading receiver coordinates from %s' %(self.getRecfile()) + # self.recCoordlist = coordlist + # return self.recCoordlist - def getSrcCoordlist(self): - if self.srcCoordlist is None: - coordlist = open(self.getSourcefile(), 'r').readlines() - # print 'Reading shot coordinates from %s' %(self.getSourcefile()) - self.srcCoordlist = coordlist - return self.srcCoordlist + # def getSrcCoordlist(self): + # if self.srcCoordlist is None: + # coordlist = open(self.getSourcefile(), 'r').readlines() + # # print 'Reading shot coordinates from %s' %(self.getSourcefile()) + # self.srcCoordlist = coordlist + # return self.srcCoordlist def getTimeArray(self, traceID): return self.timeArray[traceID] @@ -484,13 +478,7 @@ class SeismicShot(object): if traceID == 0: # artificial traceID 0 with pick at t = 0 return self.getSrcLoc() - coordlist = self.getRecCoordlist() - for i in range(0, len(coordlist)): - if int(coordlist[i].split()[0]) == traceID: - x = coordlist[i].split()[1] - y = coordlist[i].split()[2] - z = coordlist[i].split()[3] - return float(x), float(y), float(z) + return self.getReceiverCoords()[traceID] raise ValueError("traceID %s not found" % traceID) @@ -499,14 +487,7 @@ class SeismicShot(object): Returns the location (x, y, z) of the shot. SOURCE FILE MUST BE SET FIRST, TO BE IMPROVED. ''' - coordlist = self.getSrcCoordlist() - for i in range(0, len(coordlist)): - if coordlist[i].split()[0] == self.paras['shotnumber']: - x = coordlist[i].split()[1] - y = coordlist[i].split()[2] - z = coordlist[i].split()[3] - return float(x), float(y), float(z) - # return float(self.getSingleStream(traceID)[0].stats.seg2['SOURCE_LOCATION']) + return self.getSourceCoords() def getTraceIDs4Dist(self, distance=0, distancebin=(0, 0)): ########## nur fuer 2D benutzt, 'distance bins' ########## From a8b7eff561f4b849b392ef24ccdd37a39c393c70 Mon Sep 17 00:00:00 2001 From: sebastianp Date: Wed, 29 Jun 2016 15:29:58 +0200 Subject: [PATCH 0985/1144] [task] implementing new methods for pdf comparison. --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/phases.py | 2 +- pylot/core/pick/compare.py | 80 +++++++++++++++++++++++++++++++++++++- pylot/core/util/pdf.py | 23 +++++++++++ 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 671949c9..e760bf7c 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -7c5a-dirty +8714-dirty diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index e3c7e90a..60d4d180 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -180,7 +180,7 @@ def picksdict_from_picks(evt): try: onsets = picks[station] except KeyError as e: - print(e) + #print(e) onsets = {} mpp = pick.time spe = pick.time_errors.uncertainty diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index b39f8ef8..b2d93a1c 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- import copy - +import os import numpy as np - +import glob import matplotlib.pyplot as plt from obspy import read_events @@ -336,3 +336,79 @@ class PDFDictionary(object): plt.setp([a.get_yticklabels() for a in axarr[:, 0]], visible=False) return fig + + +class PDFstatistics(object): + def __init__(self, directory): + self.directory = directory + self.stations = {} + self.p_std = {} + self.s_std = {} + self.makeDirlist() + self.getData() + self.getStatistics() + #self.showData() + + def makeDirlist(self, fn_pattern='*'): + self.dirlist = glob.glob1(self.directory, fn_pattern) + #print self.dirlist + self.evtdict = {} + for rd in self.dirlist: + #print rd + self.evtdict[rd] = glob.glob1((os.path.join(self.directory, rd)), '*.xml') + #print rd, self.evtdict[rd] + + def getData(self): + arraylen = 0 + for dir in self.dirlist: + for evt in self.evtdict[dir]: + self.stations[evt] = [] + self.p_std[evt] = [] + self.s_std[evt] = [] + self.getPDFDict(dir, evt) + for station, pdfs in self.pdfdict.pdf_data.items(): + #print station, pdfs + try: + p_std = pdfs['P'].standard_deviation() + except KeyError: + p_std = 0 + try: + s_std = pdfs['S'].standard_deviation() + except KeyError: + s_std = 0 + self.stations[evt].append(station) + self.p_std[evt].append(p_std) + self.s_std[evt].append(s_std) + arraylen += 1 + self.makeArray(arraylen) + + def makeArray(self, arraylen): + index = 0 + self.p_stdarray = np.zeros(arraylen) + self.s_stdarray = np.zeros(arraylen) + for evt in self.p_std.keys(): + for n in range(len(self.p_std[evt])): + self.p_stdarray[index] = self.p_std[evt][n] + self.s_stdarray[index] = self.s_std[evt][n] + index += 1 + + def showData(self): + #figure(p, s) = plt.pyplot.subplots(2) + #p.hist(self.p_stdarray, Bins=100, range=[min(self.p_stdarray),max(self.p_stdarray)/256]) + #s.hist(self.s_stdarray, Bins=100, range=[min(self.s_stdarray),max(self.s_stdarray)/256]) + #p.set_title('Histogramm der P-Wellen-Standartabweichung') + #s.set_title('Histogramm der S-Wellen-Standartabweichung') + # plt.show() + pass + + + def getPDFDict(self, month, evt): + self.pdfdict = PDFDictionary.from_quakeml(os.path.join(self.directory,month,evt)) + + def getStatistics(self): + self.p_mean = self.p_stdarray.mean() + self.p_std_std = self.p_stdarray.std() + self.p_median = np.median(self.p_stdarray) + self.s_mean = self.s_stdarray.mean() + self.s_std_std = self.s_stdarray.std() + self.s_median = np.median(self.s_stdarray) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index c2956e90..ed6ea70c 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -294,6 +294,29 @@ class ProbabilityDensityFunction(object): return None return self.data[find_nearest(self.axis, value)] * self.incr + def quantile(self, prob_value, eps=0.01): + l = self.axis[0] + r = self.axis[-1] + m = (r - l) / 2 + diff = prob_value - self.prob_lt_val(m) + while abs(diff) > eps: + if diff > 0: + l = m + else: + r = m + m = (r - l) / 2 + diff = prob_value - self.prob_lt_val(m) + return m + + + pass + + def quantile_distance(self, prob_value): + ql = self.quantile(prob_value) + qu = self.quantile(1 - prob_value) + + return qu - ql + def plot(self, label=None): import matplotlib.pyplot as plt From e8cbc2f37754450857ef7fca2464c0a011f88e32 Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Thu, 30 Jun 2016 13:14:38 +0200 Subject: [PATCH 0986/1144] [change] implementation of quantile calculation corrected --- pylot/core/util/pdf.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index ed6ea70c..71ccfaa6 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -297,20 +297,18 @@ class ProbabilityDensityFunction(object): def quantile(self, prob_value, eps=0.01): l = self.axis[0] r = self.axis[-1] - m = (r - l) / 2 + m = (r + l) / 2 diff = prob_value - self.prob_lt_val(m) while abs(diff) > eps: if diff > 0: l = m else: r = m - m = (r - l) / 2 + m = (r + l) / 2 diff = prob_value - self.prob_lt_val(m) + print(m, prob_value, self.prob_lt_val(m)) return m - - pass - def quantile_distance(self, prob_value): ql = self.quantile(prob_value) qu = self.quantile(1 - prob_value) From 5c7123af66c3e200e5763ce200ad560679d22e39 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 5 Jul 2016 10:38:28 +0200 Subject: [PATCH 0987/1144] added a feature to generate a survey object from a SeisArray --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 16 +++-- .../active/generate_survey_layout_minimal.py | 61 +++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 pylot/core/active/generate_survey_layout_minimal.py diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index b6a48568..e39e4c8d 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -90,9 +90,11 @@ class gui_control(object): if self.continueDialogMessage('Use geometry information of active Seismic Array?'): if self.gen_survey_fromSeisArray(): self.initNewSurvey() - else: - if self.gen_survey_fromSRfiles(): - self.initNewSurvey() + return + else: + return + if self.gen_survey_fromSRfiles(): + self.initNewSurvey() def initNewSurvey(self): self.survey.setArtificialPick(0, 0) # artificial pick at source origin @@ -204,8 +206,8 @@ class gui_control(object): self.printDialogMessage('Got no Seismic Array.') return if self.checkConnected2SurveyState(): - if self.continueDialogMessage('Existing Survey already got Seismic Array object. Continue?'): - pass + if not self.continueDialogMessage('Existing Survey already got Seismic Array object. Continue?'): + return self.survey.seisarray = self.seisarray self.setConnected2SurveyState(True) self.survey._initiate_SRfiles() @@ -323,6 +325,10 @@ class gui_control(object): self.printDialogMessage('Wrong input file of type %s, expected %s.' %(type(survey), activeSeismoPick.Survey)) return + if self.checkSeisArrayState() and survey.seisarray is not None: + if not self.continueDialogMessage('Survey got existing Seismic Array.' + ' Do you want to overwrite the current Seismic Array?'): + return self.survey = survey self.setSurveyState(True) if self.survey.picked: diff --git a/pylot/core/active/generate_survey_layout_minimal.py b/pylot/core/active/generate_survey_layout_minimal.py new file mode 100644 index 00000000..c0af304b --- /dev/null +++ b/pylot/core/active/generate_survey_layout_minimal.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'generate_survey_layout_minimal.ui' +# +# Created: Tue Jun 28 14:40:07 2016 +# by: pyside-uic 0.2.15 running on PySide 1.2.2 +# +# WARNING! All changes made in this file will be lost! + +from PySide import QtCore, QtGui + +class Ui_generate_survey_minimal(object): + def setupUi(self, generate_survey_minimal): + generate_survey_minimal.setObjectName("generate_survey_minimal") + generate_survey_minimal.resize(325, 83) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + generate_survey_minimal.setWindowIcon(icon) + self.verticalLayout = QtGui.QVBoxLayout(generate_survey_minimal) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.lineEdit_obs = QtGui.QLineEdit(generate_survey_minimal) + self.lineEdit_obs.setObjectName("lineEdit_obs") + self.gridLayout.addWidget(self.lineEdit_obs, 0, 1, 1, 1) + self.label_obs = QtGui.QLabel(generate_survey_minimal) + self.label_obs.setObjectName("label_obs") + self.gridLayout.addWidget(self.label_obs, 0, 0, 1, 1) + self.pushButton_obs = QtGui.QPushButton(generate_survey_minimal) + self.pushButton_obs.setObjectName("pushButton_obs") + self.gridLayout.addWidget(self.pushButton_obs, 0, 2, 1, 1) + self.verticalLayout.addLayout(self.gridLayout) + self.buttonBox = QtGui.QDialogButtonBox(generate_survey_minimal) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(generate_survey_minimal) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), generate_survey_minimal.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_survey_minimal.reject) + QtCore.QMetaObject.connectSlotsByName(generate_survey_minimal) + + def retranslateUi(self, generate_survey_minimal): + generate_survey_minimal.setWindowTitle(QtGui.QApplication.translate("generate_survey_minimal", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setToolTip(QtGui.QApplication.translate("generate_survey_minimal", "\n" +"\n" +"

Specifiy directory containing seismograms for each shot.

\n" +"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" +"

\n" +"

For example:

\n" +"

\n" +"

Shot number 100 containing seismograms for all traces with the name:

\n" +"

\n" +"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs.setText(QtGui.QApplication.translate("generate_survey_minimal", "Seismogram\n" +"Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_obs.setText(QtGui.QApplication.translate("generate_survey_minimal", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + From 0be69de6f749de7cb9cdaee06ac9718d1775035e Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 5 Jul 2016 13:59:01 +0200 Subject: [PATCH 0988/1144] busy adding VTK tools to main UI --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 53 ++++- pylot/core/active/asp3d_layout.py | 29 ++- pylot/core/active/fmtomo_parameters_layout.py | 2 +- .../core/active/generate_seisarray_layout.py | 43 +++- pylot/core/active/generate_survey_layout.py | 2 +- .../active/generate_survey_layout_minimal.py | 2 +- .../core/active/picking_parameters_layout.py | 2 +- pylot/core/active/seismicArrayPreparation.py | 47 ++++- pylot/core/active/vtk_tools_layout.py | 188 ++++++++++++++++++ 9 files changed, 345 insertions(+), 23 deletions(-) create mode 100644 pylot/core/active/vtk_tools_layout.py diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index e39e4c8d..b91caada 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -12,6 +12,7 @@ from generate_survey_layout import * from generate_survey_layout_minimal import * from generate_seisarray_layout import * from picking_parameters_layout import * +from vtk_tools_layout import * from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils, seismicArrayPreparation from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas @@ -50,6 +51,7 @@ class gui_control(object): QtCore.QObject.connect(self.mainUI.picker, QtCore.SIGNAL("clicked()"), self.callPicker) QtCore.QObject.connect(self.mainUI.postprocessing, QtCore.SIGNAL("clicked()"), self.postprocessing) QtCore.QObject.connect(self.mainUI.fmtomo, QtCore.SIGNAL("clicked()"), self.startFMTOMO) + QtCore.QObject.connect(self.mainUI.vtk_tools, QtCore.SIGNAL("clicked()"), self.startVTKtools) QtCore.QObject.connect(self.mainUI.comboBox, QtCore.SIGNAL("activated(int)"), self.replotStat) def gen_seisarray(self): @@ -73,7 +75,10 @@ class gui_control(object): srcfile = self.gen_new_seisarray.lineEdit_src.text() recfile = self.gen_new_seisarray.lineEdit_rec.text() ptsfile = self.gen_new_seisarray.lineEdit_pts.text() - self.seisarray = seismicArrayPreparation.SeisArray(recfile) + if self.gen_new_seisarray.radioButton_interpolatable.isChecked(): + self.seisarray = seismicArrayPreparation.SeisArray(recfile, True) + elif self.gen_new_seisarray.radioButton_normal.isChecked(): + self.seisarray = seismicArrayPreparation.SeisArray(recfile, False) if len(srcfile) > 0: self.seisarray.addSourceLocations(srcfile) if len(ptsfile) > 0: @@ -247,6 +252,9 @@ class gui_control(object): if not self.checkSurveyState(): self.printDialogMessage('No Survey defined.') return + if not self.checkPickState(): + self.printDialogMessage('Survey not picked.') + return fmtomo_parameters = QtGui.QDialog(self.mainwindow) ui = Ui_fmtomo_parameters() ui.setupUi(fmtomo_parameters) @@ -256,6 +264,15 @@ class gui_control(object): self.connectButtons_startFMTOMO() self.getFMTOMOparameters(ui, fmtomo_parameters) + def startVTKtools(self): + vtk_tools = QtGui.QDialog(self.mainwindow) + ui = Ui_vtk_tools() + ui.setupUi(vtk_tools) + + self.vtk_tools_ui = ui + self.connectButtons_vtk_tools() + #self.getFMTOMOparameters(ui, fmtomo_parameters) + def getFMTOMOparameters(self, ui, fmtomo_parameters): if fmtomo_parameters.exec_(): fmtomo_dir = ui.fmtomo_dir.text() @@ -291,6 +308,40 @@ class gui_control(object): QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_customgrid, QtCore.SIGNAL("clicked()"), self.chooseCustomgrid) QtCore.QObject.connect(self.fmtomo_parameters_ui.browse_simuldir, QtCore.SIGNAL("clicked()"), self.chooseSimuldir) + def connectButtons_vtk_tools(self): + QtCore.QObject.connect(self.vtk_tools_ui.pushButton_vg, QtCore.SIGNAL("clicked()"), self.chooseVgrid) + QtCore.QObject.connect(self.vtk_tools_ui.pushButton_vgref, QtCore.SIGNAL("clicked()"), self.chooseVgridref) + QtCore.QObject.connect(self.vtk_tools_ui.pushButton_rays, QtCore.SIGNAL("clicked()"), self.chooseRaysIn) + QtCore.QObject.connect(self.vtk_tools_ui.pushButton_raysout, QtCore.SIGNAL("clicked()"), self.chooseRaysOutDir) + QtCore.QObject.connect(self.vtk_tools_ui.start_vg, QtCore.SIGNAL("clicked()"), self.startvgvtk) + QtCore.QObject.connect(self.vtk_tools_ui.start_rays, QtCore.SIGNAL("clicked()"), self.startraysvtk) + + def chooseVgrid(self): + self.vtk_tools_ui.lineEdit_vg.setText(self.openFile()) + + def chooseVgridref(self): + self.vtk_tools_ui.lineEdit_vgref.setText(self.openFile()) + + def chooseRaysIn(self): + self.vtk_tools_ui.lineEdit_rays.setText(self.openFile()) + + def chooseRaysOutDir(self): + self.vtk_tools_ui.lineEdit_raysout.setText(self.browseDir()) + + def startvgvtk(self): + if self.vtk_tools_ui.radioButton_abs.isChecked(): + fmtomoUtils.vgrids2VTK(inputfile = self.vtk_tools_ui.lineEdit_vg.text(), + outputfile = 'vgrids_rel.vtk', + absOrRel='abs') + elif self.vtk_tools_ui.radioButton_rel.isChecked(): + fmtomoUtils.vgrids2VTK(inputfile = self.vtk_tools_ui.lineEdit_vg.text(), + outputfile = 'vgrids_rel.vtk', + absOrRel='rel', + inputfileref = self.vtk_tools_ui.lineEdit_vgref.text()) + + def startraysvtk(self): + fmtomoUtils.rays2VTK() + def chooseFMTOMOdir(self): self.fmtomo_parameters_ui.fmtomo_dir.setText(self.browseDir()) diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index a1830674..9b48dea7 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Tue Jun 28 14:40:06 2016 +# Created: Tue Jul 5 13:55:55 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -27,8 +27,8 @@ class Ui_MainWindow(object): MainWindow.setWindowIcon(icon) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") - self.horizontalLayout_4 = QtGui.QHBoxLayout(self.centralwidget) - self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.gridLayout = QtGui.QGridLayout(self.centralwidget) + self.gridLayout.setObjectName("gridLayout") self.formLayout = QtGui.QFormLayout() self.formLayout.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) @@ -237,6 +237,24 @@ class Ui_MainWindow(object): self.fmtomo = QtGui.QPushButton(self.centralwidget) self.fmtomo.setObjectName("fmtomo") self.verticalLayout_4.addWidget(self.fmtomo) + self.label_9 = QtGui.QLabel(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_9.sizePolicy().hasHeightForWidth()) + self.label_9.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Sans Serif") + font.setPointSize(10) + font.setWeight(75) + font.setBold(True) + self.label_9.setFont(font) + self.label_9.setAlignment(QtCore.Qt.AlignCenter) + self.label_9.setObjectName("label_9") + self.verticalLayout_4.addWidget(self.label_9) + self.vtk_tools = QtGui.QPushButton(self.centralwidget) + self.vtk_tools.setObjectName("vtk_tools") + self.verticalLayout_4.addWidget(self.vtk_tools) self.verticalLayout_3.addLayout(self.verticalLayout_4) self.verticalLayout_5.addLayout(self.verticalLayout_3) self.formLayout.setLayout(0, QtGui.QFormLayout.LabelRole, self.verticalLayout_5) @@ -259,7 +277,7 @@ class Ui_MainWindow(object): self.horizontalLayout_br.setObjectName("horizontalLayout_br") self.verticalLayout_right.addLayout(self.horizontalLayout_br) self.formLayout.setLayout(0, QtGui.QFormLayout.FieldRole, self.verticalLayout_right) - self.horizontalLayout_4.addLayout(self.formLayout) + self.gridLayout.addLayout(self.formLayout, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 905, 23)) @@ -304,4 +322,7 @@ class Ui_MainWindow(object): self.label_8.setText(QtGui.QApplication.translate("MainWindow", "Simulation", None, QtGui.QApplication.UnicodeUTF8)) self.fmtomo.setToolTip(QtGui.QApplication.translate("MainWindow", "Set parameters and call Fast Marching Tomography algorithm.", None, QtGui.QApplication.UnicodeUTF8)) self.fmtomo.setText(QtGui.QApplication.translate("MainWindow", "FMTOMO Simulation", None, QtGui.QApplication.UnicodeUTF8)) + self.label_9.setText(QtGui.QApplication.translate("MainWindow", "Visualization", None, QtGui.QApplication.UnicodeUTF8)) + self.vtk_tools.setToolTip(QtGui.QApplication.translate("MainWindow", "Set parameters and call Fast Marching Tomography algorithm.", None, QtGui.QApplication.UnicodeUTF8)) + self.vtk_tools.setText(QtGui.QApplication.translate("MainWindow", "VTK tools", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index 1c11112b..70c8a32a 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Tue Jun 28 14:40:06 2016 +# Created: Tue Jul 5 13:55:55 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index 0d6d75e1..9bb3173f 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Tue Jun 28 14:40:06 2016 +# Created: Tue Jul 5 13:55:55 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,7 +12,7 @@ from PySide import QtCore, QtGui class Ui_generate_seisarray(object): def setupUi(self, generate_seisarray): generate_seisarray.setObjectName("generate_seisarray") - generate_seisarray.resize(400, 221) + generate_seisarray.resize(403, 221) generate_seisarray.setMinimumSize(QtCore.QSize(400, 220)) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) @@ -23,14 +23,27 @@ class Ui_generate_seisarray(object): self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_4 = QtGui.QVBoxLayout() self.verticalLayout_4.setObjectName("verticalLayout_4") + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") self.label_rec = QtGui.QLabel(generate_seisarray) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_rec.sizePolicy().hasHeightForWidth()) self.label_rec.setSizePolicy(sizePolicy) + self.label_rec.setToolTip("") self.label_rec.setObjectName("label_rec") - self.verticalLayout_4.addWidget(self.label_rec) + self.horizontalLayout.addWidget(self.label_rec) + spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.radioButton_normal = QtGui.QRadioButton(generate_seisarray) + self.radioButton_normal.setChecked(True) + self.radioButton_normal.setObjectName("radioButton_normal") + self.horizontalLayout.addWidget(self.radioButton_normal) + self.radioButton_interpolatable = QtGui.QRadioButton(generate_seisarray) + self.radioButton_interpolatable.setObjectName("radioButton_interpolatable") + self.horizontalLayout.addWidget(self.radioButton_interpolatable) + self.verticalLayout_4.addLayout(self.horizontalLayout) self.horizontalLayout_4 = QtGui.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.lineEdit_rec = QtGui.QLineEdit(generate_seisarray) @@ -95,11 +108,27 @@ class Ui_generate_seisarray(object): def retranslateUi(self, generate_seisarray): generate_seisarray.setWindowTitle(QtGui.QApplication.translate("generate_seisarray", "Generate new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" + self.label_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Receivers", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButton_normal.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" "\n" -"

Load measured receiver input file. The input file must be in the following format:

\n" +"

Load normal measured receiver input file. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, separated by spaces:

\n" +"

\n" +"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Geophone with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" +"

\n" +"

50 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButton_normal.setText(QtGui.QApplication.translate("generate_seisarray", "normal [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButton_interpolatable.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" +"\n" +"

Load measured receiver input file that can be interpolated. The input file must be in the following format:

\n" "

\n" "

Containing in each line, separated by spaces:

\n" "

\n" @@ -109,7 +138,7 @@ class Ui_generate_seisarray(object): "

Third geophone on the second receiver line with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" "

\n" "

50 2 3 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Receivers [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButton_interpolatable.setText(QtGui.QApplication.translate("generate_seisarray", "interpolatable [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.label_src.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" "\n" +"

Load normal measured receiver input file. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, separated by spaces:

\n" +"

\n" +"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Geophone with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" +"

\n" +"

50 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButton_abs.setText(QtGui.QApplication.translate("vtk_tools", "absolute [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButton_rel.setToolTip(QtGui.QApplication.translate("vtk_tools", "\n" +"\n" +"

Load measured receiver input file that can be interpolated. The input file must be in the following format:

\n" +"

\n" +"

Containing in each line, separated by spaces:

\n" +"

\n" +"

[trace ID (int)] [receiver line ID (int)] [number of the geophone on receiver line (int)] [X (float)] [Y (float)] [Z (float)]

\n" +"

\n" +"

For example:

\n" +"

Third geophone on the second receiver line with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" +"

\n" +"

50 2 3 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButton_rel.setText(QtGui.QApplication.translate("vtk_tools", "relative [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("vtk_tools", "Browse for velocity grid file (\'vgrids.in\'):", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_vg.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("vtk_tools", "Browse for reference velocity grid file (\'vgridsref.in\'):", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_vgref.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.start_vg.setText(QtGui.QApplication.translate("vtk_tools", "Start", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setToolTip(QtGui.QApplication.translate("vtk_tools", "Create VTK files from the FMTOMO output file \'rays.dat\'.\n" +"This will generate one file of ray paths for each individual source.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_src.setText(QtGui.QApplication.translate("vtk_tools", "Rays to VTK [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("vtk_tools", "Browse for input file (\'rays.dat\'):", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_rays.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("vtk_tools", "Specify output directory:", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_raysout.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.start_rays.setText(QtGui.QApplication.translate("vtk_tools", "Start", None, QtGui.QApplication.UnicodeUTF8)) + From 5293bb6ec78275db1a7183eaece1f9b3d9c25b4a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 6 Jul 2016 11:40:08 +0200 Subject: [PATCH 0989/1144] activating vtk_tools widget, bugfix on merging rays.dat (too many linebreaks) --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 64 ++++++++++++++++--- pylot/core/active/asp3d_layout.py | 2 +- pylot/core/active/fmtomoUtils.py | 12 ++-- pylot/core/active/fmtomo_parameters_layout.py | 2 +- .../core/active/generate_seisarray_layout.py | 2 +- pylot/core/active/generate_survey_layout.py | 2 +- .../active/generate_survey_layout_minimal.py | 2 +- .../core/active/picking_parameters_layout.py | 2 +- pylot/core/active/vtk_tools_layout.py | 35 +++++++++- 9 files changed, 100 insertions(+), 23 deletions(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index b91caada..34cda260 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -271,7 +271,10 @@ class gui_control(object): self.vtk_tools_ui = ui self.connectButtons_vtk_tools() - #self.getFMTOMOparameters(ui, fmtomo_parameters) + self.openVTKdialog(ui, vtk_tools) + + def openVTKdialog(self, ui, vtk_tools): + vtk_tools.exec_() def getFMTOMOparameters(self, ui, fmtomo_parameters): if fmtomo_parameters.exec_(): @@ -313,34 +316,77 @@ class gui_control(object): QtCore.QObject.connect(self.vtk_tools_ui.pushButton_vgref, QtCore.SIGNAL("clicked()"), self.chooseVgridref) QtCore.QObject.connect(self.vtk_tools_ui.pushButton_rays, QtCore.SIGNAL("clicked()"), self.chooseRaysIn) QtCore.QObject.connect(self.vtk_tools_ui.pushButton_raysout, QtCore.SIGNAL("clicked()"), self.chooseRaysOutDir) + QtCore.QObject.connect(self.vtk_tools_ui.pushButton_parav, QtCore.SIGNAL("clicked()"), self.openFileParaview) QtCore.QObject.connect(self.vtk_tools_ui.start_vg, QtCore.SIGNAL("clicked()"), self.startvgvtk) QtCore.QObject.connect(self.vtk_tools_ui.start_rays, QtCore.SIGNAL("clicked()"), self.startraysvtk) + QtCore.QObject.connect(self.vtk_tools_ui.radioButton_rel, QtCore.SIGNAL("clicked()"), self.activateVgref) + QtCore.QObject.connect(self.vtk_tools_ui.radioButton_abs, QtCore.SIGNAL("clicked()"), self.deactivateVgref) + + def openFileParaview(self): + os.system('paraview %s &'%self.vtk_tools_ui.lineEdit_vgout.text()) + + def activateVgref(self): + self.vtk_tools_ui.lineEdit_vgref.setEnabled(True) + self.vtk_tools_ui.pushButton_vgref.setEnabled(True) + + def deactivateVgref(self): + self.vtk_tools_ui.lineEdit_vgref.setEnabled(False) + self.vtk_tools_ui.pushButton_vgref.setEnabled(False) + + def checkVgStartButton(self): + ui = self.vtk_tools_ui + if ui.radioButton_rel.isChecked(): + if ui.lineEdit_vg.text() != '' and ui.lineEdit_vgref.text() != '': + ui.start_vg.setEnabled(True) + else: + ui.start_vg.setEnabled(False) + if ui.radioButton_abs.isChecked(): + if ui.lineEdit_vg.text() != '': + ui.start_vg.setEnabled(True) + else: + ui.start_vg.setEnabled(False) + + def checkRaysStartButton(self): + ui = self.vtk_tools_ui + if ui.lineEdit_rays.text() != '' and ui.lineEdit_raysout.text() != '': + ui.start_rays.setEnabled(True) + else: + ui.start_rays.setEnabled(False) def chooseVgrid(self): self.vtk_tools_ui.lineEdit_vg.setText(self.openFile()) + self.checkVgStartButton() def chooseVgridref(self): self.vtk_tools_ui.lineEdit_vgref.setText(self.openFile()) + self.checkVgStartButton() def chooseRaysIn(self): self.vtk_tools_ui.lineEdit_rays.setText(self.openFile()) + self.checkRaysStartButton() def chooseRaysOutDir(self): self.vtk_tools_ui.lineEdit_raysout.setText(self.browseDir()) + self.checkRaysStartButton() def startvgvtk(self): - if self.vtk_tools_ui.radioButton_abs.isChecked(): - fmtomoUtils.vgrids2VTK(inputfile = self.vtk_tools_ui.lineEdit_vg.text(), - outputfile = 'vgrids_rel.vtk', + ui = self.vtk_tools_ui + if ui.lineEdit_vgout.text() == '': + if not self.printDialogMessage('Please specify output filename.'): + return + if ui.radioButton_abs.isChecked(): + fmtomoUtils.vgrids2VTK(inputfile = ui.lineEdit_vg.text(), + outputfile = ui.lineEdit_vgout.text(), absOrRel='abs') - elif self.vtk_tools_ui.radioButton_rel.isChecked(): - fmtomoUtils.vgrids2VTK(inputfile = self.vtk_tools_ui.lineEdit_vg.text(), - outputfile = 'vgrids_rel.vtk', + elif ui.radioButton_rel.isChecked(): + fmtomoUtils.vgrids2VTK(inputfile = ui.lineEdit_vg.text(), + outputfile = ui.lineEdit_vgout.text(), absOrRel='rel', - inputfileref = self.vtk_tools_ui.lineEdit_vgref.text()) + inputfileref = ui.lineEdit_vgref.text()) def startraysvtk(self): - fmtomoUtils.rays2VTK() + ui = self.vtk_tools_ui + fmtomoUtils.rays2VTK(ui.lineEdit_rays.text(), ui.lineEdit_raysout.text()) def chooseFMTOMOdir(self): self.fmtomo_parameters_ui.fmtomo_dir.setText(self.browseDir()) diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index 9b48dea7..e879e2e6 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Tue Jul 5 13:55:55 2016 +# Created: Wed Jul 6 11:39:58 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index bf08b1d3..2c5d975e 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -498,7 +498,7 @@ class Tomo3d(object): raypoints = [] for j in range(int(npoints)): - raypoints.append(raysfile.readline() + '\n') + raypoints.append(raysfile.readline()) raysecs[sec] = {'npoints': npoints, 'region': region, @@ -739,10 +739,12 @@ def rays2VTK(fnin, fdirout='./vtk_files/', nthPoint=50): while True: raynumber += 1 firstline = infile.readline() - if firstline == '': break # break at EOF - raynumber = int(firstline.split()[0]) - shotnumber = int(firstline.split()[1]) - rayValid = int(firstline.split()[4]) # is zero if the ray is invalid + if firstline == '': + break # break at EOF + fl_list = firstline.split() + raynumber = int(fl_list[0]) + shotnumber = int(fl_list[1]) + rayValid = int(fl_list[4]) # is zero if the ray is invalid if rayValid == 0: print('Invalid ray number %d for shot number %d' % (raynumber, shotnumber)) continue diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index 70c8a32a..a46f6163 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Tue Jul 5 13:55:55 2016 +# Created: Wed Jul 6 11:39:58 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index 9bb3173f..523fba2e 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Tue Jul 5 13:55:55 2016 +# Created: Wed Jul 6 11:39:58 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_survey_layout.py b/pylot/core/active/generate_survey_layout.py index 57cf02b0..950e1f10 100644 --- a/pylot/core/active/generate_survey_layout.py +++ b/pylot/core/active/generate_survey_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout.ui' # -# Created: Tue Jul 5 13:55:56 2016 +# Created: Wed Jul 6 11:39:58 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_survey_layout_minimal.py b/pylot/core/active/generate_survey_layout_minimal.py index 59fb1923..d17c0ccc 100644 --- a/pylot/core/active/generate_survey_layout_minimal.py +++ b/pylot/core/active/generate_survey_layout_minimal.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout_minimal.ui' # -# Created: Tue Jul 5 13:55:56 2016 +# Created: Wed Jul 6 11:39:58 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index af133884..76cc69c3 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Tue Jul 5 13:55:56 2016 +# Created: Wed Jul 6 11:39:58 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/vtk_tools_layout.py b/pylot/core/active/vtk_tools_layout.py index 5fd15149..9f9a342b 100644 --- a/pylot/core/active/vtk_tools_layout.py +++ b/pylot/core/active/vtk_tools_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'vtk_tools_layout.ui' # -# Created: Tue Jul 5 13:55:56 2016 +# Created: Wed Jul 6 11:39:58 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,7 +12,7 @@ from PySide import QtCore, QtGui class Ui_vtk_tools(object): def setupUi(self, vtk_tools): vtk_tools.setObjectName("vtk_tools") - vtk_tools.resize(406, 407) + vtk_tools.resize(362, 432) self.verticalLayout_8 = QtGui.QVBoxLayout(vtk_tools) self.verticalLayout_8.setObjectName("verticalLayout_8") self.verticalLayout = QtGui.QVBoxLayout() @@ -27,6 +27,10 @@ class Ui_vtk_tools(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_rec.sizePolicy().hasHeightForWidth()) self.label_rec.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setWeight(75) + font.setBold(True) + self.label_rec.setFont(font) self.label_rec.setToolTip("") self.label_rec.setObjectName("label_rec") self.horizontalLayout.addWidget(self.label_rec) @@ -71,6 +75,19 @@ class Ui_vtk_tools(object): self.pushButton_vgref.setObjectName("pushButton_vgref") self.horizontalLayout_4.addWidget(self.pushButton_vgref) self.verticalLayout_7.addLayout(self.horizontalLayout_4) + self.horizontalLayout_3 = QtGui.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.label_5 = QtGui.QLabel(vtk_tools) + self.label_5.setObjectName("label_5") + self.horizontalLayout_3.addWidget(self.label_5) + self.lineEdit_vgout = QtGui.QLineEdit(vtk_tools) + self.lineEdit_vgout.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lineEdit_vgout.setObjectName("lineEdit_vgout") + self.horizontalLayout_3.addWidget(self.lineEdit_vgout) + self.pushButton_parav = QtGui.QPushButton(vtk_tools) + self.pushButton_parav.setObjectName("pushButton_parav") + self.horizontalLayout_3.addWidget(self.pushButton_parav) + self.verticalLayout_7.addLayout(self.horizontalLayout_3) self.start_vg = QtGui.QPushButton(vtk_tools) self.start_vg.setEnabled(False) self.start_vg.setObjectName("start_vg") @@ -90,6 +107,10 @@ class Ui_vtk_tools(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_src.sizePolicy().hasHeightForWidth()) self.label_src.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setWeight(75) + font.setBold(True) + self.label_src.setFont(font) self.label_src.setObjectName("label_src") self.verticalLayout_2.addWidget(self.label_src) self.verticalLayout_3 = QtGui.QVBoxLayout() @@ -130,7 +151,7 @@ class Ui_vtk_tools(object): self.verticalLayout_8.addLayout(self.verticalLayout) self.buttonBox = QtGui.QDialogButtonBox(vtk_tools) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout_8.addWidget(self.buttonBox) @@ -176,6 +197,14 @@ class Ui_vtk_tools(object): self.pushButton_vg.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("vtk_tools", "Browse for reference velocity grid file (\'vgridsref.in\'):", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_vgref.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("vtk_tools", "Output Filename", None, QtGui.QApplication.UnicodeUTF8)) + self.lineEdit_vgout.setText(QtGui.QApplication.translate("vtk_tools", "vgrids.vtk", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_parav.setToolTip(QtGui.QApplication.translate("vtk_tools", "\n" +"\n" +"

Call Paraview (Shell command: paraview) for the specified output filename in the current directory.

", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_parav.setText(QtGui.QApplication.translate("vtk_tools", "<- Paraview", None, QtGui.QApplication.UnicodeUTF8)) self.start_vg.setText(QtGui.QApplication.translate("vtk_tools", "Start", None, QtGui.QApplication.UnicodeUTF8)) self.label_src.setToolTip(QtGui.QApplication.translate("vtk_tools", "Create VTK files from the FMTOMO output file \'rays.dat\'.\n" "This will generate one file of ray paths for each individual source.", None, QtGui.QApplication.UnicodeUTF8)) From 55a589b5257a2f004a805e83644a5cc9f1d1f9ce Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 7 Jul 2016 14:25:37 +0200 Subject: [PATCH 0990/1144] Added Surface plots, text windows and plotting of single shots. --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 147 +++++++++++++++--- pylot/core/active/activeSeismoPick.py | 5 +- pylot/core/active/asp3d_layout.py | 102 ++++++++++-- pylot/core/active/fmtomoUtils.py | 2 +- pylot/core/active/fmtomo_parameters_layout.py | 2 +- .../core/active/generate_seisarray_layout.py | 2 +- pylot/core/active/generate_survey_layout.py | 2 +- .../active/generate_survey_layout_minimal.py | 2 +- .../core/active/picking_parameters_layout.py | 42 ++--- pylot/core/active/seismicArrayPreparation.py | 32 ++-- pylot/core/active/seismicshot.py | 11 +- pylot/core/active/surveyUtils.py | 8 +- pylot/core/active/vtk_tools_layout.py | 27 ++-- 13 files changed, 294 insertions(+), 90 deletions(-) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index 34cda260..6b63621b 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -31,7 +31,11 @@ class gui_control(object): self.applypixmap = self.mainwindow.style().standardPixmap(QtGui.QStyle.SP_DialogApplyButton) self.setInitStates() self.addArrayPlot() + self.addSurfacePlot() self.addStatPlots() + self.mainUI.progressBar.setVisible(False) + self.printSurveyTextbox() + self.printSeisArrayTextbox() def setInitStates(self): self.setPickState(False) @@ -52,7 +56,10 @@ class gui_control(object): QtCore.QObject.connect(self.mainUI.postprocessing, QtCore.SIGNAL("clicked()"), self.postprocessing) QtCore.QObject.connect(self.mainUI.fmtomo, QtCore.SIGNAL("clicked()"), self.startFMTOMO) QtCore.QObject.connect(self.mainUI.vtk_tools, QtCore.SIGNAL("clicked()"), self.startVTKtools) - QtCore.QObject.connect(self.mainUI.comboBox, QtCore.SIGNAL("activated(int)"), self.replotStat) + QtCore.QObject.connect(self.mainUI.comboBox_stats, QtCore.SIGNAL("activated(int)"), self.refreshPickedWidgets) + QtCore.QObject.connect(self.mainUI.shot_left, QtCore.SIGNAL("clicked()"), self.decreaseShotnumber) + QtCore.QObject.connect(self.mainUI.shot_right, QtCore.SIGNAL("clicked()"), self.increaseShotnumber) + QtCore.QObject.connect(self.mainUI.plot_shot, QtCore.SIGNAL("clicked()"), self.plotShot) def gen_seisarray(self): disconnect = False @@ -140,6 +147,11 @@ class gui_control(object): self.seisArrayCanvas = FigureCanvas(self.seisArrayFigure) self.mainUI.horizontalLayout_tr.addWidget(self.seisArrayCanvas) + def addSurfacePlot(self): + self.surfaceFigure = Figure() + self.surfaceCanvas = FigureCanvas(self.surfaceFigure) + self.mainUI.horizontalLayout_tr.addWidget(self.surfaceCanvas) + def addStatPlots(self): self.statFigure_left = Figure() self.statCanvas_left = FigureCanvas(self.statFigure_left) @@ -147,52 +159,136 @@ class gui_control(object): self.statFigure_right = Figure() self.statCanvas_right = FigureCanvas(self.statFigure_right) self.mainUI.horizontalLayout_br.addWidget(self.statCanvas_right) - self.addItems2ComboBox() + self.addItems2StatsComboBox() - def addItems2ComboBox(self): - self.mainUI.comboBox.insertItem(0, 'picked traces') - self.mainUI.comboBox.insertItem(1, 'mean SNR') - self.mainUI.comboBox.insertItem(2, 'median SNR') - self.mainUI.comboBox.insertItem(3, 'mean SPE') - self.mainUI.comboBox.insertItem(4, 'median SPE') - self.mainUI.comboBox.setEnabled(False) + def addItems2StatsComboBox(self): + self.mainUI.comboBox_stats.insertItem(0, 'picked traces') + self.mainUI.comboBox_stats.insertItem(1, 'mean SNR') + self.mainUI.comboBox_stats.insertItem(2, 'median SNR') + self.mainUI.comboBox_stats.insertItem(3, 'mean SPE') + self.mainUI.comboBox_stats.insertItem(4, 'median SPE') + self.enablePickedTools(False) + + def addItems2ShotsComboBox(self): + shotnumbers = self.survey.data.keys() + shotnumbers.sort() + for index, shotnumber in enumerate(shotnumbers): + self.mainUI.comboBox_shots.insertItem(index, 'Shot: %s'%shotnumber) + self.mainUI.comboBox_shots.setMaxCount(len(shotnumbers)) + + def increaseShotnumber(self): + currentIndex = self.mainUI.comboBox_shots.currentIndex() + maxindex = self.mainUI.comboBox_shots.maxCount() - 1 + if currentIndex == maxindex: + self.mainUI.comboBox_shots.setCurrentIndex(0) + else: + self.mainUI.comboBox_shots.setCurrentIndex(currentIndex + 1) + + def decreaseShotnumber(self): + currentIndex = self.mainUI.comboBox_shots.currentIndex() + maxindex = self.mainUI.comboBox_shots.maxCount() - 1 + if currentIndex == 0: + self.mainUI.comboBox_shots.setCurrentIndex(maxindex) + else: + self.mainUI.comboBox_shots.setCurrentIndex(currentIndex - 1) + + def plotShot(self): + shotnumber = int(self.mainUI.comboBox_shots.currentText().split()[1]) + self.survey.data[shotnumber].matshow() def addArrayAxes(self): self.seisArrayAx = self.seisArrayFigure.add_subplot(111) + def addSurfaceAxes(self): + self.surfaceAx = self.surfaceFigure.add_subplot(111, projection = '3d') + def addStatAxes(self): self.statAx_left = self.statFigure_left.add_subplot(111) self.statAx_right = self.statFigure_right.add_subplot(111) + def enablePickedTools(self, bool): + self.mainUI.comboBox_stats.setEnabled(bool) + self.mainUI.comboBox_shots.setEnabled(bool) + self.mainUI.shot_left.setEnabled(bool) + self.mainUI.shot_right.setEnabled(bool) + self.mainUI.plot_shot.setEnabled(bool) + if bool == False: + self.mainUI.comboBox_shots.clear() + def replotArray(self): self.seisArrayFigure.clf() self.addArrayAxes() self.plotArray() self.seisArrayCanvas.draw() - def plotArray(self): - self.seisarray.plotArray2D(self.seisArrayAx, highlight_measured = True) + def replotSurface(self): + self.surfaceFigure.clf() + self.addSurfaceAxes() + self.plotSurface() + self.surfaceCanvas.draw() - def replotStat(self): + def plotArray(self): + self.seisarray.plotArray2D(self.seisArrayAx, highlight_measured = True, plot_topo = True) + + def plotSurface(self): + self.seisarray.plotSurface3D(ax = self.surfaceAx, exag = True) + self.seisarray.plotArray3D(ax = self.surfaceAx, legend = False, markersize = 3) + + def refreshPickedWidgets(self): self.statFigure_left.clf() self.statFigure_right.clf() self.addStatAxes() - self.plotStat() + self.InitPickedWidgets() self.statCanvas_left.draw() self.statCanvas_right.draw() - def plotStat(self): + def InitPickedWidgets(self): if self.checkPickState(): - surveyUtils.plotScatterStats4Receivers(self.survey, self.mainUI.comboBox.currentText(), self.statAx_left) - surveyUtils.plotScatterStats4Shots(self.survey, self.mainUI.comboBox.currentText(), self.statAx_right) + surveyUtils.plotScatterStats4Receivers(self.survey, self.mainUI.comboBox_stats.currentText(), self.statAx_left) + surveyUtils.plotScatterStats4Shots(self.survey, self.mainUI.comboBox_stats.currentText(), self.statAx_right) + self.addItems2ShotsComboBox() + + def printSurveyTextbox(self, init = True): + if init == True: + surveytup = (0, 0, 0, 0) + else: + survey = self.survey + nshots = len(survey.getShotlist()) + tt = survey.countAllTraces() + pt = survey.countAllPickedTraces() + rate = float(pt) / float(tt) * 100 + surveytup = (nshots, tt, pt, rate) + surveyTitle = 'SURVEY:\n' + surveyText = 'Number of Sources: %s\nTotal Traces: %s\nPicked Traces: %s (%4.2f%%)'%surveytup + string = surveyTitle + surveyText + self.mainUI.textBox_survey.setText(string) + + def printSeisArrayTextbox(self, init = True): + if init == True: + seistup = (0, 0, 0) + else: + seisarray = self.seisarray + nshots = len(seisarray.getSourceCoordinates()) + nrec = len(seisarray.getReceiverCoordinates()) + nadd = len(seisarray.getMeasuredTopo()) + seistup = (nshots, nrec, nadd) + seisArrayTitle = 'SEISARRAY:\n' + seisArrayText = 'Sources: %s\nReceivers: %s\nAdditional Points:%s'%seistup + string = seisArrayTitle + seisArrayText + self.mainUI.textBox_seisarray.setText(string) def interpolate_receivers(self): if not self.checkSeisArrayState(): self.printDialogMessage('No Seismic Array defined.') return self.seisarray.interpolateAll() - self.replotArray() + self.refreshSeisArrayWidgets() + def refreshSeisArrayWidgets(self): + self.replotArray() + self.replotSurface() + self.printSeisArrayTextbox(init = False) + def getPickParameters(self, ui, Picking_parameters): if Picking_parameters.exec_(): ncores = int(ui.ncores.value()) @@ -316,6 +412,7 @@ class gui_control(object): QtCore.QObject.connect(self.vtk_tools_ui.pushButton_vgref, QtCore.SIGNAL("clicked()"), self.chooseVgridref) QtCore.QObject.connect(self.vtk_tools_ui.pushButton_rays, QtCore.SIGNAL("clicked()"), self.chooseRaysIn) QtCore.QObject.connect(self.vtk_tools_ui.pushButton_raysout, QtCore.SIGNAL("clicked()"), self.chooseRaysOutDir) + QtCore.QObject.connect(self.vtk_tools_ui.pushButton_vtkout, QtCore.SIGNAL("clicked()"), self.newFileVTK) QtCore.QObject.connect(self.vtk_tools_ui.pushButton_parav, QtCore.SIGNAL("clicked()"), self.openFileParaview) QtCore.QObject.connect(self.vtk_tools_ui.start_vg, QtCore.SIGNAL("clicked()"), self.startvgvtk) QtCore.QObject.connect(self.vtk_tools_ui.start_rays, QtCore.SIGNAL("clicked()"), self.startraysvtk) @@ -388,6 +485,9 @@ class gui_control(object): ui = self.vtk_tools_ui fmtomoUtils.rays2VTK(ui.lineEdit_rays.text(), ui.lineEdit_raysout.text()) + def newFileVTK(self): + self.vtk_tools_ui.lineEdit_vgout.setText(self.saveFile()) + def chooseFMTOMOdir(self): self.fmtomo_parameters_ui.fmtomo_dir.setText(self.browseDir()) @@ -405,6 +505,7 @@ class gui_control(object): self.printDialogMessage('No Survey defined.') return self.survey.plotAllPicks() + self.refreshPickedWidgets() # wait until finished def load_survey(self): if self.checkSurveyState(): @@ -465,12 +566,11 @@ class gui_control(object): return if not type(seisarray) == seismicArrayPreparation.SeisArray: self.printDialogMessage('Wrong input file of type %s, expected %s.' - %(type(survey), seismicArrayPreparation.SeisArray)) + %(type(seisarray), seismicArrayPreparation.SeisArray)) return if disconnect: self.setConnected2SurveyState(False) self.seisarray = seisarray - self.replotArray() self.setSeisArrayState(True) def save_seisarray(self): @@ -494,6 +594,7 @@ class gui_control(object): def setSurveyState(self, state): if state == True: self.mainUI.survey_active.setPixmap(self.applypixmap) + self.printSurveyTextbox(init = False) elif state == False: self.mainUI.survey_active.setPixmap(self.cancelpixmap) @@ -512,8 +613,8 @@ class gui_control(object): def setPickState(self, state): if state == True and self.checkSurveyState(): self.mainUI.picked_active.setPixmap(self.applypixmap) - self.replotStat() - self.mainUI.comboBox.setEnabled(True) + self.refreshPickedWidgets() + self.enablePickedTools(True) self.survey.picked = True elif state == True and self.checkSurveyState() is False: self.printDialogMessage('No Survey defined.') @@ -523,13 +624,13 @@ class gui_control(object): if self.checkSurveyState(): self.statFigure_left.clf() self.statFigure_right.clf() - self.mainUI.comboBox.setEnabled(False) + self.enablePickedTools(False) self.survey.picked = False def setSeisArrayState(self, state): if state == True: self.mainUI.seisarray_active.setPixmap(self.applypixmap) - self.replotArray() + self.refreshSeisArrayWidgets() elif state == False: self.mainUI.seisarray_active.setPixmap(self.cancelpixmap) if self.seisArrayFigure is not None: diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 541e8e33..8fe3e42b 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -320,12 +320,12 @@ class Survey(object): self.setEarllate() print('\npickAllShots: Finished\n') + self.picked = True ntraces = self.countAllTraces() pickedtraces = self.countAllPickedTraces() print('Picked %s / %s traces (%d %%)\n' % (pickedtraces, ntraces, float(pickedtraces) / float(ntraces) * 100.)) - self.picked = True def filterSNR(self): print('Starting filterSNR...') @@ -572,6 +572,9 @@ class Survey(object): Counts all picked traces of the survey. ''' count = 0 + if not self.picked: + return count + for shot in self.data.values(): for traceID in shot.getTraceIDlist(): if shot.getPickFlag(traceID) is not 0: diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index e879e2e6..aef7f8cf 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Wed Jul 6 11:39:58 2016 +# Created: Thu Jul 7 14:25:26 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -13,7 +13,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.setEnabled(True) - MainWindow.resize(905, 707) + MainWindow.resize(1029, 858) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -27,8 +27,8 @@ class Ui_MainWindow(object): MainWindow.setWindowIcon(icon) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") - self.gridLayout = QtGui.QGridLayout(self.centralwidget) - self.gridLayout.setObjectName("gridLayout") + self.verticalLayout_10 = QtGui.QVBoxLayout(self.centralwidget) + self.verticalLayout_10.setObjectName("verticalLayout_10") self.formLayout = QtGui.QFormLayout() self.formLayout.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) @@ -110,8 +110,17 @@ class Ui_MainWindow(object): self.label_6.setObjectName("label_6") self.horizontalLayout_2.addWidget(self.label_6) self.verticalLayout.addLayout(self.horizontalLayout_2) + self.textBox_seisarray = QtGui.QTextEdit(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.textBox_seisarray.sizePolicy().hasHeightForWidth()) + self.textBox_seisarray.setSizePolicy(sizePolicy) + self.textBox_seisarray.setObjectName("textBox_seisarray") + self.verticalLayout.addWidget(self.textBox_seisarray) self.verticalLayout_5.addLayout(self.verticalLayout) self.line = QtGui.QFrame(self.centralwidget) + self.line.setMinimumSize(QtCore.QSize(0, 5)) self.line.setFrameShape(QtGui.QFrame.HLine) self.line.setFrameShadow(QtGui.QFrame.Sunken) self.line.setObjectName("line") @@ -183,8 +192,18 @@ class Ui_MainWindow(object): self.label.setObjectName("label") self.horizontalLayout.addWidget(self.label) self.verticalLayout_2.addLayout(self.horizontalLayout) + self.textBox_survey = QtGui.QTextEdit(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.textBox_survey.sizePolicy().hasHeightForWidth()) + self.textBox_survey.setSizePolicy(sizePolicy) + self.textBox_survey.setTextInteractionFlags(QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse) + self.textBox_survey.setObjectName("textBox_survey") + self.verticalLayout_2.addWidget(self.textBox_survey) self.verticalLayout_5.addLayout(self.verticalLayout_2) self.line_3 = QtGui.QFrame(self.centralwidget) + self.line_3.setMinimumSize(QtCore.QSize(0, 5)) self.line_3.setFrameShape(QtGui.QFrame.HLine) self.line_3.setFrameShadow(QtGui.QFrame.Sunken) self.line_3.setObjectName("line_3") @@ -213,6 +232,7 @@ class Ui_MainWindow(object): self.postprocessing.setObjectName("postprocessing") self.verticalLayout_3.addWidget(self.postprocessing) self.line_2 = QtGui.QFrame(self.centralwidget) + self.line_2.setMinimumSize(QtCore.QSize(0, 5)) self.line_2.setFrameShape(QtGui.QFrame.HLine) self.line_2.setFrameShadow(QtGui.QFrame.Sunken) self.line_2.setObjectName("line_2") @@ -255,6 +275,12 @@ class Ui_MainWindow(object): self.vtk_tools = QtGui.QPushButton(self.centralwidget) self.vtk_tools.setObjectName("vtk_tools") self.verticalLayout_4.addWidget(self.vtk_tools) + self.progressBar = QtGui.QProgressBar(self.centralwidget) + self.progressBar.setEnabled(True) + self.progressBar.setProperty("value", 0) + self.progressBar.setTextVisible(True) + self.progressBar.setObjectName("progressBar") + self.verticalLayout_4.addWidget(self.progressBar) self.verticalLayout_3.addLayout(self.verticalLayout_4) self.verticalLayout_5.addLayout(self.verticalLayout_3) self.formLayout.setLayout(0, QtGui.QFormLayout.LabelRole, self.verticalLayout_5) @@ -269,18 +295,69 @@ class Ui_MainWindow(object): self.line_4.setFrameShadow(QtGui.QFrame.Sunken) self.line_4.setObjectName("line_4") self.verticalLayout_right.addWidget(self.line_4) - self.comboBox = QtGui.QComboBox(self.centralwidget) - self.comboBox.setEnabled(False) - self.comboBox.setObjectName("comboBox") - self.verticalLayout_right.addWidget(self.comboBox) + self.horizontalLayout_3 = QtGui.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.verticalLayout_6 = QtGui.QVBoxLayout() + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.label_11 = QtGui.QLabel(self.centralwidget) + self.label_11.setObjectName("label_11") + self.verticalLayout_6.addWidget(self.label_11) + self.comboBox_stats = QtGui.QComboBox(self.centralwidget) + self.comboBox_stats.setEnabled(False) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(3) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.comboBox_stats.sizePolicy().hasHeightForWidth()) + self.comboBox_stats.setSizePolicy(sizePolicy) + self.comboBox_stats.setMinimumSize(QtCore.QSize(450, 0)) + self.comboBox_stats.setObjectName("comboBox_stats") + self.verticalLayout_6.addWidget(self.comboBox_stats) + self.horizontalLayout_3.addLayout(self.verticalLayout_6) + self.line_5 = QtGui.QFrame(self.centralwidget) + self.line_5.setFrameShape(QtGui.QFrame.VLine) + self.line_5.setFrameShadow(QtGui.QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.horizontalLayout_3.addWidget(self.line_5) + self.verticalLayout_9 = QtGui.QVBoxLayout() + self.verticalLayout_9.setObjectName("verticalLayout_9") + self.label_10 = QtGui.QLabel(self.centralwidget) + self.label_10.setObjectName("label_10") + self.verticalLayout_9.addWidget(self.label_10) + self.horizontalLayout_4 = QtGui.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.shot_left = QtGui.QPushButton(self.centralwidget) + self.shot_left.setMaximumSize(QtCore.QSize(25, 16777215)) + self.shot_left.setObjectName("shot_left") + self.horizontalLayout_4.addWidget(self.shot_left) + self.comboBox_shots = QtGui.QComboBox(self.centralwidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(1) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.comboBox_shots.sizePolicy().hasHeightForWidth()) + self.comboBox_shots.setSizePolicy(sizePolicy) + self.comboBox_shots.setMinimumSize(QtCore.QSize(120, 0)) + self.comboBox_shots.setMaximumSize(QtCore.QSize(100, 16777215)) + self.comboBox_shots.setObjectName("comboBox_shots") + self.horizontalLayout_4.addWidget(self.comboBox_shots) + self.shot_right = QtGui.QPushButton(self.centralwidget) + self.shot_right.setMaximumSize(QtCore.QSize(25, 16777215)) + self.shot_right.setObjectName("shot_right") + self.horizontalLayout_4.addWidget(self.shot_right) + self.plot_shot = QtGui.QPushButton(self.centralwidget) + self.plot_shot.setMaximumSize(QtCore.QSize(80, 16777215)) + self.plot_shot.setObjectName("plot_shot") + self.horizontalLayout_4.addWidget(self.plot_shot) + self.verticalLayout_9.addLayout(self.horizontalLayout_4) + self.horizontalLayout_3.addLayout(self.verticalLayout_9) + self.verticalLayout_right.addLayout(self.horizontalLayout_3) self.horizontalLayout_br = QtGui.QHBoxLayout() self.horizontalLayout_br.setObjectName("horizontalLayout_br") self.verticalLayout_right.addLayout(self.horizontalLayout_br) self.formLayout.setLayout(0, QtGui.QFormLayout.FieldRole, self.verticalLayout_right) - self.gridLayout.addLayout(self.formLayout, 0, 0, 1, 1) + self.verticalLayout_10.addLayout(self.formLayout) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 905, 23)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1029, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtGui.QStatusBar(MainWindow) @@ -325,4 +402,9 @@ class Ui_MainWindow(object): self.label_9.setText(QtGui.QApplication.translate("MainWindow", "Visualization", None, QtGui.QApplication.UnicodeUTF8)) self.vtk_tools.setToolTip(QtGui.QApplication.translate("MainWindow", "Set parameters and call Fast Marching Tomography algorithm.", None, QtGui.QApplication.UnicodeUTF8)) self.vtk_tools.setText(QtGui.QApplication.translate("MainWindow", "VTK tools", None, QtGui.QApplication.UnicodeUTF8)) + self.label_11.setText(QtGui.QApplication.translate("MainWindow", "Plot Receiver and Shots of the Array colord by:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_10.setText(QtGui.QApplication.translate("MainWindow", "Plot a single Shot:", None, QtGui.QApplication.UnicodeUTF8)) + self.shot_left.setText(QtGui.QApplication.translate("MainWindow", "<", None, QtGui.QApplication.UnicodeUTF8)) + self.shot_right.setText(QtGui.QApplication.translate("MainWindow", ">", None, QtGui.QApplication.UnicodeUTF8)) + self.plot_shot.setText(QtGui.QApplication.translate("MainWindow", "Plot", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/fmtomoUtils.py b/pylot/core/active/fmtomoUtils.py index 2c5d975e..ea69637a 100644 --- a/pylot/core/active/fmtomoUtils.py +++ b/pylot/core/active/fmtomoUtils.py @@ -763,7 +763,7 @@ def rays2VTK(fnin, fdirout='./vtk_files/', nthPoint=50): infile.close() for shotnumber in rays.keys(): - fnameout = fdirout + 'rays%03d.vtk' % (shotnumber) + fnameout = os.path.join(fdirout, 'rays%03d.vtk'%(shotnumber)) outfile = open(fnameout, 'w') nPoints = 0 diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index a46f6163..b9b858ae 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Wed Jul 6 11:39:58 2016 +# Created: Thu Jul 7 14:25:26 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_seisarray_layout.py b/pylot/core/active/generate_seisarray_layout.py index 523fba2e..c4a65c3c 100644 --- a/pylot/core/active/generate_seisarray_layout.py +++ b/pylot/core/active/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Wed Jul 6 11:39:58 2016 +# Created: Thu Jul 7 14:25:26 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_survey_layout.py b/pylot/core/active/generate_survey_layout.py index 950e1f10..c756aac7 100644 --- a/pylot/core/active/generate_survey_layout.py +++ b/pylot/core/active/generate_survey_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout.ui' # -# Created: Wed Jul 6 11:39:58 2016 +# Created: Thu Jul 7 14:25:26 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/generate_survey_layout_minimal.py b/pylot/core/active/generate_survey_layout_minimal.py index d17c0ccc..4cd1c270 100644 --- a/pylot/core/active/generate_survey_layout_minimal.py +++ b/pylot/core/active/generate_survey_layout_minimal.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout_minimal.ui' # -# Created: Wed Jul 6 11:39:58 2016 +# Created: Thu Jul 7 14:25:26 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index 76cc69c3..544d736c 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Wed Jul 6 11:39:58 2016 +# Created: Thu Jul 7 14:25:26 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,10 +12,10 @@ from PySide import QtCore, QtGui class Ui_picking_parameters(object): def setupUi(self, picking_parameters): picking_parameters.setObjectName("picking_parameters") - picking_parameters.resize(325, 400) - picking_parameters.setMinimumSize(QtCore.QSize(325, 400)) - self.verticalLayout_3 = QtGui.QVBoxLayout(picking_parameters) - self.verticalLayout_3.setObjectName("verticalLayout_3") + picking_parameters.resize(321, 253) + picking_parameters.setMinimumSize(QtCore.QSize(0, 0)) + self.gridLayout = QtGui.QGridLayout(picking_parameters) + self.gridLayout.setObjectName("gridLayout") self.label_7 = QtGui.QLabel(picking_parameters) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -28,7 +28,7 @@ class Ui_picking_parameters(object): font.setBold(True) self.label_7.setFont(font) self.label_7.setObjectName("label_7") - self.verticalLayout_3.addWidget(self.label_7) + self.gridLayout.addWidget(self.label_7, 0, 0, 1, 1) self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.label = QtGui.QLabel(picking_parameters) @@ -45,7 +45,7 @@ class Ui_picking_parameters(object): self.ncores.setMaximum(10000) self.ncores.setObjectName("ncores") self.horizontalLayout.addWidget(self.ncores) - self.verticalLayout_3.addLayout(self.horizontalLayout) + self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) self.horizontalLayout_3 = QtGui.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.verticalLayout_2 = QtGui.QVBoxLayout() @@ -78,7 +78,7 @@ class Ui_picking_parameters(object): self.lineEdit_vmax.setObjectName("lineEdit_vmax") self.verticalLayout.addWidget(self.lineEdit_vmax) self.horizontalLayout_3.addLayout(self.verticalLayout) - self.verticalLayout_3.addLayout(self.horizontalLayout_3) + self.gridLayout.addLayout(self.horizontalLayout_3, 2, 0, 1, 1) self.horizontalLayout_4 = QtGui.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.label_4 = QtGui.QLabel(picking_parameters) @@ -93,7 +93,7 @@ class Ui_picking_parameters(object): self.lineEdit_folm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lineEdit_folm.setObjectName("lineEdit_folm") self.horizontalLayout_4.addWidget(self.lineEdit_folm) - self.verticalLayout_3.addLayout(self.horizontalLayout_4) + self.gridLayout.addLayout(self.horizontalLayout_4, 3, 0, 1, 1) self.horizontalLayout_5 = QtGui.QHBoxLayout() self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.label_5 = QtGui.QLabel(picking_parameters) @@ -109,7 +109,7 @@ class Ui_picking_parameters(object): self.checkBox_AIC.setChecked(True) self.checkBox_AIC.setObjectName("checkBox_AIC") self.horizontalLayout_5.addWidget(self.checkBox_AIC) - self.verticalLayout_3.addLayout(self.horizontalLayout_5) + self.gridLayout.addLayout(self.horizontalLayout_5, 4, 0, 1, 1) self.horizontalLayout_6 = QtGui.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.label_6 = QtGui.QLabel(picking_parameters) @@ -131,12 +131,12 @@ class Ui_picking_parameters(object): self.lineEdit_aicright.setObjectName("lineEdit_aicright") self.horizontalLayout_7.addWidget(self.lineEdit_aicright) self.horizontalLayout_6.addLayout(self.horizontalLayout_7) - self.verticalLayout_3.addLayout(self.horizontalLayout_6) + self.gridLayout.addLayout(self.horizontalLayout_6, 5, 0, 1, 1) self.buttonBox = QtGui.QDialogButtonBox(picking_parameters) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_3.addWidget(self.buttonBox) + self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 1) self.retranslateUi(picking_parameters) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), picking_parameters.accept) @@ -146,15 +146,21 @@ class Ui_picking_parameters(object): def retranslateUi(self, picking_parameters): picking_parameters.setWindowTitle(QtGui.QApplication.translate("picking_parameters", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) self.label_7.setText(QtGui.QApplication.translate("picking_parameters", "Choose parameters for FMTOMO simulation", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("picking_parameters", "nproc", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("picking_parameters", "vmin [m/s]", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setToolTip(QtGui.QApplication.translate("picking_parameters", "Amount of CPU kernels used.", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("picking_parameters", "nproc [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setToolTip(QtGui.QApplication.translate("picking_parameters", "Minimum permitted direct velocity (apparent velocity!).", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("picking_parameters", "vmin [m/s] [?]", None, QtGui.QApplication.UnicodeUTF8)) self.lineEdit_vmin.setText(QtGui.QApplication.translate("picking_parameters", "333", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setText(QtGui.QApplication.translate("picking_parameters", "vmax [m/s]", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setToolTip(QtGui.QApplication.translate("picking_parameters", "Maximum permitted direct velocity (apparent velocity!).", None, QtGui.QApplication.UnicodeUTF8)) + self.label_3.setText(QtGui.QApplication.translate("picking_parameters", "vmax [m/s] [?]", None, QtGui.QApplication.UnicodeUTF8)) self.lineEdit_vmax.setText(QtGui.QApplication.translate("picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setText(QtGui.QApplication.translate("picking_parameters", "Fraction of local maximum", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setToolTip(QtGui.QApplication.translate("picking_parameters", "Value between 0 and 1 for threshold picking algorithm (Default = 0.6).", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setText(QtGui.QApplication.translate("picking_parameters", "Fraction of local maximum [?]", None, QtGui.QApplication.UnicodeUTF8)) self.lineEdit_folm.setText(QtGui.QApplication.translate("picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) - self.label_5.setText(QtGui.QApplication.translate("picking_parameters", "AIC", None, QtGui.QApplication.UnicodeUTF8)) - self.label_6.setText(QtGui.QApplication.translate("picking_parameters", "AIC window indices\n" + self.label_5.setToolTip(QtGui.QApplication.translate("picking_parameters", "Use additional Akaike Information Criterion for picking.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("picking_parameters", "AIC [?]", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setToolTip(QtGui.QApplication.translate("picking_parameters", "Samples before and after initial HOS pick.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setText(QtGui.QApplication.translate("picking_parameters", "AIC window samples\n" "(only if AIC checked)", None, QtGui.QApplication.UnicodeUTF8)) self.lineEdit_aicleft.setText(QtGui.QApplication.translate("picking_parameters", "15", None, QtGui.QApplication.UnicodeUTF8)) self.lineEdit_aicright.setText(QtGui.QApplication.translate("picking_parameters", "0", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index abfda89f..5ba2fd8d 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -837,10 +837,10 @@ class SeisArray(object): if highlight_measured == True: ax.plot(xmr, ymr, 'r.', markersize=pointsize, label='measured receivers') - ax.text(0.5, 1.05,'2D plot of seismic array\n %s'%self.recfile, - horizontalalignment='center', verticalalignment='center', - transform=ax.transAxes) - #plt.title('2D plot of seismic array %s' % self.recfile) + ax.figure.text(0.5, 0.95,'2D plot of seismic array\n %s'%self.recfile, + horizontalalignment='center', verticalalignment='center', + transform = ax.figure.transFigure) + ax.set_xlabel('X [m]') ax.set_ylabel('Y [m]') ax.set_aspect('equal') @@ -853,12 +853,12 @@ class SeisArray(object): ax.annotate((' ' + str(shotnumber)), xy=(self._getXshot(shotnumber), self._getYshot(shotnumber)), fontsize='x-small', color='b') - def plotArray3D(self, ax=None): + def plotArray3D(self, ax=None, legend = True, markersize = 10): import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D - plt.interactive(True) if ax == None: + plt.interactive(True) fig = plt.figure() ax = plt.axes(projection='3d') @@ -867,19 +867,23 @@ class SeisArray(object): xrc, yrc, zrc = self.getReceiverLists() xsc, ysc, zsc = self.getSourceLocsLists() - plt.title('3D plot of seismic array.') + ax.figure.text(0.5, 0.95,'3D plot of seismic array\n %s'%self.recfile, + horizontalalignment='center', verticalalignment='center', + transform = ax.figure.transFigure) + if len(xmt) > 0: - ax.plot(xmt, ymt, zmt, 'b.', markersize=10, label='measured topo points') + ax.plot(xmt, ymt, zmt, 'b.', markersize=markersize, label='measured topo points') if len(xrc) > 0: - ax.plot(xrc, yrc, zrc, 'k.', markersize=10, label='all receivers') + ax.plot(xrc, yrc, zrc, 'k.', markersize=markersize, label='all receivers') if len(xmr) > 0: - ax.plot(xmr, ymr, zmr, 'ro', label='measured receivers') + ax.plot(xmr, ymr, zmr, 'ro', markersize=markersize, label='measured receivers') if len(xsc) > 0: - ax.plot(xsc, ysc, zsc, 'b*', label='shot locations') + ax.plot(xsc, ysc, zsc, 'b*', markersize=markersize, label='shot locations') ax.set_xlabel('X [m]'); ax.set_ylabel('Y [m]'); ax.set_zlabel('Z [m]') - ax.legend() + if legend == True: + ax.legend() return ax @@ -887,9 +891,9 @@ class SeisArray(object): from matplotlib import cm import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D - plt.interactive(True) if ax == None: + plt.interactive(True) fig = plt.figure() ax = plt.axes(projection='3d') @@ -908,7 +912,7 @@ class SeisArray(object): zgrid = griddata((x, y), z, (xgrid, ygrid), method=method) surf = ax.plot_surface(xgrid, ygrid, zgrid, linewidth=0, cmap=cm.jet, vmin=min(z), vmax=max(z)) - cbar = plt.colorbar(surf) + cbar = ax.figure.colorbar(surf) cbar.set_label('Elevation [m]') if exag == False: diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py index 4f42fade..d31f1de2 100644 --- a/pylot/core/active/seismicshot.py +++ b/pylot/core/active/seismicshot.py @@ -908,15 +908,16 @@ class SeismicShot(object): alpha=0.85) ax.text(0.5, 0.95, 'shot: %s' % self.getShotnumber(), transform=ax.transAxes , horizontalalignment='center') - sc = ax.scatter(x, y, c=z, s=30, label='picked shots', vmin=tmin, vmax=tmax, cmap=cmap, linewidths=1.5) - label = None + sc = ax.scatter(x, y, c=z, s=30, label='active traces', vmin=tmin, vmax=tmax, cmap=cmap, linewidths=1.5) for xyz in zip(xcut, ycut, zcut): x, y, z = xyz + count += 1 if z > tmax: - count += 1 z = 'w' - if count == 1: - label = 'cut out shots' + if count == 1: + label = 'inactive traces' + else: + label = None ax.scatter(x, y, c=z, s=30, edgecolor='m', label=label, vmin=tmin, vmax=tmax, cmap=cmap, linewidths=1.5) if colorbar == True: cbar = plt.colorbar(sc) diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index 5edf7af3..bcff2f70 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -208,8 +208,8 @@ def plotScatterStats4Shots(survey, variable, ax = None): ax.text(0.5, 1.05,'Plot of all shots', horizontalalignment='center', verticalalignment='center', transform=ax.transAxes) - ax.set_xlabel('X') - ax.set_ylabel('Y') + ax.set_xlabel('X [m]') + ax.set_ylabel('Y [m]') ax.set_aspect('equal') cbar = ax.figure.colorbar(sc) cbar.set_label(variable) @@ -272,8 +272,8 @@ def plotScatterStats4Receivers(survey, variable, ax = None): ax.text(0.5, 1.05,'Plot of all receivers', horizontalalignment='center', verticalalignment='center', transform=ax.transAxes) - ax.set_xlabel('X') - ax.set_ylabel('Y') + ax.set_xlabel('X [m]') + ax.set_ylabel('Y [m]') ax.set_aspect('equal') cbar = ax.figure.colorbar(sc) cbar.set_label(variable) diff --git a/pylot/core/active/vtk_tools_layout.py b/pylot/core/active/vtk_tools_layout.py index 9f9a342b..2c350756 100644 --- a/pylot/core/active/vtk_tools_layout.py +++ b/pylot/core/active/vtk_tools_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'vtk_tools_layout.ui' # -# Created: Wed Jul 6 11:39:58 2016 +# Created: Thu Jul 7 14:25:26 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,9 +12,9 @@ from PySide import QtCore, QtGui class Ui_vtk_tools(object): def setupUi(self, vtk_tools): vtk_tools.setObjectName("vtk_tools") - vtk_tools.resize(362, 432) - self.verticalLayout_8 = QtGui.QVBoxLayout(vtk_tools) - self.verticalLayout_8.setObjectName("verticalLayout_8") + vtk_tools.resize(422, 471) + self.verticalLayout_9 = QtGui.QVBoxLayout(vtk_tools) + self.verticalLayout_9.setObjectName("verticalLayout_9") self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_4 = QtGui.QVBoxLayout() @@ -75,15 +75,21 @@ class Ui_vtk_tools(object): self.pushButton_vgref.setObjectName("pushButton_vgref") self.horizontalLayout_4.addWidget(self.pushButton_vgref) self.verticalLayout_7.addLayout(self.horizontalLayout_4) - self.horizontalLayout_3 = QtGui.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.verticalLayout_8 = QtGui.QVBoxLayout() + self.verticalLayout_8.setObjectName("verticalLayout_8") + self.verticalLayout_7.addLayout(self.verticalLayout_8) self.label_5 = QtGui.QLabel(vtk_tools) self.label_5.setObjectName("label_5") - self.horizontalLayout_3.addWidget(self.label_5) + self.verticalLayout_7.addWidget(self.label_5) + self.horizontalLayout_3 = QtGui.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.lineEdit_vgout = QtGui.QLineEdit(vtk_tools) self.lineEdit_vgout.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lineEdit_vgout.setObjectName("lineEdit_vgout") self.horizontalLayout_3.addWidget(self.lineEdit_vgout) + self.pushButton_vtkout = QtGui.QPushButton(vtk_tools) + self.pushButton_vtkout.setObjectName("pushButton_vtkout") + self.horizontalLayout_3.addWidget(self.pushButton_vtkout) self.pushButton_parav = QtGui.QPushButton(vtk_tools) self.pushButton_parav.setObjectName("pushButton_parav") self.horizontalLayout_3.addWidget(self.pushButton_parav) @@ -148,12 +154,12 @@ class Ui_vtk_tools(object): self.verticalLayout_5.addWidget(self.start_rays) self.verticalLayout_2.addLayout(self.verticalLayout_5) self.verticalLayout.addLayout(self.verticalLayout_2) - self.verticalLayout_8.addLayout(self.verticalLayout) + self.verticalLayout_9.addLayout(self.verticalLayout) self.buttonBox = QtGui.QDialogButtonBox(vtk_tools) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_8.addWidget(self.buttonBox) + self.verticalLayout_9.addWidget(self.buttonBox) self.retranslateUi(vtk_tools) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), vtk_tools.accept) @@ -197,8 +203,9 @@ class Ui_vtk_tools(object): self.pushButton_vg.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("vtk_tools", "Browse for reference velocity grid file (\'vgridsref.in\'):", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_vgref.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_5.setText(QtGui.QApplication.translate("vtk_tools", "Output Filename", None, QtGui.QApplication.UnicodeUTF8)) + self.label_5.setText(QtGui.QApplication.translate("vtk_tools", "Output Filename:", None, QtGui.QApplication.UnicodeUTF8)) self.lineEdit_vgout.setText(QtGui.QApplication.translate("vtk_tools", "vgrids.vtk", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_vtkout.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_parav.setToolTip(QtGui.QApplication.translate("vtk_tools", "\n" "\n" +"

Specifiy directory containing seismograms for each shot.

\n" +"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" +"

\n" +"

For example:

\n" +"

\n" +"

Shot number 100 containing seismograms for all traces with the name:

\n" +"

\n" +"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs_2.setText(QtGui.QApplication.translate("generate_survey", "*Shotnumber*", None, QtGui.QApplication.UnicodeUTF8)) + self.fend.setText(QtGui.QApplication.translate("generate_survey", ".dat", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/generate_survey_layout_minimal.py b/pylot/core/active/generate_survey_layout_minimal.py index 4cd1c270..0ffa32a1 100644 --- a/pylot/core/active/generate_survey_layout_minimal.py +++ b/pylot/core/active/generate_survey_layout_minimal.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout_minimal.ui' # -# Created: Thu Jul 7 14:25:26 2016 +# Created: Tue Jul 12 14:03:29 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,12 +12,12 @@ from PySide import QtCore, QtGui class Ui_generate_survey_minimal(object): def setupUi(self, generate_survey_minimal): generate_survey_minimal.setObjectName("generate_survey_minimal") - generate_survey_minimal.resize(325, 83) + generate_survey_minimal.resize(382, 139) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) generate_survey_minimal.setWindowIcon(icon) - self.verticalLayout = QtGui.QVBoxLayout(generate_survey_minimal) - self.verticalLayout.setObjectName("verticalLayout") + self.verticalLayout_2 = QtGui.QVBoxLayout(generate_survey_minimal) + self.verticalLayout_2.setObjectName("verticalLayout_2") self.gridLayout = QtGui.QGridLayout() self.gridLayout.setObjectName("gridLayout") self.lineEdit_obs = QtGui.QLineEdit(generate_survey_minimal) @@ -29,12 +29,30 @@ class Ui_generate_survey_minimal(object): self.pushButton_obs = QtGui.QPushButton(generate_survey_minimal) self.pushButton_obs.setObjectName("pushButton_obs") self.gridLayout.addWidget(self.pushButton_obs, 0, 2, 1, 1) - self.verticalLayout.addLayout(self.gridLayout) + self.verticalLayout_2.addLayout(self.gridLayout) + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.label = QtGui.QLabel(generate_survey_minimal) + self.label.setObjectName("label") + self.verticalLayout.addWidget(self.label) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.fstart = QtGui.QLineEdit(generate_survey_minimal) + self.fstart.setObjectName("fstart") + self.horizontalLayout.addWidget(self.fstart) + self.label_obs_2 = QtGui.QLabel(generate_survey_minimal) + self.label_obs_2.setObjectName("label_obs_2") + self.horizontalLayout.addWidget(self.label_obs_2) + self.fend = QtGui.QLineEdit(generate_survey_minimal) + self.fend.setObjectName("fend") + self.horizontalLayout.addWidget(self.fend) + self.verticalLayout.addLayout(self.horizontalLayout) + self.verticalLayout_2.addLayout(self.verticalLayout) self.buttonBox = QtGui.QDialogButtonBox(generate_survey_minimal) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.verticalLayout.addWidget(self.buttonBox) + self.verticalLayout_2.addWidget(self.buttonBox) self.retranslateUi(generate_survey_minimal) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), generate_survey_minimal.accept) @@ -58,4 +76,19 @@ class Ui_generate_survey_minimal(object): self.label_obs.setText(QtGui.QApplication.translate("generate_survey_minimal", "Seismogram\n" "Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) self.pushButton_obs.setText(QtGui.QApplication.translate("generate_survey_minimal", "Browse", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("generate_survey_minimal", "File structure:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs_2.setToolTip(QtGui.QApplication.translate("generate_survey_minimal", "\n" +"\n" +"

Specifiy directory containing seismograms for each shot.

\n" +"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" +"

\n" +"

For example:

\n" +"

\n" +"

Shot number 100 containing seismograms for all traces with the name:

\n" +"

\n" +"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) + self.label_obs_2.setText(QtGui.QApplication.translate("generate_survey_minimal", "*Shotnumber*", None, QtGui.QApplication.UnicodeUTF8)) + self.fend.setText(QtGui.QApplication.translate("generate_survey_minimal", ".dat", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/picking_parameters_layout.py b/pylot/core/active/picking_parameters_layout.py index 544d736c..153dca18 100644 --- a/pylot/core/active/picking_parameters_layout.py +++ b/pylot/core/active/picking_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Thu Jul 7 14:25:26 2016 +# Created: Tue Jul 12 14:03:30 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,10 +12,10 @@ from PySide import QtCore, QtGui class Ui_picking_parameters(object): def setupUi(self, picking_parameters): picking_parameters.setObjectName("picking_parameters") - picking_parameters.resize(321, 253) + picking_parameters.resize(321, 254) picking_parameters.setMinimumSize(QtCore.QSize(0, 0)) - self.gridLayout = QtGui.QGridLayout(picking_parameters) - self.gridLayout.setObjectName("gridLayout") + self.verticalLayout_3 = QtGui.QVBoxLayout(picking_parameters) + self.verticalLayout_3.setObjectName("verticalLayout_3") self.label_7 = QtGui.QLabel(picking_parameters) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -28,7 +28,7 @@ class Ui_picking_parameters(object): font.setBold(True) self.label_7.setFont(font) self.label_7.setObjectName("label_7") - self.gridLayout.addWidget(self.label_7, 0, 0, 1, 1) + self.verticalLayout_3.addWidget(self.label_7) self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.label = QtGui.QLabel(picking_parameters) @@ -45,7 +45,7 @@ class Ui_picking_parameters(object): self.ncores.setMaximum(10000) self.ncores.setObjectName("ncores") self.horizontalLayout.addWidget(self.ncores) - self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) + self.verticalLayout_3.addLayout(self.horizontalLayout) self.horizontalLayout_3 = QtGui.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.verticalLayout_2 = QtGui.QVBoxLayout() @@ -78,7 +78,7 @@ class Ui_picking_parameters(object): self.lineEdit_vmax.setObjectName("lineEdit_vmax") self.verticalLayout.addWidget(self.lineEdit_vmax) self.horizontalLayout_3.addLayout(self.verticalLayout) - self.gridLayout.addLayout(self.horizontalLayout_3, 2, 0, 1, 1) + self.verticalLayout_3.addLayout(self.horizontalLayout_3) self.horizontalLayout_4 = QtGui.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.label_4 = QtGui.QLabel(picking_parameters) @@ -89,11 +89,28 @@ class Ui_picking_parameters(object): self.label_4.setSizePolicy(sizePolicy) self.label_4.setObjectName("label_4") self.horizontalLayout_4.addWidget(self.label_4) - self.lineEdit_folm = QtGui.QLineEdit(picking_parameters) - self.lineEdit_folm.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.lineEdit_folm.setObjectName("lineEdit_folm") - self.horizontalLayout_4.addWidget(self.lineEdit_folm) - self.gridLayout.addLayout(self.horizontalLayout_4, 3, 0, 1, 1) + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.slider_folm = QtGui.QSlider(picking_parameters) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.slider_folm.sizePolicy().hasHeightForWidth()) + self.slider_folm.setSizePolicy(sizePolicy) + self.slider_folm.setMinimum(1) + self.slider_folm.setMaximum(100) + self.slider_folm.setProperty("value", 60) + self.slider_folm.setOrientation(QtCore.Qt.Horizontal) + self.slider_folm.setTickPosition(QtGui.QSlider.TicksBelow) + self.slider_folm.setTickInterval(10) + self.slider_folm.setObjectName("slider_folm") + self.horizontalLayout_2.addWidget(self.slider_folm) + self.label_folm = QtGui.QLabel(picking_parameters) + self.label_folm.setAlignment(QtCore.Qt.AlignCenter) + self.label_folm.setObjectName("label_folm") + self.horizontalLayout_2.addWidget(self.label_folm) + self.horizontalLayout_4.addLayout(self.horizontalLayout_2) + self.verticalLayout_3.addLayout(self.horizontalLayout_4) self.horizontalLayout_5 = QtGui.QHBoxLayout() self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.label_5 = QtGui.QLabel(picking_parameters) @@ -109,7 +126,7 @@ class Ui_picking_parameters(object): self.checkBox_AIC.setChecked(True) self.checkBox_AIC.setObjectName("checkBox_AIC") self.horizontalLayout_5.addWidget(self.checkBox_AIC) - self.gridLayout.addLayout(self.horizontalLayout_5, 4, 0, 1, 1) + self.verticalLayout_3.addLayout(self.horizontalLayout_5) self.horizontalLayout_6 = QtGui.QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.label_6 = QtGui.QLabel(picking_parameters) @@ -131,12 +148,12 @@ class Ui_picking_parameters(object): self.lineEdit_aicright.setObjectName("lineEdit_aicright") self.horizontalLayout_7.addWidget(self.lineEdit_aicright) self.horizontalLayout_6.addLayout(self.horizontalLayout_7) - self.gridLayout.addLayout(self.horizontalLayout_6, 5, 0, 1, 1) + self.verticalLayout_3.addLayout(self.horizontalLayout_6) self.buttonBox = QtGui.QDialogButtonBox(picking_parameters) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 1) + self.verticalLayout_3.addWidget(self.buttonBox) self.retranslateUi(picking_parameters) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), picking_parameters.accept) @@ -154,9 +171,9 @@ class Ui_picking_parameters(object): self.label_3.setToolTip(QtGui.QApplication.translate("picking_parameters", "Maximum permitted direct velocity (apparent velocity!).", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("picking_parameters", "vmax [m/s] [?]", None, QtGui.QApplication.UnicodeUTF8)) self.lineEdit_vmax.setText(QtGui.QApplication.translate("picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setToolTip(QtGui.QApplication.translate("picking_parameters", "Value between 0 and 1 for threshold picking algorithm (Default = 0.6).", None, QtGui.QApplication.UnicodeUTF8)) + self.label_4.setToolTip(QtGui.QApplication.translate("picking_parameters", "Value between 0 and 1 for threshold picking algorithm (Default = 60%).", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("picking_parameters", "Fraction of local maximum [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_folm.setText(QtGui.QApplication.translate("picking_parameters", "0.6", None, QtGui.QApplication.UnicodeUTF8)) + self.label_folm.setText(QtGui.QApplication.translate("picking_parameters", "60 %", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setToolTip(QtGui.QApplication.translate("picking_parameters", "Use additional Akaike Information Criterion for picking.", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("picking_parameters", "AIC [?]", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setToolTip(QtGui.QApplication.translate("picking_parameters", "Samples before and after initial HOS pick.", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 5ba2fd8d..08766f1c 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -28,6 +28,7 @@ class SeisArray(object): self._init_interpolatable() elif interpolatable == False: self._init_normal() + self.set2D() def _init_normal(self): self.interpolatable = False @@ -84,6 +85,27 @@ class SeisArray(object): gphoneNum = float(line.split()[2]) self._geophoneNumbers[traceID] = gphoneNum + def check2D(self): + x, y, z = self.getAllMeasuredPointsLists() + if self._check0(x) or self._check0(y): + return True + else: + return False + + def set2D(self): + if self.check2D(): + self.twoDim = True + else: + self.twoDim = False + + def _check0(self, lst): + for element in lst: + if element == 0: + pass + else: + return False + return True + def _getReceiverlines(self): return self._receiverlines @@ -816,7 +838,7 @@ class SeisArray(object): print "Exported coordinates for %s traces to file > %s" % (count, filename) recfile_out.close() - def plotArray2D(self, ax = None, plot_topo=False, highlight_measured=False, annotations=True, pointsize=10): + def plotArray2D(self, ax = None, plot_topo=False, highlight_measured=False, annotations=True, pointsize=10, twoDim = False): import matplotlib.pyplot as plt if ax == None: plt.interactive(True) @@ -843,7 +865,8 @@ class SeisArray(object): ax.set_xlabel('X [m]') ax.set_ylabel('Y [m]') - ax.set_aspect('equal') + if twoDim == False: + ax.set_aspect('equal') ax.legend(prop={'size':7}) if annotations == True: for traceID in self.getReceiverCoordinates().keys(): @@ -887,7 +910,7 @@ class SeisArray(object): return ax - def plotSurface3D(self, ax=None, step=0.5, method='linear', exag=False): + def plotSurface3D(self, ax=None, step=0.5, method='linear', exag=False, twoDim = False): from matplotlib import cm import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D @@ -917,7 +940,8 @@ class SeisArray(object): if exag == False: ax.set_zlim(-(max(x) - min(x) / 2), (max(x) - min(x) / 2)) - ax.set_aspect('equal') + if twoDim == False: + ax.set_aspect('equal') ax.set_xlabel('X [m]'); ax.set_ylabel('Y [m]'); diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py index bcff2f70..dcf31a2a 100644 --- a/pylot/core/active/surveyUtils.py +++ b/pylot/core/active/surveyUtils.py @@ -155,7 +155,7 @@ def cleanUp(survey): for shot in survey.data.values(): shot.traces4plot = {} -def plotScatterStats4Shots(survey, variable, ax = None): +def plotScatterStats4Shots(survey, variable, ax = None, twoDim = False): """ Statistics, scatter plot. @@ -210,7 +210,8 @@ def plotScatterStats4Shots(survey, variable, ax = None): transform=ax.transAxes) ax.set_xlabel('X [m]') ax.set_ylabel('Y [m]') - ax.set_aspect('equal') + if not twoDim: + ax.set_aspect('equal') cbar = ax.figure.colorbar(sc) cbar.set_label(variable) @@ -219,7 +220,7 @@ def plotScatterStats4Shots(survey, variable, ax = None): fontsize='x-small', color='k') -def plotScatterStats4Receivers(survey, variable, ax = None): +def plotScatterStats4Receivers(survey, variable, ax = None, twoDim = False): """ Statistics, scatter plot. @@ -274,7 +275,8 @@ def plotScatterStats4Receivers(survey, variable, ax = None): transform=ax.transAxes) ax.set_xlabel('X [m]') ax.set_ylabel('Y [m]') - ax.set_aspect('equal') + if not twoDim: + ax.set_aspect('equal') cbar = ax.figure.colorbar(sc) cbar.set_label(variable) diff --git a/pylot/core/active/vtk_tools_layout.py b/pylot/core/active/vtk_tools_layout.py index 2c350756..4aa06778 100644 --- a/pylot/core/active/vtk_tools_layout.py +++ b/pylot/core/active/vtk_tools_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'vtk_tools_layout.ui' # -# Created: Thu Jul 7 14:25:26 2016 +# Created: Tue Jul 12 14:03:30 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! From ed85d0ef12a20571300145e43be52592afd311e5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 12 Jul 2016 14:39:28 +0200 Subject: [PATCH 0996/1144] added minimum cushion for 2D case (for input file generation) --- pylot/core/active/ActiveSeismoPick3D_GUI.py | 2 ++ pylot/core/active/seismicArrayPreparation.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index 67fb9e68..01449aec 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -424,6 +424,8 @@ class gui_control(object): cwd = os.getcwd() interpolationMethod = 'linear' os.chdir(simuldir) + if self.seisarray.twoDim: + interpolationMethod = 'nearest' self.survey.seisarray.generateFMTOMOinputFromArray(propgrid, vgrid, (bbot, btop), cushionfactor, interpolationMethod, customgrid = customgrid, writeVTK = False) diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py index 08766f1c..b43fdd69 100644 --- a/pylot/core/active/seismicArrayPreparation.py +++ b/pylot/core/active/seismicArrayPreparation.py @@ -440,6 +440,7 @@ class SeisArray(object): for phi in phiGrid: xval = self._getDistance(phi) yval = self._getDistance(theta) + z = griddata((measured_x, measured_y), measured_z, (xval, yval), method=method) # in case the point lies outside, nan will be returned. Find nearest: if np.isnan(z) == True: @@ -601,6 +602,11 @@ class SeisArray(object): theta_min, theta_max = (self._getAngle(min(y)), self._getAngle(max(y))) cushionPhi = abs(phi_max - phi_min) * cushionfactor cushionTheta = abs(theta_max - theta_min) * cushionfactor + # 2D Case only: + if cushionPhi == 0.: + cushionPhi = 0.05 + if cushionTheta == 0.: + cushionTheta = 0.05 phiWE = (phi_min - cushionPhi, phi_max + cushionPhi) thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) return thetaSN, phiWE From 39184ef1506f9f72598b70045f9ee9d976171047 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 12 Jul 2016 15:31:59 +0200 Subject: [PATCH 0997/1144] [change] new concept for evaluation of the pdf instead of holding a sampled array works now also for difference pdfs using a scipy's spline interpolation capabilities --- pylot/core/util/pdf.py | 47 +++++++++++++++++++++++++--------------- pylot/core/util/utils.py | 12 ++++++++++ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 9518ba0f..dfc11ac6 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -3,9 +3,8 @@ import warnings import numpy as np -import scipy.optimize from obspy import UTCDateTime -from pylot.core.util.utils import find_nearest, clims +from pylot.core.util.utils import fit_curve, find_nearest, clims from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() @@ -57,7 +56,7 @@ def exp_parameter(te, tm, tl, eta): return tm, sig1, sig2, a -def gauss_branches(k, mu, sig1, sig2, a1, a2): +def gauss_branches(k, (mu, sig1, sig2, a1, a2)): ''' function gauss_branches takes an axes x, a center value mu, two sigma values sig1 and sig2 and two scaling factors a1 and a2 and return a @@ -91,7 +90,7 @@ def gauss_branches(k, mu, sig1, sig2, a1, a2): return _func(k, mu, sig1, sig2, a1, a2) -def exp_branches(k, mu, sig1, sig2, a): +def exp_branches(k, (mu, sig1, sig2, a)): ''' function exp_branches takes an axes x, a center value mu, two sigma values sig1 and sig2 and a scaling factor a and return a @@ -130,17 +129,22 @@ class ProbabilityDensityFunction(object): version = __version__ - def __init__(self, x0, incr, npts, pdf, mu, params): + def __init__(self, x0, incr, npts, pdf, mu, params, eta=0.01): self.x0 = x0 self.incr = incr self.npts = npts self.axis = create_axis(x0, incr, npts) self.mu = mu + self.eta = eta self._pdf = pdf self.params = params def __add__(self, other): + assert self.eta == other.eta, 'decline factors differ please use equally defined pdfs for comparison' + + eta = self.eta + x0, incr, npts = self.commonparameter(other) axis = create_axis(x0, incr, npts) @@ -153,16 +157,19 @@ class ProbabilityDensityFunction(object): npts = pdf.size x0 *= 2 axis = create_axis(x0, incr, npts) + mu = axis[np.where(pdf == max(pdf))][0] - params, pcov = scipy.optimize.curve_fit(branches['gauss'], axis, pdf) + func, params = fit_curve(axis, pdf) - mu = axis[np.where(pdf == max(pdf))] - - return ProbabilityDensityFunction(x0, incr, npts, branches['gauss'], mu, - params) + return ProbabilityDensityFunction(x0, incr, npts, func, mu, + params, eta) def __sub__(self, other): + assert self.eta == other.eta, 'decline factors differ please use equally defined pdfs for comparison' + + eta = self.eta + x0, incr, npts = self.commonparameter(other) axis = create_axis(x0, incr, npts) @@ -176,16 +183,12 @@ class ProbabilityDensityFunction(object): midpoint = npts / 2 x0 = -incr * midpoint axis = create_axis(x0, incr, npts) - mu = axis[np.where(pdf == max(pdf))][0] - bounds = ([mu, 0., 0., 0., 0.],[mu, np.inf, np.inf, np.inf, np.inf]) - - params, pcov = scipy.optimize.curve_fit(branches['gauss'], axis, pdf, - bounds=bounds) + func, params = fit_curve(axis, pdf) return ProbabilityDensityFunction(x0, incr, npts, branches['gauss'], mu, - params) + params, eta) def __nonzero__(self): prec = self.precision(self.incr) @@ -204,7 +207,15 @@ class ProbabilityDensityFunction(object): return prec if prec >= 0 else 0 def data(self, value): - return self._pdf(value, *self.params) + return self._pdf(value, self.params) + + @property + def eta(self): + return self._eta + + @eta.setter + def eta(self, value): + self._eta = value @property def mu(self): @@ -271,7 +282,7 @@ class ProbabilityDensityFunction(object): # return the object return ProbabilityDensityFunction(x0, incr, npts, pdf, barycentre, - params) + params, decfact) def broadcast(self, pdf, si, ei, data): try: diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 521c548b..b24efb4d 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -3,6 +3,7 @@ import hashlib import numpy as np +from scipy.interpolate import splrep, splev import os import pwd import re @@ -16,6 +17,17 @@ def _pickle_method(m): else: return getattr, (m.im_self, m.im_func.func_name) +def fit_curve(x, y): + + return splev, splrep(x, y) + +def getindexbounds(f, eta): + mi = f.argmax() + m = max(f) + b = m * eta + l = find_nearest(f[:mi], b) + u = find_nearest(f[mi:], b) + mi + return mi, l, u def worker(func, input, cores='max', async=False): import multiprocessing From 8cef8ff2db94c9d3336c1bf5648483e591852847 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 12 Jul 2016 16:19:27 +0200 Subject: [PATCH 0998/1144] [bugfix] due to changes in the usage of ProbabiltyDensityFunction.data expectation and standard deviation were wrongly calculated --- pylot/core/util/pdf.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index dfc11ac6..e0e3f078 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -187,7 +187,7 @@ class ProbabilityDensityFunction(object): func, params = fit_curve(axis, pdf) - return ProbabilityDensityFunction(x0, incr, npts, branches['gauss'], mu, + return ProbabilityDensityFunction(x0, incr, npts, func, mu, params, eta) def __nonzero__(self): @@ -303,16 +303,15 @@ class ProbabilityDensityFunction(object): ''' rval = 0 - axis = self.axis - self.x0 - for n, x in enumerate(axis): - rval += x * self.data(n) - return rval * self.incr + self.x0 + for x in self.axis: + rval += x * self.data(x) + return rval * self.incr def standard_deviation(self): mu = self.mu rval = 0 - for n, x in enumerate(self.axis): - rval += (x - mu) ** 2 * self.data(n) + for x in self.axis: + rval += (x - mu) ** 2 * self.data(x) return rval * self.incr def prob_lt_val(self, value): From e3100562da3ed3ccce1bdbfb9f1eaf1f845a9a69 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 13 Jul 2016 10:04:13 +0200 Subject: [PATCH 0999/1144] pseudo 2D model generation working after some fixes (+2 cushion for propgrid) --- pylot/core/active/asp3d_layout.py | 2 +- pylot/core/active/fmtomo_parameters_layout.py | 75 ++++++++++--------- .../core/active/generate_seisarray_layout.py | 2 +- pylot/core/active/generate_survey_layout.py | 2 +- .../active/generate_survey_layout_minimal.py | 2 +- .../core/active/picking_parameters_layout.py | 4 +- pylot/core/active/seismicArrayPreparation.py | 9 ++- pylot/core/active/vtk_tools_layout.py | 2 +- 8 files changed, 55 insertions(+), 43 deletions(-) diff --git a/pylot/core/active/asp3d_layout.py b/pylot/core/active/asp3d_layout.py index 96ac98b9..3392a068 100644 --- a/pylot/core/active/asp3d_layout.py +++ b/pylot/core/active/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Tue Jul 12 14:03:29 2016 +# Created: Tue Jul 12 21:41:12 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! diff --git a/pylot/core/active/fmtomo_parameters_layout.py b/pylot/core/active/fmtomo_parameters_layout.py index 4689a38c..04f4ad60 100644 --- a/pylot/core/active/fmtomo_parameters_layout.py +++ b/pylot/core/active/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Tue Jul 12 14:03:29 2016 +# Created: Tue Jul 12 21:41:12 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -105,8 +105,10 @@ class Ui_fmtomo_parameters(object): self.label_13 = QtGui.QLabel(fmtomo_parameters) self.label_13.setObjectName("label_13") self.horizontalLayout.addWidget(self.label_13) - self.cushion = QtGui.QLineEdit(fmtomo_parameters) - self.cushion.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.cushion = QtGui.QSpinBox(fmtomo_parameters) + self.cushion.setMinimum(2) + self.cushion.setMaximum(100) + self.cushion.setProperty("value", 10) self.cushion.setObjectName("cushion") self.horizontalLayout.addWidget(self.cushion) self.label_12 = QtGui.QLabel(fmtomo_parameters) @@ -159,34 +161,46 @@ class Ui_fmtomo_parameters(object): self.label_7.setAlignment(QtCore.Qt.AlignCenter) self.label_7.setObjectName("label_7") self.gridLayout.addWidget(self.label_7, 0, 3, 1, 1) - self.label_8 = QtGui.QLabel(fmtomo_parameters) - self.label_8.setObjectName("label_8") - self.gridLayout.addWidget(self.label_8, 1, 0, 1, 1) - self.pgrid_x = QtGui.QLineEdit(fmtomo_parameters) - self.pgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_x.setObjectName("pgrid_x") - self.gridLayout.addWidget(self.pgrid_x, 1, 1, 1, 1) - self.pgrid_y = QtGui.QLineEdit(fmtomo_parameters) - self.pgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_y.setObjectName("pgrid_y") - self.gridLayout.addWidget(self.pgrid_y, 1, 2, 1, 1) - self.pgrid_z = QtGui.QLineEdit(fmtomo_parameters) - self.pgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.pgrid_z.setObjectName("pgrid_z") - self.gridLayout.addWidget(self.pgrid_z, 1, 3, 1, 1) self.label_9 = QtGui.QLabel(fmtomo_parameters) self.label_9.setObjectName("label_9") self.gridLayout.addWidget(self.label_9, 2, 0, 1, 1) - self.invgrid_x = QtGui.QLineEdit(fmtomo_parameters) - self.invgrid_x.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.pgrid_x = QtGui.QSpinBox(fmtomo_parameters) + self.pgrid_x.setMinimum(2) + self.pgrid_x.setMaximum(10000) + self.pgrid_x.setProperty("value", 100) + self.pgrid_x.setObjectName("pgrid_x") + self.gridLayout.addWidget(self.pgrid_x, 1, 1, 1, 1) + self.pgrid_y = QtGui.QSpinBox(fmtomo_parameters) + self.pgrid_y.setMinimum(2) + self.pgrid_y.setMaximum(10000) + self.pgrid_y.setProperty("value", 100) + self.pgrid_y.setObjectName("pgrid_y") + self.gridLayout.addWidget(self.pgrid_y, 1, 2, 1, 1) + self.pgrid_z = QtGui.QSpinBox(fmtomo_parameters) + self.pgrid_z.setMinimum(2) + self.pgrid_z.setMaximum(10000) + self.pgrid_z.setProperty("value", 120) + self.pgrid_z.setObjectName("pgrid_z") + self.gridLayout.addWidget(self.pgrid_z, 1, 3, 1, 1) + self.label_8 = QtGui.QLabel(fmtomo_parameters) + self.label_8.setObjectName("label_8") + self.gridLayout.addWidget(self.label_8, 1, 0, 1, 1) + self.invgrid_x = QtGui.QSpinBox(fmtomo_parameters) + self.invgrid_x.setMinimum(2) + self.invgrid_x.setMaximum(10000) + self.invgrid_x.setProperty("value", 60) self.invgrid_x.setObjectName("invgrid_x") self.gridLayout.addWidget(self.invgrid_x, 2, 1, 1, 1) - self.invgrid_y = QtGui.QLineEdit(fmtomo_parameters) - self.invgrid_y.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_y = QtGui.QSpinBox(fmtomo_parameters) + self.invgrid_y.setMinimum(2) + self.invgrid_y.setMaximum(10000) + self.invgrid_y.setProperty("value", 60) self.invgrid_y.setObjectName("invgrid_y") self.gridLayout.addWidget(self.invgrid_y, 2, 2, 1, 1) - self.invgrid_z = QtGui.QLineEdit(fmtomo_parameters) - self.invgrid_z.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.invgrid_z = QtGui.QSpinBox(fmtomo_parameters) + self.invgrid_z.setMinimum(2) + self.invgrid_z.setMaximum(10000) + self.invgrid_z.setProperty("value", 80) self.invgrid_z.setObjectName("invgrid_z") self.gridLayout.addWidget(self.invgrid_z, 2, 3, 1, 1) self.verticalLayout.addLayout(self.gridLayout) @@ -255,24 +269,17 @@ class Ui_fmtomo_parameters(object): self.label_13.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Extension of the model around the maximum values of the receiver grid (X and Y).\n" "Too low values will cause error.", None, QtGui.QApplication.UnicodeUTF8)) self.label_13.setText(QtGui.QApplication.translate("fmtomo_parameters", "Cushion factor [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.cushion.setText(QtGui.QApplication.translate("fmtomo_parameters", "10", None, QtGui.QApplication.UnicodeUTF8)) self.label_12.setText(QtGui.QApplication.translate("fmtomo_parameters", "%", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("fmtomo_parameters", "Number of Ponts", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("fmtomo_parameters", "X", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("fmtomo_parameters", "Y", None, QtGui.QApplication.UnicodeUTF8)) self.label_7.setText(QtGui.QApplication.translate("fmtomo_parameters", "Z", None, QtGui.QApplication.UnicodeUTF8)) - self.label_8.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Number of points for forward simulation in X, Y, Z.\n" -"Must be higher than number of inversion grid points.", None, QtGui.QApplication.UnicodeUTF8)) - self.label_8.setText(QtGui.QApplication.translate("fmtomo_parameters", "Propagation Grid [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_x.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_y.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.pgrid_z.setText(QtGui.QApplication.translate("fmtomo_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) self.label_9.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Number of inversion grid (velocity grid) points in X, Y, Z.\n" "Must be lower than for propagation grid.", None, QtGui.QApplication.UnicodeUTF8)) self.label_9.setText(QtGui.QApplication.translate("fmtomo_parameters", "Inversion Grid [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_x.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_y.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) - self.invgrid_z.setText(QtGui.QApplication.translate("fmtomo_parameters", "50", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Number of points for forward simulation in X, Y, Z.\n" +"Must be higher than number of inversion grid points.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setText(QtGui.QApplication.translate("fmtomo_parameters", "Propagation Grid [?]", None, QtGui.QApplication.UnicodeUTF8)) self.label_15.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "\n" "\n" -"

Specifiy simple input velocity file from which linear gradients will be evaluated.

\n" -"

\n" -"

For example:

\n" -"

\n" -"

First gradient: Surface velocity: 0.5 km/s rising to 1.0 km/s at a depth of 5 m below the surface.

\n" -"

Second gradient: Continuous change to another gradient from 1.0 km/s to 4.0 km/s at 60 m depth below the surface.

\n" -"

\n" -"

0 0.5

\n" -"

-5 1.0

\n" -"

-5 1.0

\n" -"

-60 4.0

\n" -"

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_15.setText(QtGui.QApplication.translate("fmtomo_parameters", "Custom velocity\n" -"grid (earthmodel) [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_customgrid.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_16.setToolTip(QtGui.QApplication.translate("fmtomo_parameters", "Specifiy directory for FMTOMO simulation.", None, QtGui.QApplication.UnicodeUTF8)) - self.label_16.setText(QtGui.QApplication.translate("fmtomo_parameters", "Simulation\n" -"Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.browse_simuldir.setText(QtGui.QApplication.translate("fmtomo_parameters", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/generate_seisarray_layout.py b/pylot/core/active/gui/generate_seisarray_layout.py deleted file mode 100644 index e22b7488..00000000 --- a/pylot/core/active/gui/generate_seisarray_layout.py +++ /dev/null @@ -1,175 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'generate_seisarray_layout.ui' -# -# Created: Mon Aug 29 10:26:23 2016 -# by: pyside-uic 0.2.15 running on PySide 1.2.2 -# -# WARNING! All changes made in this file will be lost! - -from PySide import QtCore, QtGui - -class Ui_generate_seisarray(object): - def setupUi(self, generate_seisarray): - generate_seisarray.setObjectName("generate_seisarray") - generate_seisarray.resize(403, 221) - generate_seisarray.setMinimumSize(QtCore.QSize(400, 220)) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) - generate_seisarray.setWindowIcon(icon) - self.verticalLayout_5 = QtGui.QVBoxLayout(generate_seisarray) - self.verticalLayout_5.setObjectName("verticalLayout_5") - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.verticalLayout_4 = QtGui.QVBoxLayout() - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.label_rec = QtGui.QLabel(generate_seisarray) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_rec.sizePolicy().hasHeightForWidth()) - self.label_rec.setSizePolicy(sizePolicy) - self.label_rec.setToolTip("") - self.label_rec.setObjectName("label_rec") - self.horizontalLayout.addWidget(self.label_rec) - spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.radioButton_normal = QtGui.QRadioButton(generate_seisarray) - self.radioButton_normal.setChecked(True) - self.radioButton_normal.setObjectName("radioButton_normal") - self.horizontalLayout.addWidget(self.radioButton_normal) - self.radioButton_interpolatable = QtGui.QRadioButton(generate_seisarray) - self.radioButton_interpolatable.setObjectName("radioButton_interpolatable") - self.horizontalLayout.addWidget(self.radioButton_interpolatable) - self.verticalLayout_4.addLayout(self.horizontalLayout) - self.horizontalLayout_4 = QtGui.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.lineEdit_rec = QtGui.QLineEdit(generate_seisarray) - self.lineEdit_rec.setObjectName("lineEdit_rec") - self.horizontalLayout_4.addWidget(self.lineEdit_rec) - self.pushButton_rec = QtGui.QPushButton(generate_seisarray) - self.pushButton_rec.setObjectName("pushButton_rec") - self.horizontalLayout_4.addWidget(self.pushButton_rec) - self.verticalLayout_4.addLayout(self.horizontalLayout_4) - self.verticalLayout.addLayout(self.verticalLayout_4) - self.verticalLayout_2 = QtGui.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label_src = QtGui.QLabel(generate_seisarray) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_src.sizePolicy().hasHeightForWidth()) - self.label_src.setSizePolicy(sizePolicy) - self.label_src.setObjectName("label_src") - self.verticalLayout_2.addWidget(self.label_src) - self.horizontalLayout_5 = QtGui.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.lineEdit_src = QtGui.QLineEdit(generate_seisarray) - self.lineEdit_src.setObjectName("lineEdit_src") - self.horizontalLayout_5.addWidget(self.lineEdit_src) - self.pushButton_src = QtGui.QPushButton(generate_seisarray) - self.pushButton_src.setObjectName("pushButton_src") - self.horizontalLayout_5.addWidget(self.pushButton_src) - self.verticalLayout_2.addLayout(self.horizontalLayout_5) - self.verticalLayout.addLayout(self.verticalLayout_2) - self.verticalLayout_3 = QtGui.QVBoxLayout() - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.label_obs = QtGui.QLabel(generate_seisarray) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_obs.sizePolicy().hasHeightForWidth()) - self.label_obs.setSizePolicy(sizePolicy) - self.label_obs.setObjectName("label_obs") - self.verticalLayout_3.addWidget(self.label_obs) - self.horizontalLayout_6 = QtGui.QHBoxLayout() - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.lineEdit_pts = QtGui.QLineEdit(generate_seisarray) - self.lineEdit_pts.setObjectName("lineEdit_pts") - self.horizontalLayout_6.addWidget(self.lineEdit_pts) - self.pushButton_obs = QtGui.QPushButton(generate_seisarray) - self.pushButton_obs.setObjectName("pushButton_obs") - self.horizontalLayout_6.addWidget(self.pushButton_obs) - self.verticalLayout_3.addLayout(self.horizontalLayout_6) - self.verticalLayout.addLayout(self.verticalLayout_3) - self.verticalLayout_5.addLayout(self.verticalLayout) - self.buttonBox = QtGui.QDialogButtonBox(generate_seisarray) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_5.addWidget(self.buttonBox) - - self.retranslateUi(generate_seisarray) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_seisarray.reject) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), generate_seisarray.accept) - QtCore.QMetaObject.connectSlotsByName(generate_seisarray) - - def retranslateUi(self, generate_seisarray): - generate_seisarray.setWindowTitle(QtGui.QApplication.translate("generate_seisarray", "Generate new Seismic Array", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Receivers", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_normal.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" -"\n" -"

Load normal measured receiver input file. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Geophone with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" -"

\n" -"

50 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_normal.setText(QtGui.QApplication.translate("generate_seisarray", "normal [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_interpolatable.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" -"\n" -"

Load measured receiver input file that can be interpolated. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[trace ID (int)] [receiver line ID (int)] [number of the geophone on receiver line (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Third geophone on the second receiver line with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" -"

\n" -"

50 2 3 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_interpolatable.setText(QtGui.QApplication.translate("generate_seisarray", "interpolation [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_rec.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" -"\n" -"

Load measured sources input file to improve interpolation precision. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[source ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Shot number 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" -"

\n" -"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setText(QtGui.QApplication.translate("generate_seisarray", "Measured Sources (optional) [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_src.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setToolTip(QtGui.QApplication.translate("generate_seisarray", "\n" -"\n" -"

Load measured points input file to improve interpolation precision. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[point ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Point number 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" -"

\n" -"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Additional measured points (optional) [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_obs.setText(QtGui.QApplication.translate("generate_seisarray", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/generate_survey_layout.py b/pylot/core/active/gui/generate_survey_layout.py deleted file mode 100644 index 8afada23..00000000 --- a/pylot/core/active/gui/generate_survey_layout.py +++ /dev/null @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'generate_survey_layout.ui' -# -# Created: Mon Aug 29 10:26:23 2016 -# by: pyside-uic 0.2.15 running on PySide 1.2.2 -# -# WARNING! All changes made in this file will be lost! - -from PySide import QtCore, QtGui - -class Ui_generate_survey(object): - def setupUi(self, generate_survey): - generate_survey.setObjectName("generate_survey") - generate_survey.resize(382, 220) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) - generate_survey.setWindowIcon(icon) - self.verticalLayout_2 = QtGui.QVBoxLayout(generate_survey) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.gridLayout = QtGui.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.lineEdit_rec = QtGui.QLineEdit(generate_survey) - self.lineEdit_rec.setObjectName("lineEdit_rec") - self.gridLayout.addWidget(self.lineEdit_rec, 0, 1, 1, 1) - self.pushButton_rec = QtGui.QPushButton(generate_survey) - self.pushButton_rec.setObjectName("pushButton_rec") - self.gridLayout.addWidget(self.pushButton_rec, 0, 2, 1, 1) - self.label_rec = QtGui.QLabel(generate_survey) - self.label_rec.setObjectName("label_rec") - self.gridLayout.addWidget(self.label_rec, 0, 0, 1, 1) - self.lineEdit_obs = QtGui.QLineEdit(generate_survey) - self.lineEdit_obs.setObjectName("lineEdit_obs") - self.gridLayout.addWidget(self.lineEdit_obs, 2, 1, 1, 1) - self.label_obs = QtGui.QLabel(generate_survey) - self.label_obs.setObjectName("label_obs") - self.gridLayout.addWidget(self.label_obs, 2, 0, 1, 1) - self.pushButton_obs = QtGui.QPushButton(generate_survey) - self.pushButton_obs.setObjectName("pushButton_obs") - self.gridLayout.addWidget(self.pushButton_obs, 2, 2, 1, 1) - self.label_src = QtGui.QLabel(generate_survey) - self.label_src.setObjectName("label_src") - self.gridLayout.addWidget(self.label_src, 1, 0, 1, 1) - self.lineEdit_src = QtGui.QLineEdit(generate_survey) - self.lineEdit_src.setObjectName("lineEdit_src") - self.gridLayout.addWidget(self.lineEdit_src, 1, 1, 1, 1) - self.pushButton_src = QtGui.QPushButton(generate_survey) - self.pushButton_src.setObjectName("pushButton_src") - self.gridLayout.addWidget(self.pushButton_src, 1, 2, 1, 1) - self.verticalLayout_2.addLayout(self.gridLayout) - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.line_2 = QtGui.QFrame(generate_survey) - self.line_2.setFrameShape(QtGui.QFrame.HLine) - self.line_2.setFrameShadow(QtGui.QFrame.Sunken) - self.line_2.setObjectName("line_2") - self.verticalLayout.addWidget(self.line_2) - self.label = QtGui.QLabel(generate_survey) - self.label.setObjectName("label") - self.verticalLayout.addWidget(self.label) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.fstart = QtGui.QLineEdit(generate_survey) - self.fstart.setObjectName("fstart") - self.horizontalLayout.addWidget(self.fstart) - self.label_obs_2 = QtGui.QLabel(generate_survey) - self.label_obs_2.setObjectName("label_obs_2") - self.horizontalLayout.addWidget(self.label_obs_2) - self.fend = QtGui.QLineEdit(generate_survey) - self.fend.setObjectName("fend") - self.horizontalLayout.addWidget(self.fend) - self.verticalLayout.addLayout(self.horizontalLayout) - self.verticalLayout_2.addLayout(self.verticalLayout) - self.buttonBox = QtGui.QDialogButtonBox(generate_survey) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_2.addWidget(self.buttonBox) - - self.retranslateUi(generate_survey) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), generate_survey.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_survey.reject) - QtCore.QMetaObject.connectSlotsByName(generate_survey) - - def retranslateUi(self, generate_survey): - generate_survey.setWindowTitle(QtGui.QApplication.translate("generate_survey", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_rec.setText(QtGui.QApplication.translate("generate_survey", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setToolTip(QtGui.QApplication.translate("generate_survey", "\n" -"\n" -"

Load receiver input file. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Trace ID 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" -"

\n" -"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setText(QtGui.QApplication.translate("generate_survey", "Receiver\n" -"File [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setToolTip(QtGui.QApplication.translate("generate_survey", "\n" -"\n" -"

Specifiy directory containing seismograms for each shot.

\n" -"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" -"

\n" -"

For example:

\n" -"

\n" -"

Shot number 100 containing seismograms for all traces with the name:

\n" -"

\n" -"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setText(QtGui.QApplication.translate("generate_survey", "Seismogram\n" -"Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_obs.setText(QtGui.QApplication.translate("generate_survey", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setToolTip(QtGui.QApplication.translate("generate_survey", "\n" -"\n" -"

Load sources input file. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Source number 100 with the coordinates (12.3 [m], 100.5 [m], 20.3 [m]).

\n" -"

\n" -"

100 12.3 100.5 20.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setText(QtGui.QApplication.translate("generate_survey", "Source\n" -"File [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_src.setText(QtGui.QApplication.translate("generate_survey", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("generate_survey", "File structure:", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs_2.setToolTip(QtGui.QApplication.translate("generate_survey", "\n" -"\n" -"

Specifiy directory containing seismograms for each shot.

\n" -"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" -"

\n" -"

For example:

\n" -"

\n" -"

Shot number 100 containing seismograms for all traces with the name:

\n" -"

\n" -"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs_2.setText(QtGui.QApplication.translate("generate_survey", "*Shotnumber*", None, QtGui.QApplication.UnicodeUTF8)) - self.fend.setText(QtGui.QApplication.translate("generate_survey", ".dat", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/generate_survey_layout_minimal.py b/pylot/core/active/gui/generate_survey_layout_minimal.py deleted file mode 100644 index c33302be..00000000 --- a/pylot/core/active/gui/generate_survey_layout_minimal.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'generate_survey_layout_minimal.ui' -# -# Created: Mon Aug 29 10:26:23 2016 -# by: pyside-uic 0.2.15 running on PySide 1.2.2 -# -# WARNING! All changes made in this file will be lost! - -from PySide import QtCore, QtGui - -class Ui_generate_survey_minimal(object): - def setupUi(self, generate_survey_minimal): - generate_survey_minimal.setObjectName("generate_survey_minimal") - generate_survey_minimal.resize(382, 139) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap("../asp3d_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) - generate_survey_minimal.setWindowIcon(icon) - self.verticalLayout_2 = QtGui.QVBoxLayout(generate_survey_minimal) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.gridLayout = QtGui.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.lineEdit_obs = QtGui.QLineEdit(generate_survey_minimal) - self.lineEdit_obs.setObjectName("lineEdit_obs") - self.gridLayout.addWidget(self.lineEdit_obs, 0, 1, 1, 1) - self.label_obs = QtGui.QLabel(generate_survey_minimal) - self.label_obs.setObjectName("label_obs") - self.gridLayout.addWidget(self.label_obs, 0, 0, 1, 1) - self.pushButton_obs = QtGui.QPushButton(generate_survey_minimal) - self.pushButton_obs.setObjectName("pushButton_obs") - self.gridLayout.addWidget(self.pushButton_obs, 0, 2, 1, 1) - self.verticalLayout_2.addLayout(self.gridLayout) - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.label = QtGui.QLabel(generate_survey_minimal) - self.label.setObjectName("label") - self.verticalLayout.addWidget(self.label) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.fstart = QtGui.QLineEdit(generate_survey_minimal) - self.fstart.setObjectName("fstart") - self.horizontalLayout.addWidget(self.fstart) - self.label_obs_2 = QtGui.QLabel(generate_survey_minimal) - self.label_obs_2.setObjectName("label_obs_2") - self.horizontalLayout.addWidget(self.label_obs_2) - self.fend = QtGui.QLineEdit(generate_survey_minimal) - self.fend.setObjectName("fend") - self.horizontalLayout.addWidget(self.fend) - self.verticalLayout.addLayout(self.horizontalLayout) - self.verticalLayout_2.addLayout(self.verticalLayout) - self.buttonBox = QtGui.QDialogButtonBox(generate_survey_minimal) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_2.addWidget(self.buttonBox) - - self.retranslateUi(generate_survey_minimal) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), generate_survey_minimal.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), generate_survey_minimal.reject) - QtCore.QMetaObject.connectSlotsByName(generate_survey_minimal) - - def retranslateUi(self, generate_survey_minimal): - generate_survey_minimal.setWindowTitle(QtGui.QApplication.translate("generate_survey_minimal", "Generate new Survey", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setToolTip(QtGui.QApplication.translate("generate_survey_minimal", "\n" -"\n" -"

Specifiy directory containing seismograms for each shot.

\n" -"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" -"

\n" -"

For example:

\n" -"

\n" -"

Shot number 100 containing seismograms for all traces with the name:

\n" -"

\n" -"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs.setText(QtGui.QApplication.translate("generate_survey_minimal", "Seismogram\n" -"Directory [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_obs.setText(QtGui.QApplication.translate("generate_survey_minimal", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("generate_survey_minimal", "File structure:", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs_2.setToolTip(QtGui.QApplication.translate("generate_survey_minimal", "\n" -"\n" -"

Specifiy directory containing seismograms for each shot.

\n" -"

Currently in the format SEGY with each file named \'shotnumber*_pickle.dat\'.

\n" -"

\n" -"

For example:

\n" -"

\n" -"

Shot number 100 containing seismograms for all traces with the name:

\n" -"

\n" -"

100_pickle.dat

", None, QtGui.QApplication.UnicodeUTF8)) - self.label_obs_2.setText(QtGui.QApplication.translate("generate_survey_minimal", "*Shotnumber*", None, QtGui.QApplication.UnicodeUTF8)) - self.fend.setText(QtGui.QApplication.translate("generate_survey_minimal", ".dat", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/picking_parameters_layout.py b/pylot/core/active/gui/picking_parameters_layout.py deleted file mode 100644 index a6b2c3f2..00000000 --- a/pylot/core/active/gui/picking_parameters_layout.py +++ /dev/null @@ -1,277 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'picking_parameters_layout.ui' -# -# Created: Mon Aug 29 10:26:23 2016 -# by: pyside-uic 0.2.15 running on PySide 1.2.2 -# -# WARNING! All changes made in this file will be lost! - -from PySide import QtCore, QtGui - -class Ui_picking_parameters(object): - def setupUi(self, picking_parameters): - picking_parameters.setObjectName("picking_parameters") - picking_parameters.resize(422, 798) - picking_parameters.setMinimumSize(QtCore.QSize(0, 700)) - self.verticalLayout_4 = QtGui.QVBoxLayout(picking_parameters) - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.label_7 = QtGui.QLabel(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth()) - self.label_7.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setFamily("Sans Serif") - font.setWeight(75) - font.setBold(True) - self.label_7.setFont(font) - self.label_7.setObjectName("label_7") - self.verticalLayout_4.addWidget(self.label_7) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.label = QtGui.QLabel(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) - self.label.setSizePolicy(sizePolicy) - self.label.setObjectName("label") - self.horizontalLayout.addWidget(self.label) - self.ncores = QtGui.QSpinBox(picking_parameters) - self.ncores.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.ncores.setMinimum(1) - self.ncores.setMaximum(10000) - self.ncores.setObjectName("ncores") - self.horizontalLayout.addWidget(self.ncores) - self.verticalLayout_4.addLayout(self.horizontalLayout) - self.horizontalLayout_3 = QtGui.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.verticalLayout_2 = QtGui.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label_2 = QtGui.QLabel(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) - self.label_2.setSizePolicy(sizePolicy) - self.label_2.setObjectName("label_2") - self.verticalLayout_2.addWidget(self.label_2) - self.lineEdit_vmin = QtGui.QLineEdit(picking_parameters) - self.lineEdit_vmin.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.lineEdit_vmin.setObjectName("lineEdit_vmin") - self.verticalLayout_2.addWidget(self.lineEdit_vmin) - self.horizontalLayout_3.addLayout(self.verticalLayout_2) - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.label_3 = QtGui.QLabel(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) - self.label_3.setSizePolicy(sizePolicy) - self.label_3.setObjectName("label_3") - self.verticalLayout.addWidget(self.label_3) - self.lineEdit_vmax = QtGui.QLineEdit(picking_parameters) - self.lineEdit_vmax.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.lineEdit_vmax.setObjectName("lineEdit_vmax") - self.verticalLayout.addWidget(self.lineEdit_vmax) - self.horizontalLayout_3.addLayout(self.verticalLayout) - self.verticalLayout_4.addLayout(self.horizontalLayout_3) - self.horizontalLayout_4 = QtGui.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.label_4 = QtGui.QLabel(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth()) - self.label_4.setSizePolicy(sizePolicy) - self.label_4.setObjectName("label_4") - self.horizontalLayout_4.addWidget(self.label_4) - self.horizontalLayout_2 = QtGui.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.slider_folm = QtGui.QSlider(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.slider_folm.sizePolicy().hasHeightForWidth()) - self.slider_folm.setSizePolicy(sizePolicy) - self.slider_folm.setMinimum(1) - self.slider_folm.setMaximum(100) - self.slider_folm.setProperty("value", 60) - self.slider_folm.setOrientation(QtCore.Qt.Horizontal) - self.slider_folm.setTickPosition(QtGui.QSlider.TicksBelow) - self.slider_folm.setTickInterval(10) - self.slider_folm.setObjectName("slider_folm") - self.horizontalLayout_2.addWidget(self.slider_folm) - self.label_folm = QtGui.QLabel(picking_parameters) - self.label_folm.setAlignment(QtCore.Qt.AlignCenter) - self.label_folm.setObjectName("label_folm") - self.horizontalLayout_2.addWidget(self.label_folm) - self.horizontalLayout_4.addLayout(self.horizontalLayout_2) - self.verticalLayout_4.addLayout(self.horizontalLayout_4) - self.horizontalLayout_5 = QtGui.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.label_5 = QtGui.QLabel(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth()) - self.label_5.setSizePolicy(sizePolicy) - self.label_5.setObjectName("label_5") - self.horizontalLayout_5.addWidget(self.label_5) - self.checkBox_AIC = QtGui.QCheckBox(picking_parameters) - self.checkBox_AIC.setText("") - self.checkBox_AIC.setChecked(True) - self.checkBox_AIC.setObjectName("checkBox_AIC") - self.horizontalLayout_5.addWidget(self.checkBox_AIC) - self.verticalLayout_4.addLayout(self.horizontalLayout_5) - self.horizontalLayout_6 = QtGui.QHBoxLayout() - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.label_6 = QtGui.QLabel(picking_parameters) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth()) - self.label_6.setSizePolicy(sizePolicy) - self.label_6.setObjectName("label_6") - self.horizontalLayout_6.addWidget(self.label_6) - self.horizontalLayout_7 = QtGui.QHBoxLayout() - self.horizontalLayout_7.setObjectName("horizontalLayout_7") - self.lineEdit_aicleft = QtGui.QLineEdit(picking_parameters) - self.lineEdit_aicleft.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.lineEdit_aicleft.setObjectName("lineEdit_aicleft") - self.horizontalLayout_7.addWidget(self.lineEdit_aicleft) - self.lineEdit_aicright = QtGui.QLineEdit(picking_parameters) - self.lineEdit_aicright.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.lineEdit_aicright.setObjectName("lineEdit_aicright") - self.horizontalLayout_7.addWidget(self.lineEdit_aicright) - self.horizontalLayout_6.addLayout(self.horizontalLayout_7) - self.verticalLayout_4.addLayout(self.horizontalLayout_6) - self.verticalLayout_snr = QtGui.QVBoxLayout() - self.verticalLayout_snr.setObjectName("verticalLayout_snr") - self.line = QtGui.QFrame(picking_parameters) - self.line.setFrameShape(QtGui.QFrame.HLine) - self.line.setFrameShadow(QtGui.QFrame.Sunken) - self.line.setObjectName("line") - self.verticalLayout_snr.addWidget(self.line) - self.horizontalLayout_9 = QtGui.QHBoxLayout() - self.horizontalLayout_9.setObjectName("horizontalLayout_9") - self.label_8 = QtGui.QLabel(picking_parameters) - self.label_8.setObjectName("label_8") - self.horizontalLayout_9.addWidget(self.label_8) - self.radioButton_const = QtGui.QRadioButton(picking_parameters) - self.radioButton_const.setChecked(True) - self.radioButton_const.setObjectName("radioButton_const") - self.horizontalLayout_9.addWidget(self.radioButton_const) - self.radioButton_dyn = QtGui.QRadioButton(picking_parameters) - self.radioButton_dyn.setObjectName("radioButton_dyn") - self.horizontalLayout_9.addWidget(self.radioButton_dyn) - self.verticalLayout_snr.addLayout(self.horizontalLayout_9) - self.horizontalLayout_10 = QtGui.QHBoxLayout() - self.horizontalLayout_10.setObjectName("horizontalLayout_10") - self.label_13 = QtGui.QLabel(picking_parameters) - self.label_13.setObjectName("label_13") - self.horizontalLayout_10.addWidget(self.label_13) - self.doubleSpinBox_constSNR = QtGui.QDoubleSpinBox(picking_parameters) - self.doubleSpinBox_constSNR.setMaximumSize(QtCore.QSize(100, 16777215)) - self.doubleSpinBox_constSNR.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.doubleSpinBox_constSNR.setDecimals(1) - self.doubleSpinBox_constSNR.setMinimum(1.0) - self.doubleSpinBox_constSNR.setMaximum(10000.0) - self.doubleSpinBox_constSNR.setSingleStep(0.1) - self.doubleSpinBox_constSNR.setProperty("value", 8.0) - self.doubleSpinBox_constSNR.setObjectName("doubleSpinBox_constSNR") - self.horizontalLayout_10.addWidget(self.doubleSpinBox_constSNR) - self.verticalLayout_snr.addLayout(self.horizontalLayout_10) - self.gridLayout_2 = QtGui.QGridLayout() - self.gridLayout_2.setObjectName("gridLayout_2") - self.label_9 = QtGui.QLabel(picking_parameters) - self.label_9.setObjectName("label_9") - self.gridLayout_2.addWidget(self.label_9, 1, 0, 1, 1) - self.label_10 = QtGui.QLabel(picking_parameters) - self.label_10.setObjectName("label_10") - self.gridLayout_2.addWidget(self.label_10, 2, 0, 1, 1) - self.p1 = QtGui.QDoubleSpinBox(picking_parameters) - self.p1.setDecimals(6) - self.p1.setMinimum(-100.0) - self.p1.setMaximum(100.0) - self.p1.setSingleStep(0.0001) - self.p1.setProperty("value", 0.004) - self.p1.setObjectName("p1") - self.gridLayout_2.addWidget(self.p1, 2, 1, 1, 1) - self.label_11 = QtGui.QLabel(picking_parameters) - self.label_11.setObjectName("label_11") - self.gridLayout_2.addWidget(self.label_11, 1, 2, 1, 1) - self.label_12 = QtGui.QLabel(picking_parameters) - self.label_12.setObjectName("label_12") - self.gridLayout_2.addWidget(self.label_12, 2, 2, 1, 1) - self.p2 = QtGui.QDoubleSpinBox(picking_parameters) - self.p2.setDecimals(6) - self.p2.setMinimum(-100.0) - self.p2.setMaximum(100.0) - self.p2.setSingleStep(0.001) - self.p2.setProperty("value", -0.0007) - self.p2.setObjectName("p2") - self.gridLayout_2.addWidget(self.p2, 2, 3, 1, 1) - self.shift_dist = QtGui.QSpinBox(picking_parameters) - self.shift_dist.setMinimum(-10000) - self.shift_dist.setMaximum(10000) - self.shift_dist.setProperty("value", 30) - self.shift_dist.setObjectName("shift_dist") - self.gridLayout_2.addWidget(self.shift_dist, 1, 1, 1, 1) - self.shift_snr = QtGui.QSpinBox(picking_parameters) - self.shift_snr.setMinimum(-10000) - self.shift_snr.setMaximum(10000) - self.shift_snr.setProperty("value", 100) - self.shift_snr.setObjectName("shift_snr") - self.gridLayout_2.addWidget(self.shift_snr, 1, 3, 1, 1) - self.verticalLayout_snr.addLayout(self.gridLayout_2) - self.verticalLayout_4.addLayout(self.verticalLayout_snr) - spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.verticalLayout_4.addItem(spacerItem) - self.vlayout_plot = QtGui.QVBoxLayout() - self.vlayout_plot.setObjectName("vlayout_plot") - self.verticalLayout_4.addLayout(self.vlayout_plot) - self.buttonBox = QtGui.QDialogButtonBox(picking_parameters) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_4.addWidget(self.buttonBox) - - self.retranslateUi(picking_parameters) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), picking_parameters.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), picking_parameters.reject) - QtCore.QMetaObject.connectSlotsByName(picking_parameters) - - def retranslateUi(self, picking_parameters): - picking_parameters.setWindowTitle(QtGui.QApplication.translate("picking_parameters", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) - self.label_7.setText(QtGui.QApplication.translate("picking_parameters", "Choose parameters for FMTOMO simulation", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setToolTip(QtGui.QApplication.translate("picking_parameters", "Amount of CPU kernels used.", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("picking_parameters", "nproc [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setToolTip(QtGui.QApplication.translate("picking_parameters", "Minimum permitted direct velocity (apparent velocity!).", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("picking_parameters", "vmin [m/s] [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_vmin.setText(QtGui.QApplication.translate("picking_parameters", "100", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setToolTip(QtGui.QApplication.translate("picking_parameters", "Maximum permitted direct velocity (apparent velocity!).", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setText(QtGui.QApplication.translate("picking_parameters", "vmax [m/s] [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_vmax.setText(QtGui.QApplication.translate("picking_parameters", "5000", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setToolTip(QtGui.QApplication.translate("picking_parameters", "Value between 1 and 100 % for threshold picking algorithm (Default = 60%).", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setText(QtGui.QApplication.translate("picking_parameters", "Fraction of local maximum [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.label_folm.setText(QtGui.QApplication.translate("picking_parameters", "60 %", None, QtGui.QApplication.UnicodeUTF8)) - self.label_5.setToolTip(QtGui.QApplication.translate("picking_parameters", "Use additional Akaike Information Criterion for picking.", None, QtGui.QApplication.UnicodeUTF8)) - self.label_5.setText(QtGui.QApplication.translate("picking_parameters", "AIC [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.label_6.setToolTip(QtGui.QApplication.translate("picking_parameters", "Samples before and after initial HOS pick.", None, QtGui.QApplication.UnicodeUTF8)) - self.label_6.setText(QtGui.QApplication.translate("picking_parameters", "AIC window samples\n" -"(only if AIC checked)", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_aicleft.setText(QtGui.QApplication.translate("picking_parameters", "15", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_aicright.setText(QtGui.QApplication.translate("picking_parameters", "0", None, QtGui.QApplication.UnicodeUTF8)) - self.label_8.setText(QtGui.QApplication.translate("picking_parameters", "SNR threshold", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_const.setText(QtGui.QApplication.translate("picking_parameters", "constant", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_dyn.setText(QtGui.QApplication.translate("picking_parameters", "dynamic (advanced)", None, QtGui.QApplication.UnicodeUTF8)) - self.label_13.setText(QtGui.QApplication.translate("picking_parameters", "Constant SNR threshold:", None, QtGui.QApplication.UnicodeUTF8)) - self.label_9.setText(QtGui.QApplication.translate("picking_parameters", "Shift distance", None, QtGui.QApplication.UnicodeUTF8)) - self.label_10.setText(QtGui.QApplication.translate("picking_parameters", "p1", None, QtGui.QApplication.UnicodeUTF8)) - self.label_11.setText(QtGui.QApplication.translate("picking_parameters", "Shift SNR", None, QtGui.QApplication.UnicodeUTF8)) - self.label_12.setText(QtGui.QApplication.translate("picking_parameters", "p2", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/postprocessing_layout.py b/pylot/core/active/gui/postprocessing_layout.py deleted file mode 100644 index 0cb3d539..00000000 --- a/pylot/core/active/gui/postprocessing_layout.py +++ /dev/null @@ -1,178 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'postprocessing_layout.ui' -# -# Created: Mon Aug 29 10:26:23 2016 -# by: pyside-uic 0.2.15 running on PySide 1.2.2 -# -# WARNING! All changes made in this file will be lost! - -from PySide import QtCore, QtGui - -class Ui_postprocessing(object): - def setupUi(self, postprocessing): - postprocessing.setObjectName("postprocessing") - postprocessing.resize(825, 617) - self.verticalLayout = QtGui.QVBoxLayout(postprocessing) - self.verticalLayout.setObjectName("verticalLayout") - self.horizontalLayout_2 = QtGui.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.verticalLayout_3 = QtGui.QVBoxLayout() - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.label = QtGui.QLabel(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) - self.label.setSizePolicy(sizePolicy) - self.label.setMinimumSize(QtCore.QSize(0, 20)) - self.label.setMaximumSize(QtCore.QSize(16777215, 30)) - self.label.setObjectName("label") - self.verticalLayout_3.addWidget(self.label) - self.horizontalLayout_4 = QtGui.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.pushButton_rect = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_rect.sizePolicy().hasHeightForWidth()) - self.pushButton_rect.setSizePolicy(sizePolicy) - self.pushButton_rect.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_rect.setCheckable(False) - self.pushButton_rect.setObjectName("pushButton_rect") - self.buttonGroup = QtGui.QButtonGroup(postprocessing) - self.buttonGroup.setObjectName("buttonGroup") - self.buttonGroup.addButton(self.pushButton_rect) - self.horizontalLayout_4.addWidget(self.pushButton_rect) - self.pushButton_poly = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_poly.sizePolicy().hasHeightForWidth()) - self.pushButton_poly.setSizePolicy(sizePolicy) - self.pushButton_poly.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_poly.setCheckable(False) - self.pushButton_poly.setObjectName("pushButton_poly") - self.buttonGroup.addButton(self.pushButton_poly) - self.horizontalLayout_4.addWidget(self.pushButton_poly) - self.pushButton_undo = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_undo.sizePolicy().hasHeightForWidth()) - self.pushButton_undo.setSizePolicy(sizePolicy) - self.pushButton_undo.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_undo.setCheckable(False) - self.pushButton_undo.setObjectName("pushButton_undo") - self.horizontalLayout_4.addWidget(self.pushButton_undo) - self.verticalLayout_3.addLayout(self.horizontalLayout_4) - self.horizontalLayout_2.addLayout(self.verticalLayout_3) - self.line = QtGui.QFrame(postprocessing) - self.line.setFrameShape(QtGui.QFrame.VLine) - self.line.setFrameShadow(QtGui.QFrame.Sunken) - self.line.setObjectName("line") - self.horizontalLayout_2.addWidget(self.line) - self.verticalLayout_4 = QtGui.QVBoxLayout() - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.label_3 = QtGui.QLabel(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) - self.label_3.setSizePolicy(sizePolicy) - self.label_3.setMinimumSize(QtCore.QSize(0, 20)) - self.label_3.setMaximumSize(QtCore.QSize(16777215, 30)) - self.label_3.setObjectName("label_3") - self.verticalLayout_4.addWidget(self.label_3) - self.horizontalLayout_6 = QtGui.QHBoxLayout() - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.pushButton_plot = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_plot.sizePolicy().hasHeightForWidth()) - self.pushButton_plot.setSizePolicy(sizePolicy) - self.pushButton_plot.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_plot.setCheckable(False) - self.pushButton_plot.setDefault(False) - self.pushButton_plot.setFlat(False) - self.pushButton_plot.setObjectName("pushButton_plot") - self.horizontalLayout_6.addWidget(self.pushButton_plot) - self.pushButton_delete = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_delete.sizePolicy().hasHeightForWidth()) - self.pushButton_delete.setSizePolicy(sizePolicy) - self.pushButton_delete.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_delete.setObjectName("pushButton_delete") - self.horizontalLayout_6.addWidget(self.pushButton_delete) - self.verticalLayout_4.addLayout(self.horizontalLayout_6) - self.horizontalLayout_2.addLayout(self.verticalLayout_4) - spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.horizontalLayout_2.addItem(spacerItem) - self.verticalLayout_2 = QtGui.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label_2 = QtGui.QLabel(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth()) - self.label_2.setSizePolicy(sizePolicy) - self.label_2.setMinimumSize(QtCore.QSize(0, 20)) - self.label_2.setMaximumSize(QtCore.QSize(16777215, 30)) - self.label_2.setObjectName("label_2") - self.verticalLayout_2.addWidget(self.label_2) - self.horizontalLayout_5 = QtGui.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.pushButton_snr = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_snr.sizePolicy().hasHeightForWidth()) - self.pushButton_snr.setSizePolicy(sizePolicy) - self.pushButton_snr.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_snr.setObjectName("pushButton_snr") - self.horizontalLayout_5.addWidget(self.pushButton_snr) - self.pushButton_pe = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_pe.sizePolicy().hasHeightForWidth()) - self.pushButton_pe.setSizePolicy(sizePolicy) - self.pushButton_pe.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_pe.setObjectName("pushButton_pe") - self.horizontalLayout_5.addWidget(self.pushButton_pe) - self.pushButton_spe = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.pushButton_spe.sizePolicy().hasHeightForWidth()) - self.pushButton_spe.setSizePolicy(sizePolicy) - self.pushButton_spe.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_spe.setObjectName("pushButton_spe") - self.horizontalLayout_5.addWidget(self.pushButton_spe) - self.verticalLayout_2.addLayout(self.horizontalLayout_5) - self.horizontalLayout_2.addLayout(self.verticalLayout_2) - self.verticalLayout.addLayout(self.horizontalLayout_2) - self.verticalLayout_plot = QtGui.QVBoxLayout() - self.verticalLayout_plot.setObjectName("verticalLayout_plot") - self.verticalLayout.addLayout(self.verticalLayout_plot) - - self.retranslateUi(postprocessing) - QtCore.QMetaObject.connectSlotsByName(postprocessing) - - def retranslateUi(self, postprocessing): - postprocessing.setWindowTitle(QtGui.QApplication.translate("postprocessing", "Postprocessing", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("postprocessing", "Selection", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_rect.setText(QtGui.QApplication.translate("postprocessing", "Rectangle", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_poly.setText(QtGui.QApplication.translate("postprocessing", "Polygon", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_undo.setText(QtGui.QApplication.translate("postprocessing", "Unselect", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setText(QtGui.QApplication.translate("postprocessing", "Action", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_plot.setText(QtGui.QApplication.translate("postprocessing", "Plot", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_delete.setText(QtGui.QApplication.translate("postprocessing", "Delete", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("postprocessing", "Color by (refreshes plot)", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_snr.setText(QtGui.QApplication.translate("postprocessing", "SNR", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_pe.setText(QtGui.QApplication.translate("postprocessing", "PE", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_spe.setText(QtGui.QApplication.translate("postprocessing", "SPE", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/repicking_layout.py b/pylot/core/active/gui/repicking_layout.py deleted file mode 100644 index 717cc7d1..00000000 --- a/pylot/core/active/gui/repicking_layout.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'repicking_layout.ui' -# -# Created: Mon Aug 29 10:26:23 2016 -# by: pyside-uic 0.2.15 running on PySide 1.2.2 -# -# WARNING! All changes made in this file will be lost! - -from PySide import QtCore, QtGui - -class Ui_repicking(object): - def setupUi(self, repicking): - repicking.setObjectName("repicking") - repicking.resize(640, 480) - self.verticalLayout = QtGui.QVBoxLayout(repicking) - self.verticalLayout.setObjectName("verticalLayout") - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.pushButton_repick = QtGui.QPushButton(repicking) - self.pushButton_repick.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_repick.setObjectName("pushButton_repick") - self.horizontalLayout.addWidget(self.pushButton_repick) - self.pushButton_delete = QtGui.QPushButton(repicking) - self.pushButton_delete.setMaximumSize(QtCore.QSize(16777215, 30)) - self.pushButton_delete.setObjectName("pushButton_delete") - self.horizontalLayout.addWidget(self.pushButton_delete) - spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.verticalLayout.addLayout(self.horizontalLayout) - self.verticalLayout_plot = QtGui.QVBoxLayout() - self.verticalLayout_plot.setObjectName("verticalLayout_plot") - self.verticalLayout.addLayout(self.verticalLayout_plot) - self.buttonBox = QtGui.QDialogButtonBox(repicking) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Close) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout.addWidget(self.buttonBox) - - self.retranslateUi(repicking) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), repicking.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), repicking.reject) - QtCore.QMetaObject.connectSlotsByName(repicking) - - def retranslateUi(self, repicking): - repicking.setWindowTitle(QtGui.QApplication.translate("repicking", "Repicking", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_repick.setText(QtGui.QApplication.translate("repicking", "Repick", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_delete.setText(QtGui.QApplication.translate("repicking", "Delete", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/vtk_tools_layout.py b/pylot/core/active/gui/vtk_tools_layout.py deleted file mode 100644 index 53d05106..00000000 --- a/pylot/core/active/gui/vtk_tools_layout.py +++ /dev/null @@ -1,224 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'vtk_tools_layout.ui' -# -# Created: Mon Aug 29 10:26:23 2016 -# by: pyside-uic 0.2.15 running on PySide 1.2.2 -# -# WARNING! All changes made in this file will be lost! - -from PySide import QtCore, QtGui - -class Ui_vtk_tools(object): - def setupUi(self, vtk_tools): - vtk_tools.setObjectName("vtk_tools") - vtk_tools.resize(422, 471) - self.verticalLayout_9 = QtGui.QVBoxLayout(vtk_tools) - self.verticalLayout_9.setObjectName("verticalLayout_9") - self.verticalLayout = QtGui.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.verticalLayout_4 = QtGui.QVBoxLayout() - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.label_rec = QtGui.QLabel(vtk_tools) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_rec.sizePolicy().hasHeightForWidth()) - self.label_rec.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setWeight(75) - font.setBold(True) - self.label_rec.setFont(font) - self.label_rec.setToolTip("") - self.label_rec.setObjectName("label_rec") - self.horizontalLayout.addWidget(self.label_rec) - spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.radioButton_abs = QtGui.QRadioButton(vtk_tools) - self.radioButton_abs.setChecked(True) - self.radioButton_abs.setObjectName("radioButton_abs") - self.horizontalLayout.addWidget(self.radioButton_abs) - self.radioButton_rel = QtGui.QRadioButton(vtk_tools) - self.radioButton_rel.setObjectName("radioButton_rel") - self.horizontalLayout.addWidget(self.radioButton_rel) - self.verticalLayout_4.addLayout(self.horizontalLayout) - self.verticalLayout_6 = QtGui.QVBoxLayout() - self.verticalLayout_6.setObjectName("verticalLayout_6") - self.label_3 = QtGui.QLabel(vtk_tools) - self.label_3.setObjectName("label_3") - self.verticalLayout_6.addWidget(self.label_3) - self.horizontalLayout_2 = QtGui.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.lineEdit_vg = QtGui.QLineEdit(vtk_tools) - self.lineEdit_vg.setObjectName("lineEdit_vg") - self.horizontalLayout_2.addWidget(self.lineEdit_vg) - self.pushButton_vg = QtGui.QPushButton(vtk_tools) - self.pushButton_vg.setObjectName("pushButton_vg") - self.horizontalLayout_2.addWidget(self.pushButton_vg) - self.verticalLayout_6.addLayout(self.horizontalLayout_2) - self.verticalLayout_4.addLayout(self.verticalLayout_6) - self.verticalLayout_7 = QtGui.QVBoxLayout() - self.verticalLayout_7.setObjectName("verticalLayout_7") - self.label_4 = QtGui.QLabel(vtk_tools) - self.label_4.setObjectName("label_4") - self.verticalLayout_7.addWidget(self.label_4) - self.horizontalLayout_4 = QtGui.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.lineEdit_vgref = QtGui.QLineEdit(vtk_tools) - self.lineEdit_vgref.setEnabled(False) - self.lineEdit_vgref.setObjectName("lineEdit_vgref") - self.horizontalLayout_4.addWidget(self.lineEdit_vgref) - self.pushButton_vgref = QtGui.QPushButton(vtk_tools) - self.pushButton_vgref.setEnabled(False) - self.pushButton_vgref.setObjectName("pushButton_vgref") - self.horizontalLayout_4.addWidget(self.pushButton_vgref) - self.verticalLayout_7.addLayout(self.horizontalLayout_4) - self.verticalLayout_8 = QtGui.QVBoxLayout() - self.verticalLayout_8.setObjectName("verticalLayout_8") - self.verticalLayout_7.addLayout(self.verticalLayout_8) - self.label_5 = QtGui.QLabel(vtk_tools) - self.label_5.setObjectName("label_5") - self.verticalLayout_7.addWidget(self.label_5) - self.horizontalLayout_3 = QtGui.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.lineEdit_vgout = QtGui.QLineEdit(vtk_tools) - self.lineEdit_vgout.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.lineEdit_vgout.setObjectName("lineEdit_vgout") - self.horizontalLayout_3.addWidget(self.lineEdit_vgout) - self.pushButton_vtkout = QtGui.QPushButton(vtk_tools) - self.pushButton_vtkout.setObjectName("pushButton_vtkout") - self.horizontalLayout_3.addWidget(self.pushButton_vtkout) - self.pushButton_parav = QtGui.QPushButton(vtk_tools) - self.pushButton_parav.setObjectName("pushButton_parav") - self.horizontalLayout_3.addWidget(self.pushButton_parav) - self.verticalLayout_7.addLayout(self.horizontalLayout_3) - self.start_vg = QtGui.QPushButton(vtk_tools) - self.start_vg.setEnabled(False) - self.start_vg.setObjectName("start_vg") - self.verticalLayout_7.addWidget(self.start_vg) - self.verticalLayout_4.addLayout(self.verticalLayout_7) - self.verticalLayout.addLayout(self.verticalLayout_4) - self.verticalLayout_2 = QtGui.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.line = QtGui.QFrame(vtk_tools) - self.line.setFrameShape(QtGui.QFrame.HLine) - self.line.setFrameShadow(QtGui.QFrame.Sunken) - self.line.setObjectName("line") - self.verticalLayout_2.addWidget(self.line) - self.label_src = QtGui.QLabel(vtk_tools) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_src.sizePolicy().hasHeightForWidth()) - self.label_src.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setWeight(75) - font.setBold(True) - self.label_src.setFont(font) - self.label_src.setObjectName("label_src") - self.verticalLayout_2.addWidget(self.label_src) - self.verticalLayout_3 = QtGui.QVBoxLayout() - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.label = QtGui.QLabel(vtk_tools) - self.label.setObjectName("label") - self.verticalLayout_3.addWidget(self.label) - self.horizontalLayout_6 = QtGui.QHBoxLayout() - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.lineEdit_rays = QtGui.QLineEdit(vtk_tools) - self.lineEdit_rays.setObjectName("lineEdit_rays") - self.horizontalLayout_6.addWidget(self.lineEdit_rays) - self.pushButton_rays = QtGui.QPushButton(vtk_tools) - self.pushButton_rays.setObjectName("pushButton_rays") - self.horizontalLayout_6.addWidget(self.pushButton_rays) - self.verticalLayout_3.addLayout(self.horizontalLayout_6) - self.verticalLayout_2.addLayout(self.verticalLayout_3) - self.verticalLayout_5 = QtGui.QVBoxLayout() - self.verticalLayout_5.setObjectName("verticalLayout_5") - self.label_2 = QtGui.QLabel(vtk_tools) - self.label_2.setObjectName("label_2") - self.verticalLayout_5.addWidget(self.label_2) - self.horizontalLayout_5 = QtGui.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.lineEdit_raysout = QtGui.QLineEdit(vtk_tools) - self.lineEdit_raysout.setObjectName("lineEdit_raysout") - self.horizontalLayout_5.addWidget(self.lineEdit_raysout) - self.pushButton_raysout = QtGui.QPushButton(vtk_tools) - self.pushButton_raysout.setObjectName("pushButton_raysout") - self.horizontalLayout_5.addWidget(self.pushButton_raysout) - self.verticalLayout_5.addLayout(self.horizontalLayout_5) - self.start_rays = QtGui.QPushButton(vtk_tools) - self.start_rays.setEnabled(False) - self.start_rays.setObjectName("start_rays") - self.verticalLayout_5.addWidget(self.start_rays) - self.verticalLayout_2.addLayout(self.verticalLayout_5) - self.verticalLayout.addLayout(self.verticalLayout_2) - self.verticalLayout_9.addLayout(self.verticalLayout) - self.buttonBox = QtGui.QDialogButtonBox(vtk_tools) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_9.addWidget(self.buttonBox) - - self.retranslateUi(vtk_tools) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), vtk_tools.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), vtk_tools.reject) - QtCore.QMetaObject.connectSlotsByName(vtk_tools) - - def retranslateUi(self, vtk_tools): - vtk_tools.setWindowTitle(QtGui.QApplication.translate("vtk_tools", "VTK tools", None, QtGui.QApplication.UnicodeUTF8)) - self.label_rec.setText(QtGui.QApplication.translate("vtk_tools", "Velocity grid to VTK", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_abs.setToolTip(QtGui.QApplication.translate("vtk_tools", "\n" -"\n" -"

Load normal measured receiver input file. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[trace ID (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Geophone with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" -"

\n" -"

50 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_abs.setText(QtGui.QApplication.translate("vtk_tools", "absolute [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_rel.setToolTip(QtGui.QApplication.translate("vtk_tools", "\n" -"\n" -"

Load measured receiver input file that can be interpolated. The input file must be in the following format:

\n" -"

\n" -"

Containing in each line, separated by spaces:

\n" -"

\n" -"

[trace ID (int)] [receiver line ID (int)] [number of the geophone on receiver line (int)] [X (float)] [Y (float)] [Z (float)]

\n" -"

\n" -"

For example:

\n" -"

Third geophone on the second receiver line with the trace ID 50 and the coordinates (10.5 [m], 20.4 [m], 30.3 [m]).

\n" -"

\n" -"

50 2 3 10.5 20.4 30.3

", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButton_rel.setText(QtGui.QApplication.translate("vtk_tools", "relative [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.label_3.setText(QtGui.QApplication.translate("vtk_tools", "Browse for velocity grid file (\'vgrids.in\'):", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_vg.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_4.setText(QtGui.QApplication.translate("vtk_tools", "Browse for reference velocity grid file (\'vgridsref.in\'):", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_vgref.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_5.setText(QtGui.QApplication.translate("vtk_tools", "Output Filename:", None, QtGui.QApplication.UnicodeUTF8)) - self.lineEdit_vgout.setText(QtGui.QApplication.translate("vtk_tools", "vgrids.vtk", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_vtkout.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_parav.setToolTip(QtGui.QApplication.translate("vtk_tools", "\n" -"\n" -"

Call Paraview (Shell command: paraview) for the specified output filename in the current directory.

", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_parav.setText(QtGui.QApplication.translate("vtk_tools", "<- Paraview", None, QtGui.QApplication.UnicodeUTF8)) - self.start_vg.setText(QtGui.QApplication.translate("vtk_tools", "Start", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setToolTip(QtGui.QApplication.translate("vtk_tools", "Create VTK files from the FMTOMO output file \'rays.dat\'.\n" -"This will generate one file of ray paths for each individual source.", None, QtGui.QApplication.UnicodeUTF8)) - self.label_src.setText(QtGui.QApplication.translate("vtk_tools", "Rays to VTK [?]", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("vtk_tools", "Browse for input file (\'rays.dat\'):", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_rays.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("vtk_tools", "Specify output directory:", None, QtGui.QApplication.UnicodeUTF8)) - self.pushButton_raysout.setText(QtGui.QApplication.translate("vtk_tools", "Browse", None, QtGui.QApplication.UnicodeUTF8)) - self.start_rays.setText(QtGui.QApplication.translate("vtk_tools", "Start", None, QtGui.QApplication.UnicodeUTF8)) - diff --git a/pylot/core/active/gui/windows.py b/pylot/core/active/gui/windows.py deleted file mode 100644 index 00bb7119..00000000 --- a/pylot/core/active/gui/windows.py +++ /dev/null @@ -1,773 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import os -import numpy as np -import matplotlib -import matplotlib.pyplot as plt -from PySide import QtCore, QtGui -from pylot.core.active import surveyUtils, activeSeismoPick, seismicArrayPreparation, fmtomoUtils -from generate_survey_layout import Ui_generate_survey -from generate_survey_layout_minimal import Ui_generate_survey_minimal -from generate_seisarray_layout import Ui_generate_seisarray -from picking_parameters_layout import Ui_picking_parameters -from fmtomo_parameters_layout import Ui_fmtomo_parameters -from vtk_tools_layout import Ui_vtk_tools -from postprocessing_layout import Ui_postprocessing -from repicking_layout import Ui_repicking - -from pylot.core.active.surveyPlotTools import regions - -matplotlib.use('Qt4Agg') -matplotlib.rcParams['backend.qt4']='PySide' - -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar -from matplotlib.figure import Figure - -def openFile(name = 'Open'): - dialog = QtGui.QFileDialog() - dialog.setWindowTitle(name) #not working yet - filename = dialog.getOpenFileName() - if len(filename[0]) > 0: - return filename[0] - -def saveFile(name = 'Save'): - dialog = QtGui.QFileDialog() - dialog.setWindowTitle(name) - filename = dialog.getSaveFileName() - if len(filename[0]) > 0: - return filename[0] - -def browseDir(name = 'Open Directory'): - dialog = QtGui.QFileDialog() - dialog.setWindowTitle(name) - directory = dialog.getExistingDirectory() - if len(directory) > 0: - return directory - -def getMaxCPU(): - import multiprocessing - return multiprocessing.cpu_count() - -def printDialogMessage(message): - qmb = QtGui.QMessageBox() - qmb.setText(message) - qmb.setStandardButtons(QtGui.QMessageBox.Ok) - qmb.setIcon(QtGui.QMessageBox.Warning) - qmb.exec_() - -def continueDialogExists(name): - qmb = QtGui.QMessageBox() - qmb.setText('%s object already exists. Overwrite?'%name) - qmb.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) - qmb.setIcon(QtGui.QMessageBox.Warning) - answer = qmb.exec_() - if answer == 1024: - return True - else: - return False - -def continueDialogMessage(message): - qmb = QtGui.QMessageBox() - qmb.setText(message) - qmb.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) - qmb.setIcon(QtGui.QMessageBox.Warning) - answer = qmb.exec_() - if answer == 1024: - return True - else: - return False - - -class Gen_SeisArray(object): - def __init__(self, mainwindow): - self.mainwindow = mainwindow - self.seisarray = None - self.srcfile = None - self.recfile = None - self.ptsfile = None - self.init_dialog() - self.start_dialog() - - def init_dialog(self): - qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_generate_seisarray() - ui.setupUi(qdialog) - self.ui = ui - self.qdialog = qdialog - self.connectButtons() - - def start_dialog(self): - if self.qdialog.exec_(): - self.refresh_selection() - if self.ui.radioButton_interpolatable.isChecked(): - self.seisarray = seismicArrayPreparation.SeisArray(self.recfile, True) - elif self.ui.radioButton_normal.isChecked(): - self.seisarray = seismicArrayPreparation.SeisArray(self.recfile, False) - if len(self.srcfile) > 0: - self.seisarray.addSourceLocations(self.srcfile) - if len(self.ptsfile) > 0: - self.seisarray.addMeasuredTopographyPoints(self.ptsfile) - self.executed = True - else: - self.refresh_selection() - self.executed = False - - def refresh_selection(self): - self.srcfile = self.ui.lineEdit_src.text() - self.recfile = self.ui.lineEdit_rec.text() - self.ptsfile = self.ui.lineEdit_pts.text() - - def get_seisarray(self): - if self.seisarray is not None: - return self.seisarray - - def connectButtons(self): - QtCore.QObject.connect(self.ui.pushButton_rec, QtCore.SIGNAL("clicked()"), self.chooseMeasuredRec) - QtCore.QObject.connect(self.ui.pushButton_src, QtCore.SIGNAL("clicked()"), self.chooseMeasuredSrc) - QtCore.QObject.connect(self.ui.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseMeasuredPts) - - def chooseMeasuredSrc(self): - self.ui.lineEdit_src.setText(openFile('Open measured sources file.')) - - def chooseMeasuredRec(self): - self.ui.lineEdit_rec.setText(openFile('Open measured receivers file.')) - - def chooseMeasuredPts(self): - self.ui.lineEdit_pts.setText(openFile('Open measured points file.')) - - -class Gen_Survey_from_SA(object): - def __init__(self, mainwindow, seisarray): - self.mainwindow = mainwindow - self.seisarray = seisarray - self.survey = None - self.obsdir = None - self.fstart = 'shot' - self.fend = '.dat' - self.init_dialog() - self.start_dialog() - - def init_dialog(self): - qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_generate_survey_minimal() - ui.setupUi(qdialog) - self.ui = ui - self.qdialog = qdialog - self.connectButtons() - - def start_dialog(self): - if self.qdialog.exec_(): - self.refresh_selection() - self.survey = activeSeismoPick.Survey(self.obsdir, seisArray = self.seisarray, - useDefaultParas = True, fstart = self.fstart, - fend = self.fend) - self.executed = True - else: - self.refresh_selection() - self.executed = False - - def update_seisarray(self, seisarray): - self.seisarray = seisarray - - def refresh_selection(self): - self.obsdir = self.ui.lineEdit_obs.text() - self.fstart = self.ui.fstart.text() - self.fend = self.ui.fend.text() - - def get_survey(self): - return self.survey - - def connectButtons(self): - QtCore.QObject.connect(self.ui.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseObsdir) - - def chooseObsdir(self): - self.ui.lineEdit_obs.setText(browseDir('Choose observation directory.')) - - -class Gen_Survey_from_SR(object): - def __init__(self, mainwindow): - self.mainwindow = mainwindow - self.survey = None - self.obsdir = None - self.srcfile = None - self.recfile = None - self.fstart = 'shot' - self.fend = '.dat' - self.init_dialog() - self.start_dialog() - - def init_dialog(self): - qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_generate_survey() - ui.setupUi(qdialog) - self.ui = ui - self.qdialog = qdialog - self.connectButtons() - - def start_dialog(self): - if self.qdialog.exec_(): - self.refresh_selection() - self.survey = activeSeismoPick.Survey(self.obsdir, self.srcfile, self.recfile, - useDefaultParas = True, - fstart = self.fstart, fend = self.fend) - self.executed = True - else: - self.refresh_selection() - self.executed = False - - def refresh_selection(self): - self.obsdir = self.ui.lineEdit_obs.text() - self.srcfile = self.ui.lineEdit_src.text() - self.recfile = self.ui.lineEdit_rec.text() - self.fstart = self.ui.fstart.text() - self.fend = self.ui.fend.text() - - def get_survey(self): - return self.survey - - def connectButtons(self): - QtCore.QObject.connect(self.ui.pushButton_obs, QtCore.SIGNAL("clicked()"), self.chooseObsdir) - QtCore.QObject.connect(self.ui.pushButton_src, QtCore.SIGNAL("clicked()"), self.chooseSourcefile) - QtCore.QObject.connect(self.ui.pushButton_rec, QtCore.SIGNAL("clicked()"), self.chooseRecfile) - - def chooseObsdir(self): - self.ui.lineEdit_obs.setText(browseDir('Choose observation directory.')) - - def chooseSourcefile(self): - self.ui.lineEdit_src.setText(openFile('Open sourcefile.')) - - def chooseRecfile(self): - self.ui.lineEdit_rec.setText(openFile('Open receiverfile.')) - - -class Call_autopicker(object): - def __init__(self, mainwindow, survey): - self.mainwindow = mainwindow - self.survey = survey - self.maxSRdist = None - self.dists_p = [] - self.snr_p = [] - self.lines = [] - self.init_dialog() - self.refresh_selection() - self.enableDynSNR(False) - self.start_dialog() - - def init_dialog(self): - qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_picking_parameters() - ui.setupUi(qdialog) - ui.ncores.setMaximum(getMaxCPU()) - self.ui = ui - self.qdialog = qdialog - self.initSNRplot() - self.connectButtons() - - def getMaxSRdist(self): - if self.maxSRdist is not None: - return self.maxSRdist - else: - SRdists = [] - for shot in self.survey.data.values(): - for traceID in shot.getTraceIDlist(): - SRdists.append(shot.getDistance(traceID)) - self.maxSRdist = max(SRdists) - return self.maxSRdist - - def update_survey(self, survey): - self.survey = survey - - def initSNRplot(self): - self.snrFig = Figure() - self.snrCanvas = FigureCanvas(self.snrFig) - self.ui.vlayout_plot.addWidget(self.snrCanvas) - self.snrToolbar = NavigationToolbar(self.snrCanvas, self.mainwindow) - self.ui.vlayout_plot.addWidget(self.snrToolbar) - - def prepFigure(self, refresh = True): - fig = self.snrFig - if fig.axes == []: - ax = fig.add_subplot(111) - xlim = None - ylim = None - else: - ax = fig.axes[0] - xlim = ax.get_xlim() - ylim = ax.get_ylim() - #ax.clear() - if not refresh: - self.plotPicks(ax) - else: - self.clear_lines() - - return fig, ax, xlim, ylim - - def clear_lines(self): - for line in self.lines: - line.remove() - self.lines = [] - - def finishFigure(self, ax, xlim, ylim): - ax.set_xlim(xlim) - ax.set_ylim(ylim) - - def finishNewFigure(self, ax): - xlim = None - ylim = None - ax.set_xlabel('Distance [m]') - ax.set_ylabel('SNR') - return xlim, ylim - - def plotPicks(self, ax): - if self.survey.picked: - if self.dists_p == [] or self.snr_p == []: - for shot in self.survey.data.values(): - for traceID in shot.getTraceIDlist(): - self.dists_p.append(shot.getDistance(traceID)) - self.snr_p.append(shot.getSNR(traceID)[0]) - - ax.scatter(self.dists_p, self.snr_p, s = 0.5, c='k') - - def plotConstSNR(self, refresh = True): - fig, ax, xlim, ylim = self.prepFigure(refresh) - - snrthreshold = float(self.ui.doubleSpinBox_constSNR.text()) - line = ax.hlines(snrthreshold, 0, self.getMaxSRdist(), 'b') - self.lines.append(line) - - if refresh == False: - xlim, ylim = self.finishNewFigure(ax) - - if self.survey.picked: - self.finishFigure(ax, xlim, ylim) - - self.snrCanvas.draw() - - def plotDynSNR(self, refresh = True): - fig, ax, xlim, ylim = self.prepFigure(refresh) - snrthresholds = [] - shiftSNR = float(self.ui.shift_snr.value()) - shiftDist = float(self.ui.shift_dist.value()) - p1 = float(self.ui.p1.value()) - p2 = float(self.ui.p2.value()) - dists = np.arange(0, self.getMaxSRdist() + 1, 1) - - for dist in dists: - dist += shiftDist - snrthresholds.append(surveyUtils.snr_fit_func(surveyUtils.get_fit_fn(p1, p2), dist, shiftSNR)) - self.lines = ax.plot(dists, snrthresholds, 'b') - - if refresh == False: - xlim, ylim = self.finishNewFigure(ax) - - if self.survey.picked: - self.finishFigure(ax, xlim, ylim) - - self.snrCanvas.draw() - - def plotSNR(self, refresh = True): - if self.ui.radioButton_const.isChecked(): - self.plotConstSNR(refresh) - if self.ui.radioButton_dyn.isChecked(): - self.plotDynSNR(refresh) - - def snr_toggle(self): - if self.ui.radioButton_const.isChecked(): - self.enableDynSNR(False) - self.enableConstSNR(True) - if self.ui.radioButton_dyn.isChecked(): - self.enableConstSNR(False) - self.enableDynSNR(True) - self.plotSNR(refresh = True) - - def enableDynSNR(self, bool): - self.ui.shift_dist.setEnabled(bool) - self.ui.shift_snr.setEnabled(bool) - self.ui.p1.setEnabled(bool) - self.ui.p2.setEnabled(bool) - - def enableConstSNR(self, bool): - self.ui.doubleSpinBox_constSNR.setEnabled(bool) - - def start_dialog(self): - self.plotSNR(refresh = False) - if self.qdialog.exec_(): - self.refresh_selection() - - if self.AIC == True: - HosAic = 'aic' - else: - HosAic = 'hos' - - surveyUtils.setDynamicFittedSNR(self.survey.getShotDict(), shiftdist = self.shiftDist, - shiftSNR = self.shiftSNR, p1 = self.p1, p2 = self.p2) - - self.survey.pickAllShots(vmin = self.vmin, vmax = self.vmax, - folm = self.folm/100., HosAic = HosAic, - aicwindow = self.aicwindow, cores = self.ncores) - - #QtGui.qApp.processEvents() # test - self.executed = True - self.clear_lines() - else: - self.refresh_selection() - self.executed = False - self.clear_lines() - - def refreshFolm(self): - self.ui.label_folm.setText('%s %%'%self.ui.slider_folm.value()) - - def refresh_selection(self): - self.ncores = int(self.ui.ncores.value()) - self.vmin = float(self.ui.lineEdit_vmin.text()) - self.vmax = float(self.ui.lineEdit_vmax.text()) - self.folm = float(self.ui.slider_folm.value()) - self.AIC = self.ui.checkBox_AIC.isChecked() - self.aicwindow = (int(self.ui.lineEdit_aicleft.text()), int(self.ui.lineEdit_aicright.text())) - self.shiftSNR = float(self.ui.shift_snr.value()) - self.shiftDist = float(self.ui.shift_dist.value()) - self.p1 = float(self.ui.p1.value()) - self.p2 = float(self.ui.p2.value()) - - def connectButtons(self): - QtCore.QObject.connect(self.ui.slider_folm, QtCore.SIGNAL("valueChanged(int)"), self.refreshFolm) - QtCore.QObject.connect(self.ui.shift_snr, QtCore.SIGNAL("valueChanged(int)"), self.plotSNR) - QtCore.QObject.connect(self.ui.shift_dist, QtCore.SIGNAL("valueChanged(int)"), self.plotSNR) - QtCore.QObject.connect(self.ui.p1, QtCore.SIGNAL("valueChanged(double)"), self.plotSNR) - QtCore.QObject.connect(self.ui.p2, QtCore.SIGNAL("valueChanged(double)"), self.plotSNR) - QtCore.QObject.connect(self.ui.doubleSpinBox_constSNR, QtCore.SIGNAL("valueChanged(double)"), self.plotSNR) - QtCore.QObject.connect(self.ui.radioButton_const, QtCore.SIGNAL("clicked()"), self.snr_toggle) - QtCore.QObject.connect(self.ui.radioButton_dyn, QtCore.SIGNAL("clicked()"), self.snr_toggle) - - def chooseObsdir(self): - self.ui.lineEdit_obs.setText(browseDir('Choose observation directory.')) - - def chooseSourcefile(self): - self.ui.lineEdit_src.setText(openFile('Open sourcefile.')) - - def chooseRecfile(self): - self.ui.lineEdit_rec.setText(openFile('Open receiverfile.')) - - -class Call_FMTOMO(object): - def __init__(self, mainwindow, survey): - self.mainwindow = mainwindow - self.survey = survey - self.init_dialog() - self.refresh_selection() - self.start_dialog() - - def init_dialog(self): - qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_fmtomo_parameters() - ui.setupUi(qdialog) - ui.nproc.setMaximum(getMaxCPU()) - self.ui = ui - self.qdialog = qdialog - self.connectButtons() - - def start_dialog(self): - if self.qdialog.exec_(): - self.refresh_selection() - - if not os.path.isdir(self.picks_dir): - err = os.mkdir(self.picks_dir) # error not handled yet - - self.survey.exportFMTOMO(self.picks_dir) - - cwd = os.getcwd() - interpolationMethod = 'linear' - os.chdir(self.simuldir) - if self.survey.seisarray.twoDim: - interpolationMethod = 'nearest' - self.survey.seisarray.generateFMTOMOinputFromArray(self.propgrid, self.vgrid, (self.bbot, self.btop), - self.cushionfactor/100., interpolationMethod, - customgrid = self.customgrid, writeVTK = True) - os.chdir(cwd) - - tomo = fmtomoUtils.Tomo3d(self.fmtomo_dir, self.simuldir) - tomo.runTOMO3D(self.nproc, self.nIter) - - self.executed = True - else: - self.refresh_selection() - self.executed = False - - def update_survey(self, survey): - self.survey = survey - - def refresh_selection(self): - self.fmtomo_dir = self.ui.fmtomo_dir.text() - self.nIter = int(self.ui.nIter.value()) - self.nproc = int(self.ui.nproc.value()) - self.btop = float(self.ui.btop.text()) - self.bbot = float(self.ui.bbot.text()) - self.propgrid = (int(self.ui.pgrid_x.value()), int(self.ui.pgrid_y.value()), int(self.ui.pgrid_z.value())) - self.vgrid = (int(self.ui.invgrid_x.value()), int(self.ui.invgrid_y.value()), int(self.ui.invgrid_z.value())) - self.cushionfactor = float(self.ui.cushion.value()) - self.customgrid = self.ui.customgrid.text() - self.simuldir = self.ui.simuldir.text() - self.picks_dir = os.path.join(self.simuldir, 'picks') - - def connectButtons(self): - QtCore.QObject.connect(self.ui.browse_tomodir, QtCore.SIGNAL("clicked()"), self.chooseFMTOMOdir) - QtCore.QObject.connect(self.ui.browse_customgrid, QtCore.SIGNAL("clicked()"), self.chooseCustomgrid) - QtCore.QObject.connect(self.ui.browse_simuldir, QtCore.SIGNAL("clicked()"), self.chooseSimuldir) - - def chooseFMTOMOdir(self): - self.ui.fmtomo_dir.setText(browseDir()) - - def chooseCustomgrid(self): - self.ui.customgrid.setText(openFile()) - - def chooseSimuldir(self): - self.ui.simuldir.setText(browseDir()) - - -class Call_VTK_dialog(object): - def __init__(self, mainwindow): - self.mainwindow = mainwindow - self.init_dialog() - self.refresh_selection() - self.start_dialog() - - def init_dialog(self): - qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_vtk_tools() - ui.setupUi(qdialog) - self.ui = ui - self.qdialog = qdialog - self.connectButtons() - - def start_dialog(self): - self.qdialog.exec_() - self.refresh_selection() - - def refresh_selection(self): - self.vg = self.ui.lineEdit_vg.text() - self.vgout = self.ui.lineEdit_vgout.text() - self.rays = self.ui.lineEdit_rays.text() - self.raysout = self.ui.lineEdit_raysout.text() - - def checkVgStartButton(self): - ui = self.ui - if ui.radioButton_rel.isChecked(): - if ui.lineEdit_vg.text() != '' and ui.lineEdit_vgref.text() != '': - ui.start_vg.setEnabled(True) - else: - ui.start_vg.setEnabled(False) - if ui.radioButton_abs.isChecked(): - if ui.lineEdit_vg.text() != '': - ui.start_vg.setEnabled(True) - else: - ui.start_vg.setEnabled(False) - - def checkRaysStartButton(self): - ui = self.ui - if ui.lineEdit_rays.text() != '' and ui.lineEdit_raysout.text() != '': - ui.start_rays.setEnabled(True) - else: - ui.start_rays.setEnabled(False) - - def connectButtons(self): - QtCore.QObject.connect(self.ui.pushButton_vg, QtCore.SIGNAL("clicked()"), self.chooseVgrid) - QtCore.QObject.connect(self.ui.pushButton_vgref, QtCore.SIGNAL("clicked()"), self.chooseVgridref) - QtCore.QObject.connect(self.ui.pushButton_rays, QtCore.SIGNAL("clicked()"), self.chooseRaysIn) - QtCore.QObject.connect(self.ui.pushButton_raysout, QtCore.SIGNAL("clicked()"), self.chooseRaysOutDir) - QtCore.QObject.connect(self.ui.pushButton_vtkout, QtCore.SIGNAL("clicked()"), self.newFileVTK) - QtCore.QObject.connect(self.ui.pushButton_parav, QtCore.SIGNAL("clicked()"), self.openFileParaview) - QtCore.QObject.connect(self.ui.start_vg, QtCore.SIGNAL("clicked()"), self.startvgvtk) - QtCore.QObject.connect(self.ui.start_rays, QtCore.SIGNAL("clicked()"), self.startraysvtk) - QtCore.QObject.connect(self.ui.radioButton_rel, QtCore.SIGNAL("clicked()"), self.activateVgref) - QtCore.QObject.connect(self.ui.radioButton_abs, QtCore.SIGNAL("clicked()"), self.deactivateVgref) - - def openFileParaview(self): - os.system('paraview %s &'%self.ui.lineEdit_vgout.text()) - - def activateVgref(self): - self.ui.lineEdit_vgref.setEnabled(True) - self.ui.pushButton_vgref.setEnabled(True) - - def deactivateVgref(self): - self.ui.lineEdit_vgref.setEnabled(False) - self.ui.pushButton_vgref.setEnabled(False) - - def chooseVgrid(self): - self.ui.lineEdit_vg.setText(openFile()) - self.checkVgStartButton() - - def chooseVgridref(self): - self.ui.lineEdit_vgref.setText(openFile()) - self.checkVgStartButton() - - def chooseRaysIn(self): - self.ui.lineEdit_rays.setText(openFile()) - self.checkRaysStartButton() - - def chooseRaysOutDir(self): - self.ui.lineEdit_raysout.setText(browseDir()) - self.checkRaysStartButton() - - def startvgvtk(self): - ui = self.ui - if ui.lineEdit_vgout.text() == '': - return - if ui.radioButton_abs.isChecked(): - fmtomoUtils.vgrids2VTK(inputfile = ui.lineEdit_vg.text(), - outputfile = ui.lineEdit_vgout.text(), - absOrRel='abs') - elif ui.radioButton_rel.isChecked(): - fmtomoUtils.vgrids2VTK(inputfile = ui.lineEdit_vg.text(), - outputfile = ui.lineEdit_vgout.text(), - absOrRel='rel', - inputfileref = ui.lineEdit_vgref.text()) - - def startraysvtk(self): - ui = self.ui - fmtomoUtils.rays2VTK(ui.lineEdit_rays.text(), ui.lineEdit_raysout.text()) - - def newFileVTK(self): - self.ui.lineEdit_vgout.setText(saveFile()) - -class Postprocessing(object): - def __init__(self, mainwindow, survey): - self.mainwindow = mainwindow - self.survey = survey - self.init_widget() - self.start_widget() - self.inkByVal = 'snrlog' - - def init_widget(self): - qwidget = QtGui.QWidget()# - ui = Ui_postprocessing() - ui.setupUi(qwidget) - self.ui = ui - self.qwidget = qwidget - self.initPlot() - self.newPlot() - self.connectButtons() - self.region = regions(self.ax, self.cbar, self.survey, qt_interface = True) - - def start_widget(self): - self.qwidget.show() - - def initPlot(self): - self.figure = Figure() - self.canvas = FigureCanvas(self.figure) - self.ui.verticalLayout_plot.addWidget(self.canvas) - self.toolbar = NavigationToolbar(self.canvas, self.mainwindow) - self.ui.verticalLayout_plot.addWidget(self.toolbar) - - def newPlot(self): - ax = self.figure.add_subplot(111) - dists, picks, snrlog, pe, spe = self.survey.preparePlotAllPicks(plotRemoved = False) - self.dists = dists - self.picks = picks - self.inkDict = {'snrlog': snrlog, - 'pe': pe, - 'spe': spe} - - ax, cbar, sc = self.survey.createPlot(dists, picks, snrlog, 'log10(SNR)', ax = ax, cbar = None) - self.cbar = self.figure.colorbar(sc, ax = ax, fraction=0.05) - self.ax = ax - self.draw() - - def refreshPlot(self): - self.ax.clear() - ax = self.ax - ax, cbar, sc = self.survey.createPlot(self.dists, self.picks, self.inkDict[self.inkByVal], - self.inkByVal, ax = ax, cbar = self.cbar) - #self.cbar = self.figure.colorbar(sc, fraction=0.05) - self.draw() - - def update_survey(self, survey): - self.survey = survey - - def get_survey(self): - return self.survey - - def draw(self): - self.canvas.draw() - - def connectButtons(self): - QtCore.QObject.connect(self.ui.pushButton_rect, QtCore.SIGNAL("clicked()"), self.chooseRect) - QtCore.QObject.connect(self.ui.pushButton_poly, QtCore.SIGNAL("clicked()"), self.choosePoly) - QtCore.QObject.connect(self.ui.pushButton_plot, QtCore.SIGNAL("clicked()"), self.plotPicks) - QtCore.QObject.connect(self.ui.pushButton_delete, QtCore.SIGNAL("clicked()"), self.deleteSelected) - QtCore.QObject.connect(self.ui.pushButton_undo, QtCore.SIGNAL("clicked()"), self.undoSelection) - QtCore.QObject.connect(self.ui.pushButton_snr, QtCore.SIGNAL("clicked()"), self.refrSNR) - QtCore.QObject.connect(self.ui.pushButton_pe, QtCore.SIGNAL("clicked()"), self.refrPE) - QtCore.QObject.connect(self.ui.pushButton_spe, QtCore.SIGNAL("clicked()"), self.refrSPE) - - def chooseRect(self): - self.region.chooseRectangles() - - def choosePoly(self): - self.region.choosePolygon() - - def disconnectRect(self): - self.region.disconnectRect() - - def disconnectPoly(self): - self.region.disconnectPoly() - - def plotPicks(self): - self.region.plotTracesInActiveRegions() - - def deleteSelected(self): - self.region.setAllActiveRegionsForDeletion() - message = 'Are you sure you want to delete all marked picks?' - if continueDialogMessage(message): - self.region.deleteAllMarkedPicks() - else: - self.region.refreshFigure() - - def undoSelection(self): - self.region.deselectLastSelection() - - def refrSNR(self): - self.region.refreshLog10SNR() - - def refrPE(self): - self.region.refreshPickerror() - - def refrSPE(self): - self.region.refreshSPE() - - -class Repicking(object): - def __init__(self, mainwindow, region, shot, traceID): - self.mainwindow = mainwindow - self.region = region - self.shot = shot - self.traceID = traceID - self.init_dialog() - self.start_dialog() - - def init_dialog(self): - qdialog = QtGui.QDialog(self.mainwindow) - ui = Ui_repicking() - ui.setupUi(qdialog) - self.ui = ui - self.qdialog = qdialog - self.connectButtons() - - def start_dialog(self): - self.qdialog.exec_() - - def connectButtons(self): - QtCore.QObject.connect(self.ui.pushButton_repick, QtCore.SIGNAL("clicked()"), self.repick) - QtCore.QObject.connect(self.ui.pushButton_delete, QtCore.SIGNAL("clicked()"), self.delete) - - def initPlot(self): - self.figure = Figure() - self.canvas = FigureCanvas(self.figure) - self.ui.verticalLayout_plot.addWidget(self.canvas) - self.toolbar = NavigationToolbar(self.canvas, self.mainwindow) - self.ui.verticalLayout_plot.addWidget(self.toolbar) - - def plot(self): - self.shot.plot_traces(self.traceID, figure = self.figure, buttons = False) - self.ax = ax - self.draw() diff --git a/pylot/core/active/run_asp3d.py b/pylot/core/active/run_asp3d.py deleted file mode 100755 index 0b7202e1..00000000 --- a/pylot/core/active/run_asp3d.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import sys -import os -from obspy import read -from obspy import Stream -from obspy import Trace -from datetime import datetime -import numpy as np - -from pylot.core.active import surveyUtils -from pylot.core.active import seismicshot -from pylot.core.active import activeSeismoPick -from pylot.core.active import fmtomoUtils -from pylot.core.active import seismicArrayPreparation -# reload(seismicshot) -# reload(surveyUtils) -# reload(activeSeismoPick) - -##################################################################################### -# parameter definitions:############################################################# -cutwindow = (0, 0.15) # cut out a part of the trace [seconds] -tmovwind = 0.1 # size of the moving window -windowsize = (30, 0) # windowsize for AIC picker (indices around HOS picks [time/sampling rate] !!!) -folm = 0.6 # fraction of local maximum for threshold picker -tsignal = 0.03 -tgap = 0.0007 - -nproc = 1 - -vmin = 333 -vmax = 5500 - -HosAic = 'aic' # can be 'hos' or 'aic' - -rockeskyll = False -GZB = True -bb1 = False - -# Simulation parameters ############################################################# -simulation = True - -niter = 4 - -bottomBoundary = -50.0 -topBoundary = 5.0 -nPointsPropgrid = (100, 100, 100) -nPointsVgrid = (30, 30, 30) -cushionfactor = 0.1 -interpolationMethod = 'linear' -mygrid = '../mygrid.in' - -cwd = os.getcwd() -simuldir = 'fmtomo_simulation' -pickdir = 'picks' -fmtomopath = '/rscratch/minos22/marcel/flachseismik/fmtomo/GZB_clean/' -###################################################################################### -###################################################################################### -if rockeskyll == True: - receiverfile = "Geophone_interpoliert_rockes" - sourcefile = "Schusspunkte_rockes" - obsdir = "/rscratch/minos22/marcel/flachseismik/rockeskyll_200615_270615/" - filename = 'survey_rockes.pickle' -elif GZB == True: - receiverfile = "Geophone_interpoliert_GZB" - sourcefile = "Schusspunkte_GZB" - #sourcefile = "Schusspunkte_GZB_1shot" - obsdir = "/rscratch/minos22/marcel/flachseismik/GZB_26_06_15_01/" - filename = 'survey_GZB.pickle' -elif bb1 == True: - receiverfile = "Geophone_Marcel" - sourcefile = "Schusspunkte_Marcel" - obsdir = "/rscratch/minos22/marcel/flachseismik/bachelor_bausenberg/" - filename = 'survey_bb1.pickle' -###################################################################################### - -starttime = datetime.now() - -print('\n--------------------Starting Script at %s -------------------\n' %starttime.time()) -print('directory: %s\nsourcefile: %s\nreceiverfile: %s\nsurvey output filename: %s\n' %(obsdir, sourcefile, receiverfile, filename)) -if HosAic == 'aic': print('picking with AIC\n') -if HosAic == 'hos': print('picking with HOS\n') - -survey = activeSeismoPick.Survey(obsdir, os.path.join(obsdir, sourcefile), - os.path.join(obsdir, receiverfile), useDefaultParas = False) -survey.setParametersForAllShots(cutwindow, tmovwind, tsignal, tgap) -surveyUtils.setDynamicFittedSNR(survey.getShotDict()) -#surveyUtils.setConstantSNR(survey.getShotDict(), 0) -survey.setArtificialPick(0, 0) # artificial pick at source origin -print('\nDone after %s seconds!\n------------------------------------------------------------------------------\n' % (datetime.now() - starttime).seconds) -survey.pickAllShots(vmin, vmax, folm, HosAic, windowsize, cores = nproc) -survey.cleanBySPE(maxSPE = 0.0075) -survey.saveSurvey(filename) -print('\n--- Finished picking ---') -print'Elapsed time:', datetime.now()-starttime - - -###################################################################################### -if simulation == False: - sys.exit() - -survey = activeSeismoPick.Survey.from_pickle(filename) - -if not os.path.isdir(os.path.join(cwd, simuldir)): - err = os.mkdir(os.path.join(cwd, simuldir)) - -picks = os.path.join(simuldir, pickdir) - -if not os.path.isdir(os.path.join(cwd, picks)): - err2 = os.mkdir(os.path.join(cwd, picks)) - -survey.exportFMTOMO(picks) - -####### hard coded -os.chdir(simuldir) -survey.loadArray(obsdir, 'Geophone_eingemessen_GZB', 'Schusspunkte_GZB') -survey.seisArray.generateFMTOMOinputFromArray(nPointsPropgrid, nPointsVgrid, - (bottomBoundary, topBoundary), cushionfactor, - interpolationMethod, customgrid = mygrid, - writeVTK = False) - -os.chdir(cwd) -####### test - -tomo = fmtomoUtils.Tomo3d(fmtomopath, simuldir) -tomo.runTOMO3D(nproc, niter) - -print('\n--- Finished script ---') -print'Elapsed time:', datetime.now()-starttime diff --git a/pylot/core/active/seismicArrayPreparation.py b/pylot/core/active/seismicArrayPreparation.py deleted file mode 100644 index 907e9161..00000000 --- a/pylot/core/active/seismicArrayPreparation.py +++ /dev/null @@ -1,1122 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -import numpy as np -from scipy.interpolate import griddata - -def readMygridNlayers(filename): - infile = open(filename, 'r') - nlayers = len(infile.readlines()) / 2 - infile.close() - - return nlayers - -def readMygrid(filename): - ztop = []; - zbot = []; - vtop = []; - vbot = [] - infile = open(filename, 'r') - nlayers = readMygridNlayers(filename) - - print('\nreadMygrid: Reading file %s.' % filename) - for index in range(nlayers): - line1 = infile.readline() - line2 = infile.readline() - ztop.append(float(line1.split()[0])) - vtop.append(float(line1.split()[1])) - zbot.append(float(line2.split()[0])) - vbot.append(float(line2.split()[1])) - print('Layer %s:\n[Top: v = %s [km/s], z = %s [m]]' - '\n[Bot: v = %s [km/s], z = %s [m]]' - % (index + 1, vtop[index], ztop[index], - vbot[index], zbot[index])) - - if not ztop[0] == 0: - print('ERROR: there must be a velocity set for z = 0 in the file %s' % filename) - print('e.g.:\n0 0.33\n-5 1.0\netc.') - - infile.close() - return ztop, zbot, vtop, vbot - - -class SeisArray(object): - ''' - Can be used to interpolate missing values of a receiver grid, if only support points were measured. - Input file should contain in each line: ('traceID' 'receiverLineID' 'number of the geophone on recLine' 'X' 'Y' 'Z') - - Can be used to generate a velocity grid file (vgrids.in) for FMTOMO with a topography adapting gradient. - - Can be used to generate an interface file for FMTOMO (right now only interface.z used by grid3dg) for the topography. - - Supports vtk output for sources and receivers. - Note: Source and Receiver files for FMTOMO will be generated by the Survey object (because traveltimes will be added directly). - ''' - - def __init__(self, recfile, interpolatable = False): - self.recfile = recfile - self._receiverCoords = {} - self._measuredReceivers = {} - self._measuredTopo = {} - self._sourceCoords = {} - self._receiverlist = open(self.recfile, 'r').readlines() - if interpolatable == True: - self._init_interpolatable() - elif interpolatable == False: - self._init_normal() - self.set2D() - - def _init_normal(self): - self.interpolatable = False - self._setReceiverCoords_normal() - - def _init_interpolatable(self): - self.interpolatable = True - self._receiverlines = {} - self._geophoneNumbers = {} - self._setReceiverCoords_interp() - self._setGeophoneNumbers() - self._generateReceiverlines() - - def _generateReceiverlines(self): - ''' - Connects the traceIDs to the lineIDs - for each receiverline in a dictionary. - ''' - for receiver in self._receiverlist: - traceID = int(receiver.split()[0]) - lineID = int(receiver.split()[1]) - if not lineID in self._receiverlines.keys(): - self._receiverlines[lineID] = [] - self._receiverlines[lineID].append(traceID) - - def _setReceiverCoords_interp(self): - ''' - Fills the three x, y, z dictionaries with measured coordinates - in case of interpolatable measured input data. - ''' - for line in self._getReceiverlist(): - traceID = int(line.split()[0]) - x = float(line.split()[3]) - y = float(line.split()[4]) - z = float(line.split()[5]) - self._receiverCoords[traceID] = (x, y, z) - self._measuredReceivers[traceID] = (x, y, z) - - def _setReceiverCoords_normal(self): - ''' - Fills the three x, y, z dictionaries with measured coordinates - ''' - for line in self._getReceiverlist(): - traceID = int(line.split()[0]) - x = float(line.split()[1]) - y = float(line.split()[2]) - z = float(line.split()[3]) - self._receiverCoords[traceID] = (x, y, z) - self._measuredReceivers[traceID] = (x, y, z) - - def _setGeophoneNumbers(self): - for line in self._getReceiverlist(): - traceID = int(line.split()[0]) - gphoneNum = float(line.split()[2]) - self._geophoneNumbers[traceID] = gphoneNum - - def check2D(self): - x, y, z = self.getAllMeasuredPointsLists() - if self._check0(x) or self._check0(y): - return True - else: - return False - - def set2D(self): - if self.check2D(): - self.twoDim = True - else: - self.twoDim = False - - def _check0(self, lst): - for element in lst: - if element == 0: - pass - else: - return False - return True - - def _getReceiverlines(self): - return self._receiverlines - - def _getReceiverlist(self): - return self._receiverlist - - def getReceiverCoordinates(self): - return self._receiverCoords - - def _getXreceiver(self, traceID): - return self._receiverCoords[traceID][0] - - def _getYreceiver(self, traceID): - return self._receiverCoords[traceID][1] - - def _getZreceiver(self, traceID): - return self._receiverCoords[traceID][2] - - def _getXshot(self, shotnumber): - return self._sourceCoords[shotnumber][0] - - def _getYshot(self, shotnumber): - return self._sourceCoords[shotnumber][1] - - def _getZshot(self, shotnumber): - return self._sourceCoords[shotnumber][2] - - def _getReceiverValue(self, traceID, coordinate): - setCoordinate = {'X': self._getXreceiver, - 'Y': self._getYreceiver, - 'Z': self._getZreceiver} - return setCoordinate[coordinate](traceID) - - def _getGeophoneNumber(self, traceID): - return self._geophoneNumbers[traceID] - - def getMeasuredReceivers(self): - return self._measuredReceivers - - def getMeasuredTopo(self): - return self._measuredTopo - - def getSourceCoordinates(self): - return self._sourceCoords - - def _setXvalue(self, traceID, value): - self._checkKey(traceID) - self._receiverCoords[traceID][0] = value - - def _setYvalue(self, traceID, value): - self._checkKey(traceID) - self._receiverCoords[traceID][1] = value - - def _setZvalue(self, traceID, value): - self._checkKey(traceID) - self._receiverCoords[traceID][2] = value - - def _setValue(self, traceID, coordinate, value): - setCoordinate = {'X': self._setXvalue, - 'Y': self._setYvalue, - 'Z': self._setZvalue} - setCoordinate[coordinate](traceID, value) - - def _checkKey(self, traceID): - if not traceID in self._receiverCoords.keys(): - self._receiverCoords[traceID] = [None, None, None] - - def _checkTraceIDdirection(self, traceID1, traceID2): - if traceID2 > traceID1: - direction = +1 - return direction - if traceID2 < traceID1: - direction = -1 - return direction - err_msg = "Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) - raise RuntimeError(err_msg) - - def _checkCoordDirection(self, traceID1, traceID2, coordinate): - ''' - Checks whether the interpolation direction is positive or negative - ''' - if self._getReceiverValue(traceID1, coordinate) < self._getReceiverValue(traceID2, coordinate): - direction = +1 - return direction - if self._getReceiverValue(traceID1, coordinate) > self._getReceiverValue(traceID2, coordinate): - direction = -1 - return direction - err_msg = "Same Value for traceID1 = %s and traceID2 = %s" % (traceID1, traceID2) - raise RuntimeError(err_msg) - - def _interpolateMeanDistances(self, traceID1, traceID2, coordinate): - ''' - Returns the mean distance between two traceID's depending on the number of geophones in between - ''' - num_spaces = abs(self._getGeophoneNumber(traceID1) - self._getGeophoneNumber(traceID2)) - mean_distance = abs( - self._getReceiverValue(traceID1, coordinate) - self._getReceiverValue(traceID2, coordinate)) / num_spaces - return mean_distance - - def checkInterpolatable(self): - if self.interpolatable == False: - print('Not interpolatable.') - return self.interpolatable - - def interpolateValues(self, coordinate): - ''' - Interpolates and sets all values (linear) for coordinate = 'X', 'Y' or 'Z' - ''' - if not self.checkInterpolatable(): - return - for lineID in self._getReceiverlines().keys(): - number_measured = len(self._getReceiverlines()[lineID]) - for index, traceID1 in enumerate(self._getReceiverlines()[lineID]): - if index + 1 < number_measured: - traceID2 = self._getReceiverlines()[lineID][index + 1] - - traceID_dir = self._checkTraceIDdirection(traceID1, traceID2) - traceID_interp = traceID1 + traceID_dir - - coord_dir = self._checkCoordDirection(traceID1, traceID2, coordinate) - mean_distance = self._interpolateMeanDistances(traceID1, traceID2, coordinate) - - while (traceID_dir * traceID_interp) < (traceID_dir * traceID2): - self._setValue(traceID_interp, coordinate, - (self._getReceiverValue(traceID_interp - traceID_dir, coordinate) - + coord_dir * (mean_distance))) - traceID_interp += traceID_dir - - def addMeasuredTopographyPoints(self, filename): - ''' - Use more measured points for a higher precision of height interpolation. - Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') - ''' - topolist = open(filename, 'r').readlines() - for line in topolist: - line = line.split() - pointID = int(line[0]) - x = float(line[1]) - y = float(line[2]) - z = float(line[3]) - self._measuredTopo[pointID] = (x, y, z) - - def addSourceLocations(self, filename): - ''' - Use source locations for a higher precision of height interpolation. - Input file should contain in each line: ('point ID' 'X' 'Y' 'Z') - - Source locations must be added before they can be written to vtk files. - ''' - topolist = open(filename, 'r').readlines() - for line in topolist: - line = line.split() - pointID = int(line[0]) - x = float(line[1]) - y = float(line[2]) - z = float(line[3]) - self._sourceCoords[pointID] = (x, y, z) - - def interpZcoords4rec(self, method='linear'): - ''' - Interpolates z values for all receivers. - ''' - measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() - - for traceID in self.getReceiverCoordinates().keys(): - if type(self.getReceiverCoordinates()[traceID]) is not tuple: - z = griddata((measured_x, measured_y), measured_z, - (self._getXreceiver(traceID), self._getYreceiver(traceID)), method=method) - self._setZvalue(traceID, float(z)) - - def _getAngle(self, distance): - ''' - Function returns the angle on a Sphere of the radius R = 6371 [km] for a distance [km]. - ''' - PI = np.pi - R = 6371. - angle = distance * 180. / (PI * R) - return angle - - def _getDistance(self, angle): - ''' - Function returns the distance [km] on a Sphere of the radius R = 6371 [km] for an angle. - ''' - PI = np.pi - R = 6371. - distance = angle / 180. * (PI * R) - return distance - - def getMeasuredReceiverLists(self): - ''' - Returns a list of all measured receivers known to SeisArray. - ''' - x = []; - y = []; - z = [] - for traceID in self.getMeasuredReceivers().keys(): - x.append(self.getMeasuredReceivers()[traceID][0]) - y.append(self.getMeasuredReceivers()[traceID][1]) - z.append(self.getMeasuredReceivers()[traceID][2]) - return x, y, z - - def getMeasuredTopoLists(self): - ''' - Returns a list of all measured topography points known to the SeisArray. - ''' - x = []; - y = []; - z = [] - for pointID in self.getMeasuredTopo().keys(): - x.append(self.getMeasuredTopo()[pointID][0]) - y.append(self.getMeasuredTopo()[pointID][1]) - z.append(self.getMeasuredTopo()[pointID][2]) - return x, y, z - - def getSourceLocsLists(self): - ''' - Returns a list of all measured source locations known to SeisArray. - ''' - x = []; - y = []; - z = [] - for pointID in self.getSourceCoordinates().keys(): - x.append(self.getSourceCoordinates()[pointID][0]) - y.append(self.getSourceCoordinates()[pointID][1]) - z.append(self.getSourceCoordinates()[pointID][2]) - return x, y, z - - def getAllMeasuredPointsLists(self): - ''' - Returns a list of all measured points known to SeisArray. - ''' - mtopo_x, mtopo_y, mtopo_z = self.getMeasuredTopoLists() - msource_x, msource_y, msource_z = self.getSourceLocsLists() - mrec_x, mrec_y, mrec_z = self.getMeasuredReceiverLists() - - x = mtopo_x + mrec_x + msource_x - y = mtopo_y + mrec_y + msource_y - z = mtopo_z + mrec_z + msource_z - return x, y, z - - def getReceiverLists(self): - ''' - Returns a list of all receivers (measured and interpolated). - ''' - x = []; - y = []; - z = [] - for traceID in self.getReceiverCoordinates().keys(): - x.append(self.getReceiverCoordinates()[traceID][0]) - y.append(self.getReceiverCoordinates()[traceID][1]) - z.append(self.getReceiverCoordinates()[traceID][2]) - return x, y, z - - def _interpolateXY4rec(self): - ''' - Interpolates the X and Y coordinates for all receivers. - ''' - for coordinate in ('X', 'Y'): - self.interpolateValues(coordinate) - - def interpolateAll(self): - if not self.checkInterpolatable(): - return - self._interpolateXY4rec() - self.interpZcoords4rec() - print('Interpolated receiver locations.') - - def interpolateTopography(self, nTheta, nPhi, thetaSN, phiWE, elevation=0.25, method='linear'): - ''' - Interpolate Z values on a regular grid with cushion nodes e.g. to use it as FMTOMO topography interface. - Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). - - :param: nTheta, number of points in theta (NS) - type: integer - - :param: nPhi, number of points in phi (WE) - type: integer - - :param: thetaSN (S, N) extensions of the model in degree - type: tuple - - :param: phiWE (W, E) extensions of the model in degree - type: tuple - - :param: elevation, default: 0.25 (elevate topography so that no source lies above the surface) - type: float - ''' - return self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, elevation, method) - - def interpolateOnRegularGrid(self, nTheta, nPhi, thetaSN, phiWE, elevation, method='linear'): - ''' - Interpolate Z values on a regular grid with cushion nodes e.g. to use it as FMTOMO topography interface. - Returns a surface in form of a list of points [[x1, y1, z1], [x2, y2, y2], ...] (cartesian). - - :param: nTheta, number of points in theta (NS) - type: integer - - :param: nPhi, number of points in phi (WE) - type: integer - - :param: thetaSN (S, N) extensions of the model in degree - type: tuple - - :param: phiWE (W, E) extensions of the model in degree - type: tuple - - :param: elevation, default: 0.25 (elevate topography so that no source lies above the surface) - type: float - ''' - - surface = [] - - print "Interpolating interface on regular grid with the dimensions:" - print "nTheta = %s, nPhi = %s, thetaSN = %s, phiWE = %s" % (nTheta, nPhi, thetaSN, phiWE) - print "method = %s, elevation = %s" % (method, elevation) - - thetaS, thetaN = thetaSN - phiW, phiE = phiWE - - measured_x, measured_y, measured_z = self.getAllMeasuredPointsLists() - - # need to determine the delta to add two cushion nodes around the min/max values - deltaTheta = (thetaN - thetaS) / (nTheta - 1) - deltaPhi = (phiE - phiW) / (nPhi - 1) - - thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num=nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num=nPhi + 2) # +2 cushion nodes - - nTotal = len(thetaGrid) * len(phiGrid); - count = 0 - for theta in thetaGrid: - for phi in phiGrid: - xval = self._getDistance(phi) - yval = self._getDistance(theta) - - z = griddata((measured_x, measured_y), measured_z, (xval, yval), method=method) - # in case the point lies outside, nan will be returned. Find nearest: - if np.isnan(z) == True: - z = griddata((measured_x, measured_y), measured_z, (xval, yval), method='nearest') - z = float(z) + elevation - surface.append((xval, yval, z)) - count += 1 - progress = float(count) / float(nTotal) * 100 - self._update_progress(progress) - - return surface - - def generateFMTOMOinputFromArray(self, nPointsPropgrid, nPointsInvgrid, - zBotTop, cushionfactor, interpolationMethod='linear', - customgrid='mygrid.in', writeVTK=True): - ''' - Generate FMTOMO input files from the SeisArray dimensions. - Generates: vgridsref.in, interfacesref.in, propgrid.in - - :param: nPointsPropgrid, number of points in each direction of the propagation grid (x, y, z) - :type: tuple - - :param: nPointsInvgrid, number of points in each direction of the inversion grid (x, y, z) - :type: tuple - - :param: zBotTop, (bottom, top) dimensions of the model - :type: tuple - - :param: cushionfactor, adds cushioning around the model (0.1 = 10%) - :type: float - ''' - - nPhiP, nThetaP, nRP = nPointsPropgrid - nPhiI, nThetaI, nRI = nPointsInvgrid - - print('\n------------------------------------------------------------') - print('Automatically generating input for FMTOMO from array size.') - print('Propgrid: nR = %s, nTheta = %s, nPhi = %s' % (nRP, nThetaP, nPhiP)) - print('Interpolation Grid and Interfaces Grid: nR = %s, nTheta = %s, nPhi = %s' % (nRI, nThetaI, nPhiI)) - print('Bottom and Top of model: (%s, %s)' % (zBotTop[0], zBotTop[1])) - print('Method: %s, customgrid = %s' % (interpolationMethod, customgrid)) - print('------------------------------------------------------------') - - def getZmin(surface): - z = [] - for point in surface: - z.append(point[2]) - return min(z) - - self.generatePropgrid(nThetaP, nPhiP, nRP, zBotTop, cushionfactor=cushionfactor, - cushionpropgrid=0.05) - surface = self.generateVgrid(nThetaI, nPhiI, nRI, zBotTop, method=interpolationMethod, - cushionfactor=cushionfactor, infilename=customgrid, - returnTopo=True) - - depthmax = abs(zBotTop[0] - getZmin(surface)) - 1.0 # cushioning for the bottom interface - - interf1, interf2 = self.generateInterfaces(nThetaI, nPhiI, depthmax, cushionfactor=cushionfactor, - returnInterfaces=True, method=interpolationMethod) - - if writeVTK == True: - from pylot.core.active import fmtomoUtils - self.surface2VTK(interf1, filename='interface1.vtk') - self.surface2VTK(interf2, filename='interface2.vtk') - self.receivers2VTK() - self.sources2VTK() - #fmtomoUtils.vgrids2VTK() - - def generateReceiversIn(self, outfilename='receivers.in'): - outfile = open(outfilename, 'w') - - recx, recy, recz = self.getReceiverLists() - nsrc = len(self.getSourceCoordinates()) - outfile.write('%s\n' % (len(zip(recx, recy, recz)) * nsrc)) - - for index in range(nsrc): - for point in zip(recx, recy, recz): - rx, ry, rz = point - rad = - rz - lat = self._getAngle(ry) - lon = self._getAngle(rx) - outfile.write('%15s %15s %15s\n' % (rad, lat, lon)) - outfile.write('%15s\n' % (1)) - outfile.write('%15s\n' % (index + 1)) - outfile.write('%15s\n' % (1)) - - outfile.close() - - def generateInterfaces(self, nTheta, nPhi, depthmax, cushionfactor=0.1, - outfilename='interfacesref.in', method='linear', - returnInterfaces=False): - ''' - Create an interfacesref.in file for FMTOMO from the SeisArray boundaries. - :param: nTheta, number of points in Theta - type: int - - :param: nPhi, number of points in Phi - type: int - - :param: depthmax, maximum depth of the model (below topography) - type: float - - :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) - type: float - ''' - - print('\n------------------------------------------------------------') - print('Generating interfaces...') - nInterfaces = 2 - - # generate dimensions of the grid from array - thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) - - thetaS, thetaN = thetaSN - phiW, phiE = phiWE - R = 6371. - - outfile = open(outfilename, 'w') - - # determine the deltas - deltaTheta = abs(thetaN - thetaS) / float((nTheta - 1)) - deltaPhi = abs(phiE - phiW) / float((nPhi - 1)) - - # write header for interfaces grid file (in RADIANS) - outfile.write('%10s\n' % (nInterfaces)) - outfile.write('%10s %10s\n' % (nTheta + 2, nPhi + 2)) # +2 cushion nodes - outfile.write('%10s %10s\n' % (np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) - outfile.write('%10s %10s\n' % (np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) - - interface1 = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method=method) - interface2 = self.interpolateOnRegularGrid(nTheta, nPhi, thetaSN, phiWE, -depthmax, method=method) - - for point in interface1: - z = point[2] - outfile.write('%10s\n' % (z + R)) - - outfile.write('\n') - for point in interface2: - z = point[2] - outfile.write('%10s\n' % (z + R)) - - outfile.close() - - if returnInterfaces == True: - return interface1, interface2 - - print('Finished generating interfaces.') - print('------------------------------------------------------------') - - def getThetaPhiFromArray(self, cushionfactor=0.1): - ''' - Determine and returns PhiWE (tuple: (West, East)) and thetaSN (tuple (South, North)) from the SeisArray boundaries. - - :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) - type: float - ''' - x, y, _ = self.getAllMeasuredPointsLists() - phi_min, phi_max = (self._getAngle(min(x)), self._getAngle(max(x))) - theta_min, theta_max = (self._getAngle(min(y)), self._getAngle(max(y))) - cushionPhi = abs(phi_max - phi_min) * cushionfactor - cushionTheta = abs(theta_max - theta_min) * cushionfactor - # 2D Case only: - if cushionPhi == 0.: - cushionPhi = 0.1 - if cushionTheta == 0.: - cushionTheta = 0.1 - phiWE = (phi_min - cushionPhi, phi_max + cushionPhi) - thetaSN = (theta_min - cushionTheta, theta_max + cushionTheta) - return thetaSN, phiWE - - def generatePropgrid(self, nTheta, nPhi, nR, Rbt, cushionfactor, cushionpropgrid=0.05, - refinement=(5, 5), outfilename='propgrid.in'): - ''' - Create a propergation grid file for FMTOMO using SeisArray boundaries - - :param: nTheta, number of points in Theta - type: int - - :param: nPhi, number of points in Phi - type: int - - :param: nR, number of points in R - type: int - - :param: Rbt (bot, top) extensions of the model in m - type: tuple - - :param: cushionfactor, add some extra space to the model (default: 0.1 = 10%) - type: float - - :param: cushionpropogrid, cushionfactor for the propagationgrid (cushion direction - opposing to vgrids cushionfactor) - type: float - - :param: refinement, (refinement factor, number of local cells for refinement) used by FMTOMO - type: tuple - ''' - outfile = open(outfilename, 'w') - - print('\n------------------------------------------------------------') - print('Generating Propagation Grid for nTheta = %s, nPhi' - ' = %s, nR = %s and a cushioning of %s' - % (nTheta, nPhi, nR, cushionpropgrid)) - print('Bottom of the grid: %s, top of the grid %s' - % (Rbt[0], Rbt[1])) - - thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) - - # +2 cushion nodes - nTheta += 2 - nPhi += 2 - nR += 2 - - thetaS = thetaSN[0] + cushionpropgrid - thetaN = thetaSN[1] - cushionpropgrid - phiW = phiWE[0] + cushionpropgrid - phiE = phiWE[1] - cushionpropgrid - rbot = Rbt[0] - rtop = Rbt[1] - - deltaTheta = abs(thetaN - thetaS) / float(nTheta - 1) - deltaPhi = abs(phiE - phiW) / float(nPhi - 1) - deltaR = abs(rbot - rtop) / float(nR - 1) - - outfile.write('%10s %10s %10s\n' % (nR, nTheta, nPhi)) - outfile.write('%10s %10s %10s\n' % (deltaR, deltaTheta, deltaPhi)) - outfile.write('%10s %10s %10s\n' % (rtop, thetaS, phiW)) - outfile.write('%10s %10s\n' % refinement) - - outfile.close() - - print('Created Propagation Grid and saved it to %s' % outfilename) - print('------------------------------------------------------------') - - def generateVgrid(self, nTheta, nPhi, nR, Rbt, thetaSN=None, - phiWE=None, cushionfactor=0.1, - outfilename='vgridsref.in', method='linear', - infilename='mygrid.in', returnTopo=False): - ''' - Generate a velocity grid for fmtomo regarding topography with a linear gradient starting at the topography with 0.34 [km/s]. - - :param: nTheta, number of points in theta (NS) - type: integer - - :param: nPhi, number of points in phi (WE) - type: integer - - :param: nR, number of points in depth - type: integer - - :param: thetaSN (S, N) extensions of the model in degree - type: tuple - - :param: phiWE (W, E) extensions of the model in degree - type: tuple - - :param: Rbt (bot, top) extensions of the model in m - type: tuple - - :param: vbot, velocity at the bottom of the model - type: real - - :param: method, interpolation method for topography - type: str - ''' - print('\n------------------------------------------------------------') - print('generateVgrid: Starting...') - - # def getRad(angle): - # PI = np.pi - # rad = angle / 180 * PI - # return rad - - R = 6371. - vmin = 0.34 - decm = 0.3 # diagonal elements of the covariance matrix (grid3dg's default value is 0.3) - outfile = open(outfilename, 'w') - - # generate dimensions of the grid from array - if thetaSN is None and phiWE is None: - thetaSN, phiWE = self.getThetaPhiFromArray(cushionfactor) - - thetaS, thetaN = thetaSN - phiW, phiE = phiWE - rbot = Rbt[0] + R - rtop = Rbt[1] + R - - # need to determine the delta to add two cushion nodes around the min/max values - deltaTheta = abs(thetaN - thetaS) / float((nTheta - 1)) - deltaPhi = abs(phiE - phiW) / float((nPhi - 1)) - deltaR = abs(rbot - rtop) / float((nR - 1)) - - # create a regular grid including +2 cushion nodes in every direction - thetaGrid = np.linspace(thetaS - deltaTheta, thetaN + deltaTheta, num=nTheta + 2) # +2 cushion nodes - phiGrid = np.linspace(phiW - deltaPhi, phiE + deltaPhi, num=nPhi + 2) # +2 cushion nodes - rGrid = np.linspace(rbot - deltaR, rtop + deltaR, num=nR + 2) # +2 cushion nodes - - nTotal = len(rGrid) * len(thetaGrid) * len(phiGrid) - print("Total number of grid nodes: %s" % nTotal) - - # write header for velocity grid file (in RADIANS) - outfile.write('%10s %10s \n' % (1, 1)) - outfile.write('%10s %10s %10s\n' % (nR + 2, nTheta + 2, nPhi + 2)) - outfile.write('%10s %10s %10s\n' % (deltaR, np.deg2rad(deltaTheta), np.deg2rad(deltaPhi))) - outfile.write( - '%10s %10s %10s\n' % (rbot - deltaR, np.deg2rad(thetaS - deltaTheta), np.deg2rad(phiW - deltaPhi))) - - surface = self.interpolateTopography(nTheta, nPhi, thetaSN, phiWE, method=method) - - nlayers = readMygridNlayers(infilename) - ztop, zbot, vtop, vbot = readMygrid(infilename) - - print("\nGenerating velocity grid for FMTOMO. " - "Output filename = %s, interpolation method = %s" % (outfilename, method)) - print("nTheta = %s, nPhi = %s, nR = %s, " - "thetaSN = %s, phiWE = %s, Rbt = %s" % (nTheta, nPhi, nR, thetaSN, phiWE, Rbt)) - count = 0 - - for radius in rGrid: - for theta in thetaGrid: - for phi in phiGrid: - xval = self._getDistance(phi) - yval = self._getDistance(theta) - for point in surface: - if point[0] == xval and point[1] == yval: - topo = point[2] - break - depth = -(R + topo - radius) - if depth > 1: - vel = 0.0 - elif 1 >= depth > 0: # cushioning around topography - vel = vtop[0] - else: - for index in range(nlayers): - if (ztop[index]) >= depth > (zbot[index]): - vel = (depth - ztop[index]) / (zbot[index] - ztop[index]) * ( - vbot[index] - vtop[index]) + vtop[index] - break - if not (ztop[index]) >= depth > (zbot[index]): - err_msg = 'ERROR in grid inputfile, could not find velocity' - 'for a z-value of %s in the inputfile' % (depth - topo) - raise ValueError(err_msg) - count += 1 - if not vel >= 0: - err_msg = 'vel < 0; z, topo, zbot, vbot, vtop:', - depth, topo, zbot[index], vbot[index], vtop[index] - raise ValueError(err_msg) - - outfile.write('%10s %10s\n' % (vel, decm)) - - progress = float(count) / float(nTotal) * 100 - self._update_progress(progress) - - print('\nWrote %d points to file %s for %d layers' % (count, outfilename, nlayers)) - print('------------------------------------------------------------') - outfile.close() - - if returnTopo == True: - return surface - - def exportAll(self, filename='interpolated_receivers.out'): - ''' - Exports all receivers to an input file for ActiveSeismoPick3D. - ''' - recfile_out = open(filename, 'w') - count = 0 - for traceID in self.getReceiverCoordinates().keys(): - count += 1 - x, y, z = self.getReceiverCoordinates()[traceID] - recfile_out.write('%5s %15s %15s %15s\n' % (traceID, x, y, z)) - print "Exported coordinates for %s traces to file > %s" % (count, filename) - recfile_out.close() - - def plotArray2D(self, ax = None, plot_topo=False, highlight_measured=False, annotations=True, pointsize=10, twoDim = False): - import matplotlib.pyplot as plt - if ax == None: - plt.interactive(True) - fig = plt.figure() - ax = plt.axes() - xmt, ymt, zmt = self.getMeasuredTopoLists() - xsc, ysc, zsc = self.getSourceLocsLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xrc, yrc, zrc = self.getReceiverLists() - - if len(xrc) > 0: - ax.plot(xrc, yrc, 'k.', markersize=pointsize, label='all receivers') - if len(xsc) > 0: - ax.plot(xsc, ysc, 'b*', markersize=pointsize, label='shot locations') - - if plot_topo == True: - ax.plot(xmt, ymt, 'b.', markersize=pointsize, label='measured topo points') - if highlight_measured == True: - ax.plot(xmr, ymr, 'r.', markersize=pointsize, label='measured receivers') - - ax.figure.text(0.5, 0.95,'2D plot of seismic array\n %s'%self.recfile, - horizontalalignment='center', verticalalignment='center', - transform = ax.figure.transFigure) - - ax.set_xlabel('X [m]') - ax.set_ylabel('Y [m]') - if twoDim == False: - ax.set_aspect('equal') - ax.legend(prop={'size':7}) - if annotations == True: - for traceID in self.getReceiverCoordinates().keys(): - ax.annotate((' ' + str(traceID)), xy=(self._getXreceiver(traceID), self._getYreceiver(traceID)), - fontsize='x-small', color='k') - for shotnumber in self.getSourceCoordinates().keys(): - ax.annotate((' ' + str(shotnumber)), xy=(self._getXshot(shotnumber), self._getYshot(shotnumber)), - fontsize='x-small', color='b') - - def plotArray3D(self, ax=None, legend = True, markersize = 10): - import matplotlib.pyplot as plt - from mpl_toolkits.mplot3d import Axes3D - - if ax == None: - plt.interactive(True) - fig = plt.figure() - ax = plt.axes(projection='3d') - - xmt, ymt, zmt = self.getMeasuredTopoLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - xrc, yrc, zrc = self.getReceiverLists() - xsc, ysc, zsc = self.getSourceLocsLists() - - ax.figure.text(0.5, 0.95,'3D plot of seismic array\n %s'%self.recfile, - horizontalalignment='center', verticalalignment='center', - transform = ax.figure.transFigure) - - if len(xmt) > 0: - ax.plot(xmt, ymt, zmt, 'b.', markersize=markersize, label='measured topo points') - if len(xrc) > 0: - ax.plot(xrc, yrc, zrc, 'k.', markersize=markersize, label='all receivers') - if len(xmr) > 0: - ax.plot(xmr, ymr, zmr, 'ro', markersize=markersize, label='measured receivers') - if len(xsc) > 0: - ax.plot(xsc, ysc, zsc, 'b*', markersize=markersize, label='shot locations') - ax.set_xlabel('X [m]'); - ax.set_ylabel('Y [m]'); - ax.set_zlabel('Z [m]') - if legend == True: - ax.legend() - - return ax - - def plotSurface3D(self, ax=None, step=0.5, method='linear', exag=False, twoDim = False): - from matplotlib import cm - import matplotlib.pyplot as plt - from mpl_toolkits.mplot3d import Axes3D - - if ax == None: - plt.interactive(True) - fig = plt.figure() - ax = plt.axes(projection='3d') - - xmt, ymt, zmt = self.getMeasuredTopoLists() - xmr, ymr, zmr = self.getMeasuredReceiverLists() - - x = xmt + xmr - y = ymt + ymr - z = zmt + zmr - - xaxis = np.arange(min(x) + 1, max(x), step) - yaxis = np.arange(min(y) + 1, max(y), step) - - xgrid, ygrid = np.meshgrid(xaxis, yaxis) - - zgrid = griddata((x, y), z, (xgrid, ygrid), method=method) - - surf = ax.plot_surface(xgrid, ygrid, zgrid, linewidth=0, cmap=cm.jet, vmin=min(z), vmax=max(z)) - cbar = ax.figure.colorbar(surf) - cbar.set_label('Elevation [m]') - - if exag == False: - ax.set_zlim(-(max(x) - min(x) / 2), (max(x) - min(x) / 2)) - if twoDim == False: - ax.set_aspect('equal') - - ax.set_xlabel('X [m]'); - ax.set_ylabel('Y [m]'); - ax.set_zlabel('Z [m]') - ax.legend() - - return ax - - def _update_progress(self, progress): - sys.stdout.write("%d%% done \r" % (progress)) - sys.stdout.flush() - - def surface2VTK(self, surface, filename='surface.vtk'): - ''' - Generates a vtk file from all points of a surface as generated by interpolateTopography. - ''' - outfile = open(filename, 'w') - - nPoints = len(surface) - - # write header - print("Writing header for VTK file...") - outfile.write('# vtk DataFile Version 3.1\n') - outfile.write('Surface Points\n') - outfile.write('ASCII\n') - outfile.write('DATASET POLYDATA\n') - outfile.write('POINTS %15d float\n' % (nPoints)) - - # write coordinates - print("Writing coordinates to VTK file...") - for point in surface: - x = point[0] - y = point[1] - z = point[2] - - outfile.write('%10f %10f %10f \n' % (x, y, z)) - - outfile.write('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) - - # write indices - print("Writing indices to VTK file...") - for index in range(nPoints): - outfile.write('%10d %10d\n' % (1, index)) - - # outfile.write('POINT_DATA %15d\n' %(nPoints)) - # outfile.write('SCALARS traceIDs int %d\n' %(1)) - # outfile.write('LOOKUP_TABLE default\n') - - # # write traceIDs - # print("Writing traceIDs to VTK file...") - # for traceID in traceIDs: - # outfile.write('%10d\n' %traceID) - - outfile.close() - print("Wrote %d points to file: %s" % (nPoints, filename)) - return - - def receivers2VTK(self, filename='receivers.vtk'): - ''' - Generates a vtk file from all receivers of the SeisArray object. - ''' - outfile = open(filename, 'w') - traceIDs = [] - - for traceID in self.getReceiverCoordinates(): - traceIDs.append(traceID) - - nPoints = len(traceIDs) - - # write header - print("Writing header for VTK file...") - outfile.write('# vtk DataFile Version 3.1\n') - outfile.write('Receivers with traceIDs\n') - outfile.write('ASCII\n') - outfile.write('DATASET POLYDATA\n') - outfile.write('POINTS %15d float\n' % (nPoints)) - - # write coordinates - print("Writing coordinates to VTK file...") - for traceID in traceIDs: - x = self._getXreceiver(traceID) - y = self._getYreceiver(traceID) - z = self._getZreceiver(traceID) - - outfile.write('%10f %10f %10f \n' % (x, y, z)) - - outfile.write('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) - - # write indices - print("Writing indices to VTK file...") - for index in range(nPoints): - outfile.write('%10d %10d\n' % (1, index)) - - outfile.write('POINT_DATA %15d\n' % (nPoints)) - outfile.write('SCALARS traceIDs int %d\n' % (1)) - outfile.write('LOOKUP_TABLE default\n') - - # write traceIDs - print("Writing traceIDs to VTK file...") - for traceID in traceIDs: - outfile.write('%10d\n' % traceID) - - outfile.close() - print("Wrote %d receiver for to file: %s" % (nPoints, filename)) - return - - def sources2VTK(self, filename='sources.vtk'): - ''' - Generates a vtk-file for all source locations in the SeisArray object. - ''' - outfile = open(filename, 'w') - shotnumbers = [] - - for shotnumber in self.getSourceCoordinates(): - shotnumbers.append(shotnumber) - - nPoints = len(shotnumbers) - - # write header - print("Writing header for VTK file...") - outfile.write('# vtk DataFile Version 3.1\n') - outfile.write('Shots with shotnumbers\n') - outfile.write('ASCII\n') - outfile.write('DATASET POLYDATA\n') - outfile.write('POINTS %15d float\n' % (nPoints)) - - # write coordinates - print("Writing coordinates to VTK file...") - for shotnumber in shotnumbers: - x = self._getXshot(shotnumber) - y = self._getYshot(shotnumber) - z = self._getZshot(shotnumber) - - outfile.write('%10f %10f %10f \n' % (x, y, z)) - - outfile.write('VERTICES %15d %15d\n' % (nPoints, 2 * nPoints)) - - # write indices - print("Writing indices to VTK file...") - for index in range(nPoints): - outfile.write('%10d %10d\n' % (1, index)) - - outfile.write('POINT_DATA %15d\n' % (nPoints)) - outfile.write('SCALARS shotnumbers int %d\n' % (1)) - outfile.write('LOOKUP_TABLE default\n') - - # write shotnumber - print("Writing shotnumbers to VTK file...") - for shotnumber in shotnumbers: - outfile.write('%10d\n' % shotnumber) - - outfile.close() - print("Wrote %d sources to file: %s" % (nPoints, filename)) - return - - def saveSeisArray(self, filename='seisArray.pickle'): - import cPickle - outfile = open(filename, 'wb') - - cPickle.dump(self, outfile, -1) - print('saved SeisArray to file %s' % (filename)) - - @staticmethod - def from_pickle(filename): - import cPickle - infile = open(filename, 'rb') - return cPickle.load(infile) diff --git a/pylot/core/active/seismicshot.py b/pylot/core/active/seismicshot.py deleted file mode 100644 index e74ecaed..00000000 --- a/pylot/core/active/seismicshot.py +++ /dev/null @@ -1,957 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import numpy as np -from obspy.core import read -from obspy import Stream -from obspy import Trace -from pylot.core.pick.charfuns import HOScf -from pylot.core.pick.charfuns import AICcf -from pylot.core.pick.utils import getSNR -from pylot.core.pick.utils import earllatepicker -import matplotlib.pyplot as plt -import warnings -import copy_reg -import types -from pylot.core.util.utils import worker, _pickle_method - -copy_reg.pickle(types.MethodType, _pickle_method) - -plt.interactive('True') - -class SeismicShot(object): - ''' - SuperClass for a seismic shot object. - ''' - def __init__(self, obsfile): - ''' - Initialize seismic shot object giving an inputfile. - - :param: obsfile, ((!SEG2/SEGY!)) file readable by obspy - :type: string - ''' - self.traces = read(obsfile) - self.renameChannelIDs() - # self.recCoordlist = None - # self.srcCoordlist = None - self.traceIDs = None - self.picks = {} - self.pwindow = {} - self.manualpicks = {} - self.snr = {} - self.snrthreshold = {} - self.timeArray = {} - self.traces4plot = {} - self.paras = {} - self.paras['shotname'] = obsfile - self.folm = None - - def renameChannelIDs(self): - for trace in self.traces: - if trace.stats._format == 'SEG2': - trace.stats.channel = int(trace.stats.seg2['CHANNEL_NUMBER']) - - def removeEmptyTraces(self): - traceIDs = [] - removed = [] - traceIDs = self.getReceiverCoords().keys() - - for trace in self.traces: - try: - traceIDs.index(int(trace.stats.channel)) - except: - self.traces.remove(trace) - removed.append(int(trace.stats.channel)) - - if len(removed) > 0: - return removed - - def removeTrace(self, traceID): - for trace in self.traces: - if traceID == trace.stats.channel: - self.traces.remove(trace) - - def updateTraceList(self): - ''' - Looks for empty traces, returns a list of deleted traceIDs. - ''' - traceIDs = [] - for traceID in self.getTraceIDlist(): - if traceID not in self.getStreamTraceIDs(): - self.traceIDs.remove(traceID) - traceIDs.append(traceID) - return traceIDs - - def setParameters(self, name, value): - self.paras[name] = value - - def setVmin(self, vmin): - self.setParameters('vmin', vmin) - - def setVmax(self, vmax): - self.setParameters('vmax', vmax) - - def setCut(self, cut): - self.setParameters('cut', cut) - - def setTmovwind(self, tmovwind): - self.setParameters('tmovwind', tmovwind) - - def setOrder(self, order): - self.setParameters('order', order) - - def setTsignal(self, tsignal): - self.setParameters('tsignal', tsignal) - - def setTgap(self, tgap): - self.setParameters('tgap', tgap) - - def setShotnumber(self, shotnumber): - self.setParameters('shotnumber', shotnumber) - - def setReceiverCoords(self, receiver): - self.setParameters('receiverLoc', receiver) - - def setSourceCoords(self, source): - self.setParameters('sourceLoc', source) - - def setMethod(self, method): - self.setParameters('method', method) - - def setAicwindow(self, aicwindow): - self.setParameters('aicwindow', aicwindow) - - def setFolm(self, folm): - self.setParameters('folm', folm) - - def setDynPickwindow(self, traceID, cutdist = 5.): - distance = self.getDistance(traceID) # receive distance - - vmin = self.getVmin() - vmax = self.getVmax() - - pickwin_used = self.getCut() - cutwindow = self.getCut() - - # for higher distances use a linear vmin/vmax to cut out late/early regions with high noise - if distance > cutdist: - pwleft = distance / vmax - pwright = distance / vmin - if pwright > cutwindow[1]: - pwright = cutwindow[1] - pickwin_used = (pwleft, pwright) - - self.setPickwindow(traceID, pickwin_used) - - def getMethod(self): - return self.paras['method'] - - def getAicwindow(self): - return self.paras['aicwindow'] - - def getFolm(self): - return self.paras['folm'] - - def getParas(self): - return self.paras - - def getShotnumber(self): - return self.paras['shotnumber'] - - def getSourceCoords(self): - return self.paras['sourceLoc'] - - def getReceiverCoords(self): - return self.paras['receiverLoc'] - - def getCut(self): - return self.paras['cut'] - - def getTmovwind(self): - return self.paras['tmovwind'] - - def getOrder(self): - return self.paras['order'] - - def getTsignal(self): - return self.paras['tsignal'] - - def getTgap(self): - return self.paras['tgap'] - - def getShotnumber(self): - return self.paras['shotnumber'] - - def getVmin(self): - return self.paras['vmin'] - - def getVmax(self): - return self.paras['vmax'] - - def getManualPick(self, traceID): - if not self.getManualPickFlag(traceID) == 0: - return self.manualpicks[traceID]['mpp'] - - def getManualEarliest(self, traceID): - return self.manualpicks[traceID]['epp'] - - def getManualLatest(self, traceID): - return self.manualpicks[traceID]['lpp'] - - def getPick(self, traceID, returnRemoved=False): - if not self.getPickFlag(traceID) == 0: - return self.picks[traceID]['mpp'] - if returnRemoved == True: - return self.picks[traceID]['mpp'] - - def getPickIncludeRemoved(self, traceID): - return self.getPick(traceID, returnRemoved=True) - - def getEarliest(self, traceID): - return self.picks[traceID]['epp'] - - def getLatest(self, traceID): - return self.picks[traceID]['lpp'] - - def getSymmetricPickError(self, traceID): - pickerror = self.picks[traceID]['spe'] - if np.isnan(pickerror) == True: - print "SPE is NaN for shot %s, traceID %s" % (self.getShotnumber(), traceID) - return pickerror - - def getPickError(self, traceID): - pickerror = abs(self.getEarliest(traceID) - self.getLatest(traceID)) / 2 - if np.isnan(pickerror) == True: - print("PE is NaN for shot %s, traceID %s" % (self.getShotnumber(), traceID)) - return pickerror - - def getStreamTraceIDs(self): - traceIDs = [] - for trace in self.traces: - traceIDs.append(int(trace.stats.channel)) - return traceIDs - - def getTraceIDlist(self): - ''' - Returns a list containing the traceIDs. - ''' - traceIDs = [] - if self.traceIDs == None: - traceIDs = self.getReceiverCoords().keys() - self.traceIDs = traceIDs - return self.traceIDs - - def getPickwindow(self, traceID): - try: - self.pwindow[traceID] - except KeyError as e: - print('no pickwindow for trace %s, set to %s' % (traceID, self.getCut())) - self.setPickwindow(traceID, self.getCut()) - return self.pwindow[traceID] - - def getSNR(self, traceID): - return self.snr[traceID] - - def getSNRthreshold(self, traceID): - return self.snrthreshold[traceID] - - # def getRecCoordlist(self): - # if self.recCoordlist is None: - # coordlist = open(self.getRecfile(), 'r').readlines() - # # print 'Reading receiver coordinates from %s' %(self.getRecfile()) - # self.recCoordlist = coordlist - # return self.recCoordlist - - # def getSrcCoordlist(self): - # if self.srcCoordlist is None: - # coordlist = open(self.getSourcefile(), 'r').readlines() - # # print 'Reading shot coordinates from %s' %(self.getSourcefile()) - # self.srcCoordlist = coordlist - # return self.srcCoordlist - - def getTimeArray(self, traceID): - return self.timeArray[traceID] - - def getHOScf(self, traceID): - ''' - Returns the higher order statistics characteristic function for a trace using pylot. - - :param: traceID - :type: int - - :param: cut, cut out a part of the trace (t_start, t_end) [s] - :type: tuple - - :param: t2, size of the moving window [s] - :type: float - - :param: order, order of the characteristic function - :type: int - ''' - return HOScf(self.getSingleStream(traceID), self.getCut(), - self.getTmovwind(), self.getOrder(), stealthMode=True) - - def getAICcf(self, traceID): - ''' - Returns the Akaike criterion for a trace using pylot and the higher order statistics characteristic function. - - :param: traceID - :type: int - - :param: cut, cut out a part of the trace (t_start, t_end) [s] - :type: tuple - - :param: t2, size of the moving window [s] - :type: float - - :param: order, order of the characteristic function - :type: int - ''' - - st_cf = Stream() - tr_cf = Trace() - tr_cf.data = self.getHOScf(traceID).getCF() - st_cf += tr_cf - return AICcf(st_cf, self.getCut(), self.getTmovwind(), stealthMode=True) - - def getSingleStream(self, traceID): ########## SEG2 / SEGY ? ########## - ''' - Returns a Stream with only one trace (instead of just one trace) because it is needed for pylot. - - :param: traceID - :type: int - ''' - # traces = [trace for trace in self.traces if int(trace.stats.seg2['CHANNEL_NUMBER']) == traceID] - traces = [trace for trace in self.traces if int(trace.stats.channel) == traceID] - if len(traces) == 1: - return Stream(traces) - self.setPick(traceID, None) - warnings.warn('ambigious or empty traceID: %s' % traceID) - - def setPickParameters(self, folm, method = 'hos', aicwindow = (10, 0)): - self.setFolm(folm) - self.setMethod(method) - self.setAicwindow(aicwindow) - - # def pickParallel(self): - # traceIDs = self.getTraceIDlist() - # picks = [] - # #picks = worker(self.pickTrace, traceIDs) - - # # for traceID, pick in picks: - # # self.setPick(traceID, pick) - - # for traceID in traceIDs: - # trID, pick = self.pickTrace(traceID) - # picks.append([trID, pick]) - # #self.setPick(traceID, pick) - # return picks - - def pickTrace(self, traceID): - ''' - Intitiate picking for a trace. - - :param: traceID - :type: int - - :param: cutwindow (equals HOScf 'cut' variable) - :type: tuple - - :param: t2 (equals HOScf t2 variable) - :type: float - - :param: order (equals HOScf 'order' variable) - :type: int - - :param: windowsize, window around the returned HOS picktime, to search for the AIC minumum - :type: 'tuple' - - :param: folm, fraction of local maximumm - :type: 'real' - - :param: HosAic, get hos or aic pick (can be 'hos'(default) or 'aic') - :type: 'string' - ''' - self.setDynPickwindow(traceID) - - hoscf = self.getHOScf(traceID) ### determination of both, HOS and AIC (need to change threshold-picker) ### - aiccf = self.getAICcf(traceID) - - self.timeArray[traceID] = hoscf.getTimeArray() - aiccftime, hoscftime = self.threshold(hoscf, aiccf, self.getAicwindow(), self.getPickwindow(traceID), self.getFolm()) - setHosAic = {'hos': hoscftime, - 'aic': aiccftime} - - if aiccftime < self.getPickwindow(traceID)[0] and 'aic' in self.getMethod(): - return 0 - - return setHosAic[self.getMethod()] - - def setEarllatepick(self, traceID, nfac=1.5): - tgap = self.getTgap() - tsignal = self.getTsignal() - tnoise = self.getPickIncludeRemoved(traceID) - tgap - - (self.picks[traceID]['epp'], - self.picks[traceID]['lpp'], - self.picks[traceID]['spe']) = earllatepicker(self.getSingleStream(traceID), - nfac, (tnoise, tgap, tsignal), - self.getPickIncludeRemoved(traceID), - stealth_mode=True) - - def threshold(self, hoscf, aiccf, windowsize, pickwindow, folm): - ''' - Threshold picker, using the local maximum in a pickwindow to find the time at - which a fraction of the local maximum is reached for the first time. - - :param: hoscf, Higher Order Statistics Characteristic Function - :type: 'Characteristic Function' - - :param: aiccf, Characteristic Function after Akaike - :type: 'Characteristic Function' - - :param: windowsize, window around the returned HOS picktime, to search for the AIC minumum - :type: 'tuple' - - :param: pickwindow [seconds] - :type: 'tuple' - - :param: cutwindow [seconds], cut a part of the trace as in Characteristic Function - :type: 'tuple' - - :param: folm, fraction of local maximum - :type: 'real' - ''' - hoscflist = list(hoscf.getCF()) - leftb = int(pickwindow[0] / self.getCut()[1] * len(hoscflist)) - rightb = int(pickwindow[1] / self.getCut()[1] * len(hoscflist)) - - threshold = folm * (max(hoscflist[leftb: rightb]) - min(hoscflist[leftb: rightb])) + min( - hoscflist[leftb: rightb]) # combination of local maximum and threshold - - m = leftb - - while hoscflist[m] < threshold: - m += 1 - - hoscftime = list(hoscf.getTimeArray())[m] - - lb = max(0, m - windowsize[0]) # if window exceeds t = 0 - aiccfcut = list(aiccf.getCF())[lb: m + windowsize[1]] - if len(aiccfcut) > 0: - n = aiccfcut.index(min(aiccfcut)) - else: - n = 0 - - m = lb + n - - aiccftime = list(hoscf.getTimeArray())[m] - - return aiccftime, hoscftime - - def getDistance(self, traceID): - ''' - Returns the distance of the receiver with the ID == traceID to the source location (shot location). - Uses getSrcLoc and getRecLoc. - - :param: traceID - :type: int - ''' - shotX, shotY, shotZ = self.getSrcLoc() - recX, recY, recZ = self.getRecLoc(traceID) - dist = np.sqrt((shotX - recX) ** 2 + (shotY - recY) ** 2 + (shotZ - recZ) ** 2) - - if np.isnan(dist) == True: - raise ValueError("Distance is NaN for traceID %s" % traceID) - - return dist - - def getRecLoc(self, traceID): ########## input FILENAME ########## - ''' - Returns the location (x, y, z) of the receiver with the ID == traceID. - RECEIVEIVER FILE MUST BE SET FIRST, TO BE IMPROVED. - - :param: traceID - :type: int - ''' - if traceID == 0: # artificial traceID 0 with pick at t = 0 - return self.getSrcLoc() - - return self.getReceiverCoords()[traceID] - - raise ValueError("traceID %s not found" % traceID) - - def getSrcLoc(self): ########## input FILENAME ########## - ''' - Returns the location (x, y, z) of the shot. - SOURCE FILE MUST BE SET FIRST, TO BE IMPROVED. - ''' - return self.getSourceCoords() - - def getTraceIDs4Dist(self, distance=0, - distancebin=(0, 0)): ########## nur fuer 2D benutzt, 'distance bins' ########## - ''' - Returns the traceID(s) for a certain distance between source and receiver. - Used for 2D Tomography. TO BE IMPROVED. - - :param: distance - :type: real - - :param: distancebin - :type: tuple - ''' - - traceID_list = [] - for trace in self.traces: - # traceID = int(trace.stats.seg2['CHANNEL_NUMBER']) - traceID = int(trace.stats.channel) - if distance != 0: - if self.getDistance(traceID) == distance: - traceID_list.append(traceID) - if distancebin[0] >= 0 and distancebin[1] > 0: - if distancebin[0] < self.getDistance(traceID) < distancebin[1]: - traceID_list.append(traceID) - - if len(traceID_list) > 0: - return traceID_list - - def setManualPicksFromFile(self, directory='picks'): - ''' - Read manual picks from *.pck file. - The * must be identical with the shotnumber. - ''' - if directory[-1] == '/': - filename = directory + str(self.getShotnumber()) + '.pck' - else: - filename = directory + '/' + str(self.getShotnumber()) + '.pck' - infile = open(filename, 'r') - mpicks = infile.readlines() - for line in mpicks: - if line.split()[0] == []: - continue - traceID, mpp, epp, lpp = line.split() - traceID = int(traceID) - if traceID in self.picks.keys(): - self.manualpicks[traceID] = {'mpp': float(mpp), - 'epp': float(epp), - 'lpp': float(lpp)} - if float(mpp) <= 0: - self.setManualPickFlag(traceID, 0) - else: - self.setManualPickFlag(traceID, 1) - - def setPick(self, traceID, pick): ########## siehe Kommentar ########## - if not traceID in self.picks.keys(): - self.picks[traceID] = {} - self.picks[traceID]['mpp'] = pick - self.picks[traceID]['flag'] = 1 - # ++++++++++++++ Block raus genommen, da Error beim 2ten Mal picken! (Ueberschreiben von erstem Pick!) - # if not self.picks.has_key(traceID): - # self.getPick(traceID) = picks - # else: - # raise KeyError('pick to be set more than once for traceID %s' % traceID) - - # def readParameter(self, parfile): - # parlist = open(parfile,'r').readlines() - - def removePick(self, traceID): - self.setPickFlag(traceID, 0) - - def setPickFlag(self, traceID, flag): - 'Set flag = 0 if pick is invalid, else flag = 1' - self.picks[traceID]['flag'] = flag - - def getPickFlag(self, traceID): - return self.picks[traceID]['flag'] - - def setManualPickFlag(self, traceID, flag): - 'Set flag = 0 if pick is invalid, else flag = 1' - self.manualpicks[traceID]['flag'] = flag - - def getManualPickFlag(self, traceID): - return self.manualpicks[traceID]['flag'] - - def setPickwindow(self, traceID, pickwindow): - self.pwindow[traceID] = pickwindow - - def setSNR(self, traceID): ########## FORCED HOS PICK ########## - ''' - Gets the SNR using pylot and then sets the SNR for the traceID. - - :param: traceID - :type: int - - :param: (tnoise, tgap, tsignal), as used in pylot SNR - ''' - from pylot.core.pick.utils import getSNR - - tgap = self.getTgap() - tsignal = self.getTsignal() - tnoise = self.getPick(traceID) - tgap - - self.snr[traceID] = getSNR(self.getSingleStream(traceID), (tnoise, tgap, tsignal), self.getPick(traceID)) - - def setSNRthreshold(self, traceID, snrthreshold): - self.snrthreshold[traceID] = snrthreshold - - def getDistArray4ttcPlot(self): ########## nur fuer 2D benoetigt ########## - ''' - Function to create a distance array for the plots. 2D only! X DIRECTION!! - ''' - distancearray = [] - - for traceID in self.picks.keys(): - if self.getRecLoc(traceID)[0] > self.getSrcLoc()[0]: - distancearray.append(self.getDistance(traceID)) - elif self.getRecLoc(traceID)[0] <= self.getSrcLoc()[0]: - distancearray.append((-1) * self.getDistance(traceID)) - - return distancearray - - def plot2dttc(self, ax=None): - ''' - Function to plot the traveltime curve for automated picks of a shot. 2d only! ATM: X DIRECTION!! - ''' - import matplotlib.pyplot as plt - plt.interactive('True') - picks = [] - - for traceID in self.picks.keys(): - picks.append(self.getPick(traceID)) - - if ax is None: - fig = plt.figure() - ax = fig.add_subplot(111) - - # shotnumbers = [shotnumbers for (shotnumbers, shotnames) in sorted(zip(shotnumbers, shotnames))] - plotarray = sorted(zip(self.getDistArray4ttcPlot(), picks)) - x = []; - y = [] - for point in plotarray: - x.append(point[0]) - y.append(point[1]) - ax.plot(x, y, 'r', label="Automatic Picks") - ax.text(0.5, 0.9, 'shot: %s' % self.getShotnumber(), transform=ax.transAxes - , horizontalalignment='center') - - def plotmanual2dttc(self, ax=None): - ''' - Function to plot the traveltime curve for manual picks of a shot. 2D only! - ''' - import matplotlib.pyplot as plt - plt.interactive('True') - manualpicktimesarray = [] - - for traceID in self.picks.keys(): - if not traceID in self.manualpicks.keys() or self.getManualPickFlag(traceID) == 0: - manualpicktimesarray.append(None) - else: - manualpicktimesarray.append(self.getManualPick(traceID)) - - if ax is None: - fig = plt.figure() - ax = fig.add_subplot(111) - - plotarray = sorted(zip(self.getDistArray4ttcPlot(), manualpicktimesarray)) - x = []; - y = [] - for point in plotarray: - x.append(point[0]) - y.append(point[1]) - ax.plot(x, y, 'b', label="Manual Picks") - - def plotTrace(self, traceID, plotSNR=True, lw=1): - fig = plt.figure() - ax = fig.add_subplot(111) - ax = self._drawStream(traceID, ax=ax) - - tgap = self.getTgap() - tsignal = self.getTsignal() - pick = self.getPick(traceID) - tnoise = pick - tgap - snr, snrdb, noiselevel = self.getSNR(traceID) - - ax.plot([0, tnoise], [noiselevel, noiselevel], 'm', linewidth=lw, label='noise level') - ax.plot([tnoise, pick], [noiselevel, noiselevel], 'g:', linewidth=lw, label='gap') - ax.plot([tnoise + tgap, pick + tsignal], [noiselevel * snr, noiselevel * snr], 'b', linewidth=lw, - label='signal level') - ax.legend() - ax.text(0.05, 0.9, 'SNR: %s' % snr, transform=ax.transAxes) - - def plot_traces(self, traceID, figure = None, buttons = True): - from matplotlib.widgets import Button - - def onclick(event): - self.setPick(traceID, event.xdata) - if self.getSNR(traceID)[0] > 1: - self.setEarllatepick(traceID) - self._drawStream(traceID, refresh=True) - self._drawCFs(traceID, folm, refresh=True) - fig.canvas.mpl_disconnect(self.traces4plot[traceID]['cid']) - plt.draw() - - def rmPick(event=None): - self.removePick(traceID) - self._drawStream(traceID, refresh=True) - self._drawCFs(traceID, folm, refresh=True) - plt.draw() - - def connectButton(event=None): - cid = fig.canvas.mpl_connect('button_press_event', onclick) - self.traces4plot[traceID]['cid'] = cid - - def cleanup(event): - self.traces4plot[traceID] = {} - - def addButtons(fig): - axb1 = fig.add_axes([0.15, 0.91, 0.05, 0.03]) - axb2 = fig.add_axes([0.22, 0.91, 0.05, 0.03]) - button1 = Button(axb1, 'repick', color='red', hovercolor='grey') - button1.on_clicked(connectButton) - button2 = Button(axb2, 'delete', color='green', hovercolor='grey') - button2.on_clicked(rmPick) - return axb1, axb2, button1, button2 - - folm = self.folm - - if figure == None: - fig = plt.figure() - else: - fig = figure - - ax1 = fig.add_subplot(2, 1, 1) - ax2 = fig.add_subplot(2, 1, 2, sharex=ax1) - - if buttons: - axb1, axb2, button1, button2 = addButtons(fig) - else: - axb1 = None - axb2 = None - button1 = None - button2 = None - - fig.canvas.mpl_connect('close_event', cleanup) - - self.traces4plot[traceID] = dict(fig=fig, ax1=ax1, ax2=ax2, axb1=axb1, axb2=axb2, button1=button1, - button2=button2, cid=None) - - self._drawStream(traceID) - self._drawCFs(traceID, folm) - - def _drawStream(self, traceID, refresh=False, ax=None): - from pylot.core.util.utils import full_range - from pylot.core.util.utils import prepTimeAxis - - stream = self.getSingleStream(traceID) - stime = full_range(stream)[0] - timeaxis = prepTimeAxis(stime, stream[0]) - timeaxis -= stime - - if ax is None: - ax = self.traces4plot[traceID]['ax1'] - - if refresh == True: - xlim, ylim = ax.get_xlim(), ax.get_ylim() - ax.clear() - if refresh == True: - ax.set_xlim(xlim) - ax.set_ylim(ylim) - - ax.set_title('Shot: %s, traceID: %s, pick: %s' - % (self.getShotnumber(), traceID, self.getPick(traceID))) - ax.plot(timeaxis, stream[0].data, 'k', label='trace') - ax.plot([self.getPick(traceID), self.getPick(traceID)], - [ax.get_ylim()[0], - ax.get_ylim()[1]], - 'r', label='most likely') - if self.getEarliest(traceID) is not None: - ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], - [ax.get_ylim()[0], - ax.get_ylim()[1]], - 'g:', label='earliest') - if self.getLatest(traceID) is not None: - ax.plot([self.getLatest(traceID), self.getLatest(traceID)], - [ax.get_ylim()[0], - ax.get_ylim()[1]], - 'b:', label='latest') - - ax.legend() - return ax - - def _drawCFs(self, traceID, folm=None, refresh=False): - hoscf = self.getHOScf(traceID) - aiccf = self.getAICcf(traceID) - ax = self.traces4plot[traceID]['ax2'] - - if refresh == True: - xlim, ylim = ax.get_xlim(), ax.get_ylim() - ax.clear() - if refresh == True: - ax.set_xlim(xlim) - ax.set_ylim(ylim) - - ax.plot(hoscf.getTimeArray(), hoscf.getCF(), 'b', label='HOS') - ax.plot(hoscf.getTimeArray(), aiccf.getCF(), 'g', label='AIC') - ax.plot([self.getPick(traceID), self.getPick(traceID)], - [ax.get_ylim()[0], - ax.get_ylim()[1]], - 'r', label='most likely') - if self.getEarliest(traceID) is not None: - ax.plot([self.getEarliest(traceID), self.getEarliest(traceID)], - [ax.get_ylim()[0], - ax.get_ylim()[1]], - 'g:', label='earliest') - if self.getLatest(traceID) is not None: - ax.plot([self.getLatest(traceID), self.getLatest(traceID)], - [ax.get_ylim()[0], - ax.get_ylim()[1]], - 'b:', label='latest') - if folm is not None: - ax.plot([0, self.getPick(traceID)], - [folm * max(hoscf.getCF()), folm * max(hoscf.getCF())], - 'm:', label='folm = %s' % folm) - ax.set_xlabel('Time [s]') - ax.legend() - - def plot3dttc(self, step=0.5, contour=False, plotpicks=False, method='linear', ax=None): - ''' - Plots a 3D 'traveltime cone' as surface plot by interpolating on a regular grid over the traveltimes, not yet regarding the vertical offset of the receivers. - - :param: step (optional), gives the stepsize for the interpolated grid. Default is 0.5 - :type: 'float' - - :param: contour (optional), plot contour plot instead of surface plot - :type: 'logical' - - :param: plotpicks (optional), plot the data points onto the interpolated grid - :type: 'logical' - - :param: method (optional), interpolation method; can be 'linear' (default) or 'cubic' - :type: 'string' - ''' - from scipy.interpolate import griddata - from matplotlib import cm - from mpl_toolkits.mplot3d import Axes3D - - x = [] - y = [] - z = [] - for traceID in self.picks.keys(): - if self.getPickFlag(traceID) != 0: - x.append(self.getRecLoc(traceID)[0]) - y.append(self.getRecLoc(traceID)[1]) - z.append(self.getPick(traceID)) - - xaxis = np.arange(min(x) + step, max(x), step) - yaxis = np.arange(min(y) + step, max(y), step) - xgrid, ygrid = np.meshgrid(xaxis, yaxis) - zgrid = griddata((x, y), z, (xgrid, ygrid), method=method) - - if ax == None: - fig = plt.figure() - ax = plt.axes(projection='3d') - - xsrc, ysrc, zsrc = self.getSrcLoc() - - if contour == True: - ax.contour3D(xgrid, ygrid, zgrid, 20) - else: - ax.plot_surface(xgrid, ygrid, zgrid, linewidth=0, cmap=cm.jet, vmin=min(z), vmax=max(z)) - ax.plot([xsrc], [ysrc], [self.getPick(0)], 'k*', markersize=20) # plot source location - ax.plot([xsrc], [ysrc], [self.getPick(0)], 'r*', markersize=15) # plot source location - - if plotpicks == True: - ax.plot(x, y, z, 'k.') - - def plotttc(self, method, *args): - plotmethod = {'2d': self.plot2dttc, '3d': self.plot3dttc} - - plotmethod[method](*args) - - def matshow(self, ax=None, step=0.5, method='linear', plotRec=True, annotations=True, colorbar=True, legend=True): - ''' - Plots a 2D matrix of the interpolated traveltimes. This needs less performance than plot3dttc - - :param: step (optional), gives the stepsize for the interpolated grid. Default is 0.5 - :type: 'float' - - :param: method (optional), interpolation method; can be 'linear' (default) or 'cubic' - :type: 'string' - - :param: plotRec (optional), plot the receiver positions (colored scatter plot, should not be - deactivated because there might be receivers that are not inside the interpolated area) - :type: 'logical' - - :param: annotations (optional), displays traceIDs as annotations - :type: 'logical' - ''' - from scipy.interpolate import griddata - from matplotlib import cm - cmap = cm.jet - - x = []; - xcut = [] - y = []; - ycut = [] - z = []; - zcut = [] - - for traceID in self.picks.keys(): - if self.getPickFlag(traceID) != 0: - x.append(self.getRecLoc(traceID)[0]) - y.append(self.getRecLoc(traceID)[1]) - z.append(self.getPick(traceID)) - if self.getPickFlag(traceID) == 0 and self.getPickIncludeRemoved(traceID) is not None: - xcut.append(self.getRecLoc(traceID)[0]) - ycut.append(self.getRecLoc(traceID)[1]) - zcut.append(self.getPickIncludeRemoved(traceID)) - - tmin = 0.8 * min(z) # 20% cushion for colorbar - tmax = 1.2 * max(z) - - xaxis = np.arange(min(x), max(x), step) - yaxis = np.arange(min(y), max(y), step) - xgrid, ygrid = np.meshgrid(xaxis, yaxis) - zgrid = griddata((x, y), z, (xgrid, ygrid), method='linear') - - if ax == None: - fig = plt.figure() - ax = plt.axes() - - count = 0 - ax.imshow(zgrid, extent=[min(x), max(x), min(y), max(y)], vmin=tmin, vmax=tmax, cmap=cmap, origin='lower', - alpha=0.85) - ax.text(0.5, 0.95, 'shot: %s' % self.getShotnumber(), transform=ax.transAxes - , horizontalalignment='center') - sc = ax.scatter(x, y, c=z, s=30, label='active traces', vmin=tmin, vmax=tmax, cmap=cmap, linewidths=1.5) - for xyz in zip(xcut, ycut, zcut): - x, y, z = xyz - count += 1 - if z > tmax: - z = 'w' - if count == 1: - label = 'inactive traces' - else: - label = None - ax.scatter(x, y, c=z, s=30, edgecolor='m', label=label, vmin=tmin, vmax=tmax, cmap=cmap, linewidths=1.5) - if colorbar == True: - cbar = plt.colorbar(sc) - cbar.set_label('Time [s]') - - if legend == True: - ax.legend() - ax.set_xlabel('X') - ax.set_ylabel('Y') - ax.plot(self.getSrcLoc()[0], self.getSrcLoc()[1], '*k', markersize=15) # plot source location - - if annotations == True: - for traceID in self.getTraceIDlist(): - if self.getPickFlag(traceID) is not 0: - ax.annotate(' %s' % traceID, xy=(self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), - fontsize='x-small', color='k') - else: - ax.annotate(' %s' % traceID, xy=(self.getRecLoc(traceID)[0], self.getRecLoc(traceID)[1]), - fontsize='x-small', color='r') - - plt.show() diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py deleted file mode 100644 index d6303d79..00000000 --- a/pylot/core/active/surveyPlotTools.py +++ /dev/null @@ -1,537 +0,0 @@ -# -*- coding: utf-8 -*- -import matplotlib.pyplot as plt -import math -import numpy as np - -plt.interactive(True) - - -class regions(object): - ''' - A class used for manual inspection and processing of all picks for the user. - - Examples: - - regions.chooseRectangles(): - - lets the user choose several rectangular regions in the plot - - regions.plotTracesInActiveRegions(): - - creates plots (shot.plot_traces) for all traces in the active regions (i.e. chosen by e.g. chooseRectangles) - - regions.setAllActiveRegionsForDeletion(): - - highlights all shots in a the active regions for deletion - - regions.deleteAllMarkedPicks(): - - deletes the picks (pick flag set to 0) for all shots set for deletion - - regions.deselectSelection(number): - - deselects the region of number = number - - ''' - - def __init__(self, ax, cbar, survey, qt_interface = False): - self.ax = ax - self.cbar = cbar - self.cbv = 'log10SNR' - self._xlim0 = self.ax.get_xlim() - self._ylim0 = self.ax.get_ylim() - self._xlim = self.ax.get_xlim() - self._ylim = self.ax.get_ylim() - self.survey = survey - self.shot_dict = self.survey.getShotDict() - self._x0 = [] - self._y0 = [] - self._x1 = [] - self._y1 = [] - self._polyx = [] - self._polyy = [] - self._allpicks = None - self.shots_found = {} - self.shots_for_deletion = {} - self._generateList() - if not qt_interface: - self.buttons = {} - self._addButtons() - self.addTextfield() - self.drawFigure() - - def _generateList(self): - allpicks = [] - for shot in self.shot_dict.values(): - for traceID in shot.getTraceIDlist(): - allpicks.append((shot.getDistance(traceID), - shot.getPickIncludeRemoved(traceID), - shot.getShotnumber(), - traceID, - shot.getPickFlag(traceID))) - - allpicks.sort() - self._allpicks = allpicks - - def getShotDict(self): - return self.shot_dict - - def getShotsForDeletion(self): - return self.shots_for_deletion - - def _onselect_clicks(self, eclick, erelease): - '''eclick and erelease are matplotlib events at press and release''' - print 'region selected x0, y0 = (%3s, %3s), x1, y1 = (%3s, %3s)' % (eclick.xdata, - eclick.ydata, - erelease.xdata, - erelease.ydata) - x0 = min(eclick.xdata, erelease.xdata) - x1 = max(eclick.xdata, erelease.xdata) - y0 = min(eclick.ydata, erelease.ydata) - y1 = max(eclick.ydata, erelease.ydata) - - shots, numtraces = self.findTracesInShotDict((x0, x1), (y0, y1)) - self.printOutput('Found %d traces in rectangle: %s' % (numtraces, shots)) - key = self.getKey() - self.shots_found[key] = {'shots': shots, - 'selection': 'rect', - 'xvalues': (x0, x1), - 'yvalues': (y0, y1)} - self.markRectangle((x0, x1), (y0, y1), key) - self.disconnectRect() - - def _onselect_verts(self, verts): - x = verts[0][0] - y = verts[0][1] - self._polyx.append(x) - self._polyy.append(y) - - self.drawPolyLine() - - def _onpress(self, event): - if event.button == 3: - self.disconnectPoly() - self.printOutput('Disconnected polygon selection') - - def addTextfield(self, xpos=0, ypos=0.95, width=1, height=0.03): - ''' - Adds an ax for text output to the plot. - ''' - self.axtext = self.ax.figure.add_axes([xpos, - ypos, - width, - height]) - self.axtext.xaxis.set_visible(False) - self.axtext.yaxis.set_visible(False) - - def writeInTextfield(self, text=None): - self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) - self.axtext.clear() - self.axtext.text(0.01, 0.5, text, verticalalignment='center', horizontalalignment='left') - self.drawFigure() - - def _addButtons(self): - xpos1 = 0.13 - xpos2 = 0.6 - dx = 0.06 - self.addButton('Rect', self.chooseRectangles, xpos=xpos1, color='white') - self.addButton('Poly', self.choosePolygon, xpos=xpos1 + dx, color='white') - self.addButton('Plot', self.plotTracesInActiveRegions, xpos=xpos1 + 2 * dx, color='yellow') - self.addButton('SNR', self.refreshLog10SNR, xpos=xpos1 + 3 * dx, color='cyan') - self.addButton('PE', self.refreshPickerror, xpos=xpos1 + 4 * dx, color='cyan') - self.addButton('SPE', self.refreshSPE, xpos=xpos1 + 5 * dx, color='cyan') - self.addButton('DesLst', self.deselectLastSelection, xpos=xpos2 + dx, color='green') - self.addButton('SelAll', self.setAllActiveRegionsForDeletion, xpos=xpos2 + 2 * dx) - self.addButton('DelAll', self.deleteAllMarkedPicks, xpos=xpos2 + 3 * dx, color='red') - - def addButton(self, name, action, xpos, ypos=0.91, color=None): - from matplotlib.widgets import Button - self.buttons[name] = {'ax': None, - 'button': None, - 'action': action, - 'xpos': xpos} - ax = self.ax.figure.add_axes([xpos, - ypos, - 0.05, - 0.03]) - button = Button(ax, name, color=color, hovercolor='grey') - button.on_clicked(action) - self.buttons[name]['ax'] = ax - self.buttons[name]['button'] = button - self.buttons[name]['xpos'] = xpos - - def getKey(self): - if self.shots_found.keys() == []: - key = 1 - else: - key = max(self.shots_found.keys()) + 1 - return key - - def drawPolyLine(self): - self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) - x = self._polyx - y = self._polyy - if len(x) >= 2 and len(y) >= 2: - self.ax.plot(x[-2:], y[-2:], 'k', alpha=0.1) - self.drawFigure() - - def drawLastPolyLine(self): - self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) - x = self._polyx - y = self._polyy - if len(x) >= 2 and len(y) >= 2: - self.ax.plot((x[-1], x[0]), (y[-1], y[0]), 'k', alpha=0.1) - self.drawFigure() - - def finishPolygon(self): - self.drawLastPolyLine() - x = self._polyx - y = self._polyy - self._polyx = []; - self._polyy = [] - - key = self.getKey() - self.markPolygon(x, y, key=key) - - shots, numtraces = self.findTracesInPoly(x, y) - self.shots_found[key] = {'shots': shots, - 'selection': 'poly', - 'xvalues': x, - 'yvalues': y} - self.printOutput('Found %d traces in polygon: %s' % (numtraces, shots)) - - def printOutput(self, text): - print text - self.writeInTextfield(text) - - def chooseRectangles(self, event=None): - ''' - Activates matplotlib widget RectangleSelector. - ''' - from matplotlib.widgets import RectangleSelector - if hasattr(self, '_cidPoly'): - self.disconnectPoly() - self.printOutput('Select rectangle is active. Press and hold left mousebutton.') - self._cidRect = None - self._cidRect = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) - self._rectangle = RectangleSelector(self.ax, self._onselect_clicks) - return self._rectangle - - def choosePolygon(self, event=None): - ''' - Activates matplotlib widget LassoSelector. - ''' - from matplotlib.widgets import LassoSelector - if hasattr(self, '_cidRect'): - self.disconnectRect() - self.printOutput('Select polygon is active. Add points with leftclick. Finish with rightclick.') - self._cidPoly = None - self._cidPoly = self.ax.figure.canvas.mpl_connect('button_press_event', self._onpress) - self._lasso = LassoSelector(self.ax, self._onselect_verts) - return self._lasso - - def disconnectPoly(self, event=None): - if not hasattr(self, '_cidPoly'): - self.printOutput('no poly selection found') - return - self.ax.figure.canvas.mpl_disconnect(self._cidPoly) - del self._cidPoly - self.finishPolygon() - self._lasso.disconnect_events() - print 'disconnected poly selection\n' - - def disconnectRect(self, event=None): - if not hasattr(self, '_cidRect'): - self.printOutput('no rectangle selection found') - return - self.ax.figure.canvas.mpl_disconnect(self._cidRect) - del self._cidRect - self._rectangle.disconnect_events() - print 'disconnected rectangle selection\n' - - def deselectLastSelection(self, event=None): - if self.shots_found.keys() == []: - self.printOutput('No selection found.') - return - key = max(self.shots_found.keys()) - self.deselectSelection(key) - self.refreshFigure() - - def deselectSelection(self, key, color='green', alpha=0.1): - if key not in self.shots_found.keys(): - self.printOutput('No selection found.') - return - if color is not None: - if self.shots_found[key]['selection'] == 'rect': - self.markRectangle(self.shots_found[key]['xvalues'], - self.shots_found[key]['yvalues'], - key=key, color=color, alpha=alpha, - linewidth=1) - elif self.shots_found[key]['selection'] == 'poly': - self.markPolygon(self.shots_found[key]['xvalues'], - self.shots_found[key]['yvalues'], - key=key, color=color, alpha=alpha, - linewidth=1) - value = self.shots_found.pop(key) - self.printOutput('Deselected selection number %d' % key) - - def findTracesInPoly(self, x, y, picks='normal', highlight=True): - def dotproduct(v1, v2): - return sum((a * b for a, b in zip(v1, v2))) - - def getlength(v): - return math.sqrt(dotproduct(v, v)) - - def getangle(v1, v2): - return np.rad2deg(math.acos(dotproduct(v1, v2) / (getlength(v1) * getlength(v2)))) - - def insidePoly(x, y, pickX, pickY): - angle = 0 - epsilon = 1e-07 - for index in range(len(x)): - xval1 = x[index - 1]; - yval1 = y[index - 1] - xval2 = x[index]; - yval2 = y[index] - angle += getangle([xval1 - pickX, yval1 - pickY], [xval2 - pickX, yval2 - pickY]) - if 360 - epsilon <= angle <= 360 + epsilon: ### IMPROVE THAT?? - return True - - if len(x) == 0 or len(y) == 0: - self.printOutput('No polygon defined.') - return - - shots_found = {}; - numtraces = 0 - x0 = min(x); - x1 = max(x) - y0 = min(y); - y1 = max(y) - - shots, numtracesrect = self.findTracesInShotDict((x0, x1), (y0, y1), highlight=False) - for shotnumber in shots.keys(): - shot = self.shot_dict[shotnumber] - for traceID in shots[shotnumber]: - if shot.getPickFlag(traceID) is not 0: - pickX = shot.getDistance(traceID) - pickY = shot.getPick(traceID) - if insidePoly(x, y, pickX, pickY): - if shotnumber not in shots_found.keys(): - shots_found[shotnumber] = [] - shots_found[shotnumber].append(traceID) - if highlight == True: - self.highlightPick(shot, traceID) - numtraces += 1 - - self.drawFigure() - return shots_found, numtraces - - def findTracesInShotDict(self, (x0, x1), (y0, y1), picks='normal', highlight=True): - ''' - Returns traces corresponding to a certain area in the plot with all picks over the distances. - ''' - shots_found = {}; - numtraces = 0 - if picks == 'normal': - pickflag = 0 - elif picks == 'includeCutOut': - pickflag = None - - for line in self._allpicks: - dist, pick, shotnumber, traceID, flag = line - if flag == pickflag: continue ### IMPROVE THAT - if (x0 <= dist <= x1 and y0 <= pick <= y1): - if shotnumber not in shots_found.keys(): - shots_found[shotnumber] = [] - shots_found[shotnumber].append(traceID) - if highlight == True: - self.highlightPick(self.shot_dict[shotnumber], traceID) - numtraces += 1 - - self.drawFigure() - return shots_found, numtraces - - def highlightPick(self, shot, traceID, annotations=True): - ''' - Highlights a single pick for a shot(object)/shotnumber and traceID. - If annotations == True: Displays shotnumber and traceID in the plot. - ''' - if type(shot) == int: - shot = self.survey.getShotDict()[shot] - - if shot.getPickFlag(traceID) is 0: - return - - self.ax.scatter(shot.getDistance(traceID), shot.getPick(traceID), s=50, marker='o', facecolors='none', - edgecolors='m', alpha=1) - if annotations == True: - self.ax.annotate(s='s%s|t%s' % (shot.getShotnumber(), traceID), - xy=(shot.getDistance(traceID), shot.getPick(traceID)), fontsize='xx-small') - - def highlightAllActiveRegions(self): - ''' - Highlights all picks in all active regions. - ''' - for key in self.shots_found.keys(): - for shotnumber in self.shots_found[key]['shots'].keys(): - for traceID in self.shots_found[key]['shots'][shotnumber]: - self.highlightPick(self.shot_dict[shotnumber], traceID) - self.drawFigure() - - def plotTracesInActiveRegions(self, event=None, keys='all', maxfigures=20, qt = False): - ''' - Plots all traces in the active region or for all specified keys. - - :param: keys - :type: int or list - - :param: maxfigures, maximum value of figures opened - :type: int - ''' - if qt: - from pylot.core.active.gui import Repicking - - count = 0 - if keys == 'all': - keys = self.shots_found.keys() - elif type(keys) == int: - keys = [keys] - - if len(self.shots_found) > 0: - for shot in self.shot_dict.values(): - for key in keys: - for shotnumber in self.shots_found[key]['shots']: - if shot.getShotnumber() == shotnumber: - for traceID in self.shots_found[key]['shots'][shotnumber]: - count += 1 - if count > maxfigures: - print 'Maximum number of figures (%s) reached. %sth figure was not opened.' % ( - maxfigures, count) - break - shot.plot_traces(traceID) - else: - self.printOutput('No picks defined in that region(s)') - - def setAllActiveRegionsForDeletion(self, event=None): - keys = [] - for key in self.shots_found.keys(): - keys.append(key) - self.setRegionForDeletion(keys) - - def setRegionForDeletion(self, keys): - if type(keys) == int: - keys = [keys] - - for key in keys: - for shotnumber in self.shots_found[key]['shots'].keys(): - if shotnumber not in self.shots_for_deletion: - self.shots_for_deletion[shotnumber] = [] - for traceID in self.shots_found[key]['shots'][shotnumber]: - if traceID not in self.shots_for_deletion[shotnumber]: - self.shots_for_deletion[shotnumber].append(traceID) - self.deselectSelection(key, color='red', alpha=0.2) - - self.deselectSelection(key, color='red', alpha=0.2) - - self.printOutput('Set region(s) %s for deletion' % keys) - - def markAllActiveRegions(self): - for key in self.shots_found.keys(): - if self.shots_found[key]['selection'] == 'rect': - self.markRectangle(self.shots_found[key]['xvalues'], - self.shots_found[key]['yvalues'], key=key) - if self.shots_found[key]['selection'] == 'poly': - self.markPolygon(self.shots_found[key]['xvalues'], - self.shots_found[key]['yvalues'], key=key) - - def markRectangle(self, (x0, x1), (y0, y1), key=None, color='grey', alpha=0.1, linewidth=1): - ''' - Mark a rectangular region on the axes. - ''' - from matplotlib.patches import Rectangle - self.ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0, alpha=alpha, facecolor=color, linewidth=linewidth)) - if key is not None: - self.ax.text(x0 + (x1 - x0) / 2, y0 + (y1 - y0) / 2, str(key)) - self.drawFigure() - - def markPolygon(self, x, y, key=None, color='grey', alpha=0.1, linewidth=1): - from matplotlib.patches import Polygon - poly = Polygon(np.array(zip(x, y)), color=color, alpha=alpha, lw=linewidth) - self.ax.add_patch(poly) - if key is not None: - self.ax.text(min(x) + (max(x) - min(x)) / 2, min(y) + (max(y) - min(y)) / 2, str(key)) - self.drawFigure() - - def clearShotsForDeletion(self): - ''' - Clears the list of shots marked for deletion. - ''' - self.shots_for_deletion = {} - print('Cleared all shots that were set for deletion.') - - def getShotsForDeletion(self): - return self.shots_for_deletion - - def deleteAllMarkedPicks(self, event=None): - ''' - Deletes all shots set for deletion. - ''' - if len(self.getShotsForDeletion()) is 0: - self.printOutput('No shots set for deletion.') - return - - for shot in self.getShotDict().values(): - for shotnumber in self.getShotsForDeletion(): - if shot.getShotnumber() == shotnumber: - for traceID in self.getShotsForDeletion()[shotnumber]: - shot.removePick(traceID) - print "Deleted the pick for traceID %s on shot number %s" % (traceID, shotnumber) - self.clearShotsForDeletion() - self.refreshFigure() - - def highlightPicksForShot(self, shot, annotations=False): - ''' - Highlight all picks for a given shot. - ''' - if type(shot) is int: - shot = self.survey.getShotDict()[shotnumber] - - for traceID in shot.getTraceIDlist(): - if shot.getPickFlag(traceID) is not 0: - self.highlightPick(shot, traceID, annotations) - - self.drawFigure() - - def setXYlim(self, xlim, ylim): - self._xlim, self._ylim = xlim, ylim - - def refreshLog10SNR(self, event=None): - cbv = 'log10SNR' - self.cbv = cbv - self.refreshFigure(self, colorByVal=cbv) - - def refreshPickerror(self, event=None): - cbv = 'pickerror' - self.cbv = cbv - self.refreshFigure(self, colorByVal=cbv) - - def refreshSPE(self, event=None): - cbv = 'spe' - self.cbv = cbv - self.refreshFigure(self, colorByVal=cbv) - - def refreshFigure(self, event=None, colorByVal=None): - if colorByVal == None: - colorByVal = self.cbv - else: - self.cbv = colorByVal - self.printOutput('Refreshing figure...') - self.ax.clear() - self.ax = self.survey.plotAllPicks(ax=self.ax, cbar=self.cbar, refreshPlot=True, colorByVal=colorByVal) - self.setXYlim(self.ax.get_xlim(), self.ax.get_ylim()) - self.markAllActiveRegions() - self.highlightAllActiveRegions() - self.drawFigure() - self.printOutput('Done!') - - def drawFigure(self, resetAxes=True): - if resetAxes == True: - self.ax.set_xlim(self._xlim) - self.ax.set_ylim(self._ylim) - self.ax.figure.canvas.draw() - diff --git a/pylot/core/active/surveyUtils.py b/pylot/core/active/surveyUtils.py deleted file mode 100644 index 3d19650c..00000000 --- a/pylot/core/active/surveyUtils.py +++ /dev/null @@ -1,293 +0,0 @@ -from __future__ import print_function -import numpy as np - -def readParameters(parfile, parameter): - """ - - :param parfile: - :param parameter: - :return: - """ - from ConfigParser import ConfigParser - parameterConfig = ConfigParser() - parameterConfig.read('parfile') - - value = parameterConfig.get('vars', parameter).split('\t')[0] - - return value - - -def fitSNR4dist(shot_dict, shiftdist=30, shiftSNR=100): - """ - Approach to fit the decreasing SNR with wave travel distance. - - :param shot_dict: dictionary containing Seismicshot objects (e.g. survey.getShotDict()) - :param shiftdist: shift compensating curve by a certain distance to the left - :param shiftSNR: shift compensating curve by a certain SNR value to the bottom - :return: - """ - import numpy as np - dists = [] - picks = [] - snrs = [] - snr_sqrt_inv = [] - snrthresholds = [] - snrBestFit = [] - for shot in shot_dict.values(): - for traceID in shot.getTraceIDlist(): - if shot.getSNR(traceID)[0] >= 1: - dists.append(shot.getDistance(traceID)) - picks.append(shot.getPickIncludeRemoved(traceID)) - snrs.append(shot.getSNR(traceID)[0]) - snr_sqrt_inv.append(1 / np.sqrt(shot.getSNR(traceID)[0])) - fit = np.polyfit(dists, snr_sqrt_inv, 1) - fit_fn = np.poly1d(fit) - for dist in dists: - snrBestFit.append((1 / (fit_fn(dist) ** 2))) - dist += shiftdist - snrthresholds.append((1 / (fit_fn(dist) ** 2)) - shiftSNR * np.exp(-0.05 * dist)) - plotFittedSNR(dists, snrthresholds, snrs, snrBestFit) - return fit_fn #### ZU VERBESSERN, sollte fertige funktion wiedergeben - - -def plotFittedSNR(dists, snrthresholds, snrs, snrBestFit): - """ - - :param dists: - :param snrthresholds: - :param snrs: - :param snrBestFit: - :return: - """ - import matplotlib.pyplot as plt - plt.interactive(True) - fig = plt.figure() - plt.plot(dists, snrs, 'b.', markersize=2.0, label='SNR values') - dists.sort() - snrthresholds.sort(reverse=True) - snrBestFit.sort(reverse=True) - plt.plot(dists, snrthresholds, 'r', markersize=1, label='Fitted threshold') - plt.plot(dists, snrBestFit, 'k', markersize=1, label='Best fitted curve') - plt.xlabel('Distance[m]') - plt.ylabel('SNR') - plt.legend() - - -def setDynamicFittedSNR(shot_dict, shiftdist=30, shiftSNR=100, p1=0.004, p2=-0.0007): - """ - Set SNR values for a dictionary containing Seismicshots (e.g. survey.getShotDict()) - by parameters calulated from fitSNR4dist. - - :param shot_dict: - :type shot_dict: dict - :param shiftdist: - :type shiftdist: int - :param shiftSNR: - :type shiftSNR: int - :param p1: - :type p1: float - :param p2: - :type p2: float - :return: - """ - minSNR = 2.5 - # fit_fn = fitSNR4dist(shot_dict) - fit_fn = get_fit_fn(p1, p2) - for shot in shot_dict.values(): - for traceID in shot.getTraceIDlist(): ### IMPROVE - dist = shot.getDistance(traceID) + shiftdist - snrthreshold = snr_fit_func(fit_fn, dist, shiftSNR) - if snrthreshold < minSNR: - print('WARNING: SNR threshold %s lower %s. Set SNR threshold to %s.' - % (snrthreshold, minSNR, minSNR)) - shot.setSNRthreshold(traceID, minSNR) - else: - shot.setSNRthreshold(traceID, snrthreshold) - print("setDynamicFittedSNR: Finished setting of fitted SNR-threshold.\n" - "Parameters: ShiftDist = %s, ShiftSNR = %s, p1 = %s, p2 = %s" - %(shiftdist, shiftSNR, p1, p2)) - -def snr_fit_func(fit_fn, dist, shiftSNR): - snrthreshold = (1 / (fit_fn(dist) ** 2)) - shiftSNR * np.exp(-0.05 * dist) - return snrthreshold - -def get_fit_fn(p1, p2): - return np.poly1d([p1, p2]) - -def setConstantSNR(shot_dict, snrthreshold=2.5): - """ - Set a constant SNR value to all Seismicshots in a dictionary (e.g. survey.getShotDict()). - - :param shot_dict: - :param snrthreshold: - :return: - """ - for shot in shot_dict.values(): - for traceID in shot.getTraceIDlist(): - shot.setSNRthreshold(traceID, snrthreshold) - print("setConstantSNR: Finished setting of SNR threshold to a constant value of %s" % snrthreshold) - - -def findTracesInRanges(shot_dict, distancebin, pickbin): - ''' - Returns traces corresponding to a certain area in a plot with all picks over the distances. - - :param: shot_dict, dictionary containing all shots that are used - :type: dictionary - - :param: distancebin - :type: tuple, (dist1[m], dist2[m]) - - :param: pickbin - :type: tuple, (t1[s], t2[s]) - - ''' - shots_found = {} - for shot in shot_dict.values(): - if shot.getTraceIDs4Dist(distancebin=distancebin) is not None: - for traceID in shot.getTraceIDs4Dist(distancebin=distancebin): - if pickbin[0] < shot.getPick(traceID) < pickbin[1]: - if shot.getShotnumber() not in shots_found.keys(): - shots_found[shot.getShotnumber()] = [] - shots_found[shot.getShotnumber()].append(traceID) - - return shots_found - - -def cleanUp(survey): - """ - Cleans up a Survey object by removing frontend information - which can not be saved in the pickle format. - """ - for shot in survey.data.values(): - shot.traces4plot = {} - -def plotScatterStats4Shots(survey, variable, ax = None, twoDim = False): - """ - Statistics, scatter plot. - - :param survey: - :param variable: can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' - :return: - """ - import matplotlib.pyplot as plt - import numpy as np - statsShot = {} - x = [] - y = [] - value = [] - for shot in survey.data.values(): - for traceID in shot.getTraceIDlist(): - if not shot in statsShot.keys(): - statsShot[shot] = {'x': shot.getSrcLoc()[0], - 'y': shot.getSrcLoc()[1], - 'SNR': [], - 'SPE': [], - 'picked traces': 0} - - SNR = shot.getSNR(traceID)[0] - if not SNR == np.inf: - statsShot[shot]['SNR'].append(SNR) - if shot.getPickFlag(traceID) == 1: - statsShot[shot]['picked traces'] += 1 - statsShot[shot]['SPE'].append(shot.getSymmetricPickError(traceID)) - - for shot in statsShot.keys(): - statsShot[shot]['mean SNR'] = np.mean(statsShot[shot]['SNR']) - statsShot[shot]['median SNR'] = np.median(statsShot[shot]['SNR']) - statsShot[shot]['mean SPE'] = np.mean(statsShot[shot]['SPE']) - statsShot[shot]['median SPE'] = np.median(statsShot[shot]['SPE']) - - for shot in statsShot.keys(): - x.append(statsShot[shot]['x']) - y.append(statsShot[shot]['y']) - value.append(statsShot[shot][variable]) - - if ax is None: - fig = plt.figure() - ax = fig.add_subplot(111) - - size = [] - for val in value: - size.append(100 * val / max(value)) - - sc = ax.scatter(x, y, s=size, c=value) - ax.text(0.5, 1.05,'Plot of all shots', - horizontalalignment='center', verticalalignment='center', - transform=ax.transAxes) - ax.set_xlabel('X [m]') - ax.set_ylabel('Y [m]') - if not twoDim: - ax.set_aspect('equal') - cbar = ax.figure.colorbar(sc) - cbar.set_label(variable) - - for shot in statsShot.keys(): - ax.annotate(' %s' % shot.getShotnumber(), xy=(shot.getSrcLoc()[0], shot.getSrcLoc()[1]), - fontsize='x-small', color='k') - - -def plotScatterStats4Receivers(survey, variable, ax = None, twoDim = False): - """ - Statistics, scatter plot. - - :param survey: - :param variable: can be 'mean SNR', 'median SNR', 'mean SPE', 'median SPE', or 'picked traces' - :return: - """ - import matplotlib.pyplot as plt - import numpy as np - statsRec = {} - x = [] - y = [] - value = [] - for shot in survey.data.values(): - for traceID in shot.getTraceIDlist(): - if not traceID in statsRec.keys(): - statsRec[traceID] = {'x': shot.getRecLoc(traceID)[0], - 'y': shot.getRecLoc(traceID)[1], - 'SNR': [], - 'SPE': [], - 'picked traces': 0} - - SNR = shot.getSNR(traceID)[0] - if not SNR == np.inf: - statsRec[traceID]['SNR'].append(SNR) - if shot.getPickFlag(traceID) == 1: - statsRec[traceID]['picked traces'] += 1 - statsRec[traceID]['SPE'].append(shot.getSymmetricPickError(traceID)) - - for traceID in statsRec.keys(): - statsRec[traceID]['mean SNR'] = np.mean(statsRec[traceID]['SNR']) - statsRec[traceID]['median SNR'] = np.median(statsRec[traceID]['SNR']) - statsRec[traceID]['mean SPE'] = np.mean(statsRec[traceID]['SPE']) - statsRec[traceID]['median SPE'] = np.median(statsRec[traceID]['SPE']) - - for traceID in statsRec.keys(): - x.append(statsRec[traceID]['x']) - y.append(statsRec[traceID]['y']) - value.append(statsRec[traceID][variable]) - - if ax is None: - fig = plt.figure() - ax = fig.add_subplot(111) - - size = [] - for val in value: - size.append(100 * val / max(value)) - - sc = ax.scatter(x, y, s=size, c=value) - ax.text(0.5, 1.05,'Plot of all receivers', - horizontalalignment='center', verticalalignment='center', - transform=ax.transAxes) - ax.set_xlabel('X [m]') - ax.set_ylabel('Y [m]') - if not twoDim: - ax.set_aspect('equal') - cbar = ax.figure.colorbar(sc) - cbar.set_label(variable) - - shot = survey.data.values()[0] - for traceID in shot.getTraceIDlist(): - ax.annotate(' %s' % traceID, xy=(shot.getRecLoc(traceID)[0], shot.getRecLoc(traceID)[1]), - fontsize='x-small', color='k') From ae0cc5e1600845ef5ff86626ff68654075707620 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 3 Oct 2016 08:54:09 +0200 Subject: [PATCH 1142/1144] [remove] moved correlation code from PyLoT to the seisobs utils scope --- README | 35 ---------- README.md | 9 ++- pylot/core/analysis/coinctimes.py | 55 --------------- pylot/core/analysis/correlation.py | 107 ----------------------------- pylot/core/analysis/trigger.py | 50 -------------- 5 files changed, 7 insertions(+), 249 deletions(-) delete mode 100644 README delete mode 100644 pylot/core/analysis/coinctimes.py delete mode 100644 pylot/core/analysis/correlation.py delete mode 100644 pylot/core/analysis/trigger.py diff --git a/README b/README deleted file mode 100644 index c5452495..00000000 --- a/README +++ /dev/null @@ -1,35 +0,0 @@ -PyLoT - -version: 0.1 - -The Python picking and Localisation Tool - -This python library contains a graphical user interfaces for picking -seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki) -and the PySide Qt4 bindings for python to be installed first. - -PILOT has originally been developed in Mathworks' MatLab. In order to -distribute PILOT without facing portability problems, it has been decided -to redevelop the software package in Python. The great work of the ObsPy -group allows easy handling of a bunch of seismic data and PyLoT will -benefit a lot compared to the former MatLab version. - -The development of PyLoT is part of the joint research project MAGS2. - -staff: -====== - -original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT) - -developer(s): S. Wehling-Benatelli, L. Kueperkoch, K. Olbert, M. Bischoff, - C. Wollin, M. Rische - -others: A. Bruestle, T. Meier, W. Friederich - -release notes: -============== - - - -October 2013 - diff --git a/README.md b/README.md index 36643ead..b54f8166 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,19 @@ staff: original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT) developer(s): S. Wehling-Benatelli, L. Kueperkoch, K. Olbert, M. Bischoff, - C. Wollin, M. Rische + C. Wollin, M. Rische, M. Paffrath others: A. Bruestle, T. Meier, W. Friederich release notes: ============== +### Features + +- consistent manual phase picking through: + 1. predefined SNR dependant zoom level + 2. -October 2016 \ No newline at end of file +October 2016 diff --git a/pylot/core/analysis/coinctimes.py b/pylot/core/analysis/coinctimes.py deleted file mode 100644 index 9b41b0c3..00000000 --- a/pylot/core/analysis/coinctimes.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from obspy.core import read -from obspy.signal.trigger import coincidenceTrigger - - -class CoincidenceTimes(object): - def __init__(self, st, comp='Z', coinum=4, sta=1., lta=10., on=5., off=1.): - _type = 'recstalta' - self.coinclist = self.createCoincTriggerlist(data=st, trigcomp=comp, - coinum=coinum, sta=sta, - lta=lta, trigon=on, - trigoff=off, type=_type) - - def __str__(self): - n = 1 - out = '' - for time in self.getCoincTimes(): - out += 'event no. {0}: starttime is {1}\n'.format(n, time) - n += 1 - return out - - def getCoincTimes(self): - timelist = [] - for info in self.getCoincList(): - timelist.append(info['time']) - - return timelist - - def getCoincList(self): - return self.coinclist - - def createCoincTriggerlist(self, data, trigcomp, coinum, sta, lta, - trigon, trigoff, type): - ''' - uses a coincidence trigger to detect all events in the given - dataset - ''' - - triggerlist = coincidenceTrigger(type, trigon, trigoff, - data.select(component=trigcomp), - coinum, sta=sta, lta=lta) - return triggerlist - - -def main(): - data = read('/data/SDS/2014/1A/ZV??/?H?.D/*.365') - data.filter(type='bandpass', freqmin=5., freqmax=30.) - coincs = CoincidenceTimes(data) - print(coincs) - - -if __name__ == '__main__': - main() diff --git a/pylot/core/analysis/correlation.py b/pylot/core/analysis/correlation.py deleted file mode 100644 index 2cf97231..00000000 --- a/pylot/core/analysis/correlation.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import numpy as np - - -def crosscorrsingle(wf1, wf2, taumax): - ''' - Calculates the crosscorrelation between two waveforms with a defined maximum timedifference. - :param wf1: first waveformdata - :type wf1: list - :param wf2: second waveformdata - :type wf2: list - :param taumax: maximum time difference between waveforms - :type taumax: positive integer - :return: returns the crosscorrelation funktion 'c' and the lagvector 'l' - :rtype: c and l are lists - ''' - N = len(wf1) - c = np.zeros(2 * taumax - 1) - l = np.zeros(2 * taumax - 1) - for tau in range(taumax): - Cxyplus = 0 - Cxyminus = 0 - for n in range(N - tau): - Cxy1plus = wf1[n] * wf2[n + tau] - Cxy1minus = wf1[n + tau] * wf2[n] - Cxyplus = Cxyplus + Cxy1plus - Cxyminus = Cxyminus + Cxy1minus - - c[(taumax - 1) - tau] = Cxyminus - c[(taumax - 1) + tau] = Cxyplus - l[(taumax - 1) - tau] = -tau - l[(taumax - 1) + tau] = tau - return c, l - - -def crosscorrnormcalc(weights, wfs): - ''' - crosscorrnormcalc - function that calculates the normalization for the - cross correlation carried out by 'wfscrosscorr' - :param weights: weighting factors for the single components - :type weights: tuple - :param wfs: tuple of `~numpy.array` object containing waveform data - :type wfs: tuple - :return: a floating point number yielding the by 'weights' weighted energy - of the waveforms in 'wfs' - :rtype: float - ''' - - # check if the parameters are of the right type - if not isinstance(weights, tuple): - raise TypeError("type of 'weight' should be 'tuple', but is {0}".format( - type(weights))) - if not isinstance(wfs, tuple): - raise TypeError( - "type of parameter 'wfs' should be 'tuple', but is {0}".format( - type(wfs))) - sqrsumwfs = 0. - for n, wf in enumerate(wfs): - sqrsumwf = np.sum(weights[n] ** 2. * wf ** 2.) - sqrsumwfs += sqrsumwf - return np.sqrt(sqrsumwfs) - - -def wfscrosscorr(weights, wfs, taumax): - ''' - wfscrosscorr - function that calculates successive cross-correlations from a set of waveforms stored in a matrix - - base formula is: - C(i)=SUM[p=1:nComponent](eP(p)*(SUM[n=1:N]APp(x,n)*APp(y,n+i)))/(SQRT(SUM[p=1:nComponent]eP(p)^2*(SUM[n=1:N](APp(x,n)^2)))*SQRT(SUM[p=1:nComponent]eP(p)^2*(SUM[n=1:N]APp(y,n)^2))) - whereas - nComponent is the number of components - N is the number of samples - i is the lag-index - - input: - APp rowvectors containing the waveforms of each component p for which the cross-correlation is calculated - tPp rowvectros containing times - eP vector containing the weighting factors for the components (maxsize = [1x3]) - - output: - C cross-correlation function - L lag-vector - - author(s): - - SWB 26.01.2010 as arranged with Thomas Meier and Monika Bischoff - - :param weights: weighting factors for the single components - :type weights: tuple - :param wfs: tuple of `~numpy.array` object containing waveform data - :type wfs: tuple - :param taumax: maximum time difference - :type taumax: positive integer - :return: returns cross correlation function normalized by the waveform energy - ''' - - ccnorm = 0. - ccnorm = crosscorrnormcalc(weights, wfs[0]) - ccnorm *= crosscorrnormcalc(weights, wfs[1]) - - c = 0. - for n in range(len(wfs)): - cc, l = crosscorrsingle(wfs[0][n], wfs[1][n], taumax) - c += cc - return c / ccnorm, l diff --git a/pylot/core/analysis/trigger.py b/pylot/core/analysis/trigger.py deleted file mode 100644 index a1e61b36..00000000 --- a/pylot/core/analysis/trigger.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from obspy.signal.trigger import recursive_sta_lta, trigger_onset - - -def createSingleTriggerlist(st, station='ZV01', trigcomp='Z', stalta=(1, 10), - trigonoff=(6, 1)): - ''' - uses a single-station trigger to create a triggerlist for this station - :param st: obspy stream - :type st: - :param station: station name to get triggers for (optional, default = ZV01) - :type station: str - :param trigcomp: (optional, default = Z) - :type trigcomp: str - :param stalta: (optional, default = (1,10)) - :type stalta: tuple - :param trigonoff: (optional, default = (6,1)) - :type trigonoff: tuple - :return: list of triggtimes - :rtype: list - ''' - tr = st.copy().select(component=trigcomp, station=station)[0] - df = tr.stats.sampling_rate - - cft = recursive_sta_lta(tr.data, int(stalta[0] * df), int(stalta[1] * df)) - triggers = trigger_onset(cft, trigonoff[0], trigonoff[1]) - trigg = [] - for time in triggers: - trigg.append(tr.stats.starttime + time[0] / df) - return trigg - - -def createSubCoincTriggerlist(trig, station='ZV01'): - ''' - makes a triggerlist with the events, that are triggered by the - coincidence trigger and are seen at the demanded station - :param trig: list containing triggers from coincidence trigger - :type trig: list - :param station: station name to get triggers for (optional, default = ZV01) - :type station: str - :return: list of triggertimes - :rtype: list - ''' - trigg = [] - for tri in trig: - if station in tri['stations']: - trigg.append(tri['time']) - return trigg From 91c181ef2d7a6ae9ec1f33c842432815b704f621 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 4 Oct 2016 06:32:10 +0200 Subject: [PATCH 1143/1144] [README] added some necessary information to the README for first release; renamed pylot_sample.in for convenience and removed an obsolete import --- README.md | 74 +++++++++++++++++++++++----- inputs/{pylot_sample.in => pylot.in} | 0 pylot/core/pick/autopick.py | 1 - 3 files changed, 63 insertions(+), 12 deletions(-) rename inputs/{pylot_sample.in => pylot.in} (100%) diff --git a/README.md b/README.md index b54f8166..be3e6e49 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ version: 0.1a The Python picking and Localisation Tool This python library contains a graphical user interfaces for picking -seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki) +seismic phases. This software needs [ObsPy][ObsPy] and the PySide Qt4 bindings for python to be installed first. PILOT has originally been developed in Mathworks' MatLab. In order to @@ -16,7 +16,67 @@ benefit a lot compared to the former MatLab version. The development of PyLoT is part of the joint research project MAGS2. -staff: +##Installation + +At the moment there is no automatic installation procedure available for PyLoT. +Best way to install is to clone the repository and add the path to your Python path. + +####prerequisites: + +In order to run PyLoT you need to install: + +- python +- scipy +- numpy +- matplotlib +- obspy +- pyside + +####some handwork + +PyLoT needs a properties folder on your system to work. It should be situated in your home directory: + + mkdir ~/.pylot + +In the next step you have to copy some files to this directory: + + cp path-to-pylot/inputs/pylot.in ~/.pylot/ + +for local distance seismicity + + cp path-to-pylot/inputs/autoPyLoT_local.in ~/.pylot/autoPyLoT.in + +for regional distance seismicity + + cp path-to-pylot/inputs/autoPyLoT_regional.in ~/.pylot/autoPyLoT.in + +and some extra information on filtering, error estimates (just needed for reading old PILOT data) and the Richter magnitude scaling relation + + cp path-to-pylot/inputs/filter.in path-to-pylot/inputs/PILOT_TimeErrors.in path-to-pylot/inputs/richter_scaling.data ~/.pylot/ + +You may need to do some modifications to these files. Especially folder names should be reviewed. + +PyLoT has been tested on Mac OSX (10.11) and Debian Linux 8. + + +##release notes: +============== + +#### Features + +- consistent manual phase picking through predefined SNR dependant zoom level +- uniform uncertainty estimation from waveform's properties for automatic and manual picks +- pdf representation and comparison of picks taking the uncertainty intrinsically into account +- Richter and moment magnitude estimation +- location determination with external installation of [NonLinLoc](http://alomax.free.fr/nlloc/index.html) + +#### Known issues + +- Magnitude estimation from manual PyLoT takes some time (instrument correction) + +We hope to solve these with the next release. + +####staff: ====== original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT) @@ -26,15 +86,7 @@ developer(s): S. Wehling-Benatelli, L. Kueperkoch, K. Olbert, M. Bischoff, others: A. Bruestle, T. Meier, W. Friederich -release notes: -============== - -### Features - -- consistent manual phase picking through: - 1. predefined SNR dependant zoom level - 2. - +[ObsPy]: http://github.com/obspy/obspy/wiki October 2016 diff --git a/inputs/pylot_sample.in b/inputs/pylot.in similarity index 100% rename from inputs/pylot_sample.in rename to inputs/pylot.in diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index f1a186cb..35111071 100755 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -17,7 +17,6 @@ from pylot.core.pick.charfuns import CharacteristicFunction from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \ getSNR, fmpicker, checkPonsets, wadaticheck -from pylot.core.util.dataprocessing import restitute_data from pylot.core.util.utils import getPatternLine from pylot.core.io.data import Data From b8840dff13e43ef073cc3a218256151c5fe6d0bd Mon Sep 17 00:00:00 2001 From: Sebastianw Wehling-Benatelli Date: Tue, 4 Oct 2016 06:40:48 +0200 Subject: [PATCH 1144/1144] [version] edited release version --- pylot/RELEASE-VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 4c41ac0c..be128064 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -5f92-dirty +0.1a