1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

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