8000 Merge pull request #1582 from ianthomas23/1521_linear_tri_interpolator · matplotlib/matplotlib@e4d4984 · GitHub
[go: up one dir, main page]

Skip to content

Commit e4d4984

Browse files
committed
Merge pull request #1582 from ianthomas23/1521_linear_tri_interpolator
Linear tri interpolator
2 parents 24c1417 + 88dc57e commit e4d4984

File tree

12 files changed

+2103
-29
lines changed

12 files changed

+2103
-29
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
2012-12-22 Added classes for interpolation within triangular grids
2+
(LinearTriInterpolator) and to find the triangles in which points
3+
lie (TrapezoidMapTriFinder) to matplotlib.tri module. - IMT
4+
15
2012-12-05 Added MatplotlibDeprecationWarning class for signaling deprecation.
26
Matplotlib developers can use this class as follows:
37

doc/api/tri_api.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ triangular grids
55
:mod:`matplotlib.tri`
66
=====================
77

8-
.. automodule:: matplotlib.tri
9-
:members: Triangulation
8+
.. autoclass:: matplotlib.tri.Triangulation
9+
:members:
1010

11+
.. autoclass:: matplotlib.tri.TriFinder
12+
:members:
13+
14+
.. autoclass:: matplotlib.tri.TrapezoidMapTriFinder
15+
:members: __call__
16+
17+
.. autoclass:: matplotlib.tri.TriInterpolator
18+
:members:
19+
20+
.. autoclass:: matplotlib.tri.LinearTriInterpolator
21+
:members: __call__

doc/users/whats_new.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ using Inkscape for example, while preserving their intended position. For
4949
`svg` please note that you'll have to disable the default text-to-path
5050
conversion (`mpl.rc('svg', fonttype='none')`).
5151

52+
Triangular grid interpolation
53+
-----------------------------
54+
Ian Thomas added classes to perform interpolation within triangular grids
55+
(:class:`~matplotlib.tri.LinearTriInterpolator`) and a utility class to find
56+
the triangles in which points lie (
57+
:class:`~matplotlib.tri.TrapezoidMapTriFinder`).
58+
5259
.. _whats-new-1-2:
5360

5461
new in matplotlib-1.2
@@ -298,7 +305,7 @@ to address the most common layout issues.
298305
fig, axes_list = plt.subplots(2, 1)
299306
for ax in axes_list.flat:
300307
ax.set(xlabel="x-label", ylabel="y-label", title="before tight_layout")
301-
ax.locator_params(nbins=3)
308+
ax.locator_params(nbins=3)
302309

303310
plt.show()
304311

@@ -308,7 +315,7 @@ to address the most common layout issues.
308315
fig, axes_list = plt.subplots(2, 1)
309316
for ax in axes_list.flat:
310317
ax.set(xlabel="x-label", ylabel="y-label", title="after tight_layout")
311-
ax.locator_params(nbins=3)
318+
ax.locator_params(nbins=3)
312319

313320
plt.tight_layout()
314321
plt.show()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Example showing the use of a TriFinder object. As the mouse is moved over the
3+
triangulation, the triangle under the cursor is highlighted and the index of
4+
the triangle is displayed in the plot title.
5+
"""
6+
import matplotlib.pyplot as plt
7+
from matplotlib.tri import Triangulation
8+
from matplotlib.patches import Polygon
9+
import numpy as np
10+
import math
11+
12+
13+
def update_polygon(tri):
14+
if tri == -1:
15+
points = [0, 0, 0]
16+
else:
17+
points = triangulation.triangles[tri]
18+
xs = triangulation.x[points]
19+
ys = triangulation.y[points]
20+
polygon.set_xy(zip(xs, ys))
21+
22+
23+
def motion_notify(event):
24+
if event.inaxes is None:
25+
tri = -1
26+
else:
27+
tri = trifinder(event.xdata, event.ydata)
28+
update_polygon(tri)
29+
plt.title('In triangle %i' % tri)
30+
event.canvas.draw()
31+
32+
33+
# Create a Triangulation.
34+
n_angles = 16
35+
n_radii = 5
36+
min_radius = 0.25
37+
radii = np.linspace(min_radius, 0.95, n_radii)
38+
angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False)
39+
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
40+
angles[:, 1::2] += math.pi / n_angles
41+
x = (radii*np.cos(angles)).flatten()
42+
y = (radii*np.sin(angles)).flatten()
43+
triangulation = Triangulation(x, y)
44+
xmid = x[triangulation.triangles].mean(axis=1)
45+
ymid = y[triangulation.triangles].mean(axis=1)
46+
mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)
47+
triangulation.set_mask(mask)
48+
49+
# Use the triangulation's default TriFinder object.
50+
trifinder = triangulation.get_trifinder()
51+
52+
# Setup plot and callbacks.
53+
plt.subplot(111, aspect='equal')
54+
plt.triplot(triangulation, 'bo-')
55+
polygon = Polygon([[0, 0], [0, 0]], facecolor='y') # dummy data for xs,ys
56+
update_polygon(-1)
57+
plt.gca().add_patch(polygon)
58+
plt.gcf().canvas.mpl_connect('motion_notify_event', motion_notify)
59+
plt.show()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Interpolation from triangular grid to quad grid.
3+
"""
4+
import matplotlib.pyplot as plt
5+
import matplotlib.tri as mtri
6+
import numpy as np
7+
8+
9+
# Create triangulation.
10+
x = np.asarray([0, 1, 2, 3, 0.5, 1.5, 2.5, 1, 2, 1.5])
11+
y = np.asarray([0, 0, 0, 0, 1, 1, 1, 2, 2, 3])
12+
triangles = [[0, 1, 4], [1, 2, 5], [2, 3, 6], [1, 5, 4], [2, 6, 5], [4, 5, 7],
13+
[5, 6, 8], [5, 8, 7], [7, 8, 9]]
14+
triang = mtri.Triangulation(x, y, triangles)
15+
16+
# Interpolate to regularly-spaced quad grid.
17+
z = np.cos(1.5*x)*np.cos(1.5*y)
18+
interp = mtri.LinearTriInterpolator(triang, z)
19+
xi, yi = np.meshgrid(np.linspace(0, 3, 20), np.linspace(0, 3, 20))
20+
zi = interp(xi, yi)
21+
22+
# Plot the triangulation.
23+
plt.subplot(121)
24+
plt.tricontourf(triang, z)
25+
plt.triplot(triang, 'ko-')
26+
plt.title('Triangular grid')
27+
28+
# Plot interpolation to quad grid.
29+
plt.subplot(122)
30+
plt.contourf(xi, yi, zi)
31+
plt.plot(xi, yi, 'k-', alpha=0.5)
32+
plt.plot(xi.T, yi.T, 'k-', alpha=0.5)
33+
plt.title('Linear interpolation')
34+
35+
plt.show()

