diff --git a/pylot/core/active/ActiveSeismoPick3D_GUI.py b/pylot/core/active/ActiveSeismoPick3D_GUI.py index eec13af5..839fc80f 100755 --- a/pylot/core/active/ActiveSeismoPick3D_GUI.py +++ b/pylot/core/active/ActiveSeismoPick3D_GUI.py @@ -9,7 +9,7 @@ matplotlib.rcParams['backend.qt4']='PySide' from PySide import QtCore, QtGui from pylot.core.active import activeSeismoPick, surveyUtils, fmtomoUtils, seismicArrayPreparation from pylot.core.active.gui.asp3d_layout import * -from pylot.core.active.gui.windows import Gen_SeisArray, Gen_Survey_from_SA, Gen_Survey_from_SR, Call_autopicker, Call_FMTOMO, Call_VTK_dialog +from pylot.core.active.gui.windows import Gen_SeisArray, Gen_Survey_from_SA, Gen_Survey_from_SR, Call_autopicker, Call_FMTOMO, Call_VTK_dialog, Postprocessing from pylot.core.active.gui.windows import openFile, saveFile, browseDir, getMaxCPU from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas @@ -43,6 +43,7 @@ class gui_control(object): self.autopicker = None self.fmtomo = None self.vtktools = None + self.postprocessing = None def setInitStates(self): self.setPickState(False) @@ -362,8 +363,9 @@ class gui_control(object): if not self.checkSurveyState(): self.printDialogMessage('No Survey defined.') return - self.survey.plotAllPicks() - self.refreshPickedWidgets() # wait until finished + self.postprocessing = Postprocessing(self.mainwindow, self.survey) + #self.survey.plotAllPicks() + #self.refreshPickedWidgets() # wait until finished def load_survey(self): diff --git a/pylot/core/active/activeSeismoPick.py b/pylot/core/active/activeSeismoPick.py index 98d8569b..02bda997 100644 --- a/pylot/core/active/activeSeismoPick.py +++ b/pylot/core/active/activeSeismoPick.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os import sys +import math import numpy as np from pylot.core.active import seismicshot from pylot.core.active.surveyUtils import cleanUp @@ -672,10 +673,29 @@ class Survey(object): ''' import matplotlib.pyplot as plt - import math plt.interactive(True) from pylot.core.active.surveyPlotTools import regions + dist, pick, snrlog, pickerror, spe = self.preparePlotAllPicks(plotRemoved) + + color = {'log10SNR': snrlog, + 'pickerror': pickerror, + 'spe': spe} + self.color = color + if refreshPlot is False: + ax, cbar, sc = 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, sc = self.createPlot(dist, pick, color[colorByVal], + label='%s' % colorByVal, ax=ax, + cbar=cbar) + ax.legend() + return ax + + def preparePlotAllPicks(self, plotRemoved = False): dist = [] pick = [] snrlog = [] @@ -693,29 +713,15 @@ class Survey(object): pickerror.append(shot.getPickError(traceID)) spe.append(shot.getSymmetricPickError(traceID)) - color = {'log10SNR': snrlog, - 'pickerror': pickerror, - 'spe': spe} - self.color = color - if refreshPlot is False: - 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.legend() - return ax + return dist, pick, snrlog, pickerror, spe + def createPlot(self, dist, pick, inkByVal, label, ax=None, cbar=None): ''' Used by plotAllPicks. ''' import matplotlib.pyplot as plt - plt.interactive(True) + #plt.interactive(True) cm = plt.cm.jet if ax is None: print('Generating new plot...') @@ -723,7 +729,7 @@ class Survey(object): ax = fig.add_subplot(111) sc = ax.scatter(dist, pick, cmap=cm, c=inkByVal, s=5, edgecolors='none', label=label) - cbar = plt.colorbar(sc, fraction=0.05) + cbar = fig.colorbar(sc, fraction=0.05) cbar.set_label(label) ax.set_xlabel('Distance [m]') ax.set_ylabel('Time [s]') @@ -732,14 +738,16 @@ class Survey(object): else: 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) + if cbar is not None: + cbar.ax.clear() + cbar = ax.figure.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) - + return ax, cbar, sc + def _update_progress(self, shotname, tend, progress): sys.stdout.write( 'Working on shot %s. ETC is %02d:%02d:%02d [%2.2f %%]\r' % ( diff --git a/pylot/core/active/gui/asp3d_layout.py b/pylot/core/active/gui/asp3d_layout.py index 0d5553c6..359d41d0 100644 --- a/pylot/core/active/gui/asp3d_layout.py +++ b/pylot/core/active/gui/asp3d_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'asp3d_layout.ui' # -# Created: Thu Aug 4 13:55:47 2016 +# Created: Wed Aug 10 14:11: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/gui/fmtomo_parameters_layout.py b/pylot/core/active/gui/fmtomo_parameters_layout.py index ab987e0b..c63c98c6 100644 --- a/pylot/core/active/gui/fmtomo_parameters_layout.py +++ b/pylot/core/active/gui/fmtomo_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'fmtomo_parameters_layout.ui' # -# Created: Thu Aug 4 13:55:47 2016 +# Created: Wed Aug 10 14:11: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/gui/generate_seisarray_layout.py b/pylot/core/active/gui/generate_seisarray_layout.py index 94a35c79..010ed513 100644 --- a/pylot/core/active/gui/generate_seisarray_layout.py +++ b/pylot/core/active/gui/generate_seisarray_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_seisarray_layout.ui' # -# Created: Thu Aug 4 13:55:47 2016 +# Created: Wed Aug 10 14:11: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/gui/generate_survey_layout.py b/pylot/core/active/gui/generate_survey_layout.py index 87e435c3..05492585 100644 --- a/pylot/core/active/gui/generate_survey_layout.py +++ b/pylot/core/active/gui/generate_survey_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout.ui' # -# Created: Thu Aug 4 13:55:47 2016 +# Created: Wed Aug 10 14:11: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/gui/generate_survey_layout_minimal.py b/pylot/core/active/gui/generate_survey_layout_minimal.py index 17d6352b..423c3c95 100644 --- a/pylot/core/active/gui/generate_survey_layout_minimal.py +++ b/pylot/core/active/gui/generate_survey_layout_minimal.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'generate_survey_layout_minimal.ui' # -# Created: Thu Aug 4 13:55:47 2016 +# Created: Wed Aug 10 14:11: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/gui/picking_parameters_layout.py b/pylot/core/active/gui/picking_parameters_layout.py index db9ccc8c..129c1fa1 100644 --- a/pylot/core/active/gui/picking_parameters_layout.py +++ b/pylot/core/active/gui/picking_parameters_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'picking_parameters_layout.ui' # -# Created: Thu Aug 4 13:55:47 2016 +# Created: Wed Aug 10 14:11: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/gui/postprocessing_layout.py b/pylot/core/active/gui/postprocessing_layout.py index 43507ed6..15fe772b 100644 --- a/pylot/core/active/gui/postprocessing_layout.py +++ b/pylot/core/active/gui/postprocessing_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'postprocessing_layout.ui' # -# Created: Tue Aug 9 10:36:58 2016 +# Created: Wed Aug 10 14:11:08 2016 # by: pyside-uic 0.2.15 running on PySide 1.2.2 # # WARNING! All changes made in this file will be lost! @@ -12,34 +12,13 @@ from PySide import QtCore, QtGui class Ui_postprocessing(object): def setupUi(self, postprocessing): postprocessing.setObjectName("postprocessing") - postprocessing.resize(640, 479) - self.verticalLayout_2 = QtGui.QVBoxLayout(postprocessing) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.verticalLayout = QtGui.QVBoxLayout() + postprocessing.resize(663, 617) + self.verticalLayout = QtGui.QVBoxLayout(postprocessing) self.verticalLayout.setObjectName("verticalLayout") - self.gridLayout = QtGui.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.pushButton_rect = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) - 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.setObjectName("pushButton_rect") - self.horizontalLayout.addWidget(self.pushButton_rect) - self.pushButton_poly = QtGui.QPushButton(postprocessing) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) - 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.setObjectName("pushButton_poly") - self.horizontalLayout.addWidget(self.pushButton_poly) - self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) + 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) @@ -49,7 +28,42 @@ class Ui_postprocessing(object): self.label.setMinimumSize(QtCore.QSize(0, 20)) self.label.setMaximumSize(QtCore.QSize(16777215, 30)) self.label.setObjectName("label") - self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + 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.Minimum) + 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.setObjectName("pushButton_rect") + self.horizontalLayout_4.addWidget(self.pushButton_rect) + self.pushButton_poly = QtGui.QPushButton(postprocessing) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + 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.setObjectName("pushButton_poly") + self.horizontalLayout_4.addWidget(self.pushButton_poly) + self.pushButton_plot = QtGui.QPushButton(postprocessing) + self.pushButton_plot.setCheckable(False) + self.pushButton_plot.setDefault(False) + self.pushButton_plot.setFlat(False) + self.pushButton_plot.setObjectName("pushButton_plot") + self.horizontalLayout_4.addWidget(self.pushButton_plot) + 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_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) @@ -59,9 +73,9 @@ class Ui_postprocessing(object): self.label_2.setMinimumSize(QtCore.QSize(0, 20)) self.label_2.setMaximumSize(QtCore.QSize(16777215, 30)) self.label_2.setObjectName("label_2") - self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1) - self.horizontalLayout_3 = QtGui.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") + 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.Minimum) sizePolicy.setHorizontalStretch(0) @@ -70,7 +84,7 @@ class Ui_postprocessing(object): self.pushButton_snr.setSizePolicy(sizePolicy) self.pushButton_snr.setMaximumSize(QtCore.QSize(16777215, 30)) self.pushButton_snr.setObjectName("pushButton_snr") - self.horizontalLayout_3.addWidget(self.pushButton_snr) + self.horizontalLayout_5.addWidget(self.pushButton_snr) self.pushButton_pe = QtGui.QPushButton(postprocessing) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) @@ -79,7 +93,7 @@ class Ui_postprocessing(object): self.pushButton_pe.setSizePolicy(sizePolicy) self.pushButton_pe.setMaximumSize(QtCore.QSize(16777215, 30)) self.pushButton_pe.setObjectName("pushButton_pe") - self.horizontalLayout_3.addWidget(self.pushButton_pe) + self.horizontalLayout_5.addWidget(self.pushButton_pe) self.pushButton_spe = QtGui.QPushButton(postprocessing) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) @@ -88,29 +102,23 @@ class Ui_postprocessing(object): self.pushButton_spe.setSizePolicy(sizePolicy) self.pushButton_spe.setMaximumSize(QtCore.QSize(16777215, 30)) self.pushButton_spe.setObjectName("pushButton_spe") - self.horizontalLayout_3.addWidget(self.pushButton_spe) - self.gridLayout.addLayout(self.horizontalLayout_3, 1, 1, 1, 1) - self.verticalLayout.addLayout(self.gridLayout) - self.horizontalLayout_plot = QtGui.QHBoxLayout() - self.horizontalLayout_plot.setObjectName("horizontalLayout_plot") - self.verticalLayout.addLayout(self.horizontalLayout_plot) - self.verticalLayout_2.addLayout(self.verticalLayout) - self.buttonBox = QtGui.QDialogButtonBox(postprocessing) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout_2.addWidget(self.buttonBox) + 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.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), postprocessing.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), postprocessing.reject) 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.label.setText(QtGui.QApplication.translate("postprocessing", "Selection", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_plot.setText(QtGui.QApplication.translate("postprocessing", "Plot", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("postprocessing", "Color by", 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)) diff --git a/pylot/core/active/gui/vtk_tools_layout.py b/pylot/core/active/gui/vtk_tools_layout.py index de492841..0782de77 100644 --- a/pylot/core/active/gui/vtk_tools_layout.py +++ b/pylot/core/active/gui/vtk_tools_layout.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'vtk_tools_layout.ui' # -# Created: Thu Aug 4 13:55:47 2016 +# Created: Wed Aug 10 14:11: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/gui/windows.py b/pylot/core/active/gui/windows.py index 1b7c61e4..50d15b0f 100644 --- a/pylot/core/active/gui/windows.py +++ b/pylot/core/active/gui/windows.py @@ -1,6 +1,9 @@ #!/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 @@ -8,11 +11,11 @@ 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 pylot.core.active.gui.vtk_tools_layout import Ui_vtk_tools +from vtk_tools_layout import Ui_vtk_tools +from postprocessing_layout import Ui_postprocessing +from pylot.core.active.surveyPlotTools import regions -import numpy as np -import matplotlib matplotlib.use('Qt4Agg') matplotlib.rcParams['backend.qt4']='PySide' @@ -597,3 +600,88 @@ class Call_VTK_dialog(object): 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() + + 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, inkByVal = 'snrlog'): + self.ax.clear() + ax = self.ax + ax, cbar, sc = self.survey.createPlot(self.dists, self.picks, self.inkDict[inkByVal], + 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_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 plotPicks(self): + self.region.plotTracesInActiveRegions() + + def refrSNR(self): + self.refreshPlot('snrlog') + + def refrPE(self): + self.refreshPlot('pe') + + def refrSPE(self): + self.refreshPlot('spe') + diff --git a/pylot/core/active/surveyPlotTools.py b/pylot/core/active/surveyPlotTools.py index 5dd34d81..c196fc51 100644 --- a/pylot/core/active/surveyPlotTools.py +++ b/pylot/core/active/surveyPlotTools.py @@ -29,7 +29,7 @@ class regions(object): ''' - def __init__(self, ax, cbar, survey): + def __init__(self, ax, cbar, survey, qt_interface = False): self.ax = ax self.cbar = cbar self.cbv = 'log10SNR' @@ -45,12 +45,13 @@ 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() + if not qt_interface: + self.buttons = {} + self._addButtons() self.addTextfield() self.drawFigure() @@ -525,4 +526,5 @@ class regions(object): if resetAxes == True: self.ax.set_xlim(self._xlim) self.ax.set_ylim(self._ylim) - plt.draw() + self.ax.figure.canvas.draw() + diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 3d2742e5..a56e58e8 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -339,16 +339,30 @@ class PDFDictionary(object): class PDFstatistics(object): - ''' - To do: - plots for std, quantiles, - ''' + """ + This object can be used to get various statistic values from probabillity density functions. + Takes a path as argument. + """ + + def __init__(self, directory): + """Initiates some values needed when dealing with pdfs later""" self.directory = directory self.evtlist = list() self.return_phase = None + def readTheta(self, arname, dir, fnpattern): + """ + Loads an array from file into object instance. + :param arname: Name of Array beeing created. + :type arname: string + :param dir: Directory where file is to be found. + :type dir: string + :param fnpattern: file name pattern for reading multiple files into one array. + :type fnpattern: string + :return: a list with all args* from the files. + """ exec('self.' + arname +' = []') filelist = glob.glob1(dir, fnpattern) for file in filelist: @@ -359,8 +373,14 @@ class PDFstatistics(object): exec('self.' + arname + ' += list') fid.close() + def makeFileList(self, fn_pattern='*.xml'): - evtlist = list() + """ + Takes a file pattern and searches for that recursively in the set path for the object. + :param fn_pattern: A pattern that can identify all datafiles. Default Value = '*.xml' + :type fn_pattern: string + :return: creates a list of events saved in the PDFstatistics object. + """ evtlist = glob.glob1((os.path.join(self.directory)), fn_pattern) if not evtlist: for root, _, files in os.walk(self.directory): @@ -369,7 +389,9 @@ class PDFstatistics(object): evtlist.append(os.path.join(root, file)) self.evtlist = evtlist + def __iter__(self): + """Iterating over the PDFstatistics object yields every single pdf from the list of events""" assert isinstance(self.return_phase, str), 'phase has to be set before being able to iterate over items...' for evt in self.evtlist: self.getPDFDict(self.directory, evt) @@ -379,13 +401,30 @@ class PDFstatistics(object): except KeyError: continue + def set_return_phase(self, type): + """ + Sets the phase typ of event data that is returned on iteration over the object. + :param type: can be either p (p-phase) or s (s-phase). + :type type: string + :return: - + """ if type.upper() not in 'PS': raise ValueError("phase type must be either 'P' or 'S'!") else: self.return_phase = type.upper() + def getQD(self,value): + """ + Takes a probability value and and returns the distance + between two complementary quantiles. + For example: getQD(0.3) yields Quantile(1-0.3) - Quantile(0.3) + :param value: 0 < value < 0.5 + :type value: float + :return: returns a list of all quantile distances for all pdfs in + the list of events. + """ QDlist = [] for pdf in self: QD = pdf.quantile_distance(value) @@ -394,6 +433,16 @@ class PDFstatistics(object): def getQDQ(self,value): + """ + Takes a probability value and and returns the fraction of + two quantile distances. + For example: + getQDQ(x) = getQD(0.5-x)/getQD(x) + (Quantile(1-0.5-x) - Quantile(x)) / (Quantile(1-x) - Quantile(x)) + :param value: 0 < value < 0.25 + :return: returns a list of all quantile fractions for all pdfs in + the list of events. + """ QDQlist = [] for pdf in self: QDQ = pdf.qtile_dist_quot(value) @@ -402,6 +451,12 @@ class PDFstatistics(object): def getSTD(self): + """ + Iterates over PDFstatistics object and returns the standard + deviation of all pdfs in the list of events. + :return: saves an instance of self.p_stdarray or + self.s_stdarray, depending on set phase. + """ std = [] for pdf in self: try: @@ -413,6 +468,10 @@ class PDFstatistics(object): def set_stdarray(self, array): + """ + Helper function for self.getSTD(). This function + should not be called directly. + """ if self.return_phase == 'P': self.p_stdarray = array elif self.return_phase == 'S': @@ -423,6 +482,22 @@ class PDFstatistics(object): def getBinList(self,l_boundary,u_boundary,nbins = 100): + """ + Helper function for self.histplot(). Takes in two boundaries and + a number of bins and creates a list of bins which can be passed + to self.histplot(). + :param l_boundary: Any number. + :type l_boundary: float + :param u_boundary: Any number that is greater than l_boundary. + :type u_boundary: float + :param nbins: Any positive integer. + :type nbins: int + :return: A list of equidistant bins. + """ + if u_boundary <= l_boundary: + raise ValueError('Upper boundary must be greather than lower!') + elif nbins <= 0: + raise ValueError('Number of bins is not valid.') binlist = [] for i in range(nbins): binlist.append(l_boundary + i*(u_boundary-l_boundary)/nbins) @@ -430,24 +505,57 @@ class PDFstatistics(object): def histplot(self, array, binlist, xlab = 'Values', - ylab = 'Frequency', title = None, label=None): + ylab = 'Frequency', title = None, fnout = None): + """ + Method to quickly show some distribution of data. Takes array like data, + and a list of bins. Editing detail and inserting a legend is not possible. + :param array: List of values. + :type array: Array like + :param binlist: List of bins. + :type binlist: list + :param xlab: A label for the x-axes. + :type xlab: str + :param ylab: A label for the y-axes. + :type ylab: str + :param title: A title for the Plot. + :type title: str + :param fnout: A path to save the plot instead of showing. + Has to contain filename and type. Like: 'path/to/file.png' + :type fnout. str + :return: - + """ import matplotlib.pyplot as plt plt.hist(array,bins = binlist) - plt.xlabel('Values') - plt.ylabel('Frequency') + plt.xlabel(xlab) + plt.ylabel(ylab) if title: - title_str = 'Quantile distance quotient distribution' - if label: - title_str += ' (' + label + ')' - plt.title(title_str) - plt.show() + plt.title(title) + if fnout: + plt.savefig(fnout) + else: + plt.show() def getPDFDict(self, month, evt): + """ + Helper function for __iter__(). Should not be called directly. + """ self.pdfdict = PDFDictionary.from_quakeml(os.path.join(self.directory,month,evt)) def getStatistics(self): + """ + On call function will get mean, median and standard deviation values + from self.p_stdarray and self.s_stdarray. Both must be + instances before calling this function. + :return: Creates instances of self.p_mean, self.p_std_std and self.p_median + for both phases (simultaneously) for the PDFstatistics object. + """ + if not self.p_stdarray or not self.s_stdarray: + raise NotImplementedError('Arrays are not properly set yet!') + elif type(self.p_stdarray) != type(np.zeros(1)) or type(self.s_stdarray) != type(np.zeros(1)): + raise TypeError('Array is not a proper numpy array.') + self.p_mean = self.p_stdarray.mean() self.p_std_std = self.p_stdarray.std() self.p_median = np.median(self.p_stdarray) @@ -456,12 +564,22 @@ class PDFstatistics(object): self.s_median = np.median(self.s_stdarray) - def writeThetaToFile(self,array,out_dir,filename = None): - fid = open(os.path.join(out_dir,filename), 'w') + def writeThetaToFile(self,array,out_dir): + """ + Method to write array like data to file. Useful since acquiring can take + serious amount of time when dealing with large databases. + :param array: List of values. + :type array: list + :param out_dir: Path to save file to including file name. + :type out_dir: str + :return: Saves a file at given output directory. + """ + fid = open(os.path.join(out_dir), 'w') for val in array: fid.write(str(val)+'\n') fid.close() + def main(): root_dir ='/home/sebastianp/Codetesting/xmls/' Insheim = PDFstatistics(root_dir) diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py index 94c847ce..814b7112 100644 --- a/pylot/core/util/pdf.py +++ b/pylot/core/util/pdf.py @@ -363,13 +363,15 @@ class ProbabilityDensityFunction(object): return m def quantile_distance(self, prob_value): + if 0 >= prob_value or prob_value >= 0.5: + raise ValueError('Value out of range.') ql = self.quantile(prob_value) qu = self.quantile(1 - prob_value) return qu - ql def qtile_dist_quot(self,x): - if x <= 0 or x >= 0.5: + if x <= 0 or x >= 0.25: raise ValueError('Value out of range.') return self.quantile_distance(0.5-x)/self.quantile_distance(x) diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index db0f75a5..e86132d2 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -82,8 +82,8 @@ def clims(lim1, lim2): def demeanTrace(trace, window): """ - returns the DATA where each trace is demean by the average value within - WINDOW + takes a trace object and returns the same trace object but with data + demeaned within a certain time window :param trace: waveform trace object :type trace: `~obspy.core.stream.Trace` :param window: @@ -100,9 +100,9 @@ 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 + :type combo_box: `~QComboBox` :param val: Name of a combo box to search for. - :type val: + :type val: basestring :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 @@ -110,21 +110,35 @@ def findComboBoxIndex(combo_box, val): 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: + function find_nearest takes an array and a value and returns the + index of the nearest value found in the array + :param array: array containing values + :type array: `~numpy.ndarray` + :param value: number searched for + :return: index of the array item being nearest to the value + + >>> a = np.array([ 1.80339578, -0.72546654, 0.95769195, -0.98320759, 0.85922623]) + >>> find_nearest(a, 1.3) + 2 + >>> find_nearest(a, 0) + 1 + >>> find_nearest(a, 2) + 0 + >>> find_nearest(a, -1) + 3 + >>> a = np.array([ 1.1, -0.7, 0.9, -0.9, 0.8]) + >>> find_nearest(a, 0.849) + 4 ''' return (np.abs(array - value)).argmin() def fnConstructor(s): ''' - - :param s: - :type s: - :return: + takes a string and returns a valid filename (especially on windows machines) + :param s: desired filename + :type s: str + :return: valid filename ''' if type(s) is str: s = s.split(':')[-1] @@ -142,7 +156,21 @@ def fnConstructor(s): def four_digits(year): - if year + 2000 < UTCDateTime.utcnow().year: + """ + takes a two digit year integer and returns the correct four digit equivalent + from the last 100 years + :param year: two digit year + :type year: int + :return: four digit year correspondant + + >>> four_digits(20) + 1920 + >>> four_digits(16) + 2016 + >>> four_digits(00) + 2000 + """ + if year + 2000 <= UTCDateTime.utcnow().year: year += 2000 else: year += 1900 @@ -151,10 +179,11 @@ def four_digits(year): def getGlobalTimes(stream): ''' - - :param stream: - :type stream - :return: + takes a stream object and returns the latest end and the earliest start + time of all contained trace objects + :param stream: seismological data stream + :type stream: `~obspy.core.stream.Stream` + :return: minimum start time and maximum end time ''' min_start = UTCDateTime() max_end = None @@ -168,6 +197,8 @@ def getGlobalTimes(stream): def getHash(time): ''' + takes a time object and returns the corresponding SHA1 hash of the + formatted date string :param time: time object for which a hash should be calculated :type time: :class: `~obspy.core.utcdatetime.UTCDateTime` object :return: str @@ -179,27 +210,27 @@ def getHash(time): def getLogin(): ''' - returns the actual user's login id - :return: User ID + returns the actual user's login ID + :return: login ID ''' return pwd.getpwuid(os.getuid())[0] def getOwner(fn): ''' - takes filename and returns the User ID of the owner of the file - :param fn: filename + takes a filename and return the login ID of the actual owner of the file + :param fn: filename of the file tested :type fn: str - :return: User ID + :return: login ID of the file's owner ''' 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: filename + 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 @@ -221,22 +252,52 @@ def getPatternLine(fn, pattern): def isSorted(iterable): ''' - takes a python variable and returns True if the iterable is sorted and False otherwise - :param iterable: a python variable + takes an iterable and returns 'True' if the items are in order otherwise + 'False' + :param iterable: an iterable object :type iterable: - :return: + :return: Boolean + + >>> isSorted(1) + Traceback (most recent call last): + ... + AssertionError: object is not iterable; object: 1 + >>> isSorted([1,2,3,4]) + True + >>> isSorted('abcd') + True + >>> isSorted('bcad') + False + >>> isSorted([2,3,1,4]) + False ''' + assert isIterable(iterable), 'object is not iterable; object: {' \ + '0}'.format(iterable) + if type(iterable) is str: + iterable = [s for s in iterable] return sorted(iterable) == iterable +def isIterable(obj): + """ + takes a python object and returns 'True' is the object is iterable and + 'False' otherwise + :param obj: a python object + :return: True of False + """ + try: + iterator = iter(obj) + except TypeError as te: + return False + return True + def prepTimeAxis(stime, trace): ''' - takes a starttime 'stime' and a trace object 'trace' and return a vector containing as much time stamps as data values in the trace object are available - :param stime: - :type stime: - :param trace: - :type trace: - :return: time axis + takes a starttime and a trace object and returns a valid time axis for + plotting + :param stime: start time of the actual seismogram as UTCDateTime + :param trace: seismic trace object + :return: valid numpy array with time stamps for plotting ''' nsamp = trace.stats.npts srate = trace.stats.sampling_rate