Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Epoch plots #2023

Open
wants to merge 69 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
0e8e9bd
Add functions to create dictionaries to hold info for plotting
Nov 22, 2017
6061a3f
Add function for plotting, change minor formatting mistakes
Nov 22, 2017
bd0922c
Fix bugs preventing plot from being displayable
Dec 6, 2017
9846648
Add documentation on inventory objects epoch plotting
Dec 6, 2017
110d0c6
Fix flake8 issues
Dec 6, 2017
0be1f83
Fix outstanding issue with method names for inventory subcomponents
Dec 6, 2017
eceeae2
Remove trailing underscore for private methods
Dec 11, 2017
e563934
Dash channel epochs by sample rate, shade station-level blocks
Dec 11, 2017
5d8fa4e
Fix datetime object representation in epoch plot x-axis
Dec 11, 2017
128b734
Add caps to plot to prevent dash/epoch termination ambiguity
Dec 12, 2017
a5b183c
Match colors for all channels in a station, enable custom cmaps
Dec 12, 2017
40717a1
Set bounding boxes to be black colored for epoch plot
Dec 12, 2017
2974ea9
Color at station level, remove gridlines, fit plot to window
Dec 13, 2017
3a73b02
Change location and style of caps on epoch plot
Dec 14, 2017
4d7d8a3
Point epoch boundary markers inward, minor formatting fixes
Dec 14, 2017
0608288
Enable merging of channels with identical sample rate, epochs
Dec 18, 2017
f1aa96b
Remove debug-related print statements
Dec 18, 2017
427da76
Add a few comments and simplify control flow
Dec 19, 2017
b1d12ce
Fix regression caused by missed indentation
Dec 19, 2017
4c6b862
Improve merging of multi-epoch files
Dec 22, 2017
076ac0d
Fix issues with epoch plot grouping per channel/location
Jan 3, 2018
e0fdd58
Flake8 compliance changes
Jan 3, 2018
06ce3d4
Remove print statements, size plots to fit label length and count
Jan 3, 2018
211ebe9
Add inventory test, add param to disable showing plot
Feb 9, 2018
34c8342
fix typo in method signature (missing comma)
Feb 9, 2018
fbb1ad7
Flake8 compatibility, add data for epoch plot test case
Feb 12, 2018
bf1cf33
Add functions to create dictionaries to hold info for plotting
Nov 22, 2017
66d1caa
Add function for plotting, change minor formatting mistakes
Nov 22, 2017
35df09f
Fix bugs preventing plot from being displayable
Dec 6, 2017
c5b5dfb
Add documentation on inventory objects epoch plotting
Dec 6, 2017
7e66e6d
Fix flake8 issues
Dec 6, 2017
57c6567
Fix outstanding issue with method names for inventory subcomponents
Dec 6, 2017
bca02dd
Remove trailing underscore for private methods
Dec 11, 2017
f3566d8
Dash channel epochs by sample rate, shade station-level blocks
Dec 11, 2017
67f34e6
Fix datetime object representation in epoch plot x-axis
Dec 11, 2017
4936458
Add caps to plot to prevent dash/epoch termination ambiguity
Dec 12, 2017
4245ce1
Match colors for all channels in a station, enable custom cmaps
Dec 12, 2017
ea8e5be
Set bounding boxes to be black colored for epoch plot
Dec 12, 2017
7a97fa0
Color at station level, remove gridlines, fit plot to window
Dec 13, 2017
6e3db9b
Change location and style of caps on epoch plot
Dec 14, 2017
3531196
Point epoch boundary markers inward, minor formatting fixes
Dec 14, 2017
4ad1fc4
Enable merging of channels with identical sample rate, epochs
Dec 18, 2017
0228444
Remove debug-related print statements
Dec 18, 2017
0b42d0b
Add a few comments and simplify control flow
Dec 19, 2017
6803d97
Fix regression caused by missed indentation
Dec 19, 2017
c197f8a
Improve merging of multi-epoch files
Dec 22, 2017
5442ade
Fix issues with epoch plot grouping per channel/location
Jan 3, 2018
cf786c9
Flake8 compliance changes
Jan 3, 2018
08ddf42
Remove print statements, size plots to fit label length and count
Jan 3, 2018
656a12a
Add inventory test, add param to disable showing plot
Feb 9, 2018
8a5d2a1
fix typo in method signature (missing comma)
Feb 9, 2018
5175f66
Flake8 compatibility, add data for epoch plot test case
Feb 12, 2018
5db3986
Merge branch 'epoch_plots' of github.com:amkearns-usgs/obspy into epo…
Feb 13, 2018
faa5855
Add more comments for private helper methods for epoch plotting
Feb 13, 2018
704d86f
Committing minor changes in prep for rebase with master
Feb 22, 2018
7e2c59e
Set commit to enable rebase
Feb 22, 2018
084dcf6
Only import matplotlib in the places where it is needed
Mar 5, 2018
bdf378d
Fix plot showing when set not to, set up test for inventory plot
Mar 5, 2018
f1d5f74
add image to test inv. plot against
Mar 5, 2018
cd6859b
Make sure png file is uploaded as binary
Mar 5, 2018
81c6db7
Delete inventory-epoch-plot.png
Mar 5, 2018
7535994
(Re-)add inventory epoch plot test reference image
Mar 5, 2018
0b73cea
Replace ref. image with other that is aligned with generated one
Mar 5, 2018
a5e5df3
Add example of function use to docstrings
Mar 5, 2018
6f92b44
Default to show plot as true
Mar 6, 2018
3aba994
Remove dashing of lines, combine channels before sending to plot
Jun 5, 2018
f166ed5
Separate epoch groupings by location using a comma
Jun 5, 2018
60f6628
Make sure rstrip command matches end of stringified list
Jun 5, 2018
86fcf6e
Set _group_by_epochs function to be static
Jun 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
* text=auto eol=lf
*.png binary
41 changes: 40 additions & 1 deletion obspy/core/inventory/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
from future.builtins import * # NOQA
from future.utils import python_2_unicode_compatible

