Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 283f358

Browse files
author
Brian Mattern
committed
Fix manual contour label positions on sparse contours
Prior to this, contour labels were positioned at the nearest contour vertex, which in some cases (e.g., straight contours) could be quite distant from the desired location. This centers the label on the closest point on contour itself (using linear interpolation between vertices).
1 parent ca476df commit 283f358

File tree

1 file changed

+72
-6
lines changed

1 file changed

+72
-6
lines changed

lib/matplotlib/contour.py

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,18 @@ def add_label_near(self, x, y, inline=True, inline_spacing=5,
572572
conmin, segmin, imin, xmin, ymin = self.find_nearest_contour(
573573
x, y, self.labelIndiceList)[:5]
574574

575+
# The calc_label_rot_and_inline routine requires that (xmin,ymin)
576+
# be a vertex in the path. So, if it isn't, add a vertex here
577+
paths = self.collections[conmin].get_paths()
578+
lc = paths[segmin].vertices
579+
if transform:
580+
xcmin = transform.inverted().transform([xmin,ymin])
581+
else:
582+
xcmin = np.array([xmin,ymin])
583+
if not np.allclose(xcmin, lc[imin]):
584+
lc = np.r_[lc[:imin], np.array(xcmin)[None,:], lc[imin:]]
585+
paths[segmin] = mpath.Path(lc)
586+
575587
# Get index of nearest level in subset of levels used for labeling
576588
lmin = self.labelIndiceList.index(conmin)
577589

@@ -673,6 +685,59 @@ def labels(self, inline, inline_spacing):
673685
del paths[:]
674686
paths.extend(additions)
675687

688+
def _find_closest_point_on_leg(p1, p2, p0):
689+
'''find closest point to p0 on line segment connecting p1 and p2'''
690+
691+
# handle degenerate case
692+
if np.all(p2 == p1):
693+
d = np.sum((p0 - p1)**2)
694+
return d, p1
695+
696+
d21 = p2 - p1
697+
d01 = p0 - p1
698+
699+
# project on to line segment to find closest point
700+
proj = np.dot(d01, d21) / np.dot(d21, d21)
701+
if proj < 0: proj = 0
702+
if proj > 1: proj = 1
703+
pc = p1 + proj * d21
704+
705+
# find squared distance
706+
d = np.sum((pc-p0)**2)
707+
708+
return d, pc
709+
710+
def _find_closest_point_on_path(lc, point):
711+
'''
712+
lc: coordinates of vertices
713+
point: coordinates of test point
714+
'''
715+
716+
# find index of closest vertex for this segment
717+
ds = np.sum((lc - point[None,:])**2, 1)
718+
imin = np.argmin(ds)
719+
720+
dmin = 1e10
721+
xcmin = None
722+
legmin = (None,None)
723+
724+
closed = mlab.is_closed_polygon(lc)
725+
726+
# build list of legs before and after this vertex
727+
legs = []
728+
if imin > 0 or closed:
729+
legs.append(((imin-1)%len(lc),imin))
730+
if imin < len(lc) - 1 or closed:
731+
legs.append((imin,(imin+1)%len(lc)))
732+
733+
for leg in legs:
734+
d, xc = _find_closest_point_on_leg(lc[leg[0]], lc[leg[1]], point)
735+
if d < dmin:
736+
dmin = d
737+
xcmin = xc
738+
legmin = leg
739+
740+
return (dmin, xcmin, legmin)
676741

677742
class ContourSet(cm.ScalarMappable, ContourLabeler):
678743
"""
@@ -1262,26 +1327,27 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
12621327
xmin = None
12631328
ymin = None
12641329

1330+
point = np.array([x,y])
1331+
12651332
for icon in indices:
12661333
con = self.collections[icon]
12671334
trans = con.get_transform()
12681335
paths = con.get_paths()
1336+
12691337
for segNum, linepath in enumerate(paths):
12701338
lc = linepath.vertices
1271-
12721339
# transfer all data points to screen coordinates if desired
12731340
if pixel:
12741341
lc = trans.transform(lc)
12751342

1276-
ds = (lc[:, 0] - x) ** 2 + (lc[:, 1] - y) ** 2
1277-
d = min(ds)
1343+
d, xc, leg = _find_closest_point_on_path(lc, point)
12781344
if d < dmin:
12791345
dmin = d
12801346
conmin = icon
12811347
segmin = segNum
1282-
imin = mpl.mlab.find(ds == d)[0]
1283-
xmin = lc[imin, 0]
1284-
ymin = lc[imin, 1]
1348+
imin = leg[1]
1349+
xmin = xc[0]
1350+
ymin = xc[1]
12851351

12861352
return (conmin, segmin, imin, xmin, ymin, dmin)
12871353

0 commit comments

Comments
 (0)