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
« 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----------
6'''
7Client to get earthquake catalog information from
8`GEOFON <http://geofon.gfz-potsdam.de/>`_.
9'''
11import time
12import re
13import logging
14import json
16from pyrocko import model, util
17from pyrocko.moment_tensor import MomentTensor, symmat6
18from .base_catalog import EarthquakeCatalog, NotFound
20from pyrocko.util import urlopen
22logger = logging.getLogger('pyrocko.client.geofon')
24km = 1000.
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
35 def flush(self):
36 self.events = {}
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.):
48 logger.debug('In Geofon.iter_event_names(...)')
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))
53 if magmin is None:
54 magmin = ''
55 else:
56 magmin = '%g' % magmin
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]))
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)
80 if not events:
81 break
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
88 ipage += 1
90 def get_event(self, name):
91 logger.debug('In Geofon.get_event("%s")' % name)
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))
100 try:
101 ev = self._parse_event_page(page)
102 self.events[name] = ev
104 except NotFound:
105 raise NotFound(url) # reraise with url
107 ev = self.events[name]
109 if ev.moment_tensor is True:
110 ev.moment_tensor = self.get_mt(ev)
112 return ev
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)
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
127 return self._parse_mt_page(page)
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)
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)
142 return mt
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
153 return events
155 def _parse_event_page(self, page):
156 return self._parse_events_page(page, limit=1)[0]
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', ' '))
168 if ((properties.get('hasMT', 'no') == 'yes')
169 or properties['magType'] == 'Mw') and self._get_moment_tensors:
171 moment_tensor = True # flag for caller to query MT
172 else:
173 moment_tensor = None
175 status = properties['status'][:1]
176 tags = []
177 if status in 'AMC':
178 tags.append('geofon_status:%s' % status)
180 category = properties.get('evtype', '')
181 if re.match(r'^[a-zA-Z0-9]+$', category):
182 tags.append('geofon_category:%s' % category)
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)
197 return ev