forgeo-gmlib 0.6.2__cp312-cp312-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.
- forgeo/gmlib/GeologicalModel3D.py +758 -0
- forgeo/gmlib/__init__.py +9 -0
- forgeo/gmlib/_version.py +34 -0
- forgeo/gmlib/architecture/__init__.py +1 -0
- forgeo/gmlib/architecture/core.py +130 -0
- forgeo/gmlib/common.pyd +0 -0
- forgeo/gmlib/fault_network.py +171 -0
- forgeo/gmlib/geomodeller_data.py +101 -0
- forgeo/gmlib/geomodeller_project.py +396 -0
- forgeo/gmlib/myxmltools.py +30 -0
- forgeo/gmlib/pypotential2D.pyd +0 -0
- forgeo/gmlib/pypotential3D.pyd +0 -0
- forgeo/gmlib/tesselate.py +236 -0
- forgeo/gmlib/tesselate_deprecated.py +249 -0
- forgeo/gmlib/topography_reader.py +198 -0
- forgeo/gmlib/utils/__init__.py +0 -0
- forgeo/gmlib/utils/append_data.py +508 -0
- forgeo/gmlib/utils/export.py +45 -0
- forgeo/gmlib/utils/normalized_gradient.py +40 -0
- forgeo/gmlib/utils/tools.py +35 -0
- forgeo_gmlib-0.6.2.dist-info/METADATA +23 -0
- forgeo_gmlib-0.6.2.dist-info/RECORD +24 -0
- forgeo_gmlib-0.6.2.dist-info/WHEEL +5 -0
- forgeo_gmlib-0.6.2.dist-info/licenses/LICENSE +661 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is part of gmlib. It is free software.
|
|
3
|
+
# You can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
import copy
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
import forgeo.gmlib.pypotential3D as potential
|
|
11
|
+
from forgeo.gmlib.common import OrientedEvaluation
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from skimage.measure import marching_cubes
|
|
15
|
+
except ImportError:
|
|
16
|
+
raise
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from pycgal.Polygon_mesh_processing import corefine
|
|
20
|
+
from pycgal.Surface_mesh import Surface_mesh
|
|
21
|
+
except ImportError:
|
|
22
|
+
raise
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# FIXME: to be moved elsewhere
|
|
26
|
+
def zmap_to_tsurf(zmap, origin, steps):
|
|
27
|
+
nx, ny = zmap.shape
|
|
28
|
+
assert nx > 1
|
|
29
|
+
assert ny > 1
|
|
30
|
+
xy = np.vstack(
|
|
31
|
+
[
|
|
32
|
+
a.ravel()
|
|
33
|
+
for a in np.meshgrid(
|
|
34
|
+
origin[0] + steps[0] * np.arange(0, nx),
|
|
35
|
+
origin[1] + steps[1] * np.arange(0, ny),
|
|
36
|
+
)
|
|
37
|
+
]
|
|
38
|
+
)
|
|
39
|
+
vertices = np.vstack([xy, zmap.ravel(order="F")])
|
|
40
|
+
vertices = np.transpose(vertices)
|
|
41
|
+
tmp = np.hstack(
|
|
42
|
+
[
|
|
43
|
+
np.reshape(a, (-1, 1))
|
|
44
|
+
for a in [
|
|
45
|
+
np.arange(0, nx - 1),
|
|
46
|
+
np.arange(1, nx),
|
|
47
|
+
nx + np.arange(0, nx - 1), # triangle 1
|
|
48
|
+
np.arange(1, nx),
|
|
49
|
+
nx + np.arange(1, nx),
|
|
50
|
+
nx + np.arange(0, nx - 1), # triangle 2
|
|
51
|
+
]
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
triangles = np.vstack([tmp + k * nx for k in range(ny - 1)])
|
|
55
|
+
triangles.shape = (-1, 3)
|
|
56
|
+
return Surface_mesh(vertices, triangles)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _remove_faces_on_side(surface, side, centroid_value):
|
|
60
|
+
if side < 0:
|
|
61
|
+
removed_faces = [f for f, cv in zip(surface.faces(), centroid_value) if cv > 0]
|
|
62
|
+
else:
|
|
63
|
+
removed_faces = [f for f, cv in zip(surface.faces(), centroid_value) if cv < 0]
|
|
64
|
+
for f in removed_faces:
|
|
65
|
+
surface.remove_face(f)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class TopographyClipper:
|
|
69
|
+
def __init__(self, field, tesselation):
|
|
70
|
+
self.field = field
|
|
71
|
+
self.tesselation = tesselation
|
|
72
|
+
|
|
73
|
+
def clip(self, surface):
|
|
74
|
+
corefine(self.tesselation, surface)
|
|
75
|
+
self.field(surface.centroids())
|
|
76
|
+
_remove_faces_on_side(surface, 1, self.field(surface.centroids()))
|
|
77
|
+
return surface
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class Tesselator:
|
|
81
|
+
def __init__(self, box, shape):
|
|
82
|
+
self.reset(box, shape)
|
|
83
|
+
|
|
84
|
+
def reset(self, box, shape):
|
|
85
|
+
assert all(n > 0 for n in shape)
|
|
86
|
+
nx, ny, nz = shape
|
|
87
|
+
steps = (
|
|
88
|
+
np.linspace(box.xmin, box.xmax, nx),
|
|
89
|
+
np.linspace(box.ymin, box.ymax, ny),
|
|
90
|
+
np.linspace(box.zmin, box.zmax, nz),
|
|
91
|
+
)
|
|
92
|
+
coordinates = np.meshgrid(*steps, indexing="ij")
|
|
93
|
+
points = np.stack(coordinates, axis=-1)
|
|
94
|
+
points.shape = (-1, 3)
|
|
95
|
+
self.box = box
|
|
96
|
+
self.shape = shape
|
|
97
|
+
self.points = points
|
|
98
|
+
|
|
99
|
+
def rescale(self, pts):
|
|
100
|
+
box = self.box
|
|
101
|
+
nx, ny, nz = self.shape
|
|
102
|
+
return pts * np.array(
|
|
103
|
+
[
|
|
104
|
+
(box.xmax - box.xmin) / (nx - 1),
|
|
105
|
+
(box.ymax - box.ymin) / (ny - 1),
|
|
106
|
+
(box.zmax - box.zmin) / (nz - 1),
|
|
107
|
+
]
|
|
108
|
+
) + np.array([box.xmin, box.ymin, box.zmin])
|
|
109
|
+
|
|
110
|
+
def tesselate(f, v):
|
|
111
|
+
return self(f, v)
|
|
112
|
+
|
|
113
|
+
def __call__(self, f, v):
|
|
114
|
+
field_value = f(self.points)
|
|
115
|
+
field_value.shape = self.shape
|
|
116
|
+
isocontour = marching_cubes(field_value, level=v)
|
|
117
|
+
verts, faces, _normals, _values = isocontour
|
|
118
|
+
verts = self.rescale(verts)
|
|
119
|
+
return Surface_mesh(verts, faces)
|
|
120
|
+
|
|
121
|
+
def tesselate_topography(self, model):
|
|
122
|
+
assert model.topography is not None
|
|
123
|
+
topography = model.topography
|
|
124
|
+
zmap = topography.z
|
|
125
|
+
if hasattr(zmap, "shape"):
|
|
126
|
+
return zmap_to_tsurf(zmap, topography.origin, topography.steps)
|
|
127
|
+
return self(topography, 0)
|
|
128
|
+
|
|
129
|
+
def tesselate_faults(self, model, topography=None):
|
|
130
|
+
fault_tesselations = {
|
|
131
|
+
name: self(field, 0) for name, field in model.faults.items()
|
|
132
|
+
}
|
|
133
|
+
for name, surface in fault_tesselations.items():
|
|
134
|
+
if topography:
|
|
135
|
+
surface = topography.clip(surface)
|
|
136
|
+
data = model.faults_data[name]
|
|
137
|
+
assert len(data.potential_data.interfaces) == 1
|
|
138
|
+
fault_points = data.potential_data.interfaces[0]
|
|
139
|
+
for limit_name in data.stops_on:
|
|
140
|
+
try:
|
|
141
|
+
limit_surface = fault_tesselations[limit_name]
|
|
142
|
+
corefine(surface, limit_surface)
|
|
143
|
+
limit_fault = model.faults[limit_name]
|
|
144
|
+
_remove_faces_on_side(
|
|
145
|
+
surface,
|
|
146
|
+
np.mean(limit_fault(fault_points)),
|
|
147
|
+
limit_fault(surface.centroids()),
|
|
148
|
+
)
|
|
149
|
+
except KeyError:
|
|
150
|
+
pass
|
|
151
|
+
return fault_tesselations
|
|
152
|
+
|
|
153
|
+
def tesselate_interfaces(self, model, topography=None):
|
|
154
|
+
fault_tesselations = self.tesselate_faults(model, topography)
|
|
155
|
+
interface_tesselations = {}
|
|
156
|
+
for serie in model.series_info:
|
|
157
|
+
sinfo = model.series_info[serie]
|
|
158
|
+
serie_faults_names = list(sinfo.active_faults)
|
|
159
|
+
serie_faults = [model.faults[name] for name in serie_faults_names]
|
|
160
|
+
faults_limits = [
|
|
161
|
+
[
|
|
162
|
+
serie_faults_names.index(limit)
|
|
163
|
+
for limit in model.faults_data[name].stops_on
|
|
164
|
+
]
|
|
165
|
+
for name in serie_faults_names
|
|
166
|
+
]
|
|
167
|
+
fault_network = gmlib.fault_network.build(serie_faults, faults_limits)
|
|
168
|
+
# for interface in sinfo.interfaces:
|
|
169
|
+
# name, _ = interface
|
|
170
|
+
# interface_tesselations[name] = []
|
|
171
|
+
if fault_network is not None:
|
|
172
|
+
nodes, _roots, evaluations = fault_network
|
|
173
|
+
node_tesselation = [
|
|
174
|
+
fault_tesselations[fault] for fault in serie_faults_names
|
|
175
|
+
]
|
|
176
|
+
# fault drift indexes
|
|
177
|
+
fdi = [sinfo.active_faults[fault] for fault in serie_faults_names]
|
|
178
|
+
interface_patches = {name: [] for name, _ in sinfo.interfaces}
|
|
179
|
+
for evaluation in evaluations:
|
|
180
|
+
drifts = copy.copy(sinfo.drifts)
|
|
181
|
+
for fk, side in enumerate(evaluation):
|
|
182
|
+
if side > 0:
|
|
183
|
+
orientation = OrientedEvaluation.always_positive
|
|
184
|
+
elif side < 0:
|
|
185
|
+
orientation = OrientedEvaluation.always_negative
|
|
186
|
+
else:
|
|
187
|
+
assert side == 0
|
|
188
|
+
orientation = OrientedEvaluation.always_outside
|
|
189
|
+
drifts[fdi[fk]] = potential.make_drift(
|
|
190
|
+
nodes[fk].fault, orientation
|
|
191
|
+
)
|
|
192
|
+
S = potential.alternate_drifts(sinfo.field, drifts)
|
|
193
|
+
for interface in sinfo.interfaces:
|
|
194
|
+
interface_name, interface_value = interface
|
|
195
|
+
patch_tesselation = self(S, interface_value)
|
|
196
|
+
if topography:
|
|
197
|
+
topography.clip(patch_tesselation)
|
|
198
|
+
for fk, side in enumerate(evaluation):
|
|
199
|
+
if side != 0:
|
|
200
|
+
corefine(patch_tesselation, node_tesselation[fk])
|
|
201
|
+
nk = nodes[fk]
|
|
202
|
+
_remove_faces_on_side(
|
|
203
|
+
patch_tesselation,
|
|
204
|
+
side,
|
|
205
|
+
nk.fault(patch_tesselation.centroids()),
|
|
206
|
+
)
|
|
207
|
+
interface_patches[interface_name].append(patch_tesselation)
|
|
208
|
+
for interface_name, patches in interface_patches.items():
|
|
209
|
+
assert patches
|
|
210
|
+
tesselation = Surface_mesh(patches[0])
|
|
211
|
+
for patch in patches[1:]:
|
|
212
|
+
tesselation.join(patch)
|
|
213
|
+
interface_tesselations[interface_name] = tesselation
|
|
214
|
+
else: # serie is not affected by faults
|
|
215
|
+
for interface in sinfo.interfaces:
|
|
216
|
+
interface_name, interface_value = interface
|
|
217
|
+
tesselation = self(sinfo.field, interface_value)
|
|
218
|
+
if topography:
|
|
219
|
+
topography.clip(tesselation)
|
|
220
|
+
interface_tesselations[interface_name] = tesselation
|
|
221
|
+
return fault_tesselations, interface_tesselations
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def tesselate_topography(box, shape, model):
|
|
225
|
+
return Tesselator(box, shape).tesselate_topography(model)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def tesselate_faults(box, shape, model, clip_topography=True):
|
|
229
|
+
tesselator = Tesselator(box, shape)
|
|
230
|
+
topography = (
|
|
231
|
+
TopographyClipper(model.topography, tesselator.tesselate_topography(model))
|
|
232
|
+
if clip_topography
|
|
233
|
+
else None
|
|
234
|
+
)
|
|
235
|
+
return tesselator.tesselate_faults(model, topography)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def tesselate_interfaces(box, shape, model, clip_topography=True):
|
|
239
|
+
tesselator = Tesselator(box, shape)
|
|
240
|
+
topography = (
|
|
241
|
+
TopographyClipper(model.topography, tesselator.tesselate_topography(model))
|
|
242
|
+
if clip_topography
|
|
243
|
+
else None
|
|
244
|
+
)
|
|
245
|
+
faults, interfaces = tesselator.tesselate_interfaces(model, topography)
|
|
246
|
+
if topography:
|
|
247
|
+
assert "topography" not in interfaces
|
|
248
|
+
interfaces["topography"] = topography.tesselation
|
|
249
|
+
return faults, interfaces
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is part of gmlib. It is free software.
|
|
3
|
+
# You can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from .utils.tools import BBox3
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ImplicitElevationSurface:
|
|
14
|
+
def __call__(self, P):
|
|
15
|
+
P = np.reshape(np.asarray(P), (-1, 3))
|
|
16
|
+
res = P[:, 2] - self.evaluate_z(P[:, :2])
|
|
17
|
+
assert res.shape[0] > 0
|
|
18
|
+
if res.shape[0] == 1:
|
|
19
|
+
return res[0]
|
|
20
|
+
return res
|
|
21
|
+
|
|
22
|
+
def underlying_dtm(self):
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ImplicitHorizontalPlane(ImplicitElevationSurface):
|
|
27
|
+
def __init__(self, zvalue):
|
|
28
|
+
self.z = float(zvalue)
|
|
29
|
+
|
|
30
|
+
def evaluate_z(self, P):
|
|
31
|
+
P = np.reshape(np.asarray(P), (-1, 2))
|
|
32
|
+
assert P.shape[0] > 0
|
|
33
|
+
if P.shape[0] == 1:
|
|
34
|
+
return self.z
|
|
35
|
+
return np.tile(self.z, P.shape[0])
|
|
36
|
+
|
|
37
|
+
def bbox(self):
|
|
38
|
+
return BBox3(zmin=self.z, zmax=self.z)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ImplicitDTM(ImplicitElevationSurface):
|
|
42
|
+
def __init__(self, origin, steps, zmap):
|
|
43
|
+
self.origin = np.array(origin, dtype="d")
|
|
44
|
+
assert self.origin.shape == (2,)
|
|
45
|
+
self.steps = np.array(steps, copy=True, dtype="d")
|
|
46
|
+
self.invsteps = np.array([(1.0 / ds) for ds in steps], dtype="d")
|
|
47
|
+
assert self.invsteps.shape == (2,)
|
|
48
|
+
self.z = np.array(zmap, copy=True, dtype="d")
|
|
49
|
+
|
|
50
|
+
def bbox(self):
|
|
51
|
+
Ox, Oy = self.origin
|
|
52
|
+
ny, nx = self.z.shape
|
|
53
|
+
dx, dy = self.steps
|
|
54
|
+
return BBox3(Ox, Ox + nx * dx, Oy, Oy + ny * dy, self.z.min(), self.z.max())
|
|
55
|
+
|
|
56
|
+
def in_square_interpolation(self, P):
|
|
57
|
+
P = np.asarray(P, dtype="d")
|
|
58
|
+
assert P.shape == (2,)
|
|
59
|
+
DX = P - self.origin
|
|
60
|
+
DX *= self.invsteps
|
|
61
|
+
DX[DX < 0] = 0
|
|
62
|
+
i, j = DX
|
|
63
|
+
zmap = self.z
|
|
64
|
+
nx, ny = zmap.shape
|
|
65
|
+
i = min(i, nx - 1)
|
|
66
|
+
j = min(j, ny - 1)
|
|
67
|
+
ii, ij = int(i), int(j)
|
|
68
|
+
i -= ii
|
|
69
|
+
j -= ij
|
|
70
|
+
if j == 0:
|
|
71
|
+
if i == 0:
|
|
72
|
+
return zmap[ii, ij]
|
|
73
|
+
zl, zr = zmap[ii : ii + 2, ij]
|
|
74
|
+
return (1 - i) * zl + i * zr
|
|
75
|
+
if i == 0:
|
|
76
|
+
zl, zr = zmap[ii, ij : ij + 2]
|
|
77
|
+
return (1 - j) * zl + j * zr
|
|
78
|
+
# We divide the square into two triangles
|
|
79
|
+
# to be consistent with the DTM 3D representation
|
|
80
|
+
# and possible topography clipping
|
|
81
|
+
assert 0 < i < 1
|
|
82
|
+
assert 0 < j < 1
|
|
83
|
+
if i + j <= 1: # lower triangle
|
|
84
|
+
z = (1 - i - j) * zmap[ii, ij]
|
|
85
|
+
z += i * zmap[ii + 1, ij]
|
|
86
|
+
z += j * zmap[ii, ij + 1]
|
|
87
|
+
else: # upper triangle
|
|
88
|
+
z = (i + j - 1) * zmap[ii + 1, ij + 1]
|
|
89
|
+
z += (1 - i) * zmap[ii, ij + 1]
|
|
90
|
+
z += (1 - j) * zmap[ii + 1, ij]
|
|
91
|
+
return z
|
|
92
|
+
raise AssertionError()
|
|
93
|
+
|
|
94
|
+
def underlying_dtm(self):
|
|
95
|
+
zmap = self.z
|
|
96
|
+
nx, ny = zmap.shape
|
|
97
|
+
dx, dy = self.steps
|
|
98
|
+
x = np.arange(0, nx * dx, dx) + self.origin[0]
|
|
99
|
+
y = np.arange(0, ny * dy, dy) + self.origin[1]
|
|
100
|
+
xy = np.meshgrid(x, y, indexing="ij")
|
|
101
|
+
vertices = np.empty((nx * ny, 3), dtype=zmap.dtype)
|
|
102
|
+
for i, xi in enumerate(xy):
|
|
103
|
+
vertices[:, i] = xi.ravel()
|
|
104
|
+
vertices[:, -1] = zmap.ravel()
|
|
105
|
+
# Two triangles in the lower left cell
|
|
106
|
+
llc = np.array([[0, ny, 1], [ny, ny + 1, 1]], dtype=np.int64)
|
|
107
|
+
# Triangles stip on the left side
|
|
108
|
+
strip = np.vstack([llc + k for k in range(ny - 1)])
|
|
109
|
+
triangles = np.vstack([strip + ny * k for k in range(nx - 1)])
|
|
110
|
+
return vertices, triangles
|
|
111
|
+
|
|
112
|
+
def evaluate_z(self, P):
|
|
113
|
+
P = np.reshape(np.asarray(P), (-1, 2))
|
|
114
|
+
res = np.array([self.in_square_interpolation(xy) for xy in P])
|
|
115
|
+
try:
|
|
116
|
+
return float(res)
|
|
117
|
+
except TypeError:
|
|
118
|
+
pass
|
|
119
|
+
return res
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def extract_plane(f):
|
|
123
|
+
# we already have read the code character (first character on the line)
|
|
124
|
+
l = f.readline().strip().split()
|
|
125
|
+
point = tuple(float(s) for s in l[:3])
|
|
126
|
+
normal = tuple(float(s) for s in l[3:6])
|
|
127
|
+
# ux = tuple(float(s) for s in l[6:9])
|
|
128
|
+
# uy = tuple(float(s) for s in l[9:]:)
|
|
129
|
+
return point, normal
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def extract_mnt(f, decimals=8):
|
|
133
|
+
# we already have read the code character (first character on the line)
|
|
134
|
+
l = f.readline().strip().split()
|
|
135
|
+
nx, ny = (int(s) for s in l[6:8])
|
|
136
|
+
assert int(l[8]) + 2 == nx
|
|
137
|
+
assert int(l[9]) + 2 == ny
|
|
138
|
+
zmap = []
|
|
139
|
+
line = l[10:]
|
|
140
|
+
while line:
|
|
141
|
+
zmap.append(np.array([float(s) for s in line]))
|
|
142
|
+
line = f.readline().strip().split()
|
|
143
|
+
zmap = np.array(zmap)
|
|
144
|
+
x, y, z = (zmap[:, k::3] for k in range(3))
|
|
145
|
+
|
|
146
|
+
def clean(a):
|
|
147
|
+
a = np.delete(a, [1, nx - 2], axis=0)
|
|
148
|
+
return np.delete(a, [1, ny - 2], axis=1)
|
|
149
|
+
|
|
150
|
+
x, y, z = (clean(a) for a in (x, y, z))
|
|
151
|
+
dx = np.unique(np.round(x[1:, :] - x[:-1, :], decimals))
|
|
152
|
+
assert dx.shape == (1,)
|
|
153
|
+
dy = np.unique(np.round(y[:, 1:] - y[:, :-1], decimals))
|
|
154
|
+
assert dy.shape == (1,)
|
|
155
|
+
xmin, ymin = x.min(), y.min()
|
|
156
|
+
return (xmin, ymin), (float(dx), float(dy)), z
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def sec_extract(filename):
|
|
160
|
+
if os.path.exists(filename):
|
|
161
|
+
with open(filename) as f:
|
|
162
|
+
line = f.readline()
|
|
163
|
+
while not line.startswith("Surfaces"):
|
|
164
|
+
line = f.readline()
|
|
165
|
+
code = int(f.read(1))
|
|
166
|
+
if code == 1:
|
|
167
|
+
point, normal = extract_plane(f)
|
|
168
|
+
assert normal[0] == 0
|
|
169
|
+
assert normal[1] == 0
|
|
170
|
+
return ImplicitHorizontalPlane(point[2])
|
|
171
|
+
if code == 9:
|
|
172
|
+
return ImplicitDTM(*extract_mnt(f))
|
|
173
|
+
raise OSError(filename + " not found")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
from matplotlib import pyplot as plt
|
|
178
|
+
|
|
179
|
+
zmap = np.array([[0, 1], [0, 2]])
|
|
180
|
+
origin = (1, 1)
|
|
181
|
+
steps = (2, 2)
|
|
182
|
+
dtm = ImplicitDTM(origin, steps, zmap)
|
|
183
|
+
nx, ny = 20, 20
|
|
184
|
+
xmin, xmax = 0, 4
|
|
185
|
+
ymin, ymax = 0, 4
|
|
186
|
+
xy = np.meshgrid(np.linspace(xmin, xmax, nx), np.linspace(ymin, ymax, ny))
|
|
187
|
+
x, y = (a.ravel() for a in xy)
|
|
188
|
+
z = np.array([dtm.evaluate_z((xi, yi)) for xi, yi in zip(x, y)])
|
|
189
|
+
z.shape = nx, ny
|
|
190
|
+
plt.gca().set_aspect("equal")
|
|
191
|
+
box = (xmin, xmax, ymin, ymax)
|
|
192
|
+
plt.imshow(np.transpose(z)[::-1], extent=box)
|
|
193
|
+
O = origin
|
|
194
|
+
d = steps
|
|
195
|
+
cell = np.array(
|
|
196
|
+
[O, (O[0] + d[0], O[0]), (O[0] + d[0], O[0] + d[1]), (O[0], O[0] + d[1]), O]
|
|
197
|
+
)
|
|
198
|
+
plt.plot(cell[:, 0], cell[:, 1], "r")
|
|
File without changes
|