lib/matplotlib/tests/test_triangulation.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,127 @@ def test_no_modify():
131131
tri = mtri.Triangulation(points[:,0], points[:,1], triangles)
132132
edges = tri.edges
133133
assert_array_equal(old_triangles, triangles)
134+
135+
def test_trifinder():
136+
# Test points within triangles of masked triangulation.
137+
x, y = np.meshgrid(np.arange(4), np.arange(4))
138+
x = x.ravel()
139+
y = y.ravel()
140+
triangles = [[0, 1, 4], [1, 5, 4], [1, 2, 5], [2, 6, 5], [2, 3, 6],
141+
[3, 7, 6], [4, 5, 8], [5, 9, 8], [5, 6, 9], [6, 10, 9],
142+
[6, 7, 10], [7, 11, 10], [8, 9, 12], [9, 13, 12], [9, 10, 13],
143+
[10, 14, 13], [10, 11, 14], [11, 15, 14]]
144+
mask = np.zeros(len(triangles))
145+
mask[8:10] = 1
146+
triang = mtri.Triangulation(x, y, triangles, mask)
147+
trifinder = triang.get_trifinder()
148+
149+
xs = [0.25, 1.25, 2.25, 3.25]
150+
ys = [0.25, 1.25, 2.25, 3.25]
151+
xs, ys = np.meshgrid(xs, ys)
152+
xs = xs.ravel()
153+
ys = ys.ravel()
154+
tris = trifinder(xs, ys)
155+
assert_array_equal(tris, [0, 2, 4, -1, 6, -1, 10, -1,
156+
12, 14, 16, -1, -1, -1, -1, -1])
157+
tris = trifinder(xs-0.5, ys-0.5)
158+
assert_array_equal(tris, [-1, -1, -1, -1, -1, 1, 3, 5,
159+
-1, 7, -1, 11, -1, 13, 15, 17])
160+
161+
# Test points exactly on boundary edges of masked triangulation.
162+
xs = [0.5, 1.5, 2.5, 0.5, 1.5, 2.5, 1.5, 1.5, 0.0, 1.0, 2.0, 3.0]
163+
ys = [0.0, 0.0, 0.0, 3.0, 3.0, 3.0, 1.0, 2.0, 1.5, 1.5, 1.5, 1.5]
164+
tris = trifinder(xs, ys)
165+
assert_array_equal(tris, [0, 2, 4, 13, 15, 17, 3, 14, 6, 7, 10, 11])
166+
167+
# Test points exactly on boundary corners of masked triangulation.
168+
xs = [0.0, 3.0]
169+
ys = [0.0, 3.0]
170+
tris = trifinder(xs, ys)
171+
assert_array_equal(tris, [0, 17])
172+
173+
# Test triangles with horizontal colinear points. These are not valid
174+
# triangulations, but we try to deal with the simplest violations.
175+
delta = 0.0 # If +ve, triangulation is OK, if -ve triangulation invalid,
176+
# if zero have colinear points but should pass tests anyway.
177+
x = [1.5, 0, 1, 2, 3, 1.5, 1.5]
178+
y = [-1, 0, 0, 0, 0, delta, 1]
179+
triangles = [[0, 2, 1], [0, 3, 2], [0, 4, 3], [1, 2, 5], [2, 3, 5],
180+
[3, 4, 5], [1, 5, 6], [4, 6, 5]]
181+
triang = mtri.Triangulation(x, y, triangles)
182+
trifinder = triang.get_trifinder()
183+
184+
xs = [-0.1, 0.4, 0.9, 1.4, 1.9, 2.4, 2.9]
185+
ys = [-0.1, 0.1]
186+
xs, ys = np.meshgrid(xs, ys)
187+
tris = trifinder(xs, ys)
188+
assert_array_equal(tris, [[-1, 0, 0, 1, 1, 2, -1],
189+
[-1, 6, 6, 6, 7, 7, -1]])
190+
191+
# Test triangles with vertical colinear points. These are not valid
192+
# triangulations, but we try to deal with the simplest violations.
193+
delta = 0.0 # If +ve, triangulation is OK, if -ve triangulation invalid,
194+
# if zero have colinear points but should pass tests anyway.
195+
x = [-1, -delta, 0, 0, 0, 0, 1]
196+
y = [1.5, 1.5, 0, 1, 2, 3, 1.5]
197+
triangles = [[0, 1, 2], [0, 1, 5], [1, 2, 3], [1, 3, 4], [1, 4, 5],
198+
[2, 6, 3], [3, 6, 4], [4, 6, 5]]
199+
triang = mtri.Triangulation(x, y, triangles)
200+
trifinder = triang.get_trifinder()
201+
202+
xs = [-0.1, 0.1]
203+
ys = [-0.1, 0.4, 0.9, 1.4, 1.9, 2.4, 2.9]
204+
xs, ys = np.meshgrid(xs, ys)
205+
tris = trifinder(xs, ys)
206+
assert_array_equal(tris, [[-1, -1], [0, 5], [0, 5], [0, 6], [1, 6], [1, 7],
207+
[-1, -1]])
208+
209+
# Test that changing triangulation by setting a mask causes the trifinder
210+
# to be reinitialised.
211+
x = [0, 1, 0, 1]
212+
y = [0, 0, 1, 1]
213+
triangles = [[0, 1, 2], [1, 3, 2]]
214+
triang = mtri.Triangulation(x, y, triangles)
215+
trifinder = triang.get_trifinder()
216+
217+
xs = [-0.2, 0.2, 0.8, 1.2]
218+
ys = [0.5, 0.5, 0.5, 0.5]
219+
tris = trifinder(xs, ys)
220+
assert_array_equal(tris, [-1, 0, 1, -1])
221+
222+
triang.set_mask([1, 0])
223+
assert_equal(trifinder, triang.get_trifinder())
224+
tris = trifinder(xs, ys)
225+
assert_array_equal(tris, [-1, -1, 1, -1])
226+
227+
def test_triinterp():
228+
# Test points within triangles of masked triangulation.
229+
x, y = np.meshgrid(np.arange(4), np.arange(4))
230+
x = x.ravel()
231+
y = y.ravel()
232+
z = 1.23*x - 4.79*y
233+
triangles = [[0, 1, 4], [1, 5, 4], [1, 2, 5], [2, 6, 5], [2, 3, 6],
234+
[3, 7, 6], [4, 5, 8], [5, 9, 8], [5, 6, 9], [6, 10, 9],
235+
[6, 7, 10], [7, 11, 10], [8, 9, 12], [9, 13, 12], [9, 10, 13],
236+
[10, 14, 13], [10, 11, 14], [11, 15, 14]]
237+
mask = np.zeros(len(triangles))
238+
mask[8:10] = 1
239+
triang = mtri.Triangulation(x, y, triangles, mask)
240+
linear_interp = mtri.LinearTriInterpolator(triang, z)
241+
242+
xs = np.linspace(0.25, 2.75, 6)
243+
ys = [0.25, 0.75, 2.25, 2.75]
244+
xs, ys = np.meshgrid(xs, ys)
245+
xs = xs.ravel()
246+
ys = ys.ravel()
247+
zs = linear_interp(xs, ys)
248+
assert_array_almost_equal(zs, (1.23*xs - 4.79*ys))
249+
250+
# Test points outside triangulation.
251+
xs = [-0.25, 1.25, 1.75, 3.25]
252+
ys = xs
253+
xs, ys = np.meshgrid(xs, ys)
254+
xs = xs.ravel()
255+
ys = ys.ravel()
256+
zs = linear_interp(xs, ys)
257+
assert_array_equal(zs.mask, [True]*16)

lib/matplotlib/tri/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
from __future__ import print_function
66
from triangulation import *
77
from tricontour import *
8+
from trifinder import *
9+
from triinterpolate import *
810
from tripcolor import *
911
from triplot import *

0 commit comments

Comments
 (0)
0