LoopStructural 1.0.3__zip → 1.0.71.dev0__zip

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 (119) hide show
  1. Miniconda/envs/loop/Lib/site-packages/LoopStructural/__init__.py +12 -7
  2. Miniconda/envs/loop/Lib/site-packages/LoopStructural/__pycache__/__init__.cpython-36.pyc +0 -0
  3. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/__init__.cpython-36.pyc +0 -0
  4. Miniconda/envs/loop/Lib/site-packages/LoopStructural/datasets/__pycache__/_base.cpython-36.pyc +0 -0
  5. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__init__.py +3 -0
  6. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/__init__.cpython-36.pyc +0 -0
  7. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/base_structured_3d_support.cpython-36.pyc +0 -0
  8. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_fold_interpolator.cpython-36.pyc +0 -0
  9. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/discrete_interpolator.cpython-36.pyc +0 -0
  10. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/finite_difference_interpolator.cpython-36.pyc +0 -0
  11. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/geological_interpolator.cpython-36.pyc +0 -0
  12. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/operator.cpython-36.pyc +0 -0
  13. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/piecewiselinear_interpolator.cpython-36.pyc +0 -0
  14. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_grid.cpython-36.pyc +0 -0
  15. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/structured_tetra.cpython-36.pyc +0 -0
  16. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/__pycache__/surfe_wrapper.cpython-36.pyc +0 -0
  17. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/base_structured_3d_support.py +101 -0
  18. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/__pycache__/__init__.cpython-36.pyc +0 -0
  19. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.c +4137 -2716
  20. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/cython/dsi_helper.cp36-win_amd64.pyd +0 -0
  21. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_fold_interpolator.py +56 -22
  22. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/discrete_interpolator.py +61 -28
  23. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/finite_difference_interpolator.py +71 -11
  24. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/geological_interpolator.py +22 -3
  25. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/operator.py +16 -1
  26. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/piecewiselinear_interpolator.py +150 -11
  27. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_grid.py +31 -69
  28. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/structured_tetra.py +89 -45
  29. Miniconda/envs/loop/Lib/site-packages/LoopStructural/interpolators/surfe_wrapper.py +7 -8
  30. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/__pycache__/__init__.cpython-36.pyc +0 -0
  31. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/__init__.cpython-36.pyc +0 -0
  32. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/geological_model.cpython-36.pyc +0 -0
  33. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/geological_model_graph.cpython-36.pyc +0 -0
  34. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/__pycache__/stratigraphic_column.cpython-36.pyc +0 -0
  35. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/geological_model.py +515 -197
  36. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/geological_model_graph.py +881 -0
  37. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/core/stratigraphic_column.py +5 -0
  38. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__init__.py +1 -0
  39. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/__init__.cpython-36.pyc +0 -0
  40. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_builder.cpython-36.pyc +0 -0
  41. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function.cpython-36.pyc +0 -0
  42. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_function_feature.cpython-36.pyc +0 -0
  43. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/__pycache__/fault_segment.cpython-36.pyc +0 -0
  44. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_builder.py +127 -0
  45. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function.py +2 -1
  46. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_function_feature.py +2 -1
  47. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fault/fault_segment.py +30 -3
  48. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__init__.py +1 -0
  49. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/__init__.cpython-36.pyc +0 -0
  50. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/cross_product_geological_feature.cpython-36.pyc +0 -0
  51. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature.cpython-36.pyc +0 -0
  52. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/geological_feature_builder.cpython-36.pyc +0 -0
  53. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/lambda_geological_feature.cpython-36.pyc +0 -0
  54. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/region_feature.cpython-36.pyc +0 -0
  55. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame.cpython-36.pyc +0 -0
  56. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/structural_frame_builder.cpython-36.pyc +0 -0
  57. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/__pycache__/unconformity_feature.cpython-36.pyc +0 -0
  58. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/cross_product_geological_feature.py +18 -5
  59. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature.py +22 -49
  60. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/geological_feature_builder.py +171 -47
  61. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/lambda_geological_feature.py +31 -0
  62. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/region_feature.py +3 -0
  63. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame.py +28 -11
  64. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/structural_frame_builder.py +32 -22
  65. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/features/unconformity_feature.py +6 -1
  66. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/__init__.cpython-36.pyc +0 -0
  67. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold.cpython-36.pyc +0 -0
  68. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle.cpython-36.pyc +0 -0
  69. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/fold_rotation_angle_feature.cpython-36.pyc +0 -0
  70. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/foldframe.cpython-36.pyc +0 -0
  71. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/__pycache__/svariogram.cpython-36.pyc +0 -0
  72. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold.py +13 -5
  73. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle.py +5 -4
  74. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/fold_rotation_angle_feature.py +2 -1
  75. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/foldframe.py +7 -5
  76. Miniconda/envs/loop/Lib/site-packages/LoopStructural/modelling/fold/svariogram.py +2 -1
  77. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__init__.py +5 -1
  78. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/__init__.cpython-36.pyc +0 -0
  79. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/bounding_box.cpython-36.pyc +0 -0
  80. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/exceptions.cpython-36.pyc +0 -0
  81. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/helper.cpython-36.pyc +0 -0
  82. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/logging.cpython-36.pyc +0 -0
  83. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/map2loop.cpython-36.pyc +0 -0
  84. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/regions.cpython-36.pyc +0 -0
  85. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/__pycache__/utils.cpython-36.pyc +0 -0
  86. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/bounding_box.py +21 -0
  87. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/exceptions.py +2 -1
  88. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/helper.py +10 -2
  89. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/logging.py +60 -0
  90. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/map2loop.py +128 -37
  91. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/regions.py +11 -0
  92. Miniconda/envs/loop/Lib/site-packages/LoopStructural/utils/utils.py +40 -47
  93. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/__init__.cpython-36.pyc +0 -0
  94. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/map_viewer.cpython-36.pyc +0 -0
  95. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_plotter.cpython-36.pyc +0 -0
  96. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/model_visualisation.cpython-36.pyc +0 -0
  97. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/rotation_angle_plotter.cpython-36.pyc +0 -0
  98. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/sphinx_scraper.cpython-36.pyc +0 -0
  99. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/__pycache__/stratigraphic_column.cpython-36.pyc +0 -0
  100. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/map_viewer.py +236 -36
  101. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_plotter.py +2 -1
  102. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/model_visualisation.py +427 -79
  103. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/rotation_angle_plotter.py +29 -12
  104. Miniconda/envs/loop/Lib/site-packages/LoopStructural/visualisation/stratigraphic_column.py +60 -0
  105. Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/PKG-INFO +1 -1
  106. Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/SOURCES.txt +10 -5
  107. Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.71.dev0-py3.6.egg-info/requires.txt +8 -0
  108. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/__init__.cpython-36.pyc +0 -0
  109. Miniconda/envs/loop/Lib/site-packages/LoopStructural-1.0.3-py3.6.egg-info/requires.txt +0 -3
  110. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_faults.cpython-36.pyc +0 -0
  111. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_fold.cpython-36.pyc +0 -0
  112. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_interpolator.cpython-36.pyc +0 -0
  113. Miniconda/envs/loop/Lib/site-packages/tests/__pycache__/test_refolded.cpython-36.pyc +0 -0
  114. Miniconda/envs/loop/Lib/site-packages/tests/test_faults.py +0 -17
  115. Miniconda/envs/loop/Lib/site-packages/tests/test_fold.py +0 -57
  116. Miniconda/envs/loop/Lib/site-packages/tests/test_interpolator.py +0 -88
  117. Miniconda/envs/loop/Lib/site-packages/tests/test_refolded.py +0 -22
  118. /Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/dependency_links.txt +0 -0
  119. /Miniconda/envs/loop/Lib/site-packages/{LoopStructural-1.0.3-py3.6.egg-info → LoopStructural-1.0.71.dev0-py3.6.egg-info}/top_level.txt +0 -0
