LoopStructural 1.0.9__cp39-cp39-win_amd64.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.
- LoopStructural/__init__.py +36 -0
- LoopStructural/analysis/__init__.py +2 -0
- LoopStructural/analysis/_fault_displacement.py +67 -0
- LoopStructural/analysis/_fault_intersection.py +36 -0
- LoopStructural/analysis/_plane_fit.py +43 -0
- LoopStructural/datasets/__init__.py +12 -0
- LoopStructural/datasets/_base.py +65 -0
- LoopStructural/datasets/data/claudius.csv +21049 -0
- LoopStructural/datasets/data/claudiusbb.txt +2 -0
- LoopStructural/datasets/data/duplex.csv +126 -0
- LoopStructural/datasets/data/duplexbb.txt +2 -0
- LoopStructural/datasets/data/intrusion.csv +1017 -0
- LoopStructural/datasets/data/intrusionbb.txt +2 -0
- LoopStructural/datasets/data/onefoldbb.txt +2 -0
- LoopStructural/datasets/data/onefolddata.csv +2226 -0
- LoopStructural/datasets/data/refolded_bb.txt +2 -0
- LoopStructural/datasets/data/refolded_fold.csv +2126 -0
- LoopStructural/interpolators/__init__.py +34 -0
- LoopStructural/interpolators/base_structured_3d_support.py +101 -0
- LoopStructural/interpolators/cython/__init__.py +0 -0
- LoopStructural/interpolators/cython/dsi_helper.c +31046 -0
- LoopStructural/interpolators/cython/dsi_helper.cp39-win_amd64.pyd +0 -0
- LoopStructural/interpolators/discrete_fold_interpolator.py +202 -0
- LoopStructural/interpolators/discrete_interpolator.py +584 -0
- LoopStructural/interpolators/finite_difference_interpolator.py +399 -0
- LoopStructural/interpolators/geological_interpolator.py +197 -0
- LoopStructural/interpolators/operator.py +61 -0
- LoopStructural/interpolators/piecewiselinear_interpolator.py +437 -0
- LoopStructural/interpolators/structured_grid.py +421 -0
- LoopStructural/interpolators/structured_tetra.py +681 -0
- LoopStructural/interpolators/surfe_wrapper.py +118 -0
- LoopStructural/modelling/__init__.py +46 -0
- LoopStructural/modelling/core/__init__.py +0 -0
- LoopStructural/modelling/core/geological_model.py +1513 -0
- LoopStructural/modelling/core/geological_model_graph.py +880 -0
- LoopStructural/modelling/core/stratigraphic_column.py +5 -0
- LoopStructural/modelling/fault/__init__.py +4 -0
- LoopStructural/modelling/fault/fault_builder.py +143 -0
- LoopStructural/modelling/fault/fault_function.py +188 -0
- LoopStructural/modelling/fault/fault_function_feature.py +76 -0
- LoopStructural/modelling/fault/fault_segment.py +298 -0
- LoopStructural/modelling/features/__init__.py +8 -0
- LoopStructural/modelling/features/cross_product_geological_feature.py +90 -0
- LoopStructural/modelling/features/geological_feature.py +252 -0
- LoopStructural/modelling/features/geological_feature_builder.py +414 -0
- LoopStructural/modelling/features/lambda_geological_feature.py +36 -0
- LoopStructural/modelling/features/region_feature.py +34 -0
- LoopStructural/modelling/features/structural_frame.py +133 -0
- LoopStructural/modelling/features/structural_frame_builder.py +206 -0
- LoopStructural/modelling/features/unconformity_feature.py +74 -0
- LoopStructural/modelling/fold/__init__.py +8 -0
- LoopStructural/modelling/fold/fold.py +143 -0
- LoopStructural/modelling/fold/fold_rotation_angle.py +133 -0
- LoopStructural/modelling/fold/fold_rotation_angle_feature.py +58 -0
- LoopStructural/modelling/fold/foldframe.py +193 -0
- LoopStructural/modelling/fold/svariogram.py +180 -0
- LoopStructural/utils/__init__.py +18 -0
- LoopStructural/utils/bounding_box.py +21 -0
- LoopStructural/utils/exceptions.py +10 -0
- LoopStructural/utils/helper.py +382 -0
- LoopStructural/utils/logging.py +60 -0
- LoopStructural/utils/map2loop.py +353 -0
- LoopStructural/utils/regions.py +11 -0
- LoopStructural/utils/utils.py +69 -0
- LoopStructural/visualisation/__init__.py +31 -0
- LoopStructural/visualisation/map_viewer.py +322 -0
- LoopStructural/visualisation/model_plotter.py +17 -0
- LoopStructural/visualisation/model_visualisation.py +1113 -0
- LoopStructural/visualisation/rotation_angle_plotter.py +83 -0
- LoopStructural/visualisation/sphinx_scraper.py +34 -0
- LoopStructural/visualisation/stratigraphic_column.py +60 -0
- LoopStructural-1.0.9.dist-info/LICENSE +21 -0
- LoopStructural-1.0.9.dist-info/METADATA +19 -0
- LoopStructural-1.0.9.dist-info/RECORD +77 -0
- LoopStructural-1.0.9.dist-info/WHEEL +5 -0
- LoopStructural-1.0.9.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from LoopStructural.utils import getLogger
|
|
7
|
+
logger = getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MapView:
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, model = None, bounding_box=np.zeros((2,2)), nsteps=None, ax = None, **kwargs):
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
origin - lower left
|
|
20
|
+
maximum - upper right
|
|
21
|
+
nsteps - number of cells
|
|
22
|
+
kwargs
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
self.xx = None
|
|
26
|
+
self.yy = None
|
|
27
|
+
|
|
28
|
+
self._bounding_box = bounding_box
|
|
29
|
+
self._nsteps = nsteps
|
|
30
|
+
if self._nsteps is not None and self._bounding_box is not None:
|
|
31
|
+
self._update_grid()
|
|
32
|
+
if model is not None:
|
|
33
|
+
#make sure self._nsteps is 2d
|
|
34
|
+
self.model = model
|
|
35
|
+
self.ax = ax
|
|
36
|
+
if self.ax is None:
|
|
37
|
+
fig, self.ax = plt.subplots(1, figsize=(10, 10))
|
|
38
|
+
self.ax.set_aspect('equal', adjustable='box')
|
|
39
|
+
|
|
40
|
+
# set plot limits to model bounding box
|
|
41
|
+
self._xmin = self.bounding_box[0,0]
|
|
42
|
+
self._xmax = self.bounding_box[1,0]
|
|
43
|
+
self._ymin = self.bounding_box[0,1]
|
|
44
|
+
self._ymax = self.bounding_box[1,1]
|
|
45
|
+
self._update_plot_limits()
|
|
46
|
+
@property
|
|
47
|
+
def model(self):
|
|
48
|
+
return self._model
|
|
49
|
+
|
|
50
|
+
@model.setter
|
|
51
|
+
def model(self,model):
|
|
52
|
+
if model is not None:
|
|
53
|
+
bb = np.array([model.origin[:2],model.maximum[:2]])
|
|
54
|
+
self.bounding_box = bb#model.bounding_box
|
|
55
|
+
self.nsteps = model.nsteps[:2]
|
|
56
|
+
self._model = model
|
|
57
|
+
self._update_grid()
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def nsteps(self):
|
|
61
|
+
return self._nsteps
|
|
62
|
+
|
|
63
|
+
@nsteps.setter
|
|
64
|
+
def nsteps(self,nsteps):
|
|
65
|
+
if len(nsteps) != 2:
|
|
66
|
+
logger.error("Can't update nsteps, needs to be 2D")
|
|
67
|
+
return
|
|
68
|
+
self._nsteps = nsteps
|
|
69
|
+
self._update_grid()
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def bounding_box(self):
|
|
73
|
+
return self._bounding_box
|
|
74
|
+
@bounding_box.setter
|
|
75
|
+
def bounding_box(self,bounding_box):
|
|
76
|
+
self._bounding_box = bounding_box
|
|
77
|
+
self._update_grid()
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def xmin(self):
|
|
81
|
+
return self._xmin
|
|
82
|
+
|
|
83
|
+
@xmin.setter
|
|
84
|
+
def xmin(self,xmin):
|
|
85
|
+
self._xmin = xmin
|
|
86
|
+
self._update_plot_limits()
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def xmax(self):
|
|
90
|
+
return self._xmax
|
|
91
|
+
|
|
92
|
+
@xmax.setter
|
|
93
|
+
def xmax(self,xmax):
|
|
94
|
+
self._xmax = xmax
|
|
95
|
+
self._update_plot_limits()
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def ymin(self):
|
|
99
|
+
return self._ymin
|
|
100
|
+
|
|
101
|
+
@ymin.setter
|
|
102
|
+
def ymin(self,ymin):
|
|
103
|
+
self._ymin = ymin
|
|
104
|
+
self._update_plot_limits()
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def ymax(self):
|
|
108
|
+
return self._ymax
|
|
109
|
+
|
|
110
|
+
@ymax.setter
|
|
111
|
+
def ymax(self,ymax):
|
|
112
|
+
self._ymax = ymax
|
|
113
|
+
self._update_plot_limits()
|
|
114
|
+
|
|
115
|
+
def _update_plot_limits(self):
|
|
116
|
+
self.ax.set_xlim([self._xmin,self._xmax])
|
|
117
|
+
self.ax.set_ylim([self._ymin,self._ymax])
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _update_grid(self):
|
|
121
|
+
"""Internal function to update the current grid when the bounding box
|
|
122
|
+
or number of steps changes
|
|
123
|
+
"""
|
|
124
|
+
if self.nsteps is None or self.bounding_box is None:
|
|
125
|
+
return
|
|
126
|
+
x = np.linspace(self.bounding_box[0,0], self.bounding_box[1,0], self.nsteps[0])
|
|
127
|
+
y = np.linspace(self.bounding_box[0,1], self.bounding_box[1,1], self.nsteps[1])
|
|
128
|
+
self.xx, self.yy = np.meshgrid(x, y, indexing='ij')
|
|
129
|
+
self.xx = self.xx.flatten()
|
|
130
|
+
self.yy = self.yy.flatten()
|
|
131
|
+
|
|
132
|
+
def add_data(self, feature, val=True, grad=True, unfault=False, dip=True, **kwargs):
|
|
133
|
+
"""
|
|
134
|
+
Adds the data associated to the feature to the plot
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
feature : GeologicalFeature
|
|
138
|
+
the feature whose data you want to add
|
|
139
|
+
val : bool
|
|
140
|
+
whether to add value data
|
|
141
|
+
grad : bool
|
|
142
|
+
whether to add gradient data
|
|
143
|
+
unfault : bool
|
|
144
|
+
plot points in their restored location
|
|
145
|
+
dip : bool
|
|
146
|
+
whether to annotate the dip, default False
|
|
147
|
+
|
|
148
|
+
kwargs are passed to matplotlib functions and draw strike
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
# logger.warning("Plotting restored data locations")
|
|
154
|
+
ori_data = []
|
|
155
|
+
gradient_data = feature.builder.get_gradient_constraints()
|
|
156
|
+
if unfault:
|
|
157
|
+
gradient_data = feature.interpolator.get_gradient_constraints()
|
|
158
|
+
|
|
159
|
+
if gradient_data.shape[0] > 0:
|
|
160
|
+
ori_data.append(gradient_data)
|
|
161
|
+
norm_data = feature.builder.get_norm_constraints()
|
|
162
|
+
if unfault:
|
|
163
|
+
norm_data = feature.interpolator.get_norm_constraints()
|
|
164
|
+
|
|
165
|
+
if norm_data.shape[0] > 0:
|
|
166
|
+
ori_data.append(norm_data)
|
|
167
|
+
cmap = kwargs.pop('cmap','rainbow')
|
|
168
|
+
# if single colour then specify kwarg, otherwise use point value
|
|
169
|
+
if val:
|
|
170
|
+
value_data = np.copy(feature.builder.get_value_constraints())
|
|
171
|
+
if unfault:
|
|
172
|
+
value_data = np.copy(feature.interpolator.get_value_constraints())
|
|
173
|
+
|
|
174
|
+
value_data[:,:3] = self.model.rescale(value_data[:,:3],inplace=False)
|
|
175
|
+
point_colour = kwargs.pop('point_colour',None)
|
|
176
|
+
if point_colour is None:
|
|
177
|
+
self.ax.scatter(value_data[:, 0], value_data[:, 1], c=value_data[:,3],
|
|
178
|
+
vmin=feature.min(), vmax=feature.max(),cmap=cmap)
|
|
179
|
+
if point_colour is not None:
|
|
180
|
+
self.ax.scatter(value_data[:, 0], value_data[:, 1], c=point_colour)
|
|
181
|
+
if grad:
|
|
182
|
+
symb_colour = kwargs.pop('symb_colour','black')
|
|
183
|
+
symb_scale=kwargs.pop('symb_scale',1.)
|
|
184
|
+
gradient_data = np.hstack(ori_data)
|
|
185
|
+
gradient_data[:,:3] = self.model.rescale(gradient_data[:,:3],inplace=False)
|
|
186
|
+
gradient_data[:, 3:5] /= np.linalg.norm(gradient_data[:, 3:5], axis=1)[:, None]
|
|
187
|
+
t = gradient_data[:, [4, 3]] * np.array([1, -1]).T
|
|
188
|
+
n = gradient_data[:, 3:5]
|
|
189
|
+
t *= symb_scale
|
|
190
|
+
n *= 0.5*symb_scale
|
|
191
|
+
p1 = gradient_data[:, [0, 1]] - t
|
|
192
|
+
p2 = gradient_data[:, [0, 1]] + t
|
|
193
|
+
# plt.scatter(val[:,0],val[:,1],c='black')
|
|
194
|
+
self.ax.plot([p1[:, 0], p2[:, 0]], [p1[:, 1], p2[:, 1]], symb_colour)
|
|
195
|
+
p1 = gradient_data[:, [0, 1]]
|
|
196
|
+
p2 = gradient_data[:, [0, 1]] + n
|
|
197
|
+
self.ax.plot([p1[:, 0], p2[:, 0]], [p1[:, 1], p2[:, 1]], symb_colour)
|
|
198
|
+
if dip:
|
|
199
|
+
dip_v = np.rad2deg(np.arccos(gradient_data[:,5])).astype(int)
|
|
200
|
+
for d, xy, v in zip(dip_v,gradient_data[:,:2],gradient_data[:,3:6]):
|
|
201
|
+
self.ax.annotate(d,xy,xytext=xy+v[:2]*.03,fontsize='small')
|
|
202
|
+
|
|
203
|
+
def add_fault_ellipse(self, faults=None, **kwargs):
|
|
204
|
+
from matplotlib.patches import Ellipse
|
|
205
|
+
for k, f in self.model.stratigraphic_column['faults'].items():
|
|
206
|
+
center = self.model.rescale(f['FaultCenter'])
|
|
207
|
+
e = Ellipse((center[0],center[1]),
|
|
208
|
+
f['HorizontalRadius']*2,
|
|
209
|
+
f['InfluenceDistance']*2,
|
|
210
|
+
360-f['FaultDipDirection'],
|
|
211
|
+
facecolor='None',edgecolor='k')
|
|
212
|
+
|
|
213
|
+
self.ax.add_patch(e)
|
|
214
|
+
|
|
215
|
+
def add_scalar_field(self, feature, z=0, **kwargs):
|
|
216
|
+
"""
|
|
217
|
+
Plot the scalar field value on a map
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
----------
|
|
221
|
+
feature : GeologicalFeature
|
|
222
|
+
which feature to plot on the map
|
|
223
|
+
z : double/np.array
|
|
224
|
+
height
|
|
225
|
+
kwargs
|
|
226
|
+
|
|
227
|
+
Returns
|
|
228
|
+
-------
|
|
229
|
+
|
|
230
|
+
"""
|
|
231
|
+
zz = np.zeros(self.xx.shape)
|
|
232
|
+
zz[:] = z
|
|
233
|
+
v = feature.evaluate_value(self.model.scale(np.array([self.xx, self.yy, zz]).T,inplace=False))
|
|
234
|
+
return self.ax.imshow(v.reshape(self.nsteps).T,
|
|
235
|
+
extent=[self.bounding_box[0,0], self.bounding_box[1,0], self.bounding_box[0,1],
|
|
236
|
+
self.bounding_box[1,1]],
|
|
237
|
+
vmin=feature.min(), vmax=feature.max(),
|
|
238
|
+
origin='lower',
|
|
239
|
+
**kwargs)
|
|
240
|
+
|
|
241
|
+
def add_contour(self, feature, values, z=0, mask = None, **kwargs):
|
|
242
|
+
"""Add an isoline of a scalar field to the map
|
|
243
|
+
|
|
244
|
+
Parameters
|
|
245
|
+
----------
|
|
246
|
+
feature : GeologicalFeature
|
|
247
|
+
the feature to isosurface
|
|
248
|
+
values : list
|
|
249
|
+
list of values to contour
|
|
250
|
+
z : double/np.array, optional
|
|
251
|
+
elevation of map, by default 0
|
|
252
|
+
"""
|
|
253
|
+
zz = np.zeros(self.xx.shape)
|
|
254
|
+
zz[:] = z
|
|
255
|
+
v = feature.evaluate_value(self.model.scale(np.array([self.xx, self.yy, zz]).T,inplace=False))
|
|
256
|
+
if mask:
|
|
257
|
+
maskv = mask(self.model.scale(np.array([self.xx, self.yy, zz]).T,inplace=False))
|
|
258
|
+
v[~maskv] = np.nan
|
|
259
|
+
return self.ax.contour(v.reshape(self.nsteps).T,extent=[self.bounding_box[0,0], self.bounding_box[1,0], self.bounding_box[0,1],
|
|
260
|
+
self.bounding_box[1,1]],origin='lower',levels=values,**kwargs
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def add_model(self, z = 0,cmap=None):
|
|
266
|
+
"""Plot the model onto a map
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
z : int/numpy array, optional
|
|
271
|
+
height of the map surface (could also be a dem), by default 0
|
|
272
|
+
cmap : str/matplotlib colourmap, optional
|
|
273
|
+
specify a colour map, by default 'tab20'
|
|
274
|
+
"""
|
|
275
|
+
if cmap is None:
|
|
276
|
+
import matplotlib.colors as colors
|
|
277
|
+
colours = []
|
|
278
|
+
boundaries = []
|
|
279
|
+
data = []
|
|
280
|
+
for g in self.model.stratigraphic_column.keys():
|
|
281
|
+
if g == 'faults':
|
|
282
|
+
continue
|
|
283
|
+
for u, v in self.model.stratigraphic_column[g].items():
|
|
284
|
+
data.append((v['id'],v['colour']))
|
|
285
|
+
colours.append(v['colour'])
|
|
286
|
+
boundaries.append(v['id'])#print(u,v)
|
|
287
|
+
cmap = colors.ListedColormap(colours)
|
|
288
|
+
|
|
289
|
+
zz = np.zeros_like(self.xx)
|
|
290
|
+
zz[:] = z#self.bounding_box[1,2]
|
|
291
|
+
pts = np.vstack([self.xx.flatten(),self.yy.flatten(),zz.flatten()])
|
|
292
|
+
if self.model is None:
|
|
293
|
+
logger.error("Mapview needs a model assigned to plot model on map")
|
|
294
|
+
return
|
|
295
|
+
vals = self.model.evaluate_model(pts.T,scale=True)
|
|
296
|
+
return self.ax.imshow(vals.reshape(self.nsteps).T,extent=[self.bounding_box[0,0], self.bounding_box[1,0], self.bounding_box[0,1],
|
|
297
|
+
self.bounding_box[1,1]],origin='lower',cmap=cmap)
|
|
298
|
+
|
|
299
|
+
def add_fault_displacements(self,z = 0, cmap='rainbow'):
|
|
300
|
+
|
|
301
|
+
zz = np.zeros_like(self.xx)
|
|
302
|
+
zz[:] = z#self.bounding_box[1,2]
|
|
303
|
+
pts = np.vstack([self.xx.flatten(),self.yy.flatten(),zz.flatten()])
|
|
304
|
+
if self.model is None:
|
|
305
|
+
logger.error("Mapview needs a model assigned to plot model on map")
|
|
306
|
+
return
|
|
307
|
+
vals = self.model.evaluate_fault_displacements(pts.T,scale=True)
|
|
308
|
+
return self.ax.imshow(vals.reshape(self.nsteps).T,extent=[self.bounding_box[0,0], self.bounding_box[1,0], self.bounding_box[0,1],
|
|
309
|
+
self.bounding_box[1,1]],origin='lower',cmap=cmap)
|
|
310
|
+
|
|
311
|
+
def add_faults(self,**kwargs):
|
|
312
|
+
for f in self.model.features:
|
|
313
|
+
if f.type=='fault':
|
|
314
|
+
# create a function to return true if displacement > 0
|
|
315
|
+
def mask(x):
|
|
316
|
+
val = f.displacementfeature.evaluate_value(x)
|
|
317
|
+
val[np.isnan(val)] = 0
|
|
318
|
+
maskv = np.zeros(val.shape).astype(bool)
|
|
319
|
+
maskv[np.abs(val) > 0.001] = 1
|
|
320
|
+
return maskv
|
|
321
|
+
|
|
322
|
+
self.add_contour(f,0,mask=mask,**kwargs)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# import logging
|
|
2
|
+
#
|
|
3
|
+
#
|
|
4
|
+
# from LoopStructural.utils import getLogger
|
|
5
|
+
# logger = getLogger(__name__)
|
|
6
|
+
#
|
|
7
|
+
# class BaseModelPlotter:
|
|
8
|
+
# def __init__(self, model):
|
|
9
|
+
# """
|
|
10
|
+
#
|
|
11
|
+
# Parameters
|
|
12
|
+
# ----------
|
|
13
|
+
# model
|
|
14
|
+
# """
|
|
15
|
+
# self.model = model
|
|
16
|
+
#
|
|
17
|
+
# def
|