1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5from __future__ import absolute_import, division 

6 

7import logging 

8 

9from pyrocko import util 

10from pyrocko.util import urlopen 

11from pyrocko.io import quakeml 

12from .base_catalog import EarthquakeCatalog 

13 

14logger = logging.getLogger('pyrocko.client.isc') 

15 

16km = 1000. 

17 

18 

19class ISCError(Exception): 

20 pass 

21 

22 

23class ISCBlocked(ISCError): 

24 pass 

25 

26 

27class ISC(EarthquakeCatalog): 

28 ''' 

29 Interfacing the catalog of the Internation Seismological Centre (ISC). 

30 ''' 

31 

32 def __init__(self, catalog=None): 

33 self.events = {} 

34 

35 def flush(self): 

36 self.events = {} 

37 

38 def append_time_params(self, a, time_range): 

39 date_start_s, tstart_s = util.time_to_str( 

40 time_range[0], format='%Y-%m-%d %H:%M:%S').split() 

41 date_end_s, tend_s = util.time_to_str( 

42 time_range[1], format='%Y-%m-%d %H:%M:%S').split() 

43 date_start_s = date_start_s.split('-') 

44 date_end_s = date_end_s.split('-') 

45 

46 a('start_year=%s' % date_start_s[0]) 

47 a('start_month=%s' % date_start_s[1]) 

48 a('start_day=%s' % date_start_s[2]) 

49 a('start_time=%s' % tstart_s) 

50 

51 a('end_year=%s' % date_end_s[0]) 

52 a('end_month=%s' % date_end_s[1]) 

53 a('end_day=%s' % date_end_s[2]) 

54 a('end_time=%s' % tend_s) 

55 

56 def iter_event_names( 

57 self, 

58 time_range=None, 

59 magmin=None, 

60 magmax=None, 

61 latmin=-90., 

62 latmax=90., 

63 lonmin=-180., 

64 lonmax=180.): 

65 p = [] 

66 a = p.append 

67 

68 a('out_format=CATQuakeML') 

69 a('request=REVIEWED') 

70 a('searchshape=RECT') 

71 

72 self.append_time_params(a, time_range) 

73 

74 if magmin: 

75 a('min_mag=%g' % magmin) 

76 if magmax: 

77 a('max_mag=%g' % magmax) 

78 

79 a('bot_lat=%g' % latmin) 

80 a('top_lat=%g' % latmax) 

81 a('left_lon=%g' % lonmin) 

82 a('right_lon=%g' % lonmax) 

83 url = 'http://www.isc.ac.uk/cgi-bin/web-db-v4?' + '&'.join(p) 

84 

85 logger.debug('Opening URL: %s' % url) 

86 page = urlopen(url).read().decode() 

87 logger.debug('Received page (%i bytes)' % len(page)) 

88 

89 if 'The search could not be run due to problems' in page: 

90 logger.warning('%s\nurl: %s' % (page, url)) 

91 return 

92 elif 'No events were found.' in page: 

93 logger.info('No events were found.') 

94 events = [] 

95 else: 

96 try: 

97 data = quakeml.QuakeML.load_xml(string=page) 

98 except Exception: 

99 if page[:500].find( 

100 'Please try again in a few minutes') != -1: 

101 

102 raise ISCBlocked( 

103 'Apparently, we have queried ISC too eagerly:\n' 

104 + '-' * 79 + '\n' + page + '\n' + '-' * 79) 

105 else: 

106 raise ISCError( 

107 'Couldn\'t parse XML results from ISC:\n' 

108 + '-' * 79 + '\n' + page + '\n' + '-' * 79) 

109 

110 events = data.get_pyrocko_events() 

111 

112 for ev in events: 

113 self.events[ev.name] = ev 

114 

115 for ev in events: 

116 if time_range[0] <= ev.time and ev.time <= time_range[1]: 

117 yield ev.name 

118 

119 def get_event(self, name): 

120 if name not in self.events: 

121 t = self._name_to_date(name) 

122 for name2 in self.iter_event_names( 

123 time_range=(t-24*60*60, t+24*60*60)): 

124 

125 if name2 == name: 

126 break 

127 

128 return self.events[name] 

129 

130 def get_phase_markers(self, time_range, station_codes, phases): 

131 ''' 

132 Download phase picks from ISC catalog and return them as a list 

133 of `pyrocko.gui.PhaseMarker` instances. 

134 

135 :param time_range: Tuple with (tmin tmax) 

136 :param station_codes: List with ISC station codes 

137 (see http://www.isc.ac.uk/cgi-bin/stations?lista). 

138 If `station_codes` is 'global', query all ISC stations. 

139 :param phases: List of seismic phases. (e.g. ['P', 'PcP'] 

140 ''' 

141 

142 p = [] 

143 a = p.append 

144 

145 a('out_format=QuakeML') 

146 a('request=STNARRIVALS') 

147 if station_codes == 'global': 

148 a('stnsearch=GLOBAL') 

149 else: 

150 a('stnsearch=STN') 

151 a('sta_list=%s' % ','.join(station_codes)) 

152 

153 a('phaselist=%s' % ','.join(phases)) 

154 

155 self.append_time_params(a, time_range) 

156 

157 url = 'http://www.isc.ac.uk/cgi-bin/web-db-v4?' + '&'.join(p) 

158 

159 logger.debug('Opening URL: %s' % url) 

160 page = urlopen(url) 

161 page = page.read().decode() 

162 

163 if 'No stations were found.' in page: 

164 logger.info('No stations were found.') 

165 return [] 

166 

167 logger.debug('Received page (%i bytes)' % len(page)) 

168 

169 data = quakeml.QuakeML.load_xml(string=page) 

170 

171 markers = data.get_pyrocko_phase_markers() 

172 markers = self.replace_isc_codes(markers) 

173 

174 return markers 

175 

176 def replace_isc_codes(self, markers): 

177 for m in markers: 

178 new_nslc_ids = [] 

179 for (n, s, l_, c) in m.get_nslc_ids(): 

180 l_ = l_.replace('--', '') 

181 c = c.replace('???', '*') 

182 new_nslc_ids.append((n, s, l_, c)) 

183 m.nslc_ids = new_nslc_ids 

184 

185 return markers 

186 

187 def _name_to_date(self, name): 

188 ds = name[-23:] 

189 t = util.str_to_time(ds, format='%Y-%m-%d_%H-%M-%S.3FRAC') 

190 return t