@@ -4,15 +4,19 @@ A wrapper for lavavu
4
4
  """
5
5
 
6
6
  import logging
7
-
8
- import lavavu
7
+ from LoopStructural.utils import getLogger
8
+ logger = getLogger(__name__)
9
+
10
+ try:
11
+ import lavavu
12
+ from lavavu.vutils import is_notebook
13
+ except ImportError:
14
+ logger.error("Please install lavavu: pip install lavavu")
9
15
  import numpy as np
10
- from lavavu.vutils import is_notebook
11
- from skimage.measure import marching_cubes_lewiner as marching_cubes
12
-
16
+ from skimage.measure import marching_cubes
17
+ from LoopStructural.modelling.features import GeologicalFeature
13
18
  from LoopStructural.utils.helper import create_surface, get_vectors, create_box
14
19
 
15
- logger = logging.getLogger(__name__)
16
20
  # adapted/copied from pyvista for sphinx scraper
17
21
  _OPEN_VIEWERS = {}
18
22
 
@@ -40,12 +44,13 @@ class LavaVuModelViewer:
40
44
  ----------
41
45
  **kwargs : lavavu viewer kwargs
42
46
 
43
- Attributes
44
- ----------
45
- lv Lavavu.Viewer object
47
+
46
48
  objects : dictionary of objects that have been plotted
47
49
  """
48
50
  # copied from pyvista
51
+ if lavavu is None:
52
+ logger.error("Lavavu isn't installed: pip install lavavu")
53
+ return
49
54
  self._id_name = "{}-{}".format(str(hex(id(self))), len(_OPEN_VIEWERS))
50
55
  _OPEN_VIEWERS[self._id_name] = self
51
56
  #
@@ -63,11 +68,55 @@ class LavaVuModelViewer:
63
68
  logger.error("Plot area has not been defined.")
64
69
  self.bounding_box = np.array(self.bounding_box)
65
70
  self.nsteps = np.array(self.nsteps)
66
- self.model = model
71
+ self._model = model
67
72
  # prerotate to a nice view
68
73
  # self.lv.rotate([-57.657936096191406, -13.939384460449219, -6.758780479431152])
69
74
  def close(self):
70
75
  pass
