Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/dataset/tectonics.py: 99%
198 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'''
7Strain rate (`GSRM1 <https://gsrm.unavco.org/>`_) and plate boundaries
8(`PeterBird2003
9<http://peterbird.name/publications/2003_PB2002/2003_PB2002.htm>`_).
10'''
12import os.path as op
13import math
14from collections import defaultdict
16import numpy as num
18from pyrocko import util, config
19from pyrocko import orthodrome as od
20from pyrocko.guts_array import Array
21from pyrocko.guts import Object, String
22from .util import get_download_callback
25PI = math.pi
28class Plate(Object):
30 name = String.T(
31 help='Name of the tectonic plate.')
32 points = Array.T(
33 dtype=float, shape=(None, 2),
34 help='Points on the plate.')
36 def max_interpoint_distance(self):
37 p = od.latlon_to_xyz(self.points)
38 return math.sqrt(num.max(num.sum(
39 (p[num.newaxis, :, :] - p[:, num.newaxis, :])**2, axis=2)))
41 def contains_point(self, point):
42 return od.contains_point(self.points, point)
44 def contains_points(self, points):
45 return od.contains_points(self.points, points)
48class Boundary(Object):
50 plate_name1 = String.T()
51 plate_name2 = String.T()
52 kind = String.T()
53 points = Array.T(dtype=float, shape=(None, 2))
54 cpoints = Array.T(dtype=float, shape=(None, 2))
55 itypes = Array.T(dtype=int, shape=(None))
57 def split_types(self, groups=None):
58 xyz = od.latlon_to_xyz(self.points)
59 xyzmid = (xyz[1:] + xyz[:-1, :]) * 0.5
60 cxyz = od.latlon_to_xyz(self.cpoints)
61 d = od.distances3d(xyzmid[num.newaxis, :, :], cxyz[:, num.newaxis, :])
62 idmin = num.argmin(d, axis=0)
63 itypes = self.itypes[idmin]
65 if groups is None:
66 groupmap = num.arange(len(self._index_to_type))
67 else:
68 d = {}
69 for igroup, group in enumerate(groups):
70 for name in group:
71 d[name] = igroup
73 groupmap = num.array(
74 [d[name] for name in self._index_to_type],
75 dtype=int)
77 iswitch = num.concatenate(
78 ([0],
79 num.where(groupmap[itypes[1:]] != groupmap[itypes[:-1]])[0]+1,
80 [itypes.size]))
82 results = []
83 for ii in range(iswitch.size-1):
84 if groups is not None:
85 tt = [self._index_to_type[ityp] for ityp in num.unique(
86 itypes[iswitch[ii]:iswitch[ii+1]])]
87 else:
88 tt = self._index_to_type[itypes[iswitch[ii]]]
90 results.append((tt, self.points[iswitch[ii]:iswitch[ii+1]+1]))
92 return results
95class TectonicsDataset(object):
96 '''
97 Base class for datasets in :py:mod:`pyrocko.dataset.tectonics`.
98 '''
100 def __init__(self, name, data_dir, citation):
101 self.name = name
102 self.citation = citation
103 self.data_dir = data_dir
105 def fpath(self, filename):
106 return op.join(self.data_dir, filename)
108 def download_file(
109 self, url, fpath, username=None, password=None,
110 status_callback=None):
112 util.download_file(
113 url, fpath, username, password, status_callback=status_callback)
116class PlatesDataset(TectonicsDataset):
117 '''
118 Base class for datasets describing plate boundaries.
119 '''
122class PeterBird2003(PlatesDataset):
123 '''
124 An updated digital model of plate boundaries.
125 '''
127 __citation = \
128 'Bird, Peter. "An updated digital model of plate boundaries." ' \
129 'Geochemistry, Geophysics, Geosystems 4.3 (2003).'
131 def __init__(
132 self,
133 name='PeterBird2003',
134 data_dir=None,
135 raw_data_url=('https://mirror.pyrocko.org/peterbird.name/oldFTP/PB2002/%s')): # noqa
137 if data_dir is None:
138 data_dir = op.join(config.config().tectonics_dir, name)
140 PlatesDataset.__init__(
141 self,
142 name,
143 data_dir=data_dir,
144 citation=self.__citation)
146 self.raw_data_url = raw_data_url
148 self.filenames = [
149 '2001GC000252_readme.txt',
150 'PB2002_boundaries.dig.txt',
151 'PB2002_orogens.dig.txt',
152 'PB2002_plates.dig.txt',
153 'PB2002_poles.dat.txt',
154 'PB2002_steps.dat.txt']
156 self._full_names = None
158 def full_name(self, name):
159 if not self._full_names:
160 fn = util.data_file(op.join('tectonics', 'bird2003_plates.txt'))
162 with open(fn, 'r') as f:
163 self._full_names = dict(
164 line.strip().split(None, 1) for line in f)
166 return self._full_names[name]
168 def download_if_needed(self):
169 for fn in self.filenames:
170 fpath = self.fpath(fn)
171 if not op.exists(fpath):
172 self.download_file(
173 self.raw_data_url % fn, fpath,
174 status_callback=get_download_callback(
175 'Downloading Bird 2003 plate database...'))
177 def get_boundaries(self):
178 self.download_if_needed()
179 fpath = self.fpath('PB2002_steps.dat.txt')
181 d = defaultdict(list)
182 ntyp = 0
183 type_to_index = {}
184 index_to_type = []
185 with open(fpath, 'rb') as f:
186 data = []
187 for line in f:
188 lsplit = line.split()
189 name = lsplit[1].lstrip(b':').decode('ascii')
190 plate_name1 = str(name[0:2])
191 plate_name2 = str(name[3:5])
192 kind = name[2]
194 alon, alat, blon, blat = list(map(float, lsplit[2:6]))
195 mlat = (alat + blat) * 0.5
196 dlon = ((blon - alon) + 180.) % 360. - 180.
197 mlon = alon + dlon * 0.5
198 typ = str(lsplit[14].strip(b':*').decode('ascii'))
200 if typ not in type_to_index:
201 ntyp += 1
202 type_to_index[typ] = ntyp - 1
203 index_to_type.append(typ)
205 ityp = type_to_index[typ]
206 d[plate_name1, kind, plate_name2].append((mlat, mlon, ityp))
208 d2 = {}
209 for k in d:
210 d2[k] = (
211 num.array([x[:2] for x in d[k]], dtype=float),
212 num.array([x[2] for x in d[k]], dtype=int))
214 fpath = self.fpath('PB2002_boundaries.dig.txt')
215 boundaries = []
216 plate_name1 = ''
217 plate_name2 = ''
218 kind = '-'
219 with open(fpath, 'rb') as f:
220 data = []
221 for line in f:
222 if line.startswith(b'***'):
223 cpoints, itypes = d2[plate_name1, kind, plate_name2]
224 boundaries.append(Boundary(
225 plate_name1=plate_name1,
226 plate_name2=plate_name2,
227 kind=kind,
228 points=num.array(data, dtype=float),
229 cpoints=cpoints,
230 itypes=itypes))
232 boundaries[-1]._type_to_index = type_to_index
233 boundaries[-1]._index_to_type = index_to_type
235 data = []
236 elif line.startswith(b' '):
237 data.append(list(map(float, line.split(b',')))[::-1])
238 else:
239 s = line.strip().decode('ascii')
240 plate_name1 = str(s[0:2])
241 plate_name2 = str(s[3:5])
242 kind = s[2]
244 return boundaries
246 def get_plates(self):
247 self.download_if_needed()
248 fpath = self.fpath('PB2002_plates.dig.txt')
249 plates = []
250 name = ''
251 with open(fpath, 'rb') as f:
252 data = []
253 for line in f:
254 if line.startswith(b'***'):
255 plates.append(Plate(
256 name=name,
257 points=num.array(data, dtype=float)))
259 data = []
260 elif line.startswith(b' '):
261 data.append(list(map(float, line.split(b',')))[::-1])
262 else:
263 name = str(line.strip().decode('ascii'))
265 return plates
268class StrainRateDataset(TectonicsDataset):
269 '''
270 Base class for strain rate datasets.
271 '''
274class GSRM1(StrainRateDataset):
275 '''
276 Global Strain Rate Map. An integrated global model of present-day
277 plate motions and plate boundary deformation.
278 '''
280 __citation = \
281 'Kreemer, C., W.E. Holt, and A.J. Haines, "An integrated global ' \
282 'model of present-day plate motions and plate boundary ' \
283 'deformation", Geophys. J. Int., 154, 8-34, 2003.'
285 def __init__(
286 self,
287 name='GSRM1.2',
288 data_dir=None,
289 raw_data_url=('https://mirror.pyrocko.org/gsrm.unavco.org/model'
290 '/files/1.2/%s')):
292 if data_dir is None:
293 data_dir = op.join(config.config().tectonics_dir, name)
295 StrainRateDataset.__init__(
296 self,
297 name,
298 data_dir=data_dir,
299 citation=self.__citation)
301 self.raw_data_url = raw_data_url
302 self._full_names = None
303 self._names = None
305 def full_names(self):
306 if not self._full_names:
307 fn = util.data_file(op.join('tectonics', 'gsrm1_plates.txt'))
309 with open(fn, 'r') as f:
310 self._full_names = dict(
311 line.strip().split(None, 1) for line in f)
313 return self._full_names
315 def full_name(self, name):
316 name = self.plate_alt_names().get(name, name)
317 return self.full_names()[name]
319 def plate_names(self):
320 if self._names is None:
321 self._names = sorted(self.full_names().keys())
323 return self._names
325 def plate_alt_names(self):
326 # 'African Plate' is named 'Nubian Plate' in GSRM1
327 return {'AF': 'NU'}
329 def download_if_needed(self, fn):
330 fpath = self.fpath(fn)
331 if not op.exists(fpath):
332 self.download_file(
333 self.raw_data_url % fn, fpath,
334 status_callback=get_download_callback(
335 'Downloading global strain rate map...'))
337 def get_velocities(self, reference_name=None, region=None):
338 reference_name = self.plate_alt_names().get(
339 reference_name, reference_name)
341 if reference_name is None:
342 reference_name = 'NNR'
344 fn = 'velocity_%s.dat' % reference_name
346 self.download_if_needed(fn)
347 fpath = self.fpath(fn)
348 data = []
349 with open(fpath, 'rb') as f:
350 for line in f:
351 if line.strip().startswith(b'#'):
352 continue
353 t = line.split()
354 data.append(list(map(float, t)))
356 arr = num.array(data, dtype=float)
358 if region is not None:
359 points = arr[:, 1::-1]
360 mask = od.points_in_region(points, region)
361 arr = arr[mask, :]
363 lons, lats, veast, vnorth, veast_err, vnorth_err, corr = arr.T
364 return lats, lons, vnorth, veast, vnorth_err, veast_err, corr
367for cls in PeterBird2003, GSRM1:
368 cls.__doc__ += '\n\n .. note::\n Please cite: %s\n' \
369 % getattr(cls, '_%s__citation' % cls.__name__)
372__all__ = '''
373GSRM1
374PeterBird2003
375Plate'''.split()