LoopStructural 1.0.9__cp39-cp39-win32.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.
Files changed (77) hide show
  1. LoopStructural/__init__.py +36 -0
  2. LoopStructural/analysis/__init__.py +2 -0
  3. LoopStructural/analysis/_fault_displacement.py +67 -0
  4. LoopStructural/analysis/_fault_intersection.py +36 -0
  5. LoopStructural/analysis/_plane_fit.py +43 -0
  6. LoopStructural/datasets/__init__.py +12 -0
  7. LoopStructural/datasets/_base.py +65 -0
  8. LoopStructural/datasets/data/claudius.csv +21049 -0
  9. LoopStructural/datasets/data/claudiusbb.txt +2 -0
  10. LoopStructural/datasets/data/duplex.csv +126 -0
  11. LoopStructural/datasets/data/duplexbb.txt +2 -0
  12. LoopStructural/datasets/data/intrusion.csv +1017 -0
  13. LoopStructural/datasets/data/intrusionbb.txt +2 -0
  14. LoopStructural/datasets/data/onefoldbb.txt +2 -0
  15. LoopStructural/datasets/data/onefolddata.csv +2226 -0
  16. LoopStructural/datasets/data/refolded_bb.txt +2 -0
  17. LoopStructural/datasets/data/refolded_fold.csv +2126 -0
  18. LoopStructural/interpolators/__init__.py +34 -0
  19. LoopStructural/interpolators/base_structured_3d_support.py +101 -0
  20. LoopStructural/interpolators/cython/__init__.py +0 -0
  21. LoopStructural/interpolators/cython/dsi_helper.c +31046 -0
  22. LoopStructural/interpolators/cython/dsi_helper.cp39-win32.pyd +0 -0
  23. LoopStructural/interpolators/discrete_fold_interpolator.py +202 -0
  24. LoopStructural/interpolators/discrete_interpolator.py +584 -0
  25. LoopStructural/interpolators/finite_difference_interpolator.py +399 -0
  26. LoopStructural/interpolators/geological_interpolator.py +197 -0
  27. LoopStructural/interpolators/operator.py +61 -0
  28. LoopStructural/interpolators/piecewiselinear_interpolator.py +437 -0
  29. LoopStructural/interpolators/structured_grid.py +421 -0
  30. LoopStructural/interpolators/structured_tetra.py +681 -0
  31. LoopStructural/interpolators/surfe_wrapper.py +118 -0
  32. LoopStructural/modelling/__init__.py +46 -0
  33. LoopStructural/modelling/core/__init__.py +0 -0
  34. LoopStructural/modelling/core/geological_model.py +1513 -0
  35. LoopStructural/modelling/core/geological_model_graph.py +880 -0
  36. LoopStructural/modelling/core/stratigraphic_column.py +5 -0
  37. LoopStructural/modelling/fault/__init__.py +4 -0
  38. LoopStructural/modelling/fault/fault_builder.py +143 -0
  39. LoopStructural/modelling/fault/fault_function.py +188 -0
  40. LoopStructural/modelling/fault/fault_function_feature.py +76 -0
  41. LoopStructural/modelling/fault/fault_segment.py +298 -0
  42. LoopStructural/modelling/features/__init__.py +8 -0
  43. LoopStructural/modelling/features/cross_product_geological_feature.py +90 -0
  44. LoopStructural/modelling/features/geological_feature.py +252 -0
  45. LoopStructural/modelling/features/geological_feature_builder.py +414 -0
  46. LoopStructural/modelling/features/lambda_geological_feature.py +36 -0
  47. LoopStructural/modelling/features/region_feature.py +34 -0
  48. LoopStructural/modelling/features/structural_frame.py +133 -0
  49. LoopStructural/modelling/features/structural_frame_builder.py +206 -0
  50. LoopStructural/modelling/features/unconformity_feature.py +74 -0
  51. LoopStructural/modelling/fold/__init__.py +8 -0
  52. LoopStructural/modelling/fold/fold.py +143 -0
  53. LoopStructural/modelling/fold/fold_rotation_angle.py +133 -0
  54. LoopStructural/modelling/fold/fold_rotation_angle_feature.py +58 -0
  55. LoopStructural/modelling/fold/foldframe.py +193 -0
  56. LoopStructural/modelling/fold/svariogram.py +180 -0
  57. LoopStructural/utils/__init__.py +18 -0
  58. LoopStructural/utils/bounding_box.py +21 -0
  59. LoopStructural/utils/exceptions.py +10 -0
  60. LoopStructural/utils/helper.py +382 -0
  61. LoopStructural/utils/logging.py +60 -0
  62. LoopStructural/utils/map2loop.py +353 -0
  63. LoopStructural/utils/regions.py +11 -0
  64. LoopStructural/utils/utils.py +69 -0
  65. LoopStructural/visualisation/__init__.py +31 -0
  66. LoopStructural/visualisation/map_viewer.py +322 -0
  67. LoopStructural/visualisation/model_plotter.py +17 -0
  68. LoopStructural/visualisation/model_visualisation.py +1113 -0
  69. LoopStructural/visualisation/rotation_angle_plotter.py +83 -0
  70. LoopStructural/visualisation/sphinx_scraper.py +34 -0
  71. LoopStructural/visualisation/stratigraphic_column.py +60 -0
  72. LoopStructural-1.0.9.dist-info/LICENSE +21 -0
  73. LoopStructural-1.0.9.dist-info/METADATA +19 -0
  74. LoopStructural-1.0.9.dist-info/RECORD +77 -0
  75. LoopStructural-1.0.9.dist-info/WHEEL +5 -0
  76. LoopStructural-1.0.9.dist-info/top_level.txt +2 -0
  77. 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