# http://pyrocko.org - GPLv3
#
# The Pyrocko Developers, 21st Century
# ---|P------/S----------~Lg----------
'''
Strain rate (`GSRM1 <https://gsrm.unavco.org/>`_) and plate boundaries
(`PeterBird2003
<http://peterbird.name/publications/2003_PB2002/2003_PB2002.htm>`_).
'''
import os.path as op
import math
from collections import defaultdict
import numpy as num
from pyrocko import util, config
from pyrocko import orthodrome as od
from pyrocko.guts_array import Array
from pyrocko.guts import Object, String
from .util import get_download_callback
PI = math.pi
[docs]class Plate(Object):
name = String.T(
help='Name of the tectonic plate.')
points = Array.T(
dtype=float, shape=(None, 2),
help='Points on the plate.')
def max_interpoint_distance(self):
p = od.latlon_to_xyz(self.points)
return math.sqrt(num.max(num.sum(
(p[num.newaxis, :, :] - p[:, num.newaxis, :])**2, axis=2)))
def contains_point(self, point):
return od.contains_point(self.points, point)
def contains_points(self, points):
return od.contains_points(self.points, points)
[docs]class Boundary(Object):
plate_name1 = String.T()
plate_name2 = String.T()
kind = String.T()
points = Array.T(dtype=float, shape=(None, 2))
cpoints = Array.T(dtype=float, shape=(None, 2))
itypes = Array.T(dtype=int, shape=(None))
def split_types(self, groups=None):
xyz = od.latlon_to_xyz(self.points)
xyzmid = (xyz[1:] + xyz[:-1, :]) * 0.5
cxyz = od.latlon_to_xyz(self.cpoints)
d = od.distances3d(xyzmid[num.newaxis, :, :], cxyz[:, num.newaxis, :])
idmin = num.argmin(d, axis=0)
itypes = self.itypes[idmin]
if groups is None:
groupmap = num.arange(len(self._index_to_type))
else:
d = {}
for igroup, group in enumerate(groups):
for name in group:
d[name] = igroup
groupmap = num.array(
[d[name] for name in self._index_to_type],
dtype=int)
iswitch = num.concatenate(
([0],
num.where(groupmap[itypes[1:]] != groupmap[itypes[:-1]])[0]+1,
[itypes.size]))
results = []
for ii in range(iswitch.size-1):
if groups is not None:
tt = [self._index_to_type[ityp] for ityp in num.unique(
itypes[iswitch[ii]:iswitch[ii+1]])]
else:
tt = self._index_to_type[itypes[iswitch[ii]]]
results.append((tt, self.points[iswitch[ii]:iswitch[ii+1]+1]))
return results
[docs]class TectonicsDataset(object):
'''
Base class for datasets in :py:mod:`pyrocko.dataset.tectonics`.
'''
def __init__(self, name, data_dir, citation):
self.name = name
self.citation = citation
self.data_dir = data_dir
def fpath(self, filename):
return op.join(self.data_dir, filename)
def download_file(
self, url, fpath, username=None, password=None,
status_callback=None):
util.download_file(
url, fpath, username, password, status_callback=status_callback)
[docs]class PlatesDataset(TectonicsDataset):
'''
Base class for datasets describing plate boundaries.
'''
[docs]class PeterBird2003(PlatesDataset):
'''
An updated digital model of plate boundaries.
'''
__citation = \
'Bird, Peter. "An updated digital model of plate boundaries." ' \
'Geochemistry, Geophysics, Geosystems 4.3 (2003).'
def __init__(
self,
name='PeterBird2003',
data_dir=None,
raw_data_url=('https://mirror.pyrocko.org/peterbird.name/oldFTP/PB2002/%s')): # noqa
if data_dir is None:
data_dir = op.join(config.config().tectonics_dir, name)
PlatesDataset.__init__(
self,
name,
data_dir=data_dir,
citation=self.__citation)
self.raw_data_url = raw_data_url
self.filenames = [
'2001GC000252_readme.txt',
'PB2002_boundaries.dig.txt',
'PB2002_orogens.dig.txt',
'PB2002_plates.dig.txt',
'PB2002_poles.dat.txt',
'PB2002_steps.dat.txt']
self._full_names = None
def full_name(self, name):
if not self._full_names:
fn = util.data_file(op.join('tectonics', 'bird2003_plates.txt'))
with open(fn, 'r') as f:
self._full_names = dict(
line.strip().split(None, 1) for line in f)
return self._full_names[name]
def download_if_needed(self):
for fn in self.filenames:
fpath = self.fpath(fn)
if not op.exists(fpath):
self.download_file(
self.raw_data_url % fn, fpath,
status_callback=get_download_callback(
'Downloading Bird 2003 plate database...'))
def get_boundaries(self):
self.download_if_needed()
fpath = self.fpath('PB2002_steps.dat.txt')
d = defaultdict(list)
ntyp = 0
type_to_index = {}
index_to_type = []
with open(fpath, 'rb') as f:
data = []
for line in f:
lsplit = line.split()
name = lsplit[1].lstrip(b':').decode('ascii')
plate_name1 = str(name[0:2])
plate_name2 = str(name[3:5])
kind = name[2]
alon, alat, blon, blat = list(map(float, lsplit[2:6]))
mlat = (alat + blat) * 0.5
dlon = ((blon - alon) + 180.) % 360. - 180.
mlon = alon + dlon * 0.5
typ = str(lsplit[14].strip(b':*').decode('ascii'))
if typ not in type_to_index:
ntyp += 1
type_to_index[typ] = ntyp - 1
index_to_type.append(typ)
ityp = type_to_index[typ]
d[plate_name1, kind, plate_name2].append((mlat, mlon, ityp))
d2 = {}
for k in d:
d2[k] = (
num.array([x[:2] for x in d[k]], dtype=float),
num.array([x[2] for x in d[k]], dtype=int))
fpath = self.fpath('PB2002_boundaries.dig.txt')
boundaries = []
plate_name1 = ''
plate_name2 = ''
kind = '-'
with open(fpath, 'rb') as f:
data = []
for line in f:
if line.startswith(b'***'):
cpoints, itypes = d2[plate_name1, kind, plate_name2]
boundaries.append(Boundary(
plate_name1=plate_name1,
plate_name2=plate_name2,
kind=kind,
points=num.array(data, dtype=float),
cpoints=cpoints,
itypes=itypes))
boundaries[-1]._type_to_index = type_to_index
boundaries[-1]._index_to_type = index_to_type
data = []
elif line.startswith(b' '):
data.append(list(map(float, line.split(b',')))[::-1])
else:
s = line.strip().decode('ascii')
plate_name1 = str(s[0:2])
plate_name2 = str(s[3:5])
kind = s[2]
return boundaries
def get_plates(self):
self.download_if_needed()
fpath = self.fpath('PB2002_plates.dig.txt')
plates = []
name = ''
with open(fpath, 'rb') as f:
data = []
for line in f:
if line.startswith(b'***'):
plates.append(Plate(
name=name,
points=num.array(data, dtype=float)))
data = []
elif line.startswith(b' '):
data.append(list(map(float, line.split(b',')))[::-1])
else:
name = str(line.strip().decode('ascii'))
return plates
[docs]class StrainRateDataset(TectonicsDataset):
'''
Base class for strain rate datasets.
'''
[docs]class GSRM1(StrainRateDataset):
'''
Global Strain Rate Map. An integrated global model of present-day
plate motions and plate boundary deformation.
'''
__citation = \
'Kreemer, C., W.E. Holt, and A.J. Haines, "An integrated global ' \
'model of present-day plate motions and plate boundary ' \
'deformation", Geophys. J. Int., 154, 8-34, 2003.'
def __init__(
self,
name='GSRM1.2',
data_dir=None,
raw_data_url=('https://mirror.pyrocko.org/gsrm.unavco.org/model'
'/files/1.2/%s')):
if data_dir is None:
data_dir = op.join(config.config().tectonics_dir, name)
StrainRateDataset.__init__(
self,
name,
data_dir=data_dir,
citation=self.__citation)
self.raw_data_url = raw_data_url
self._full_names = None
self._names = None
def full_names(self):
if not self._full_names:
fn = util.data_file(op.join('tectonics', 'gsrm1_plates.txt'))
with open(fn, 'r') as f:
self._full_names = dict(
line.strip().split(None, 1) for line in f)
return self._full_names
def full_name(self, name):
name = self.plate_alt_names().get(name, name)
return self.full_names()[name]
def plate_names(self):
if self._names is None:
self._names = sorted(self.full_names().keys())
return self._names
def plate_alt_names(self):
# 'African Plate' is named 'Nubian Plate' in GSRM1
return {'AF': 'NU'}
def download_if_needed(self, fn):
fpath = self.fpath(fn)
if not op.exists(fpath):
self.download_file(
self.raw_data_url % fn, fpath,
status_callback=get_download_callback(
'Downloading global strain rate map...'))
def get_velocities(self, reference_name=None, region=None):
reference_name = self.plate_alt_names().get(
reference_name, reference_name)
if reference_name is None:
reference_name = 'NNR'
fn = 'velocity_%s.dat' % reference_name
self.download_if_needed(fn)
fpath = self.fpath(fn)
data = []
with open(fpath, 'rb') as f:
for line in f:
if line.strip().startswith(b'#'):
continue
t = line.split()
data.append(list(map(float, t)))
arr = num.array(data, dtype=float)
if region is not None:
points = arr[:, 1::-1]
mask = od.points_in_region(points, region)
arr = arr[mask, :]
lons, lats, veast, vnorth, veast_err, vnorth_err, corr = arr.T
return lats, lons, vnorth, veast, vnorth_err, veast_err, corr
for cls in PeterBird2003, GSRM1:
cls.__doc__ += '\n\n .. note::\n Please cite: %s\n' \
% getattr(cls, '_%s__citation' % cls.__name__)
__all__ = '''
GSRM1
PeterBird2003
Plate'''.split()