76
+
77
+ @property
78
+ def model(self):
79
+ return self._model
80
+
81
+ @model.setter
82
+ def model(self, model):
83
+ if model is not None:
84
+ self.bounding_box = np.array(model.bounding_box)
85
+ self.nsteps = np.array(model.nsteps)
86
+ self._model = model
87
+ self._nelements = self.nsteps[0]*self.nsteps[1]*self.nsteps[2]
88
+ logger.debug("Using bounding box from model")
89
+ @property
90
+ def nelements(self):
91
+ """The number of elements to use for evaluating the isosurface
92
+
93
+ Returns
94
+ -------
95
+ nelements : int
96
+ number of elements to use for isosurfacing
97
+ """
98
+ return self._nelements
99
+
100
+ @nelements.setter
101
+ def nelements(self, nelements : int):
102
+ """Setter for nelements, automatically caculates the number of equally sized elements
103
+ to isosurface. Better than specifying step distance manually
104
+
105
+ Parameters
106
+ ----------
107
+ nelements : int
108
+ [description]
109
+ """
110
+ box_vol = (self.bounding_box[1, 0]-self.bounding_box[0, 0]) * (self.bounding_box[1, 1]-self.bounding_box[0, 1]) * (self.bounding_box[1, 2]-self.bounding_box[0, 2])
111
+ ele_vol = box_vol / nelements
112
+ # calculate the step vector of a regular cube
113
+ step_vector = np.zeros(3)
114
+ step_vector[:] = ele_vol ** (1. / 3.)
115
+ # step_vector /= np.array([1,1,2])
116
+ # number of steps is the length of the box / step vector
117
+ nsteps = np.ceil((self.bounding_box[1, :] - self.bounding_box[0, :]) / step_vector).astype(int)
118
+ self.nsteps = nsteps
119
+ logger.info("Using grid with dimensions {} {} {}".format(nsteps[0],nsteps[1],nsteps[2]))
71
120
 
72
121
  def deep_clean(self):
73
122
  """[summary]
@@ -77,8 +126,8 @@ class LavaVuModelViewer:
77
126
  self.lv.clear()
78
127
  self.lv.cleardata()
79
128
  pass
80
-
81
- def add_section(self, geological_feature=None, axis='x', value=None, **kwargs):
129
+
130
+ def add_section(self, geological_feature=None, axis='x', value=None, **kwargs):
82
131
  """
83
132
 
84
133
  Plot a section/map thru the model and paint with a geological feature
@@ -98,29 +147,26 @@ class LavaVuModelViewer:
98
147
  -------
99
148
 
100
149
  """
101
-
102
150
  if axis == 'x':
103
151
  tri, yy, zz = create_surface(self.bounding_box[:, [1, 2]], self.nsteps[[1, 2]])
104
152
  xx = np.zeros(zz.shape)
105
153
  if value is None:
106
- xx[:] = np.nanmean(self.bounding_box[:, 0])
107
- else:
108
- xx[:] = value
154
+ value = np.nanmean(self.bounding_box[:, 0])
155
+ xx[:] = value
109
156
  if axis == 'y':
110
157
  tri, xx, zz = create_surface(self.bounding_box[:, [0, 2]], self.nsteps[[0, 2]])
111
158
  yy = np.zeros(xx.shape)
112
159
  if value is None:
113
- yy[:] = np.nanmean(self.bounding_box[:, 1])
114
- else:
115
- yy[:] = value
160
+ value = np.nanmean(self.bounding_box[:, 1])
161
+ yy[:] = value
116
162
  if axis == 'z':
117
163
  tri, xx, yy = create_surface(self.bounding_box[:, 0:2], self.nsteps[0:2])
118
164
  zz = np.zeros(xx.shape)
119
165
  if value is None:
120
- zz[:] = np.nanmean(self.bounding_box[:, 2])
121
- else:
122
- zz[:] = value
123
- name = kwargs.get('name', axis + '_slice')
166
+ value = np.nanmean(self.bounding_box[:, 2])
167
+ zz[:] = value
168
+ name = kwargs.get('name', geological_feature.name)
169
+ name = '{}_section_at_{}_of_{}'.format(axis,value,name)
124
170
  colour = kwargs.get('colour', 'red')
125
171
 
126
172
  # create an array to evaluate the feature on for the section
@@ -130,12 +176,13 @@ class LavaVuModelViewer:
130
176
  points[:, 2] = zz
131
177
 
132
178
  surf = self.lv.triangles(name)
133
- surf.vertices(points)
179
+ surf.vertices(self.model.rescale(points,inplace=False))
134
180
  surf.indices(tri)
135
181
  logger.info("Adding %s section at %f" % (axis, value))
136
182
  if geological_feature is None:
137
183
  surf.colours(colour)
138
- if geological_feature is not None:
184
+
185
+ if geological_feature is not None and type(geological_feature) == GeologicalFeature:
139
186
  if 'norm' in kwargs:
140
187
  surf.values(np.linalg.norm(
141
188
  geological_feature.evaluate_gradient(points), axis=1),
@@ -150,10 +197,19 @@ class LavaVuModelViewer:
150
197
  logger.info("Colouring section with %s min: %f, max: %f" % (
151
198
  geological_feature.name, geological_feature.min(), geological_feature.max()))
152
199
  surf.colourmap(cmap, range=[geological_feature.min(), geological_feature.max()])
200
+ if geological_feature == 'model' and self.model is not None:
201
+ name = kwargs.get('name','model_section')
202
+ surf.values(self.model.evaluate_model(points,scale=True),
203
+ name)
204
+ surf["colourby"] = name
205
+ cmap = lavavu.cubehelix(100)
206
+ if 'cmap' in kwargs:
207
+ cmap = kwargs['cmap']
208
+
153
209
 
154
210
  def add_isosurface(self, geological_feature, value = None, isovalue=None,
155
211
  paint_with=None, slices=None, colour='red', nslices=None,
156
- cmap=None, filename=None, **kwargs):
212
+ cmap=None, filename=None, names=None, colours=None,**kwargs):
157
213
  """ Plot the surface of a geological feature
