Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/client/isc.py: 85%

110 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2024-10-02 07:18 +0000

1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6''' 

7Client to get earthquake catalog information from 

8`ISC <http://www.isc.ac.uk/>`_. 

9''' 

10 

11import logging 

12import re 

13 

14from pyrocko import util 

15from pyrocko.util import urlopen 

16from pyrocko.io import quakeml 

17from .base_catalog import EarthquakeCatalog 

18 

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

20 

21km = 1000. 

22 

23 

24class ISCError(Exception): 

25 pass 

26 

27 

28class ISCBlocked(ISCError): 

29 pass 

30 

31 

32class ISC(EarthquakeCatalog): 

33 ''' 

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

35 ''' 

36 

37 def __init__(self, catalog=None): 

38 self.events = {} 

39 

40 def flush(self): 

41 self.events = {} 

42 

43 def append_time_params(self, a, time_range): 

44 date_start_s, tstart_s = util.time_to_str( 

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

46 date_end_s, tend_s = util.time_to_str( 

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

48 date_start_s = date_start_s.split('-') 

49 date_end_s = date_end_s.split('-') 

50 

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

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

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

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

55 

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

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

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

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

60 

61 def iter_event_names( 

62 self, 

63 time_range=None, 

64 magmin=None, 

65 magmax=None, 

66 latmin=-90., 

67 latmax=90., 

68 lonmin=-180., 

69 lonmax=180.): 

70 p = [] 

71 a = p.append 

72 

73 a('out_format=CATQuakeML') 

74 a('request=REVIEWED') 

75 a('searchshape=RECT') 

76 

77 self.append_time_params(a, time_range) 

78 

79 if magmin: 

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

81 if magmax: 

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

83 

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

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

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

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

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

89 

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

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

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

93 

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

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

96 return 

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

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

99 events = [] 

100 else: 

101 try: 

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

103 except Exception as e: 

104 if page[:500].find( 

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

106 

107 raise ISCBlocked( 

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

109 + '-' * 79 + '\n' + re.sub(r'<[^>]+>', ' ', page) 

110 + '\n' + '-' * 79) from None 

111 else: 

112 raise ISCError( 

113 "Couldn't parse XML results from ISC:\n" 

114 + '-' * 79 + '\n' + re.sub(r'<[^>]+>', ' ', page) 

115 + '\n' + '-' * 79) from e 

116 

117 events = data.get_pyrocko_events() 

118 

119 for ev in events: 

120 self.events[ev.name] = ev 

121 

122 for ev in events: 

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

124 yield ev.name 

125 

126 def get_event(self, name): 

127 if name not in self.events: 

128 t = self._name_to_date(name) 

129 for name2 in self.iter_event_names( 

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

131 

132 if name2 == name: 

133 break 

134 

135 return self.events[name] 

136 

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

138 ''' 

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

140 of `pyrocko.gui.PhaseMarker` instances. 

141 

142 :param time_range: Tuple with (tmin tmax) 

143 :param station_codes: List with ISC station codes 

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

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

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

147 ''' 

148 

149 p = [] 

150 a = p.append 

151 

152 a('out_format=QuakeML') 

153 a('request=STNARRIVALS') 

154 if station_codes == 'global': 

155 a('stnsearch=GLOBAL') 

156 else: 

157 a('stnsearch=STN') 

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

159 

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

161 

162 self.append_time_params(a, time_range) 

163 

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

165 

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

167 page = urlopen(url) 

168 page = page.read().decode() 

169 

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

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

172 return [] 

173 

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

175 if -1 != page.find( 

176 'Sorry, but your request cannot be processed at the present ' 

177 'time'): 

178 raise ISCBlocked( 

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

180 + '-' * 79 + '\n' + re.sub(r'<[^>]+>', ' ', page) 

181 + '\n' + '-' * 79) 

182 

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

184 

185 markers = data.get_pyrocko_phase_markers() 

186 markers = self.replace_isc_codes(markers) 

187 

188 return markers 

189 

190 def replace_isc_codes(self, markers): 

191 for m in markers: 

192 new_nslc_ids = [] 

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

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

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

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

197 m.nslc_ids = new_nslc_ids 

198 

199 return markers 

200 

201 def _name_to_date(self, name): 

202 ds = name[-23:] 

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

204 return t