tectonic-utils 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tectonic_utils/.DS_Store +0 -0
- tectonic_utils/__init__.py +3 -0
- tectonic_utils/cover_picture.png +0 -0
- tectonic_utils/geodesy/.DS_Store +0 -0
- tectonic_utils/geodesy/.ruff_cache/.gitignore +1 -0
- tectonic_utils/geodesy/.ruff_cache/0.1.5/15663111236935520357 +0 -0
- tectonic_utils/geodesy/.ruff_cache/CACHEDIR.TAG +1 -0
- tectonic_utils/geodesy/__init__.py +0 -0
- tectonic_utils/geodesy/datums.py +156 -0
- tectonic_utils/geodesy/euler_pole.py +170 -0
- tectonic_utils/geodesy/fault_vector_functions.py +383 -0
- tectonic_utils/geodesy/haversine.py +193 -0
- tectonic_utils/geodesy/insar_vector_functions.py +285 -0
- tectonic_utils/geodesy/linear_elastic.py +231 -0
- tectonic_utils/geodesy/test/.DS_Store +0 -0
- tectonic_utils/geodesy/test/__init__.py +0 -0
- tectonic_utils/geodesy/test/test_conversion_functions.py +74 -0
- tectonic_utils/geodesy/test/test_euler_poles.py +33 -0
- tectonic_utils/geodesy/test/test_insar_vector_functions.py +36 -0
- tectonic_utils/geodesy/utilities.py +47 -0
- tectonic_utils/geodesy/xyz2llh.py +220 -0
- tectonic_utils/read_write/.DS_Store +0 -0
- tectonic_utils/read_write/.ruff_cache/.gitignore +1 -0
- tectonic_utils/read_write/.ruff_cache/0.1.5/680373307893520726 +0 -0
- tectonic_utils/read_write/.ruff_cache/CACHEDIR.TAG +1 -0
- tectonic_utils/read_write/__init__.py +0 -0
- tectonic_utils/read_write/general_io.py +55 -0
- tectonic_utils/read_write/netcdf_read_write.py +382 -0
- tectonic_utils/read_write/read_kml.py +68 -0
- tectonic_utils/read_write/test/.DS_Store +0 -0
- tectonic_utils/read_write/test/__init__.py +0 -0
- tectonic_utils/read_write/test/example_grd.grd +0 -0
- tectonic_utils/read_write/test/test_conversion_functions.py +40 -0
- tectonic_utils/read_write/test/written_example.grd +0 -0
- tectonic_utils/seismo/.DS_Store +0 -0
- tectonic_utils/seismo/.ruff_cache/.gitignore +1 -0
- tectonic_utils/seismo/.ruff_cache/0.1.5/12911000862714636977 +0 -0
- tectonic_utils/seismo/.ruff_cache/CACHEDIR.TAG +1 -0
- tectonic_utils/seismo/MT_calculations.py +132 -0
- tectonic_utils/seismo/__init__.py +0 -0
- tectonic_utils/seismo/moment_calculations.py +44 -0
- tectonic_utils/seismo/second_focal_plane.py +138 -0
- tectonic_utils/seismo/test/.DS_Store +0 -0
- tectonic_utils/seismo/test/__init__.py +0 -0
- tectonic_utils/seismo/test/test_WC.py +19 -0
- tectonic_utils/seismo/test/test_second_focal_plane.py +16 -0
- tectonic_utils/seismo/wells_and_coppersmith.py +167 -0
- tectonic_utils-0.1.2.dist-info/LICENSE.md +21 -0
- tectonic_utils-0.1.2.dist-info/METADATA +82 -0
- tectonic_utils-0.1.2.dist-info/RECORD +51 -0
- tectonic_utils-0.1.2.dist-info/WHEEL +4 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
|
4
|
+
def wrap_lon(longitude):
|
5
|
+
"""
|
6
|
+
Ensure longitude value is between -180° and 180° (e.g., not 240° E).
|
7
|
+
|
8
|
+
:param longitude: float
|
9
|
+
:returns: wrapped longitude, from -180° to 180°
|
10
|
+
"""
|
11
|
+
if longitude > 360 or longitude < -360:
|
12
|
+
raise ValueError("longitude outside normal range (-360 to 360)")
|
13
|
+
if longitude > 180:
|
14
|
+
longitude = longitude - 360
|
15
|
+
if longitude < -180:
|
16
|
+
longitude = longitude + 360
|
17
|
+
return longitude
|
18
|
+
|
19
|
+
|
20
|
+
def get_vector_magnitude(vector):
|
21
|
+
"""
|
22
|
+
Get magnitude of a vector.
|
23
|
+
|
24
|
+
:param vector: n-component vector, any units
|
25
|
+
:type vector: array_like
|
26
|
+
:return: magnitude
|
27
|
+
:rtype: float
|
28
|
+
"""
|
29
|
+
total = 0
|
30
|
+
for i in range(len(vector)):
|
31
|
+
total = total + vector[i]*vector[i]
|
32
|
+
magnitude = np.sqrt(total)
|
33
|
+
return magnitude
|
34
|
+
|
35
|
+
|
36
|
+
def get_unit_vector(vec):
|
37
|
+
"""
|
38
|
+
Get unit vector in the direction of a given vector.
|
39
|
+
|
40
|
+
:param vec: 3-component vector, any units
|
41
|
+
:type vec: array_like
|
42
|
+
:return: unit vector
|
43
|
+
:rtype: array_like
|
44
|
+
"""
|
45
|
+
mag = np.sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])
|
46
|
+
vec = np.divide(vec, mag)
|
47
|
+
return vec
|
@@ -0,0 +1,220 @@
|
|
1
|
+
"""
|
2
|
+
Functions to convert between local enu, local llh, and global xyz coordinates.
|
3
|
+
Translated from Matlab.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
from . import datums
|
8
|
+
|
9
|
+
|
10
|
+
def xyz2llh(xyz, datum='NAD83'):
|
11
|
+
"""
|
12
|
+
XYZ2LLH calculates longitude, latitude, and height from global cartesisan coordinates.
|
13
|
+
LLH = xyz2llh(XYZ, DATUM) calculates longitude(deg), latitude(deg), and height(m) on the ellipsoid
|
14
|
+
specified by DATUM from the global cartestian coordinates given in the nx3(n=number of coordinate triples)
|
15
|
+
matrix XYZ.
|
16
|
+
DATUM can either be a vector the first two elements of which give da and df,
|
17
|
+
or it can be a string containing the name of a datum that is resolved
|
18
|
+
by the function DATUMS function.
|
19
|
+
Note that longitude occupies the first row of LLH.
|
20
|
+
See DATUMS for more information on datum parameters.
|
21
|
+
|
22
|
+
:param xyz: [x, y, z]
|
23
|
+
:type xyz: numpy array
|
24
|
+
:param datum: name of datum
|
25
|
+
:type datum: string
|
26
|
+
:returns: [lon, lat, height]
|
27
|
+
:rtype: numpy array
|
28
|
+
"""
|
29
|
+
|
30
|
+
# Check input arguments
|
31
|
+
if not isinstance(datum, str):
|
32
|
+
raise ValueError(f'Could not parse given datum: {datum}')
|
33
|
+
datum_array = datums.get_datums(datum)
|
34
|
+
if np.sum(np.isnan(datum_array)) > 0:
|
35
|
+
raise ValueError('Could not resolve datum name.')
|
36
|
+
da, df = datum_array[0][0], datum_array[0][1]
|
37
|
+
|
38
|
+
if np.shape(xyz)[1] != 3:
|
39
|
+
raise TypeError('Input xyz MUST be nx3.')
|
40
|
+
|
41
|
+
# Set constants
|
42
|
+
a = 6378137 - da
|
43
|
+
f = 1 / 298.2572235630 - df
|
44
|
+
b = (1-f) * a
|
45
|
+
e2 = 2 * f - np.square(f)
|
46
|
+
E2 = (np.square(a) - np.square(b)) / np.square(b)
|
47
|
+
|
48
|
+
# Calculate longitude, latitude, and height
|
49
|
+
llh = np.zeros(np.shape(xyz))
|
50
|
+
p = np.sqrt(np.square(xyz[:, 0]) + np.square(xyz[:, 1]))
|
51
|
+
llh[:, 0] = np.arctan2(xyz[:, 1], xyz[:, 0])
|
52
|
+
theta = np.arctan(np.divide((xyz[:, 2] * a), (p * b)))
|
53
|
+
llh[:, 1] = np.arctan((xyz[:, 2] + E2 * b * np.power(np.sin(theta), 3)) /
|
54
|
+
(p - e2 * a * np.power(np.cos(theta), 3)) )
|
55
|
+
N = a / np.sqrt(1 - e2 * np.square(np.sin(llh[:, 1])))
|
56
|
+
llh[:, 2] = p / np.cos(llh[:, 1]) - N
|
57
|
+
|
58
|
+
# Convert to degrees
|
59
|
+
llh[:, 0:2] = llh[:, 0:2]*57.295779513082323
|
60
|
+
return llh
|
61
|
+
|
62
|
+
|
63
|
+
def llh2xyz(llh, datum='NAD83'):
|
64
|
+
"""
|
65
|
+
LLH2XYZ Calculates global cartesisan coordinates from longitude, latitude, and height.
|
66
|
+
XYZ=llh2xyz(llh,DATUM) calculates global cartestian coordinates
|
67
|
+
given the nx3 (n = number of coordinate triples) matrix LLH that contains
|
68
|
+
longitude (deg), latitude (deg), and height (m) on the ellipsoid
|
69
|
+
specified by DATUM. DATUM can either be a vector the first two elements
|
70
|
+
of which give da and df, or it can be a string containing the name of a
|
71
|
+
datum that is resolved by the function DATUMS function.
|
72
|
+
Note that longitude occupies the first row of LLH.
|
73
|
+
See DATUMS for more information on datum parameters.
|
74
|
+
|
75
|
+
:param llh: [lon, lat, height]
|
76
|
+
:type llh: numpy array
|
77
|
+
:param datum: name of datum
|
78
|
+
:type datum: string
|
79
|
+
"""
|
80
|
+
|
81
|
+
# Check input arguments
|
82
|
+
if not isinstance(datum, str):
|
83
|
+
raise ValueError(f'Could not parse given datum: {datum}')
|
84
|
+
datum_array = datums.get_datums(datum)
|
85
|
+
if np.sum(np.isnan(datum_array)) > 0:
|
86
|
+
raise ValueError(f'Could not resolve datum name: {datum}')
|
87
|
+
da, df = datum_array[0][0], datum_array[0][1]
|
88
|
+
|
89
|
+
if np.shape(llh)[1] != 3:
|
90
|
+
raise TypeError('Input llh MUST be nx3.')
|
91
|
+
|
92
|
+
# Ellipsoid parameters
|
93
|
+
a = 6378137 - da
|
94
|
+
f = 1 / 298.257223563 - df
|
95
|
+
b = (1-f) * a
|
96
|
+
|
97
|
+
# Convert degrees to radians
|
98
|
+
phi = llh[:, 1] * np.pi / 180 # lat
|
99
|
+
lam = llh[:, 0] * np.pi / 180 # lon
|
100
|
+
|
101
|
+
# Convert llh to xyz
|
102
|
+
XYZ = np.zeros(np.shape(llh))
|
103
|
+
N = np.square(a) / np.sqrt(np.square(a) * np.square(np.cos(phi)) + np.square(b) * np.square(np.sin(phi)))
|
104
|
+
XYZ[:, 0] = (N + llh[:, 2]) * np.cos(phi) * np.cos(lam)
|
105
|
+
XYZ[:, 1] = (N + llh[:, 2]) * np.cos(phi) * np.sin(lam)
|
106
|
+
XYZ[:, 2] = (np.square(b) * N / np.square(a) + llh[:, 2] ) * np.sin(phi)
|
107
|
+
return XYZ
|
108
|
+
|
109
|
+
|
110
|
+
def xyz2enum(origin):
|
111
|
+
"""
|
112
|
+
XYZ2ENUM Returns a global to local transformation matrix.
|
113
|
+
T=xyz2enum(ORIGIN) Returns a transformation matrix that
|
114
|
+
tranforms coordinates in a global ECEF cartesian system
|
115
|
+
into to a local coordinate system aligned with the geographic
|
116
|
+
directions at the position ORIGIN. ORIGIN should contain a
|
117
|
+
longitude and a latitude pair (degrees). T is 3x3.
|
118
|
+
|
119
|
+
:param origin: [longitude, latitude]
|
120
|
+
:type origin: np array
|
121
|
+
"""
|
122
|
+
|
123
|
+
# Check input arguments
|
124
|
+
if len(origin) < 2:
|
125
|
+
raise ValueError('Input origin must have 2 elements, longitude and latitude (degrees).')
|
126
|
+
|
127
|
+
# Convert to radians and evaluate trigonometric functions
|
128
|
+
origin = np.multiply(origin, np.pi / 180)
|
129
|
+
s = np.sin(origin)
|
130
|
+
c = np.cos(origin)
|
131
|
+
|
132
|
+
# Make transformation matrix
|
133
|
+
T = np.array([[-s[0], c[0], 0],
|
134
|
+
[-s[1]*c[0], -s[1]*s[0], c[1]],
|
135
|
+
[c[1]*c[0], c[1]*s[0], s[1]]])
|
136
|
+
return T
|
137
|
+
|
138
|
+
|
139
|
+
def xyz2enu(d, origin, dcov=None):
|
140
|
+
"""
|
141
|
+
XYZ2ENU Transforms from global cartestian to local cartesian.
|
142
|
+
[E,ECOV]=xyz2enu(D,DCOV,ORIGIN) transforms data vector D and
|
143
|
+
data covariance DCOV from a global cartesian (XYZ) coordinate
|
144
|
+
system to a local coordinate system aligned with the geographic
|
145
|
+
directions at the position ORIGIN.
|
146
|
+
D should be either 3nx1 or 3xn (n = number of individual vectors).
|
147
|
+
DCOV should be 3nx3n.
|
148
|
+
ORIGIN should be a vector of length 2 or 3. If length 2, ORIGIN
|
149
|
+
is taken as a longitude, latitude pair (degrees); if length 3,
|
150
|
+
ORIGIN is taken as an XYZ station position. E is matrix (vector)
|
151
|
+
of transformed coordinates the same size as input D. ECOV is a
|
152
|
+
matrix containing the transformed covariance.
|
153
|
+
E=xyz2enu(D,ORIGIN) behaves as above without a data covariance matrix.
|
154
|
+
|
155
|
+
:param d: nx3 np array of x, y, z values
|
156
|
+
:type d: numpy array
|
157
|
+
:param origin: 1x3 np array (x, y, z) or 1x2 np.array (lon, lat)
|
158
|
+
:type origin: numpy array
|
159
|
+
:param dcov: 3x3 np array
|
160
|
+
:type dcov: numpy array
|
161
|
+
"""
|
162
|
+
|
163
|
+
# Check input arguments
|
164
|
+
if len(origin) > 2:
|
165
|
+
origin = np.reshape(origin, (1, 3))
|
166
|
+
origin = xyz2llh(origin)
|
167
|
+
origin = origin[0] # 1x3 1D array, contains llh
|
168
|
+
|
169
|
+
# Make transformation matrix
|
170
|
+
Tm = xyz2enum(origin)
|
171
|
+
|
172
|
+
# Transform
|
173
|
+
e = np.dot(Tm, d.T)
|
174
|
+
if dcov is not None:
|
175
|
+
ecov = np.dot(np.dot(Tm, dcov), Tm.T)
|
176
|
+
else:
|
177
|
+
ecov = None
|
178
|
+
return e.T, ecov
|
179
|
+
|
180
|
+
|
181
|
+
def enu2xyz(d, origin, dcov=None):
|
182
|
+
"""
|
183
|
+
ENU2XYZ Transforms from global cartestian to local cartesian.
|
184
|
+
[E,ECOV]=xyz2enu(D,DCOV,ORIGIN) transforms data vector D and
|
185
|
+
data covariance DCOV from a local cartesian (ENU) coordinate
|
186
|
+
system aligned with the geographic directions at the position ORIGIN
|
187
|
+
to a global (XYZ) coordinate system.
|
188
|
+
D should be either 3nx1 or 3xn (n = number of individual vectors).
|
189
|
+
DCOV should be 3nx3n.
|
190
|
+
ORIGIN should be a vector of length 2 or 3. If length 2, ORIGIN
|
191
|
+
is taken as a longitude, latitude pair (degrees); if length 3,
|
192
|
+
ORIGIN is taken as an XYZ station position. E is matrix (vector)
|
193
|
+
of transformed coordinates the same size as input D. ECOV is a
|
194
|
+
matrix containing the transformed covariance.
|
195
|
+
E=xyz2enu(D,ORIGIN) behaves as above without a data covariance matrix.
|
196
|
+
|
197
|
+
:param d: nx3 np array of e, n, u values
|
198
|
+
:type d: numpy array
|
199
|
+
:param origin: 1x3 np array (x, y, z) or 1x2 np.array (lon, lat)
|
200
|
+
:type origin: numpy array
|
201
|
+
:param dcov: 3x3 np array
|
202
|
+
:type dcov: numpy array
|
203
|
+
"""
|
204
|
+
# Check input arguments
|
205
|
+
if len(origin) > 2:
|
206
|
+
origin = np.reshape(origin, (1, 3))
|
207
|
+
origin = xyz2llh(origin)
|
208
|
+
origin = origin[0] # 1x3 1D array, contains llh
|
209
|
+
|
210
|
+
# Make transformation matrix
|
211
|
+
Tm = xyz2enum(origin)
|
212
|
+
Tminv = np.linalg.inv(Tm)
|
213
|
+
|
214
|
+
# Transform
|
215
|
+
e = np.dot(Tminv, d.T)
|
216
|
+
if dcov is not None:
|
217
|
+
ecov = np.dot(np.dot(Tminv, dcov), Tminv.T)
|
218
|
+
else:
|
219
|
+
ecov = None
|
220
|
+
return e.T, ecov
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
*
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Signature: 8a477f597d28d172789f06886806bc55
|
File without changes
|
@@ -0,0 +1,55 @@
|
|
1
|
+
"""
|
2
|
+
Functions to read common file types into structures in Python.
|
3
|
+
Example: a multi-segment file with polygons or lines, as could be used in GMT.
|
4
|
+
"""
|
5
|
+
|
6
|
+
|
7
|
+
def read_gmt_multisegment_latlon(input_file, split_delimiter=' '):
|
8
|
+
"""
|
9
|
+
Generalized GMT multisegment file reader.
|
10
|
+
Returns lon and lat in a list of lists, each element with a single segment.
|
11
|
+
|
12
|
+
:param input_file: name of input file
|
13
|
+
:type input_file: string
|
14
|
+
:param split_delimiter: delimiter between values on the same line, defaults to space
|
15
|
+
:type split_delimiter: string, optional
|
16
|
+
:returns: list of lons, list of lats
|
17
|
+
:rtype: list
|
18
|
+
"""
|
19
|
+
print("reading gmt multisegment file %s" % input_file)
|
20
|
+
ifile = open(input_file)
|
21
|
+
lon_collection, lat_collection = [], []
|
22
|
+
lon_temp, lat_temp = [], []
|
23
|
+
for line in ifile:
|
24
|
+
if line.split()[0] == '>>' or line.split()[0] == '>':
|
25
|
+
if lon_temp:
|
26
|
+
lon_collection.append(lon_temp)
|
27
|
+
lat_collection.append(lat_temp)
|
28
|
+
lon_temp, lat_temp = [], []
|
29
|
+
continue
|
30
|
+
else:
|
31
|
+
temp = line.split(split_delimiter)
|
32
|
+
lon_temp.append(float(temp[0]))
|
33
|
+
lat_temp.append(float(temp[1]))
|
34
|
+
lon_collection.append(lon_temp)
|
35
|
+
lat_collection.append(lat_temp)
|
36
|
+
return lon_collection, lat_collection
|
37
|
+
|
38
|
+
|
39
|
+
def write_gmt_multisegment(list_of_coords_segments, filename):
|
40
|
+
"""
|
41
|
+
Write a list of lists of coordiantes into a GMT-compatible multi-segment file
|
42
|
+
[(-115.5650522767964, 33.11974272741214, 0.0),
|
43
|
+
(-115.5642209309202, 33.12270979703938, 0.0),
|
44
|
+
(-115.5637985114591, 33.12561963960839, 0.0)]
|
45
|
+
|
46
|
+
:param list_of_coords_segments: list of lists, or list of tuples
|
47
|
+
:param filename: string, name of output file
|
48
|
+
"""
|
49
|
+
print("Writing file %s " % filename)
|
50
|
+
with open(filename, 'w') as ofile:
|
51
|
+
for segment in list_of_coords_segments:
|
52
|
+
ofile.write('>\n')
|
53
|
+
for coordinate in segment:
|
54
|
+
ofile.write("%f %f \n" % (coordinate[0], coordinate[1]))
|
55
|
+
return
|