from obspy.core.utcdatetime import UTCDateTime
from obspy.core.util.obspy_types import FloatWithUncertainties
from . import BaseNode
from .util import Azimuth, ClockDrift, Dip, Distance, Latitude, Longitude
from .util import (Azimuth, ClockDrift, Dip, Distance, Latitude, Longitude,
plot_inventory_epochs)


@python_2_unicode_compatible
Expand Down Expand Up @@ -365,6 +367,43 @@ def plot(self, min_freq, output="VEL", start_stage=None, end_stage=None,
unwrap_phase=unwrap_phase, plot_degrees=plot_degrees, show=show,
outfile=outfile)

def _get_epoch_plottable_struct(self):
# channel is leaf object of inventory tree -- has no sub-dictionary
# but does have a sample rate defined
# for more information see same-name method in inventory.py
plot_dict = {}
name = str(self.location_code) + "." + str(self.code)
if self.start_date is not None:
if self.end_date is None:
end = UTCDateTime.now()
else:
end = self.end_date
time_tuple = (self.start_date, end)
plot_dict[name] = ([time_tuple], {})
return plot_dict

def plot_epochs(self, outfile=None, colormap=None, show=True,
combine=True):
"""
Plot the epochs of this given inventory object.
Returns a pyplot figure which can be saved to file.
:param outfile: If included, the plot will be saved to a file with the
given filename. (Otherwise it will be displayed in a window)
:type outfile: str
:param colormap: If this parameter is included, the plot will use the
given colorspace for inventory plotting
:type colormap: matplotlib.colors.LinearSegmentedColormap
:param show: If set as true, will display the plot in a window
:type show: boolean
:param combine: If set as true, channels with matching epochs will be
merged onto the same y-axis values
:type combine: boolean
"""
plot_dict = self._get_epoch_plottable_struct()
fig = plot_inventory_epochs(plot_dict, outfile, colormap, show,
combine)
return fig


if __name__ == '__main__':
import doctest
Expand Down
62 changes: 61 additions & 1 deletion obspy/core/inventory/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import warnings

import obspy
from obspy.core.utcdatetime import UTCDateTime
from obspy.core.util.base import (ENTRY_POINTS, ComparingObject,
_read_from_plugin, NamedTemporaryFile,
download_to_file, sanitize_filename)
Expand All @@ -29,7 +30,8 @@
from obspy.core.util.obspy_types import ObsPyException, ZeroSamplingRate

from .network import Network
from .util import _unified_content_strings, _textwrap
from .util import (_unified_content_strings, _textwrap, plot_inventory_epochs,
_merge_plottable_structs)