158
214
 
159
215
  [extended_summary]
@@ -177,14 +233,19 @@ class LavaVuModelViewer:
177
233
  cmap : [type], optional
178
234
  [description], by default None
179
235
  filename: string, optional
180
- filename for exporting
236
+ filename for exporting
237
+ names: list, optional
238
+ list of names same length as slices
239
+ colours: list, optional
240
+ list of colours same length as slices
181
241
 
182
242
  Returns
183
243
  -------
184
244
  [type]
185
245
  [description]
186
246
  """
187
-
247
+ if geological_feature is None:
248
+ logger.error("Cannot add isosurface GeologicalFeature does not exist")
188
249
  # update the feature to make sure its current
189
250
  if 'update' in kwargs:
190
251
  geological_feature.update()
@@ -230,7 +291,7 @@ class LavaVuModelViewer:
230
291
  if region is not None:
231
292
  val[~region(np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T)] = np.nan
232
293
  step_vector = np.array([x[1] - x[0], y[1] - y[0], z[1] - z[0]])
233
- for isovalue in slices_:
294
+ for i, isovalue in enumerate(slices_):
234
295
  logger.info("Creating isosurface of %s at %f" % (geological_feature.name, isovalue))
235
296
 
236
297
  if isovalue > np.nanmax(val) or isovalue < np.nanmin(val):
@@ -242,23 +303,41 @@ class LavaVuModelViewer:
242
303
  isovalue,
243
304
  spacing=step_vector)
244
305
  verts += np.array([self.bounding_box[0, 0], self.bounding_box[0, 1], self.bounding_box[1, 2]])
245
- except ValueError:
246
- logger.warning("no surface to mesh, skipping")
306
+ self.model.rescale(verts)
307
+
308
+ except (ValueError, RuntimeError) as e:
309
+ print(e)
310
+ logger.warning("Cannot isosurface {} at {}, skipping".format(geological_feature.name,isovalue))
247
311
  continue
248
-
249
312
 
313
+
250
314
  name = geological_feature.name
251
315
  name = kwargs.get('name', name)
252
316
  name += '_iso_%f' % isovalue
317
+ if names is not None and len(names) == len(slices_):
318
+ name = names[i]
319
+ if name in self.lv.objects:
320
+ ii = 0
321
+ newname = name+"_{}".format(ii)
322
+ while newname in self.lv.objects:
323
+ ii+=1
324
+ newname = name+"_{}".format(ii)
325
+ name = newname
326
+
327
+ if colours is not None and len(colours) == len(slices_):
328
+ colour=colours[i]
253
329
  if filename is not None:
330
+ svalues = None
331
+ # svalues[:] = np.nan
254
332
  try:
255
333
  import meshio
334
+ meshio.write_points_cells(filename.format(name),
335
+ verts,
336
+ [("triangle", faces)]
337
+ )
256
338
  except ImportError:
257
339
  logger.error("Could not save surfaces, meshio is not installed")
258
- meshio.write_points_cells(filename.format(name),
259
- self.model.rescale(verts),
260
- [("triangle", faces)]
261
- )
340
+
262
341
  surf = self.lv.triangles(name)
263
342
  surf.vertices(verts)
264
343
  surf.indices(faces)
@@ -268,7 +347,7 @@ class LavaVuModelViewer:
268
347
  # add a property to the surface nodes for visualisation
269
348
  # calculate the mode value, just to get the most common value
270
349
  surfaceval = np.zeros(verts.shape[0])
271
- surfaceval[:] = painter.evaluate_value(verts)
350
+ surfaceval[:] = painter.evaluate_value(self.model.scale(verts))
272
351
  if painter.name is geological_feature.name:
273
352
  logger.info("Setting surface value to %f"%isovalue)
274
353
  surfaceval[:] = isovalue
@@ -278,43 +357,63 @@ class LavaVuModelViewer:
278
357
  vmax = kwargs.get('vmax', max_property_val)
279
358
  surf.colourmap(cmap, range=(vmin, vmax)) # nodes.shape[0]))
280
359
 
281
- def add_scalar_field(self, geological_feature, **kwargs):
282
- """
360
+ def add_scalar_field(self, geological_feature, name=None, cmap='rainbow', vmin=None, vmax = None, **kwargs):
361
+ """Add a block the size of the model area painted with the scalar field value
283
362
 
284
363
  Parameters
285
364
  ----------
286
365
  geological_feature : GeologicalFeature
287
366
  the geological feature to colour the scalar field by
288
- kwargs
289
- kwargs for lavavu
290
-
291
- Returns
292
- -------
293
-
367
+ name : string, optional
368
+ Name of the object for lavavu, needs to be unique for the viewer object, by default uses feature name
369
+ cmap : str, optional
370
+ mpl colourmap reference, by default 'rainbow'
371
+ vmin : double, optional
372
+ minimum value of the colourmap, by default None
373
+ vmax : double, optional
374
+ maximum value of the colourmap, by default None
294
375
  """
