Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/client/globalcmt.py: 95%
119 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 06:59 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 06:59 +0000
1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6'''
7Client to get earthquake catalog information from
8`GlobalCMT <http://www.globalcmt.org/>`_.
9'''
11import time
12import calendar
13import re
14import logging
16from pyrocko import model, util
17from pyrocko.moment_tensor import MomentTensor
18from pyrocko.util import Request, urlopen
19from .base_catalog import EarthquakeCatalog
22import numpy as num
25logger = logging.getLogger('pyrocko.client.globalcmt')
27km = 1000.
30class Anon(object):
31 pass
34class GlobalCMT(EarthquakeCatalog):
35 '''
36 Client to get earthquake catalog information from
37 `GlobalCMT <http://www.globalcmt.org/>`_.
38 '''
40 def __init__(self):
41 self.events = {}
43 def flush(self):
44 self.events = {}
46 def iter_event_names(
47 self,
48 time_range=None,
49 magmin=0.,
50 magmax=10.,
51 latmin=-90.,
52 latmax=90.,
53 lonmin=-180.,
54 lonmax=180.,
55 depthmin=0.,
56 depthmax=1000*km):
58 yearbeg, monbeg, daybeg = time.gmtime(time_range[0])[:3]
59 yearend, monend, dayend = time.gmtime(time_range[1])[:3]
61 url = 'http://www.globalcmt.org/cgi-bin/globalcmt-cgi-bin/CMT5/form?' \
62 + '&'.join([
63 'itype=ymd',
64 'yr=%i' % yearbeg, 'mo=%i' % monbeg, 'day=%i' % daybeg,
65 'otype=ymd',
66 'oyr=%i' % yearend, 'omo=%i' % monend, 'oday=%i' % dayend,
67 'jyr=1976', 'jday=1', 'ojyr=1976', 'ojday=1', 'nday=1',
68 'lmw=%g' % magmin, 'umw=%g' % magmax,
69 'lms=0', 'ums=10',
70 'lmb=0', 'umb=10',
71 'llat=%g' % latmin, 'ulat=%g' % latmax,
72 'llon=%g' % lonmin, 'ulon=%g' % lonmax,
73 'lhd=%g' % (depthmin/km), 'uhd=%g' % (depthmax/km),
74 'lts=-9999', 'uts=9999',
75 'lpe1=0', 'upe1=90',
76 'lpe2=0', 'upe2=90',
77 'list=5'])
79 while True:
80 logger.debug('Opening URL: %s' % url)
81 req = Request(url)
82 page = urlopen(req).read()
83 logger.debug('Received page (%i bytes)' % len(page))
85 events, more = self._parse_events_page(page)
87 for ev in events:
88 self.events[ev.name] = ev
90 for ev in events:
91 if time_range[0] <= ev.time and ev.time <= time_range[1]:
92 yield ev.name
94 if more:
95 url = more.decode('ascii')
96 else:
97 break
99 def get_event(self, name):
100 if name not in self.events:
101 t = self._name_to_date(name)
102 for name2 in self.iter_event_names(
103 time_range=(t-24*60*60, t+2*24*60*60)):
105 if name2 == name:
106 break
108 return self.events[name]
110 def _parse_events_page(self, page):
112 lines = page.splitlines()
113 state = 0
115 events = []
117 def complete(data):
118 try:
119 t = util.to_time_float(
120 calendar.timegm((
121 data.year, data.month, data.day,
122 data.hour, data.minute, data.seconds)))
124 m = num.array(
125 [data.mrr, data.mrt, data.mrp,
126 data.mrt, data.mtt, data.mtp,
127 data.mrp, data.mtp, data.mpp],
128 dtype=float).reshape(3, 3)
130 m *= 10.0**(data.exponent-7)
131 mt = MomentTensor(m_up_south_east=m)
132 ev = model.Event(
133 lat=data.lat,
134 lon=data.lon,
135 time=t,
136 name=data.eventname,
137 depth=data.depth_km*1000.,
138 magnitude=float(mt.moment_magnitude()),
139 duration=data.half_duration * 2.,
140 region=data.region.rstrip(),
141 catalog=data.catalog)
143 ev.moment_tensor = mt
144 events.append(ev)
146 except AttributeError:
147 pass
149 catalog = 'gCMT'
151 data = None
152 more = None
153 for line in lines:
154 if state == 0:
156 m = re.search(br'<a href="([^"]+)">More solutions', line)
157 if m:
158 more = m.group(1)
160 m = re.search(br'From Quick CMT catalog', line)
161 if m:
162 catalog = 'gCMT-Q'
164 m = re.search(br'Event name:\s+(\S+)', line)
165 if m:
166 if data:
167 complete(data)
169 data = Anon()
170 data.eventname = str(m.group(1).decode('ascii'))
171 data.catalog = catalog
173 if data:
174 m = re.search(br'Region name:\s+([^<]+)', line)
175 if m:
176 data.region = str(m.group(1).decode('ascii'))
178 m = re.search(
179 br'Date \(y/m/d\): (\d\d\d\d)/(\d+)/(\d+)', line)
181 if m:
182 data.year, data.month, data.day = (
183 int(m.group(1)), int(m.group(2)), int(m.group(3)))
185 m = re.search(br'Timing and location information', line)
186 if m:
187 state = 1
189 if state == 1:
190 toks = line.split()
191 if toks and toks[0] == b'CMT':
192 data.hour, data.minute = [int(x) for x in toks[1:3]]
193 data.seconds, data.lat, data.lon, data.depth_km = [
194 float(x) for x in toks[3:]]
196 m = re.search(br'Assumed half duration:\s+(\S+)', line)
197 if m:
198 data.half_duration = float(m.group(1))
200 m = re.search(br'Mechanism information', line)
201 if m:
202 state = 2
204 if state == 2:
205 m = re.search(br'Exponent for moment tensor:\s+(\d+)', line)
206 if m:
207 data.exponent = int(m.group(1))
209 toks = line.split()
210 if toks and toks[0] == b'CMT':
211 data.mrr, data.mtt, data.mpp, \
212 data.mrt, data.mrp, data.mtp = [
213 float(x) for x in toks[1:]]
215 m = re.search(br'^Eigenvector:', line)
216 if m:
217 state = 0
219 if data is not None:
220 complete(data)
222 return events, more
224 def _name_to_date(self, name):
226 if len(name) == 7:
227 y, m, d = time.strptime(name[:6], '%m%d%y')[:3]
228 if y > 2005:
229 y -= 100
231 else:
232 y, m, d = time.strptime(name[:8], '%Y%m%d')[:3]
234 t = util.to_time_float(
235 calendar.timegm((y, m, d, 0, 0, 0)))
236 return t