|
|
- import numpy as num
- import logging
- import math
-
- from pyrocko import plot
- from pyrocko.guts import Tuple, Float
-
- from matplotlib import pyplot as plt
- from matplotlib import lines
- from matplotlib.ticker import FuncFormatter
-
- from grond.plot.config import PlotConfig
-
- guts_prefix = 'grond'
- km = 1e3
- d2r = math.pi / 180.
-
- logger = logging.getLogger('targets.plot')
-
-
- class StationDistributionPlot(PlotConfig):
- ''' Plot showing all waveform fits for the ensemble of solutions'''
-
- name = 'seismic_stations_base'
- size_cm = Tuple.T(
- 2, Float.T(),
- default=(16., 13.),
- help='width and length of the figure in cm')
- font_size = Float.T(
- default=10,
- help='font size of all text, except station labels')
- font_size_labels = Float.T(
- default=6,
- help='font size of station labels')
-
- def plot_station_distribution(
- self, azimuths, distances, weights, labels=None,
- scatter_kwargs=dict(), annotate_kwargs=dict(), maxsize=10**2):
-
- invalid_color = plot.mpl_color('aluminium3')
-
- scatter_default = {
- 'alpha': .5,
- 'zorder': 10,
- 'c': plot.mpl_color('skyblue2'),
- }
-
- annotate_default = {
- 'alpha': .8,
- 'color': 'k',
- 'fontsize': self.font_size_labels,
- 'ha': 'right',
- 'va': 'top',
- 'xytext': (-5, -5),
- 'textcoords': 'offset points'
- }
-
- scatter_default.update(scatter_kwargs)
- annotate_default.update(annotate_kwargs)
-
- fig = plt.figure(figsize=self.size_inch)
-
- plot.mpl_margins(
- fig, nw=1, nh=1, left=3., right=10., top=3., bottom=3.,
- units=self.font_size)
-
- ax = fig.add_subplot(111, projection='polar')
-
- valid = num.isfinite(weights)
- valid[valid] = num.logical_and(valid[valid], weights[valid] > 0.0)
-
- weights = weights.copy()
- if num.sum(valid) == 0:
- weights[:] = 1.0
- weights_ref = 1.0
- else:
- weights[~valid] = weights[valid].min()
- weights_ref = plot.nice_value(weights[valid].max())
-
- if weights_ref == 0.:
- weights_ref = 1.0
-
- colors = [scatter_default['c'] if s else invalid_color
- for s in valid]
-
- scatter_default.pop('c')
-
- weights_scaled = (weights / weights_ref) * maxsize
-
- stations = ax.scatter(
- azimuths*d2r, distances, s=weights_scaled, c=colors,
- **scatter_default)
-
- if len(labels) < 30: # TODO: remove after impl. of collision detection
- if labels is not None:
- for ilbl, label in enumerate(labels):
- ax.annotate(
- label, (azimuths[ilbl]*d2r, distances[ilbl]),
- **annotate_default)
-
- ax.set_theta_zero_location('N')
- ax.set_theta_direction(-1)
- ax.tick_params('y', labelsize=self.font_size, labelcolor='gray')
- ax.grid(alpha=.3)
- ax.set_ylim(0, distances.max()*1.1)
- ax.yaxis.set_major_locator(plt.MaxNLocator(4))
- ax.yaxis.set_major_formatter(
- FuncFormatter(lambda x, pos: '%d km' % (x/km)))
-
- # Legend
- entries = 4
- valid_marker = num.argmax(valid)
- ecl = stations.get_edgecolor()
- fc = tuple(stations.get_facecolor()[valid_marker])
- ec = tuple(ecl[min(valid_marker, len(ecl)-1)])
-
- def get_min_precision(values):
- sig_prec = num.floor(
- num.isfinite(num.log10(values[values > 0])))
-
- if sig_prec.size == 0:
- return 1
-
- return int(abs(sig_prec.min())) + 1
-
- legend_artists = [
- lines.Line2D(
- [0], [0], ls='none',
- marker='o', ms=num.sqrt(rad), mfc=fc, mec=ec)
- for rad in num.linspace(maxsize, .1*maxsize, entries)
- ]
-
- sig_prec = get_min_precision(weights)
- legend_annot = [
- '{value:.{prec}f}'.format(value=val, prec=sig_prec)
- for val in num.linspace(weights_ref, .1*weights_ref, entries)
- ]
-
- if not num.all(valid):
- legend_artists.append(
- lines.Line2D(
- [0], [0], ls='none',
- marker='o', ms=num.sqrt(maxsize),
- mfc=invalid_color, mec=invalid_color))
- legend_annot.append('Excluded')
-
- legend = fig.legend(
- legend_artists, legend_annot,
- fontsize=self.font_size, loc=4,
- markerscale=1, numpoints=1,
- frameon=False)
-
- return fig, ax, legend
|