295
- name = kwargs.get('name', geological_feature.name + '_scalar_field')
376
+ if name == None:
377
+ if geological_feature is None:
378
+ name = 'unnamed scalar field'
379
+ else:
380
+ name = geological_feature.name + '_scalar_field'
381
+
296
382
  points, tri = create_box(self.bounding_box,self.nsteps)
297
383
 
298
384
  surf = self.lv.triangles(name)
299
- surf.vertices(points)
385
+ surf.vertices(self.model.rescale(points))
300
386
  surf.indices(tri)
301
- val =geological_feature.evaluate_value(points)
387
+ val =geological_feature.evaluate_value(self.model.scale(points))
302
388
  surf.values(val, geological_feature.name)
303
389
  surf["colourby"] = geological_feature.name
304
- cmap = kwargs.get('cmap',lavavu.cubehelix(100))
305
-
306
390
  logger.info("Adding scalar field of %s to viewer. Min: %f, max: %f" % (geological_feature.name,
307
391
  geological_feature.min(),
308
392
  geological_feature.max()))
309
- vmin = kwargs.get('vmin', np.nanmin(val))
310
- vmax = kwargs.get('vmax', np.nanmax(val))
393
+ if vmin == None:
394
+ vmin =np.nanmin(val)
395
+ if vmax == None:
396
+ vmax = np.nanmax(val)
311
397
  surf.colourmap(cmap, range=(vmin, vmax))
312
398
 
313
- def add_model(self, **kwargs):
399
+ def add_box(self,bounding_box,name,colour='red'):
400
+ points, tri = create_box(bounding_box,self.nsteps)
401
+
402
+ surf = self.lv.triangles(name)
403
+ surf.vertices(self.model.rescale(points))
404
+ surf.indices(tri)
405
+ surf.colours(colour)
406
+
407
+ def add_model(self, cmap = None, **kwargs):
314
408
  """Add a block model painted by stratigraphic id to the viewer
315
409
 
316
410
  Calls self.model.evaluate_model() for a cube surrounding the model.
317
411
 
412
+ Parameters
413
+ ----------
414
+ cmap : matplotlib cmap, optional
415
+ colourmap name or object from mpl
416
+
318
417
  Notes
319
418
  ------
320
419
  It is sensible to increase the viewer step sizes before running this function to
@@ -324,17 +423,36 @@ class LavaVuModelViewer:
324
423
  >>> viewer.nsteps = np.array([100,100,100])
325
424
 
326
425
  """
426
+ import matplotlib.colors as colors
427
+ from matplotlib import cm
428
+
327
429
  name = kwargs.get('name', 'geological_model')
328
430
  points, tri = create_box(self.bounding_box, self.nsteps)
329
431
 
330
432
  surf = self.lv.triangles(name)
331
- surf.vertices(points)
433
+ surf.vertices(self.model.rescale(points))
332
434
  surf.indices(tri)
333
- val = self.model.evaluate_model(points,rescale=False)
435
+ val = self.model.evaluate_model(points,scale=True)
334
436
  surf.values(val, 'model')
335
437
  surf["colourby"] = 'model'
336
- cmap = kwargs.get('cmap', lavavu.cubehelix(100))
337
438
 
439
+ if cmap is None:
440
+ import matplotlib.colors as colors
441
+ colours = []
442
+ boundaries = []
443
+ data = []
444
+ for g in self.model.stratigraphic_column.keys():
445
+ if g == 'faults':
446
+ continue
447
+ for u, v in self.model.stratigraphic_column[g].items():
448
+ data.append((v['id'],v['colour']))
449
+ colours.append(v['colour'])
450
+ boundaries.append(v['id'])#print(u,v)
451
+ cmap = colors.ListedColormap(colours).colors
452
+ # else:
453
+ # cmap = cm.get_cmap(cmap,n_units)
454
+
455
+
338
456
  # logger.info("Adding scalar field of %s to viewer. Min: %f, max: %f" % (geological_feature.name,
339
457
  # geological_feature.min(),
340
458
  # geological_feature.max()))
@@ -342,7 +460,55 @@ class LavaVuModelViewer:
342
460
  vmax = kwargs.get('vmax', np.nanmax(val))
343
461
  surf.colourmap(cmap, range=(vmin, vmax))
344
462
 
