diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..0531ea88 --- /dev/null +++ b/.mailmap @@ -0,0 +1,39 @@ +Darius Arnold +Darius Arnold +Darius Arnold +Darius Arnold + +Dennis Wlecklik + +Jeldrik Gaal + +Kaan Coekerim +Kaan Coekerim + +Ludger Kueperkoch +Ludger Kueperkoch +Ludger Kueperkoch + +Marc S. Boxberg + +Marcel Paffrath +Marcel Paffrath +Marcel Paffrath +Marcel Paffrath +Marcel Paffrath + +Sally Zimmermann + +Sebastian Wehling-Benatelli +Sebastian Wehling-Benatelli +Sebastian Wehling-Benatelli +Sebastian Wehling-Benatelli +Sebastian Wehling-Benatelli +Sebastian Wehling-Benatelli +Sebastian Wehling-Benatelli + +Thomas Moeller + +Ann-Christin Koch + +Sebastian Priebe diff --git a/PyLoT.py b/PyLoT.py index 9cb3cd67..2d854dcc 100755 --- a/PyLoT.py +++ b/PyLoT.py @@ -83,7 +83,7 @@ from pylot.core.util.event import Event from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ - ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \ + ComparisonWidget, TuneAutopicker, PylotParameterWidget, AutoPickDlg, CanvasWidget, AutoPickWidget, \ CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget, PickQualitiesFromXml, \ SpectrogramTab, SearchFileByExtensionDialog from pylot.core.util.array_map import Array_map @@ -136,7 +136,7 @@ class MainWindow(QMainWindow): self.project.parameter = self._inputs self.tap = None self.apw = None - self.paraBox = None + self.parameterWidget = None self.array_map = None self._metadata = Metadata(verbosity=0) self._eventChanged = [False, False] @@ -188,7 +188,6 @@ class MainWindow(QMainWindow): self.table_headers = ['', 'Event', 'Time', 'Lat', 'Lon', 'Depth', 'Ml', 'Mw', '[N] MP', '[N] AP', 'Tuning Set', 'Test Set', 'Notes'] - # TODO: refactor rootpath to datapath while True: try: if settings.value("user/FullName", None) is None: @@ -686,10 +685,9 @@ class MainWindow(QMainWindow): # add scroll area used in case number of traces gets too high self.wf_scroll_area = QtWidgets.QScrollArea(self) self.wf_scroll_area.setVisible(False) - self.no_data_label = QLabel('No Data') + self.no_data_label = QLabel('No Data. If data were already loaded, try to select the event again in the eventbox.') self.no_data_label.setStyleSheet('color: red') self.no_data_label.setAlignment(Qt.AlignCenter) - # create central matplotlib figure canvas widget self.init_wfWidget() @@ -1032,6 +1030,21 @@ class MainWindow(QMainWindow): fname = self.filename_from_action(action) if not fname: return + + qmb = QMessageBox(self, icon=QMessageBox.Question, + text='Do you want to overwrite this data?',) + overwrite_button = qmb.addButton('Overwrite', QMessageBox.YesRole) + merge_button = qmb.addButton('Merge', QMessageBox.NoRole) + + qmb.exec_() + + if qmb.clickedButton() == overwrite_button: + merge_strategy = 'Overwrite' + elif qmb.clickedButton() == merge_button: + merge_strategy = 'Merge' + else: + return + if not event: event = self.get_current_event() data = Data(self, event) @@ -1196,7 +1209,7 @@ class MainWindow(QMainWindow): with open(eventlist_file, 'r') as infile: eventlist_subset = [os.path.join(basepath, filename.split('\n')[0]) for filename in infile.readlines()] - msg = 'Found file "eventlist.txt" in database path. WILL ONLY USE SELECTED EVENTS out of {} events ' \ + msg = 'Found file "eventlist.txt" in datapath. WILL ONLY USE SELECTED EVENTS out of {} events ' \ 'contained in this subset' print(msg.format(len(eventlist_subset))) eventlist = [eventname for eventname in eventlist if eventname in eventlist_subset] @@ -1221,49 +1234,34 @@ class MainWindow(QMainWindow): # get path from first event in list and split them path = eventlist[0] try: - system_name = platform.system() - if system_name in ["Linux", "Darwin"]: - dirs = { - 'database': path.split('/')[-2], - 'datapath': os.path.split(path)[0], # path.split('/')[-3], - 'rootpath': '/' + os.path.join(*path.split('/')[:-3]) - } - elif system_name == "Windows": - rootpath = path.split('/')[:-3] - rootpath[0] += '/' - dirs = { - # TODO: Arrange path to meet Win standards - 'database': path.split('/')[-2], - 'datapath': path.split('/')[-3], - 'rootpath': os.path.join(*rootpath) - } + datapath = os.path.split(path)[0] + dirs = { + 'datapath': datapath, + } except Exception as e: dirs = { - 'database': '', 'datapath': '', - 'rootpath': '' } print('Warning: Could not automatically init folder structure. ({})'.format(e)) settings = QSettings() - settings.setValue("data/dataRoot", dirs['datapath']) # d irs['rootpath']) + settings.setValue("data/dataRoot", dirs['datapath']) settings.sync() if not self.project.eventlist: # init parameter object self.setParameter(show=False) # hide all parameter (show all needed parameter later) - self.paraBox.hide_parameter() + self.parameterWidget.hide_parameter() for directory in dirs.keys(): # set parameter - box = self.paraBox.boxes[directory] - self.paraBox.setValue(box, dirs[directory]) + box = self.parameterWidget.boxes[directory] + self.parameterWidget.setValue(box, dirs[directory]) # show needed parameter in box - self.paraBox.show_parameter(directory) - dirs_box = self.paraBox.get_groupbox_dialog('Directories') + self.parameterWidget.show_parameter(directory) + dirs_box = self.parameterWidget.get_groupbox_dialog('Directories') if not dirs_box.exec_(): return - self.project.rootpath = dirs['rootpath'] self.project.datapath = dirs['datapath'] else: if hasattr(self.project, 'datapath'): @@ -1272,7 +1270,6 @@ class MainWindow(QMainWindow): 'Datapath missmatch to current project!') return else: - self.project.rootpath = dirs['rootpath'] self.project.datapath = dirs['datapath'] self.project.add_eventlist(eventlist) @@ -1360,11 +1357,10 @@ class MainWindow(QMainWindow): return True def modify_project_path(self, new_rootpath): - # TODO: change root to datapath - self.project.rootpath = new_rootpath + self.project.datapath = new_rootpath for event in self.project.eventlist: - event.rootpath = new_rootpath - event.path = os.path.join(event.rootpath, event.datapath, event.database, event.pylot_id) + event.datapath = new_rootpath + event.path = os.path.join(event.datapath, event.pylot_id) event.path = event.path.replace('\\', '/') event.path = event.path.replace('//', '/') @@ -1558,7 +1554,7 @@ class MainWindow(QMainWindow): self.set_fname(self.get_data().getEventFileName(), type) return self.get_fnames(type) - def saveData(self, event=None, directory=None, outformats=['.xml', '.cnv', '.obs', '_focmec.in', '.pha']): + def saveData(self, event=None, directory=None, outformats=None): ''' Save event data to directory with specified output formats. :param event: PyLoT Event, if not set current event will be used @@ -1566,6 +1562,8 @@ class MainWindow(QMainWindow): :param outformats: str/list of output formats :return: ''' + if outformats is None: + outformats = ['.xml', '.cnv', '.obs', '_focmec.in', '.pha'] if not event: event = self.get_current_event() if not type(outformats) == list: @@ -1701,7 +1699,7 @@ class MainWindow(QMainWindow): # WIP JG def eventlistXml(self): - path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database'] + path = self._inputs['datapath'] outpath = self.project.location[:self.project.location.rfind('/')] geteventlistfromxml(path, outpath) return @@ -2428,7 +2426,7 @@ class MainWindow(QMainWindow): filterS = filteroptions['S'] minP, maxP = filterP.getFreq() minS, maxS = filterS.getFreq() - self.paraBox.params_to_gui() + self.parameterWidget.params_to_gui() def getFilterOptions(self): return self.filteroptions @@ -3176,8 +3174,8 @@ class MainWindow(QMainWindow): ttt = parameter['ttpatter'] outfile = parameter['outpatter'] eventname = self.get_current_event_name() - obsdir = os.path.join(self._inputs['rootpath'], self._inputs['datapath'], self._inputs['database'], eventname) - self.saveData(event=self.get_current_event(), directory=obsdir, outformats='.obs') + obsdir = os.path.join(self._inputs['datapath'], eventname) + self.saveData(event=self.get_current_event(), directory=obsdir, outformats=['.obs']) filename = 'PyLoT_' + eventname locpath = os.path.join(locroot, 'loc', filename) phasefile = os.path.join(obsdir, filename + '.obs') @@ -3734,6 +3732,7 @@ class MainWindow(QMainWindow): if self.project.parameter: # do this step to update default parameter on older PyLoT projects self.project.parameter.reinit_default_parameters() + PylotParameter.check_deprecated_parameters(self.project.parameter) self._inputs = self.project.parameter self.updateFilteroptions() @@ -3851,13 +3850,13 @@ class MainWindow(QMainWindow): def setParameter(self, checked=0, show=True): if checked: pass # dummy argument to receive trigger signal (checked) if called by QAction - if not self.paraBox: - self.paraBox = PylotParaBox(self._inputs, parent=self, windowflag=Qt.Window) - self.paraBox.accepted.connect(self._setDirty) - self.paraBox.accepted.connect(self.filterOptionsFromParameter) + if not self.parameterWidget: + self.parameterWidget = PylotParameterWidget(self._inputs, parent=self, windowflag=Qt.Window) + self.parameterWidget.accepted.connect(self._setDirty) + self.parameterWidget.accepted.connect(self.filterOptionsFromParameter) if show: - self.paraBox.params_to_gui() - self.paraBox.show() + self.parameterWidget.params_to_gui() + self.parameterWidget.show() def deleteAllAutopicks(self): qmb = QMessageBox(self, icon=QMessageBox.Question, @@ -3904,16 +3903,18 @@ class Project(object): Pickable class containing information of a PyLoT project, like event lists and file locations. ''' - # TODO: remove rootpath def __init__(self): self.eventlist = [] self.location = None - self.rootpath = None self.datapath = None self.dirty = False self.parameter = None self._table = None + @property + def rootpath(self): + return self.datapath + def add_eventlist(self, eventlist): ''' Add events from an eventlist containing paths to event directories. @@ -3923,8 +3924,6 @@ class Project(object): return for item in eventlist: event = Event(item) - event.rootpath = self.parameter['rootpath'] - event.database = self.parameter['database'] event.datapath = self.parameter['datapath'] if not event.path in self.getPaths(): self.eventlist.append(event) diff --git a/autoPyLoT.py b/autoPyLoT.py index 506025c5..4746c963 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -136,11 +136,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if parameter.hasParam('datastructure'): # getting information on data structure datastructure = DATASTRUCTURE[parameter.get('datastructure')]() - dsfields = {'root': parameter.get('rootpath'), - 'dpath': parameter.get('datapath'), - 'dbase': parameter.get('database')} + dsfields = {'dpath': parameter.get('datapath'),} - exf = ['root', 'dpath', 'dbase'] + exf = ['dpath'] if parameter['eventID'] != '*' and fnames == 'None': dsfields['eventID'] = parameter['eventID'] @@ -206,12 +204,10 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even locflag = 2 else: # started in tune or interactive mode - datapath = os.path.join(parameter['rootpath'], - parameter['datapath']) + datapath = parameter['datapath'] events = [] for eventID in eventid: events.append(os.path.join(datapath, - parameter['database'], eventID)) if not events: diff --git a/docs/gui.md b/docs/gui.md index 4fc735c1..018a3a81 100644 --- a/docs/gui.md +++ b/docs/gui.md @@ -203,8 +203,6 @@ The meaning of the header entries is: PyLoT GUI starts with an empty project. To add events, use the add event data button. Select one or multiple folders containing events. -[//]: <> (TODO: explain _Directories: Root path, Data path, Database path_) - ### Saving projects Save the current project from the menu with File->Save project or File->Save project as. PyLoT uses ``.plp`` files to diff --git a/inputs/pylot_global.in b/inputs/pylot_global.in index 6024b7d8..3bad82c4 100644 --- a/inputs/pylot_global.in +++ b/inputs/pylot_global.in @@ -4,10 +4,8 @@ %Parameters are optimized for %extent data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #main settings# - #rootpath# %project path - #datapath# %data path - #database# %name of data base - #eventID# %event ID for single event processing (* for all events found in database) + #datapath# %data path + #eventID# %event ID for single event processing (* for all events found in datapath) #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure True #apverbose# %choose 'True' or 'False' for terminal output diff --git a/inputs/pylot_local.in b/inputs/pylot_local.in index ccfeddd4..3f689374 100644 --- a/inputs/pylot_local.in +++ b/inputs/pylot_local.in @@ -4,10 +4,8 @@ %Parameters are optimized for %extent data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #main settings# -/DATA/Insheim #rootpath# %project path -EVENT_DATA/LOCAL #datapath# %data path -2018.02_Insheim #database# %name of data base -e0006.038.18 #eventID# %event ID for single event processing (* for all events found in database) +/DATA/Insheim/EVENT_DATA/LOCAL/2018.02_Insheim #datapath# %data path +e0006.038.18 #eventID# %event ID for single event processing (* for all events found in datapath) /DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure True #apverbose# %choose 'True' or 'False' for terminal output diff --git a/inputs/pylot_regional.in b/inputs/pylot_regional.in index fe019440..4e33d0a6 100644 --- a/inputs/pylot_regional.in +++ b/inputs/pylot_regional.in @@ -4,10 +4,8 @@ %Parameters are optimized for %extent data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #main settings# - #rootpath# %project path - #datapath# %data path - #database# %name of data base - #eventID# %event ID for single event processing (* for all events found in database) + #datapath# %data path + #eventID# %event ID for single event processing (* for all events found in datapath) #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure True #apverbose# %choose 'True' or 'False' for terminal output diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index b9a63adb..cc550f01 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -6,24 +6,14 @@ import numpy as np Default parameters used for picking """ -defaults = {'rootpath': {'type': str, - 'tooltip': 'project path', - 'value': '', - 'namestring': 'Root path'}, - - 'datapath': {'type': str, - 'tooltip': 'data path', +defaults = {'datapath': {'type': str, + 'tooltip': 'path to eventfolders', 'value': '', 'namestring': 'Data path'}, - 'database': {'type': str, - 'tooltip': 'name of data base', - 'value': '', - 'namestring': 'Database path'}, - 'eventID': {'type': str, - 'tooltip': 'event ID for single event processing (* for all events found in database)', - 'value': '', + 'tooltip': 'event ID for single event processing (* for all events found in datapath)', + 'value': '*', 'namestring': 'Event ID'}, 'extent': {'type': str, @@ -522,9 +512,7 @@ defaults = {'rootpath': {'type': str, settings_main = { 'dirs': [ - 'rootpath', 'datapath', - 'database', 'eventID', 'invdir', 'datastructure', diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index 6f2528bd..71888642 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import logging +import os from pylot.core.io import default_parameters from pylot.core.util.errors import ParameterError @@ -88,10 +90,10 @@ class PylotParameter(object): return bool(self.__parameter) def __getitem__(self, key): - try: + if key in self.__parameter: return self.__parameter[key] - except: - return None + else: + logging.warning(f'{key} not found in PylotParameter') def __setitem__(self, key, value): try: @@ -418,6 +420,28 @@ class PylotParameter(object): line = value + name + ttip fid.write(line) + @staticmethod + def check_deprecated_parameters(parameters): + if parameters.hasParam('database') and parameters.hasParam('rootpath'): + parameters['datapath'] = os.path.join(parameters['rootpath'], parameters['datapath'], + parameters['database']) + logging.warning( + f'Parameters database and rootpath are deprecated. ' + f'Tried to merge them to now path: {parameters["datapath"]}.' + ) + + remove_keys = [] + for key in parameters: + if not key in default_parameters.defaults.keys(): + remove_keys.append(key) + logging.warning(f'Removing deprecated parameter: {key}') + + for key in remove_keys: + del parameters[key] + + parameters._settings_main = default_parameters.settings_main + parameters._settings_special_pick = default_parameters.settings_special_pick + class FilterOptions(object): ''' diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 3c50d131..93043536 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -512,7 +512,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None): fid = open("%s" % filename, 'w') # write header fid.write('# EQEVENT: %s Label: EQ%s Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n' % - (parameter.get('database'), parameter.get('eventID'))) + (parameter.get('datapath'), parameter.get('eventID'))) arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere for key in arrivals: # P onsets @@ -665,7 +665,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None): print("Writing phases to %s for HYPOSAT" % filename) fid = open("%s" % filename, 'w') # write header - fid.write('%s, event %s \n' % (parameter.get('database'), parameter.get('eventID'))) + fid.write('%s, event %s \n' % (parameter.get('datapath'), parameter.get('eventID'))) arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere for key in arrivals: # P onsets diff --git a/pylot/core/util/array_map.py b/pylot/core/util/array_map.py index 90259c44..a5aecea9 100644 --- a/pylot/core/util/array_map.py +++ b/pylot/core/util/array_map.py @@ -5,12 +5,13 @@ import traceback import cartopy.crs as ccrs import cartopy.feature as cf +from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter import matplotlib import matplotlib.patheffects as PathEffects import matplotlib.pyplot as plt import numpy as np import obspy -from PySide2 import QtWidgets +from PySide2 import QtWidgets, QtGui from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from mpl_toolkits.axes_grid1.inset_locator import inset_axes from pylot.core.util.utils import identifyPhaseID @@ -24,10 +25,10 @@ matplotlib.use('Qt5Agg') class MplCanvas(FigureCanvas): - def __init__(self, parent=None, extern_axes=None, width=5, height=4, dpi=100): + def __init__(self, extern_axes=None, projection=None, width=15, height=5, dpi=100): if extern_axes is None: self.fig = plt.figure(figsize=(width, height), dpi=dpi) - self.axes = self.fig.add_subplot(111) + self.axes = self.fig.add_subplot(111, projection=projection) else: self.fig = extern_axes.figure self.axes = extern_axes @@ -63,20 +64,25 @@ class Array_map(QtWidgets.QWidget): self.highlighted_stations = [] # call functions to draw everything + self.projection = ccrs.PlateCarree() self.init_graphics() + self.ax = self.canvas.axes + self.ax.set_adjustable('datalim') + self.init_stations() self.init_crtpyMap() self.init_map() + # set original map limits to fall back on when home button is pressed - self.org_xlim = self.canvas.axes.get_xlim() - self.org_ylim = self.canvas.axes.get_ylim() + self.org_xlim = self.ax.get_xlim() + self.org_ylim = self.ax.get_ylim() + # initial map without event - self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1]) - self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1]) + self.ax.set_xlim(self.org_xlim[0], self.org_xlim[1]) + self.ax.set_ylim(self.org_ylim[0], self.org_ylim[1]) self._style = None if not hasattr(parent, '_style') else parent._style - def init_map(self): self.init_colormap() self.connectSignals() @@ -89,11 +95,11 @@ class Array_map(QtWidgets.QWidget): # initialize figure elements if self.extern_plot_axes is None: - self.canvas = MplCanvas(self) - self.plotWidget = FigureCanvas(self.canvas.fig) + self.canvas = MplCanvas(projection=self.projection) else: - self.canvas = MplCanvas(self, extern_axes=self.extern_plot_axes) - self.plotWidget = FigureCanvas(self.canvas.fig) + self.canvas = MplCanvas(extern_axes=self.extern_plot_axes) + + self.plotWidget = self.canvas # initialize GUI elements self.status_label = QtWidgets.QLabel() @@ -105,7 +111,7 @@ class Array_map(QtWidgets.QWidget): self.setLayout(self.main_box) self.top_row = QtWidgets.QHBoxLayout() - self.main_box.addLayout(self.top_row, 1) + self.main_box.addLayout(self.top_row, 0) self.comboBox_phase = QtWidgets.QComboBox() self.comboBox_phase.insertItem(0, 'P') @@ -138,10 +144,10 @@ class Array_map(QtWidgets.QWidget): self.top_row.addWidget(self.auto_refresh_box) self.top_row.addWidget(self.refresh_button) - self.main_box.addWidget(self.plotWidget, 1) + self.main_box.addWidget(self.plotWidget, 10) self.bot_row = QtWidgets.QHBoxLayout() - self.main_box.addLayout(self.bot_row, 0.3) + self.main_box.addLayout(self.bot_row, 0) self.bot_row.addWidget(QtWidgets.QLabel(''), 5) self.bot_row.addWidget(self.map_reset_button, 2) self.bot_row.addWidget(self.go2eq_button, 2) @@ -153,14 +159,12 @@ class Array_map(QtWidgets.QWidget): self.init_lat_lon_grid() def init_crtpyMap(self): - self.canvas.axes.cla() - self.canvas.axes = plt.axes(projection=ccrs.PlateCarree()) - self.canvas.axes.add_feature(cf.LAND) - self.canvas.axes.add_feature(cf.OCEAN) - self.canvas.axes.add_feature(cf.COASTLINE, linewidth=1, edgecolor='gray') - self.canvas.axes.add_feature(cf.BORDERS, alpha=0.7) - self.canvas.axes.add_feature(cf.LAKES, alpha=0.7) - self.canvas.axes.add_feature(cf.RIVERS, linewidth=1) + self.ax.add_feature(cf.LAND) + self.ax.add_feature(cf.OCEAN) + self.ax.add_feature(cf.COASTLINE, linewidth=1, edgecolor='gray') + self.ax.add_feature(cf.BORDERS, alpha=0.7) + self.ax.add_feature(cf.LAKES, alpha=0.7) + self.ax.add_feature(cf.RIVERS, linewidth=1) # parallels and meridians self.add_merid_paral() @@ -168,12 +172,8 @@ class Array_map(QtWidgets.QWidget): self.canvas.fig.tight_layout() def add_merid_paral(self): - self.gridlines = self.canvas.axes.gridlines(draw_labels=False, alpha=0.6, color='gray', - linewidth=self.linewidth / 2, zorder=7) - # TODO: current cartopy version does not support label removal. Devs are working on it. - # Should be fixed in coming cartopy versions - # self.gridlines.xformatter = LONGITUDE_FORMATTER - # self.gridlines.yformatter = LATITUDE_FORMATTER + self.gridlines = self.ax.gridlines(draw_labels=False, alpha=0.6, color='gray', + linewidth=self.linewidth / 2, zorder=7, crs=ccrs.PlateCarree()) def remove_merid_paral(self): if len(self.gridlines.xline_artists): @@ -181,24 +181,24 @@ class Array_map(QtWidgets.QWidget): self.gridlines.yline_artists[0].remove() def org_map_view(self): - self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1]) - self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1]) + self.ax.set_xlim(self.org_xlim[0], self.org_xlim[1]) + self.ax.set_ylim(self.org_ylim[0], self.org_ylim[1]) # parallels and meridians - self.remove_merid_paral() - self.add_merid_paral() + #self.remove_merid_paral() + #self.add_merid_paral() - self.canvas.axes.figure.canvas.draw_idle() + self.canvas.draw_idle() def go2eq(self): if self.eventLoc: lats, lons = self.eventLoc - self.canvas.axes.set_xlim(lons - 10, lons + 10) - self.canvas.axes.set_ylim(lats - 5, lats + 5) + self.ax.set_xlim(lons - 10, lons + 10) + self.ax.set_ylim(lats - 5, lats + 5) # parallels and meridians - self.remove_merid_paral() - self.add_merid_paral() + #self.remove_merid_paral() + #self.add_merid_paral() - self.canvas.axes.figure.canvas.draw_idle() + self.canvas.draw_idle() else: self.status_label.setText('No event information available') @@ -220,21 +220,32 @@ class Array_map(QtWidgets.QWidget): # set mouse events ----------------------------------------------------- def mouse_moved(self, event): - if not event.inaxes == self.canvas.axes: + if not event.inaxes == self.ax: return + else: + cont, inds = self.sc.contains(event) lat = event.ydata lon = event.xdata - self.status_label.setText('Latitude: {:3.5f}, Longitude: {:3.5f}'.format(lat, lon)) + text = f'Longitude: {lon:3.3f}, Latitude: {lat:3.3f}' + + if cont: + indices = inds['ind'] + text += ' | Station: ' if len(indices) == 1 else ' | Stations: ' + text += ' - '.join([self._station_onpick_ids[index] for index in indices[:5]]) + if len(indices) > 5: + text += '...' + + self.status_label.setText(text) def mouse_scroll(self, event): - if not event.inaxes == self.canvas.axes: + if not event.inaxes == self.ax: return zoom = {'up': 1. / 2., 'down': 2.} if event.button in zoom: - xlim = self.canvas.axes.get_xlim() - ylim = self.canvas.axes.get_ylim() + xlim = self.ax.get_xlim() + ylim = self.ax.get_ylim() x, y = event.xdata, event.ydata @@ -246,24 +257,24 @@ class Array_map(QtWidgets.QWidget): yb = y - 0.5 * ydiff yt = y + 0.5 * ydiff - self.canvas.axes.set_xlim(xl, xr) - self.canvas.axes.set_ylim(yb, yt) + self.ax.set_xlim(xl, xr) + self.ax.set_ylim(yb, yt) # parallels and meridians - self.remove_merid_paral() - self.add_merid_paral() + #self.remove_merid_paral() + #self.add_merid_paral() - self.canvas.axes.figure.canvas.draw_idle() + self.ax.figure.canvas.draw_idle() def mouseLeftPress(self, event): - if not event.inaxes == self.canvas.axes: + if not event.inaxes == self.ax: return self.map_x = event.xdata self.map_y = event.ydata - self.map_xlim = self.canvas.axes.get_xlim() - self.map_ylim = self.canvas.axes.get_ylim() + self.map_xlim = self.ax.get_xlim() + self.map_ylim = self.ax.get_ylim() def mouseLeftRelease(self, event): - if not event.inaxes == self.canvas.axes: + if not event.inaxes == self.ax: return new_x = event.xdata new_y = event.ydata @@ -271,13 +282,13 @@ class Array_map(QtWidgets.QWidget): dx = new_x - self.map_x dy = new_y - self.map_y - self.canvas.axes.set_xlim((self.map_xlim[0] - dx, self.map_xlim[1] - dx)) - self.canvas.axes.set_ylim(self.map_ylim[0] - dy, self.map_ylim[1] - dy) + self.ax.set_xlim((self.map_xlim[0] - dx, self.map_xlim[1] - dx)) + self.ax.set_ylim(self.map_ylim[0] - dy, self.map_ylim[1] - dy) # parallels and meridians - self.remove_merid_paral() - self.add_merid_paral() + #self.remove_merid_paral() + #self.add_merid_paral() - self.canvas.axes.figure.canvas.draw_idle() + self.ax.figure.canvas.draw_idle() def onpick(self, event): btn_msg = {1: ' in selection. Aborted', 2: ' to delete a pick on. Aborted', 3: ' to display info.'} @@ -421,6 +432,9 @@ class Array_map(QtWidgets.QWidget): picks_rel[st_id] = pick return picks_rel + def get_picks_rel_mean_corr(picks): + return get_picks_rel(picks, func=np.nanmean) + self.picks, self.uncertainties = get_picks(self.stations_dict) self.picks_rel = get_picks_rel(self.picks) @@ -469,12 +483,19 @@ class Array_map(QtWidgets.QWidget): stat_dict = self.stations_dict['{}.{}'.format(network, station)] lat = stat_dict['latitude'] lon = stat_dict['longitude'] - self.highlighted_stations.append(self.canvas.axes.scatter(lon, lat, s=self.pointsize, edgecolors=color, - facecolors='none', zorder=12, - transform=ccrs.PlateCarree(), label='deleted')) + self.highlighted_stations.append(self.ax.scatter(lon, lat, s=self.pointsize, edgecolors=color, + facecolors='none', zorder=12, + transform=ccrs.PlateCarree(), label='deleted')) def openPickDlg(self, ind): - wfdata = self._parent.get_data().getWFData() + try: + wfdata = self._parent.get_data().getWFData() + except AttributeError: + QtWidgets.QMessageBox.warning( + self, "PyLoT Warning", + "No waveform data found. Check if they were already loaded in Waveform plot tab." + ) + return wfdata_comp = self._parent.get_data().getAltWFdata() for index in ind: network, station = self._station_onpick_ids[index].split('.')[:2] @@ -520,9 +541,9 @@ class Array_map(QtWidgets.QWidget): def draw_contour_filled(self, nlevel=50): levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel) - self.contourf = self.canvas.axes.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels, - linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(), - alpha=0.4, zorder=8, cmap=self.get_colormap()) + self.contourf = self.ax.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels, + linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(), + alpha=0.4, zorder=8, cmap=self.get_colormap()) def get_colormap(self): return plt.get_cmap(self.cmaps_box.currentText()) @@ -530,18 +551,18 @@ class Array_map(QtWidgets.QWidget): def scatter_all_stations(self): stations, lats, lons = self.get_st_lat_lon_for_plot() - self.sc = self.canvas.axes.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.', - zorder=10, picker=True, edgecolor='0.5', label='Not Picked', - transform=ccrs.PlateCarree()) + self.sc = self.ax.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.', + zorder=10, picker=True, edgecolor='0.5', label='Not Picked', + transform=ccrs.PlateCarree()) self.cid = self.plotWidget.mpl_connect('pick_event', self.onpick) self._station_onpick_ids = stations if self.eventLoc: lats, lons = self.eventLoc - self.sc_event = self.canvas.axes.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11, - label='Event (might be outside map region)', marker='*', - edgecolors='black', - transform=ccrs.PlateCarree()) + self.sc_event = self.ax.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11, + label='Event (might be outside map region)', marker='*', + edgecolors='black', + transform=ccrs.PlateCarree()) def scatter_picked_stations(self): picks, uncertainties, lats, lons = self.get_picks_lat_lon() @@ -554,8 +575,8 @@ class Array_map(QtWidgets.QWidget): for uncertainty in uncertainties]) cmap = self.get_colormap() - self.sc_picked = self.canvas.axes.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap, - c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree()) + self.sc_picked = self.ax.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap, + c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree()) def annotate_ax(self): self.annotations = [] @@ -573,20 +594,20 @@ class Array_map(QtWidgets.QWidget): if st in self.marked_stations: color = 'red' self.annotations.append( - self.canvas.axes.annotate(' %s' % st, xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4., - fontweight='semibold', color=color, alpha=0.8, - transform=ccrs.PlateCarree(), zorder=14, - path_effects=[PathEffects.withStroke( - linewidth=self.pointsize / 15., foreground='k')])) + self.ax.annotate(f'{st}', xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4., + fontweight='semibold', color=color, alpha=0.8, + transform=ccrs.PlateCarree(), zorder=14, + path_effects=[PathEffects.withStroke( + linewidth=self.pointsize / 15., foreground='k')])) - self.legend = self.canvas.axes.legend(loc=1, framealpha=1) + self.legend = self.ax.legend(loc=1, framealpha=1) self.legend.set_zorder(100) self.legend.get_frame().set_facecolor((1, 1, 1, 0.95)) def add_cbar(self, label): - self.cbax_bg = inset_axes(self.canvas.axes, width="6%", height="75%", loc=5) - cbax = inset_axes(self.canvas.axes, width='2%', height='70%', loc=5) - cbar = self.canvas.axes.figure.colorbar(self.sc_picked, cax=cbax) + self.cbax_bg = inset_axes(self.ax, width="6%", height="75%", loc=5) + cbax = inset_axes(self.ax, width='2%', height='70%', loc=5) + cbar = self.ax.figure.colorbar(self.sc_picked, cax=cbax) cbar.set_label(label) cbax.yaxis.tick_left() cbax.yaxis.set_label_position('left') diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py index ede1c9f9..5745bb9f 100644 --- a/pylot/core/util/event.py +++ b/pylot/core/util/event.py @@ -22,14 +22,11 @@ class Event(ObsPyEvent): :param path: path to event directory :type path: str """ - # TODO: remove rootpath and database self.pylot_id = path.split('/')[-1] # initialize super class super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id)) self.path = path - self.database = path.split('/')[-2] self.datapath = os.path.split(path)[0] # path.split('/')[-3] - self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) self.pylot_autopicks = {} self.pylot_picks = {} self.notes = '' diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 097e7548..1858548f 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -1084,8 +1084,9 @@ def check4rotated(data, metadata=None, verbosity=1): azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth']) dips.append(metadata.get_coordinates(tr_id, t_start)['dip']) except (KeyError, TypeError) as err: - logging.error(f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information " - f"available in metadata. Stream remains unchanged.") + logging.warning(f"Rotating not possible, not all azimuth and dip information " + f"available in metadata. Stream remains unchanged.") + logging.debug(f"Rotating not possible, {err=}, {type(err)=}") return wfs_in except Exception as err: print(f"Unexpected {err=}, {type(err)=}") diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 81761323..f48980c7 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -8,6 +8,7 @@ import copy import datetime import getpass import glob +import logging import multiprocessing import os import subprocess @@ -3836,7 +3837,7 @@ class TuneAutopicker(QWidget): self.stb_names = ['aicARHfig', 'refSpick', 'el_S1pick', 'el_S2pick'] def add_parameters(self): - self.paraBox = PylotParaBox(self.parameter, parent=self, windowflag=Qt.Widget) + self.paraBox = PylotParameterWidget(self.parameter, parent=self, windowflag=Qt.Widget) self.paraBox.set_tune_mode(True) self.update_eventID() self.parameter_layout.addWidget(self.paraBox) @@ -4203,7 +4204,7 @@ class TuneAutopicker(QWidget): self.qmb.show() -class PylotParaBox(QtWidgets.QWidget): +class PylotParameterWidget(QtWidgets.QWidget): accepted = QtCore.Signal(str) rejected = QtCore.Signal(str) @@ -4317,6 +4318,11 @@ class PylotParaBox(QtWidgets.QWidget): grid = QtWidgets.QGridLayout() for index1, name in enumerate(parameter_names): + if name in ['rootpath', 'database']: + logging.warning( + f'Deprecated parameter loaded: {name}. Check if datapath is still correct in parameter widget.' + ) + continue default_item = self.parameter.get_defaults()[name] tooltip = default_item['tooltip'] tooltip += ' | type: {}'.format(default_item['type']) @@ -4886,7 +4892,7 @@ class PropTab(QWidget): def getValues(self): return None - def resetValues(self, infile=None): + def resetValues(self, infile): return None @@ -4983,12 +4989,7 @@ class InputsTab(PropTab): else: index = 2 datapath = para.get('datapath') if not para.get('datapath') is None else '' - rootpath = para.get('rootpath') if not para.get('rootpath') is None else '' - database = para.get('database') if not para.get('database') is None else '' - if isinstance(database, int): - database = str(database) - path = os.path.join(os.path.expanduser('~'), rootpath, datapath, database) - values = {"data/dataRoot": self.dataDirEdit.setText("%s" % path), + values = {"data/dataRoot": self.dataDirEdit.setText("%s" % datapath), "user/FullName": self.fullNameEdit.text(), "data/Structure": self.structureSelect.setCurrentIndex(index), "tstart": self.tstartBox.setValue(0), diff --git a/tests/test_autopickstation/autoPyLoT_global_taupy_false.in b/tests/test_autopickstation/autoPyLoT_global_taupy_false.in index 6aa2edcc..51eb51bb 100644 --- a/tests/test_autopickstation/autoPyLoT_global_taupy_false.in +++ b/tests/test_autopickstation/autoPyLoT_global_taupy_false.in @@ -4,10 +4,8 @@ %Parameters are optimized for %extent data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #main settings# -/home/darius #rootpath# %project path -alparray #datapath# %data path -waveforms_used #database# %name of data base -e0093.173.16 #eventID# %event ID for single event processing (* for all events found in database) +/home/darius/alparray/waveforms_used #datapath# %data path +e0093.173.16 #eventID# %event ID for single event processing (* for all events found in datapath) /home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure True #apverbose# %choose 'True' or 'False' for terminal output diff --git a/tests/test_autopickstation/autoPyLoT_global_taupy_true.in b/tests/test_autopickstation/autoPyLoT_global_taupy_true.in index eefaa6c2..2bdf29fc 100644 --- a/tests/test_autopickstation/autoPyLoT_global_taupy_true.in +++ b/tests/test_autopickstation/autoPyLoT_global_taupy_true.in @@ -4,10 +4,8 @@ %Parameters are optimized for %extent data sets! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #main settings# -/home/darius #rootpath# %project path -alparray #datapath# %data path -waveforms_used #database# %name of data base -e0093.173.16 #eventID# %event ID for single event processing (* for all events found in database) +/home/darius/alparray/waveforms_used #datapath# %data path +e0093.173.16 #eventID# %event ID for single event processing (* for all events found in datapath) /home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file PILOT #datastructure# %choose data structure True #apverbose# %choose 'True' or 'False' for terminal output