# Make sure this is consistent with obspy.io.stationxml! Importing it
# from there results in hard to resolve cyclic imports.
Expand Down Expand Up @@ -892,6 +894,64 @@ def plot_response(self, min_freq, output="VEL", network="*", station="*",

return fig

def _get_epoch_plottable_struct(self, combine):
# get structure of inventory epoch's data in format
# {inventory: ([(start, end)...], sample_rate, subdictionary)}
# where subdictionary is recursively the same for netwk, stn, ch data
# note that sample rate is only defined for the station level
# and subdictionary of inventory is network objects
# the conistent formatting at each level means that each level can be
# concatenated together -- i.e., each network epoch is a diff. object
# in obspy, but here they have their times merged together by name
plot_dict = {}
sub_dict = {}
for network in self.networks:
eps = network._get_epoch_plottable_struct(combine)
sub_dict = _merge_plottable_structs(sub_dict, eps)
if hasattr(self, 'start_date'):
start = self.start_date
if self.end_time is None:
end = UTCDateTime.now()
else:
end = self.end_time
else:
start = UTCDateTime(0)
end = UTCDateTime(0)
time_tuple = (start, end)
plot_dict[str('')] = ([time_tuple], sub_dict)
return plot_dict

def plot_epochs(self, outfile=None, colormap=None, show=True,
combine=True):
"""
Plot the epochs of this given inventory object.
Returns a pyplot figure which can be saved to file.
:param outfile: If included, the plot will be saved to a file with the
given filename. (Otherwise it will be displayed in a window)
:type outfile: str
:param colormap: If this parameter is included, the plot will use the
given colorspace for inventory plotting
:type colormap: matplotlib.colors.LinearSegmentedColormap
:param show: If set as true, will display the plot in a window
:type show: boolean
:param combine: If set as true, channels with matching epochs will be
merged onto the same y-axis values
:type combine: boolean

.. rubric:: Example

>>> inv = read_inventory()
>>> inv.plot_epochs(show=True) # doctest: +SKIP

.. plot::
inv = read_inventory()
inv.plot_epochs(show=True)

"""
plot_dict = self._get_epoch_plottable_struct(combine=combine)
fig = plot_inventory_epochs(plot_dict, outfile, colormap, show)
return fig


if __name__ == '__main__':
import doctest
Expand Down
57 changes: 55 additions & 2 deletions obspy/core/inventory/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
import fnmatch
import warnings

from obspy.core.utcdatetime import UTCDateTime
from obspy.core.util.obspy_types import ObsPyException, ZeroSamplingRate

from .station import Station
from .util import BaseNode, _unified_content_strings, _textwrap
from .station import (Station, _group_by_epochs)
from .util import (BaseNode, _unified_content_strings, _textwrap,
plot_inventory_epochs, _merge_plottable_structs)


@python_2_unicode_compatible
Expand Down Expand Up @@ -675,6 +677,57 @@ def plot_response(self, min_freq, output="VEL", station="*", location="*",

return fig

def _get_epoch_plottable_struct(self, combine):
# combine is boolean controlling whether or not to merge channels
# with matching epochs for any given station
# see the description for same-named method in inventory.py
plot_dict = {}
sub_dict = {}
name = str(self.code)
for station in self.stations:
eps = station._get_epoch_plottable_struct()
sub_dict = _merge_plottable_structs(sub_dict, eps)
#
if combine:
for station_key in sub_dict.keys():
(station_epoch, channel_dict) = sub_dict[station_key]
channel_dict = _group_by_epochs(channel_dict)
sub_dict[station_key] = (station_epoch, channel_dict)
if self.start_date is not None:
start = self.start_date
if self.end_date is None:
end = UTCDateTime.now()
else:
end = self.end_date
else:
start = UTCDateTime(0)
end = UTCDateTime(0)
# time tuple is just start, end times (does not include sample rates)
time_tuple = (start, end)
plot_dict[name] = ([time_tuple], sub_dict)
return plot_dict

def plot_epochs(self, outfile=None, colormap=None, show=True,
combine=True):
"""
Plot the epochs of this given inventory object.
Returns a pyplot figure which can be saved to file.
:param outfile: If included, the plot will be saved to a file with the
given filename. (Otherwise it will be displayed in a window)
:type outfile: str
:param colormap: If this parameter is included, the plot will use the
given colorspace for inventory plotting
:type colormap: matplotlib.colors.LinearSegmentedColormap
:param show: If set as true, will display the plot in a window
:type show: boolean
:param combine: If set as true, channels with matching epochs will be
merged onto the same y-axis values
:type combine: boolean
"""
plot_dict = self._get_epoch_plottable_struct(combine=combine)
fig = plot_inventory_epochs(plot_dict, outfile, colormap, show)
return fig


if __name__ == '__main__':
import doctest
Expand Down
95 changes: 93 additions & 2 deletions obspy/core/inventory/station.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@

import numpy as np

from obspy import UTCDateTime
from obspy.core.utcdatetime import UTCDateTime
from obspy.core.util.obspy_types import ObsPyException, ZeroSamplingRate

from .util import (BaseNode, Equipment, Operator, Distance, Latitude,
Longitude, _unified_content_strings, _textwrap)
Longitude, _unified_content_strings, _textwrap,
plot_inventory_epochs)


@python_2_unicode_compatible
Expand Down Expand Up @@ -507,6 +508,96 @@ def plot(self, min_freq, output="VEL", location="*", channel="*",

return fig

def _get_epoch_plottable_struct(self):
# list epochs of this inventory sub-object
# and the epochs of sub-objects (channels)
# see same method in inventory.py for more details
channel_epoch_dict = {}
plot_dict = {}
name = str(self.code)
for channel in self.channels:
single_channel_dict = channel._get_epoch_plottable_struct()
for key in single_channel_dict.keys():
# though we use an iterator here we expect only one key
if key not in channel_epoch_dict:
channel_epoch_dict[key] = single_channel_dict[key]
else:
# if station epoch contains a channel w/ multiple epochs
channel_epoch_dict[key].extend(single_channel_dict[key])
start = self.start_date
if self.end_date is None:
end = UTCDateTime.now()
else:
end = self.end_date
time_tuple = (start, end)
plot_dict[name] = ([time_tuple], channel_epoch_dict)
return plot_dict

def plot_epochs(self, outfile=None, colormap=None, show=True,
combine=True):
"""
Plot the epochs of this given inventory object.
Returns a pyplot figure which can be saved to file.
:param outfile: If included, the plot will be saved to a file with the
given filename. (Otherwise it will be displayed in a window)
:type outfile: str
:param colormap: If this parameter is included, the plot will use the
given colorspace for inventory plotting
:type colormap: matplotlib.colors.LinearSegmentedColormap
:param show: If set as true, will display the plot in a window
:type show: boolean
:param combine: If set as true, channels with matching epochs will be
merged onto the same y-axis values
:type combine: boolean
"""
plot_dict = self._get_epoch_plottable_struct()
if combine:
for key in plot_dict.keys():
(station_epoch, sub_dict) = plot_dict[key]
sub_dict = _group_by_epochs(sub_dict)
plot_dict[key] = (station_epoch, sub_dict)
fig = plot_inventory_epochs(plot_dict, outfile, colormap, show)
return fig


def _group_by_epochs(plot_dict):
# if we're just plotting a single station's data
# then we can do a simple means of combining epochs
# (we don't have to check channels' consistency over multiple epochs)
# since (lists of) tuples of immutable objects can be made as sets
# we will exploit that in order to create matching epochs
epoch_dict = {} # keys here will be epoch lists made into sets
for key in plot_dict.keys():
(channel_epochs, _) = set(plot_dict[key])
if channel_epochs not in epoch_dict.keys():
epoch_dict[channel_epochs] = [key]
else:
epoch_dict[channel_epochs].append(key)
# now epoch_dict has lists of all channels matching a given epoch
# we can simplify the plotted dict by merging them
# note that these are strings of form "[location].[channel]"
# we will set strings like "loc1: (ch1, ch2, ch3), loc2:..." etc.
# to be the keys for these matching epochs instead
new_plot_dict = {}
for epoch_set in epoch_dict.keys():
channel_strings = epoch_dict[epoch_set]
location_channel_map = {}
for channel_name in channel_strings:
split_string = channel_name.split('.')
location = split_string[0]
channel = split_string[1]
if location not in location_channel_map.keys():
location_channel_map[location] = channel
else:
location_channel_map[location].append(channel)
# now that the epoch listing exists, convert into a single string for plot dict key
combined_name = '['
for location in location_channel_map.keys():
combined_name += channel + ': ' + str(location_channel_map[location])
combined_name += ", "
combined_name = combined_name.rstrip(', ') + ']'
new_plot_dict[combined_name] = (list(epoch_set), {})
return new_plot_dict

if __name__ == '__main__':
import doctest
Expand Down
Loading