345
- def add_model_surfaces(self, faults = True, cmap='tab20', **kwargs):
463
+ def add_fault_displacements(self, cmap = 'rainbow', **kwargs):
464
+ """Add a block model painted by the fault displacement magnitude
465
+
466
+ Calls fault.displacementfeature.evaluate_value(points) for all faults
467
+
468
+ Parameters
469
+ ----------
470
+ cmap : matplotlib cmap, optional
471
+ colourmap name or object from mpl
472
+
473
+ Notes
474
+ ------
475
+ It is sensible to increase the viewer step sizes before running this function to
476
+ increase the resolution of the model as its not possible to interpolate a discrete
477
+ colourmap and this causes the model to look like a lego block.
478
+ You can update the model resolution by changing the attribute nsteps
479
+ >>> viewer.nsteps = np.array([100,100,100])
480
+
481
+ """
482
+
483
+ name = kwargs.get('name', 'fault_displacements')
484
+ points, tri = create_box(self.bounding_box, self.nsteps)
485
+
486
+ surf = self.lv.triangles(name)
487
+ surf.vertices(self.model.rescale(points))
488
+ surf.indices(tri)
489
+ vals = self.model.evaluate_fault_displacements(points)
490
+ surf.values(vals, 'displacement')
491
+ surf["colourby"] = 'displacement'
492
+
493
+ vmin = kwargs.get('vmin', np.nanmin(vals))
494
+ vmax = kwargs.get('vmax', np.nanmax(vals))
495
+ surf.colourmap(cmap, range=(vmin, vmax))
496
+
497
+ def add_fault(self,fault,step=100):
498
+ self.add_isosurface(fault,value=0,name=fault.name)
499
+ self.add_vector_field(fault,locations=self.model.regular_grid()[::step])
500
+
501
+ def unfault_grid(self,feature,grid=None):
502
+ if grid is None:
503
+ grid = self.model.regular_grid()
504
+ # apply all faults associated with a feature to a regular grid
505
+ self.add_value_data(self.model.rescale(grid,inplace=False),grid[:,2],name='Regular grid before faults',pointsize=10,)
506
+
507
+ for f in feature.faults:
508
+ grid = f.apply_to_points(grid)
509
+ self.add_value_data(self.model.rescale(grid,inplace=False),grid[:,2],name='Regular grid after faults',pointsize=10,)
510
+
511
+ def add_model_surfaces(self, strati=True, faults = True, cmap=None, fault_colour='black',**kwargs):
346
512
  """Add surfaces for all of the interfaces in the model
347
513
 
348
514
 
@@ -357,26 +523,79 @@ class LavaVuModelViewer:
357
523
  Other parameters are passed to self.add_isosurface()
358
524
 
359
525
  """
526
+ import time
360
527
  from matplotlib import cm
528
+ from matplotlib import colors
529
+ from tqdm.auto import tqdm
530
+ start = time.time()
361
531
  n_units = 0 #count how many discrete colours
362
- for g in self.model.stratigraphic_column.keys():
363
- for u in self.model.stratigraphic_column[g].keys():
364
- n_units+=1
365
- tab = cm.get_cmap(cmap,n_units)
366
- ci = 0
367
-
368
532
  for g in self.model.stratigraphic_column.keys():
369
533
  if g in self.model.feature_name_index:
370
- feature = self.model.features[self.model.feature_name_index[g]]
371
- for u, vals in self.model.stratigraphic_column[g].items():
372
- self.add_isosurface(feature, isovalue=vals['max'],name=u,colour=tab.colors[ci,:],**kwargs)
373
- ci+=1
534
+ for u in self.model.stratigraphic_column[g].keys():
535
+ n_units+=1
536
+ n_faults = 0
537
+ for f in self.model.features:
538
+ if f.type=='fault':
539
+ n_faults+=1
540
+
541
+ if cmap is None:
542
+ colours = []
543
+ boundaries = []
544
+ data = []
545
+ for g in self.model.stratigraphic_column.keys():
546
+ if g == 'faults':
547
+ # skip anything saved in faults here
548
+ continue
549
+ for u, v in self.model.stratigraphic_column[g].items():
550
+ data.append((v['id'],v['colour']))
551
+ colours.append(v['colour'])
552
+ boundaries.append(v['id'])
553
+ cmap = colors.ListedColormap(colours)
554
+ else:
555
+ cmap = cm.get_cmap(cmap,n_units)
556
+ ci = 0
557
+ cmap_colours = colors.to_rgba_array(cmap.colors)
558
+ n_surfaces = 0
559
+ if strati:
560
+ n_surfaces+=n_units
374
561
  if faults:
375
- for f in self.model.features:
376
- if f.type == 'fault':
377
- self.add_isosurface(f,isovalue=0,**kwargs)
378
-
562
+ n_surfaces+=n_faults
563
+ with tqdm(total=n_surfaces) as pbar:
564
+
565
+ if strati:
566
+ for g in self.model.stratigraphic_column.keys():
567
+ if g in self.model.feature_name_index:
568
+ feature = self.model.features[self.model.feature_name_index[g]]
569
+ names = []
570
+ values = []
571
+ colours = []
572
+ for u, vals in self.model.stratigraphic_column[g].items():
573
+ names.append(u)
574
+ values.append(vals['min'])
575
+ colours.append(cmap_colours[ci,:])
576
+ ci+=1
577
+ pbar.set_description('Isosurfacing {}'.format(feature.name))
578
+ self.add_isosurface(feature, slices=values,names=names,colours=colours,**kwargs)
579
+ pbar.update(len(values))
580
+
379
581
 
