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

111 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-10-06 15:01 +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`GEOFON <http://geofon.gfz-potsdam.de/>`_. 

9''' 

10 

11import time 

12import re 

13import logging 

14import json 

15 

16from pyrocko import model, util 

17from pyrocko.moment_tensor import MomentTensor, symmat6 

18from .base_catalog import EarthquakeCatalog, NotFound 

19 

20from pyrocko.util import urlopen 

21 

22logger = logging.getLogger('pyrocko.client.geofon') 

23 

24km = 1000. 

25 

26 

27class Geofon(EarthquakeCatalog): 

28 ''' 

29 Access the GEOFON earthquake catalog. 

30 ''' 

31 def __init__(self, get_moment_tensors=True): 

32 self.events = {} 

33 self._get_moment_tensors = get_moment_tensors 

34 

35 def flush(self): 

36 self.events = {} 

37 

38 def iter_event_names( 

39 self, 

40 time_range=None, 

41 nmax=1000, 

42 magmin=None, 

43 latmin=-90., 

44 latmax=90., 

45 lonmin=-180., 

46 lonmax=180.): 

47 

48 logger.debug('In Geofon.iter_event_names(...)') 

49 

50 dmin = time.strftime('%Y-%m-%d', time.gmtime(time_range[0])) 

51 dmax = time.strftime('%Y-%m-%d', time.gmtime(time_range[1]+24*60*60)) 

52 

53 if magmin is None: 

54 magmin = '' 

55 else: 

56 magmin = '%g' % magmin 

57 

58 ipage = 1 

59 while True: 

60 url = ('http://geofon.gfz-potsdam.de/eqinfo/list.php?' + '&'.join([ 

61 'page=%i' % ipage, 

62 'datemin=%s' % dmin, 

63 'datemax=%s' % dmax, 

64 'latmin=%g' % latmin, 

65 'latmax=%g' % latmax, 

66 'lonmin=%g' % lonmin, 

67 'lonmax=%g' % lonmax, 

68 'magmin=%s' % magmin, 

69 'fmt=geojson', 

70 'nmax=%i' % nmax])) 

71 

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

73 page = urlopen(url).read() 

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

75 events = self._parse_events_page(page) 

76 for ev in events: 

77 if ev.moment_tensor is True: 

78 ev.moment_tensor = self.get_mt(ev) 

79 

80 if not events: 

81 break 

82 

83 for ev in events: 

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

85 self.events[ev.name] = ev 

86 yield ev.name 

87 

88 ipage += 1 

89 

90 def get_event(self, name): 

91 logger.debug('In Geofon.get_event("%s")' % name) 

92 

93 if name not in self.events: 

94 url = 'http://geofon.gfz-potsdam.de/eqinfo/event.php' \ 

95 '?id=%s&fmt=geojson' % name 

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

97 page = urlopen(url).read() 

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

99 

100 try: 

101 ev = self._parse_event_page(page) 

102 self.events[name] = ev 

103 

104 except NotFound: 

105 raise NotFound(url) # reraise with url 

106 

107 ev = self.events[name] 

108 

109 if ev.moment_tensor is True: 

110 ev.moment_tensor = self.get_mt(ev) 

111 

112 return ev 

113 

114 def get_mt(self, ev): 

115 syear = time.strftime('%Y', time.gmtime(ev.time)) 

116 url = 'http://geofon.gfz-potsdam.de/data/alerts/%s/%s/mt.txt' % ( 

117 syear, ev.name) 

118 

119 try: 

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

121 page = urlopen(url).read() 

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

123 except util.HTTPError: 

124 logger.warning('No MT found for event "%s".' % ev.name) 

125 return None 

126 

127 return self._parse_mt_page(page) 

128 

129 def _parse_mt_page(self, page): 

130 d = {} 

131 for k in 'Scale', 'Mrr', 'Mtt', 'Mpp', 'Mrt', 'Mrp', 'Mtp': 

132 r = k.encode('ascii')+br'\s*=?\s*(\S+)' 

133 m = re.search(r, page) 

134 if m: 

135 s = m.group(1).replace(b'10**', b'1e') 

136 d[k.lower()] = float(s) 

137 

138 m = symmat6(*(d[x] for x in 'mrr mtt mpp mrt mrp mtp'.split())) 

139 m *= d['scale'] 

140 mt = MomentTensor(m_up_south_east=m) 

141 

142 return mt 

143 

144 def _parse_events_page(self, page, limit=None): 

145 j = json.loads(page.decode('utf-8')) 

146 events = [] 

147 for ifeature, feature in enumerate(j['features']): 

148 ev = self._json_feature_to_event(feature) 

149 events.append(ev) 

150 if limit and ifeature + 1 == limit: 

151 break 

152 

153 return events 

154 

155 def _parse_event_page(self, page): 

156 return self._parse_events_page(page, limit=1)[0] 

157 

158 def _json_feature_to_event(self, feature): 

159 name = feature['id'] 

160 lon, lat, depth = feature['geometry']['coordinates'] 

161 depth *= 1000. 

162 properties = feature['properties'] 

163 magnitude = properties['mag'] 

164 magnitude_type = properties['magType'] 

165 region = properties['place'] 

166 tevent = util.str_to_time(properties['time'].replace('T', ' ')) 

167 

168 if ((properties.get('hasMT', 'no') == 'yes') 

169 or properties['magType'] == 'Mw') and self._get_moment_tensors: 

170 

171 moment_tensor = True # flag for caller to query MT 

172 else: 

173 moment_tensor = None 

174 

175 status = properties['status'][:1] 

176 tags = [] 

177 if status in 'AMC': 

178 tags.append('geofon_status:%s' % status) 

179 

180 category = properties.get('evtype', '') 

181 if re.match(r'^[a-zA-Z0-9]+$', category): 

182 tags.append('geofon_category:%s' % category) 

183 

184 ev = model.Event( 

185 lat=float(lat), 

186 lon=float(lon), 

187 time=tevent, 

188 name=name, 

189 depth=float(depth), 

190 magnitude=float(magnitude), 

191 magnitude_type=str(magnitude_type), 

192 region=str(region), 

193 moment_tensor=moment_tensor, 

194 catalog='GEOFON', 

195 tags=tags) 

196 

197 return ev