582
+ if faults:
583
+ for f in self.model.features:
584
+ if f.type == 'fault':
585
+ def mask(x):
586
+ val = f.displacementfeature.evaluate_value(x)
587
+ val[np.isnan(val)] = 0
588
+ maskv = np.zeros(val.shape).astype(bool)
589
+ maskv[np.abs(val) > 0.001] = 1
590
+ return maskv
591
+ if f.name in self.model.stratigraphic_column['faults']:
592
+ fault_colour = self.model.stratigraphic_column['faults'][f.name].get('colour',['red'])
593
+ pbar.set_description('Isosurfacing {}'.format(f.name))
594
+
595
+ region = kwargs.pop('region',None)
596
+ self.add_isosurface(f,isovalue=0,region=mask,colour=fault_colour[0],name=f.name,**kwargs)
597
+ pbar.update(1)
598
+ print("Adding surfaces took {} seconds".format(time.time()-start))
380
599
  def add_vector_field(self, geological_feature, **kwargs):
381
600
  """
382
601
 
@@ -406,7 +625,7 @@ class LavaVuModelViewer:
406
625
  vector[mask, :] /= np.linalg.norm(vector[mask, :], axis=1)[:, None]
407
626
  vectorfield = self.lv.vectors(geological_feature.name + "_grad",
408
627
  **kwargs)
409
- vectorfield.vertices(locations[mask, :])
628
+ vectorfield.vertices(self.model.rescale(locations[mask, :],inplace=False))
410
629
  vectorfield.vectors(vector[mask, :])
411
630
  return
412
631
 
@@ -429,6 +648,7 @@ class LavaVuModelViewer:
429
648
  add_grad = True
430
649
  add_value = True
431
650
  add_tang = True
651
+ add_interface = True
432
652
  if 'name' in kwargs:
433
653
  name = kwargs['name']
434
654
  del kwargs['name']
@@ -438,26 +658,49 @@ class LavaVuModelViewer:
438
658
  add_value = kwargs['value']
439
659
  if 'tang' in kwargs:
440
660
  add_tang = kwargs['tang']
661
+ if 'interface' in kwargs:
662
+ add_interface = kwargs['interface']
441
663
  grad = feature.builder.get_gradient_constraints()
442
664
  norm = feature.builder.get_norm_constraints()
443
665
  value = feature.builder.get_value_constraints()
444
666
  tang = feature.builder.get_tangent_constraints()
667
+ interface = feature.builder.get_interface_constraints()
668
+
445
669
  if grad.shape[0] > 0 and add_grad:
446
- self.add_vector_data(grad[:, :3], grad[:, 3:6], name + "_grad_cp",
670
+ self.add_vector_data(self.model.rescale(grad[:, :3],inplace=False), grad[:, 3:6], name + "_grad_cp",
447
671
  **kwargs)
448
672
 
449
673
  if norm.shape[0] > 0 and add_grad:
450
- self.add_vector_data(norm[:, :3], norm[:, 3:6], name + "_norm_cp",
674
+ self.add_vector_data(self.model.rescale(norm[:, :3],inplace=False), norm[:, 3:6], name + "_norm_cp",
451
675
  **kwargs)
452
676
  if value.shape[0] > 0 and add_value:
453
677
  kwargs['range'] = [feature.min(), feature.max()]
454
- self.add_value_data(value[:, :3], value[:, 3], name + "_value_cp",
678
+ self.add_value_data(self.model.rescale(value[:, :3],inplace=False), value[:, 3], name + "_value_cp",
455
679
  **kwargs)
456
680
  if tang.shape[0] > 0 and add_tang:
457
- self.add_vector_data(tang[:, :3], tang[:, 3:6], name + "_tang_cp",
681
+ self.add_vector_data(self.model.rescale(tang[:, :3],inplace=False), tang[:, 3:6], name + "_tang_cp",
458
682
  **kwargs)
683
+ if interface.shape[0] > 0 and add_interface:
684
+ self.add_points(self.model.rescale(interface[:,:3],inplace=False), name + "_interface_cp")
459
685
 
460
-
686
+ def add_intersection_lineation(self, feature, **kwargs):
687
+ name = feature.name
688
+ if 'name' in kwargs:
689
+ name = kwargs['name']
690
+ del kwargs['name']
691
+ intersection = feature.fold.foldframe.calculate_intersection_lineation(
692
+ feature.builder)
693
+ gpoints = feature.builder.interpolator.get_gradient_constraints()[:,:6]
694
+ npoints = feature.builder.interpolator.get_norm_constraints()[:,:6]
695
+ points = []
696
+ if gpoints.shape[0] > 0:
697
+ points.append(gpoints)
698
+ if npoints.shape[0] > 0:
699
+ points.append(npoints)
700
+ points = np.vstack(points)
701
+ if intersection.shape[0] > 0:
702
+ self.add_vector_data(self.model.rescale(points[:,:3],inplace=False), intersection, name + "_intersection")
703
+
461
704
  def add_points(self, points, name, **kwargs):
462
705
  """
463
706
 
@@ -523,7 +766,7 @@ class LavaVuModelViewer:
523
766
  if "pointsize" not in kwargs:
524
767
  kwargs["pointsize"] = 4
525
768
  # set the colour map to diverge unless user decides otherwise
526
- cmap = kwargs.get('cmap', "spot")
769
+ cmap = kwargs.get('cmap', "rainbow")
527
770
  p = self.lv.points(name, **kwargs)
528
771
  p.vertices(position)
529
772
  p.values(value, "v")
@@ -533,7 +776,7 @@ class LavaVuModelViewer:
533
776
  logger.info('vmin {} and vmax {}'.format(kwargs['vmin'],kwargs['vmax']))
534
777
  p.colourmap(cmap, range=(kwargs['vmin'],kwargs['vmax']))
535
778
  else:
536
- p.colourmap(cmap)
779
+ p.colourmap(cmap, range=(np.nanmin(value),np.nanmax(value)))
537
780
 
538
781
  def add_fold(self, fold, **kwargs):
539
782
  """
@@ -556,6 +799,7 @@ class LavaVuModelViewer:
556
799
  xx, yy, zz = np.meshgrid(x, y, z, indexing='ij')
557
800
  locations = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T
558
801
  r2r, fold_axis, dgz = fold.get_deformed_orientation(locations)
802
+ locations = self.model.rescale(locations,inplace=False)
559
803
  self.add_vector_data(locations, r2r, fold.name + '_direction', colour='red')
560
804
  self.add_vector_data(locations, fold_axis, fold.name + '_axis', colour='black')
561
805
  self.add_vector_data(locations, dgz, fold.name + '_norm', colour='green')
@@ -578,7 +822,26 @@ class LavaVuModelViewer:
578
822
  self.lv.control.ObjectList()
579
823
  self.lv.interactive()
580
824
 
825
+ def add_support_box(self,geological_feature, paint=False, **kwargs):
826
+ name = kwargs.get('name', geological_feature.name + '_support')
827
+ box = np.vstack([geological_feature.interpolator.support.origin,geological_feature.interpolator.support.maximum])
828
+ points, tri = create_box(box,self.nsteps)
581
829
 
830
+ surf = self.lv.triangles(name)
831
+ surf.vertices(self.model.rescale(points))
832
+ surf.indices(tri)
833
+ if paint:
834
+ val =geological_feature.evaluate_value(self.model.scale(points))
835
+ surf.values(val, geological_feature.name)
836
+ surf["colourby"] = geological_feature.name
837
+ cmap = kwargs.get('cmap',lavavu.cubehelix(100))
838
+
839
+ logger.info("Adding scalar field of %s to viewer. Min: %f, max: %f" % (geological_feature.name,
840
+ geological_feature.min(),
841
+ geological_feature.max()))
842
+ vmin = kwargs.get('vmin', np.nanmin(val))
843
+ vmax = kwargs.get('vmax', np.nanmax(val))
844
+ surf.colourmap(cmap, range=(vmin, vmax))
582
845
  def set_zscale(self,zscale):
583
846
  """ Set the vertical scale for lavavu
584
847
 
@@ -648,6 +911,16 @@ class LavaVuModelViewer:
648
911
 
649
912
  """
650
913
  self.lv.image(name)
914
+
915
+ def image_array(self, **kwargs):
916
+ """Return the current viewer image image data as a numpy array
917
+
918
+ Returns
919
+ -------
920
+ image : np.array
921
+ image as a numpy array
922
+ """
923
+ return self.lv.rawimage(**kwargs).data
651
924
 
652
925
  def rotatex(self, r):
653
926
  """
@@ -730,3 +1003,78 @@ class LavaVuModelViewer:
730
1003
  """
731
1004
  self.lv.rotation(xyz)
732
1005
 
1006
+ @property
1007
+ def border(self):
1008
+ """The width of the border around the model area
1009
+
1010
+ Returns
1011
+ -------
1012
+ border : double
1013
+ [description]
1014
+ """
1015
+ return self.lv['border']
1016
+
1017
+ @border.setter
1018
+ def border(self, border):
1019
+ """Setter for the border
1020
+
1021
+ Parameters
1022
+ ----------
1023
+ border : double
1024
+ set the thickness of the border around objects
1025
+ """
1026
+ self.lv['border'] = border
1027
+
1028
+ def clear(self):
1029
+ """Remove all objects from the viewer
1030
+ """
1031
+ self.lv.clear()
1032
+
1033
+ @property
1034
+ def xmin(self):
1035
+ return self.lv['xmin']
1036
+
1037
+ @xmin.setter
1038
+ def xmin(self, xmin):
1039
+ self.lv['xmin'] = xmin
1040
+
1041
+ @property
1042
+ def xmax(self):
1043
+ return self.lv['xmax']
1044
+
1045
+ @xmax.setter
1046
+ def xmax(self, xmax):
1047
+ self.lv['xmax'] = xmax
1048
+
1049
+ @property
1050
+ def ymin(self):
1051
+ return self.lv['ymin']
1052
+
1053
+ @ymin.setter
1054
+ def ymin(self, ymin):
1055
+ self.lv['ymin'] = ymin
1056
+
1057
+ @property
1058
+ def ymax(self):
1059
+ return self.lv['ymax']
1060
+
1061
+ @ymax.setter
1062
+ def ymax(self, ymax):
1063
+ self.lv['ymax'] = ymax
1064
+
1065
+ @property
1066
+ def zmin(self):
1067
+ return self.lv['zmax']
1068
+
1069
+ @zmin.setter
1070
+ def zmin(self, zmin):
1071
+ self.lv['zmin'] = zmin
1072
+
1073
+ @property
1074
+ def zmax(self):
1075
+ return self.lv['zmax']
1076
+
1077
+ @zmax.setter
1078
+ def zmax(self, zmax):
1079
+ self.lv['zmax'] = zmax
1080
+