Skip to content

Commit

Permalink
Add support for the 'reference' attribute on the vector gm
Browse files Browse the repository at this point in the history
The 'reference' attribute has been available to read/write on
the vector graphics method, but until now has not been implemented.
Now, if it is set, the default behavior of choosing a sensible value
for the arrow length based on the screen space availabe, text size,
etc., will be overridden.  Instead the arrow length will simply be
chosen to match the reference attribute.  Hence, this feature should
be used with care, as it could result in a vector legend with either
very large or very small reference arrow.
  • Loading branch information
scottwittenburg committed Feb 14, 2019
1 parent 22418d8 commit 95cc8ef
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 24 deletions.
10 changes: 9 additions & 1 deletion tests/test_vcs_vectors_scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


class TestVCSVectorScale(basevcstest.VCSBaseTest):
def coreTest(self, scalingType, scale=None, scalerange=None):
def coreTest(self, scalingType, scale=None, scalerange=None, ref=None):
u = cdms2.createAxis(np.array([[1, 1, 0, 1, 1, 1, 0, 1],
[1, 1, 0, 1, 1, 1, 0, 1],
[1, 1, 0, 1, 1, 1, 0, 1],
Expand All @@ -32,11 +32,15 @@ def coreTest(self, scalingType, scale=None, scalerange=None):
gv.scalerange = scalerange
if scale is not None:
gv.scale = scale
if ref is not None:
gv.reference = ref
self.x.plot(u, v, gv, bg=self.bg, ratio=1)
label = scalingType
if (scalingType == "constant"):
label += "_" + str(scale)
outFilename = 'test_vcs_vectors_scale_%s.png' % label
if ref is not None:
outFilename = 'test_vcs_vectors_scale_%s_ref_%s.png' % (label, ref)
self.checkImage(outFilename)
self.x.clear()

Expand All @@ -50,4 +54,8 @@ def testVCSVectorScalingOptions(self):
# test clamping
self.coreTest("constant", scale=0.1)
self.coreTest("constant", scale=2)
# test reference
self.coreTest("constant", scale=0.5, ref=1)
self.coreTest("constant", scale=0.1, ref=5)
self.coreTest("constant", scale=2, ref=0.5)
testVCSVectorScalingOptions.vectors = 1
57 changes: 37 additions & 20 deletions vcs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2838,7 +2838,8 @@ def _createLegendString(value, unit):
def drawVectorLegend(canvas, templateLegend,
linecolor, linetype, linewidth,
unitString, maxNormInVp=1., maxNorm=1.,
minNormInVp=0., minNorm=0., bg=False, render=True):
minNormInVp=0., minNorm=0., bg=False, render=True,
reference=1e20):
"""Draws a legend with vector line/text inside a template legend box
Auto adjust text size to make it fit inside the box
Expand Down Expand Up @@ -2895,14 +2896,27 @@ def drawVectorLegend(canvas, templateLegend,
:param render: Boolean value indicating whether or not to render the new
lines.
:type render: `bool`_
: param reference: Desired length of reference vector in plot legend. The
default is to choose a reasonable size for the max vector in the legend
based on the amount of space available. This behavior can be
overridden by providing the "reference" parameter, and then the size of
the arrow will be computed to match. Be aware this may cause the arrow
to be very large (not fitting nicely within the legend) or very small,
even invisible.
: type reference: `float`_
"""

useReferenceValue = not numpy.allclose(reference, 1e20)

# Figure out space length
text = vcs.createtext(To_source=templateLegend.textorientation,
Tt_source=templateLegend.texttable)
text.x = .5
text.y = .5
maxLegendString = _createLegendString(maxNorm, unitString)
if useReferenceValue:
maxLegendString = _createLegendString(reference, unitString)
text.string = maxLegendString
maxExt = canvas.gettextextent(text)[0]

Expand All @@ -2912,27 +2926,30 @@ def drawVectorLegend(canvas, templateLegend,

# space between line and label - one character long
spaceLength = (maxExt[1] - maxExt[0]) / len(maxLegendString)
# line vector - min 2 and max 15 characters long
minMaxNormLineLength = 2 * spaceLength
maxMaxNormLineLength = 15 * spaceLength
# clamp lineLegth between 2 and 15 spaceLength
maxLineLength = maxNormInVp
if useReferenceValue:
maxLineLength = (maxNormInVp * reference) / maxNorm
else:
# line vector - min 2 and max 15 characters long
minMaxNormLineLength = 2 * spaceLength
maxMaxNormLineLength = 15 * spaceLength
# clamp lineLegth between 2 and 15 spaceLength
ratio = 1.0
if (maxLineLength < minMaxNormLineLength):
while (maxLineLength < minMaxNormLineLength):
maxLineLength *= 2
ratio *= 2
elif (maxLineLength > maxMaxNormLineLength):
while (maxLineLength > maxMaxNormLineLength):
maxLineLength /= 2
ratio /= 2

# update maxLegendString with the clamped value
if (ratio != 1):
maxLegendString = _createLegendString(maxNorm * ratio, unitString)
text.string = maxLegendString
maxExt = canvas.gettextextent(text)[0]
minLineLength = minNormInVp
ratio = 1.0
if (maxLineLength < minMaxNormLineLength):
while (maxLineLength < minMaxNormLineLength):
maxLineLength *= 2
ratio *= 2
elif (maxLineLength > maxMaxNormLineLength):
while (maxLineLength > maxMaxNormLineLength):
maxLineLength /= 2
ratio /= 2

# update maxLegendString with the clamped value
if (ratio != 1):
maxLegendString = _createLegendString(maxNorm * ratio, unitString)
text.string = maxLegendString
maxExt = canvas.gettextextent(text)[0]

maxLegendLength = maxExt[1] - maxExt[0]
maxheight = maxExt[3] - maxExt[2]
Expand Down
2 changes: 1 addition & 1 deletion vcs/vcsvtk/vectorpipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def _plotInternal(self):
minNormInVp *= worldToViewportXScale
vcs.utils.drawVectorLegend(
self._context().canvas, self._template.legend, lcolor, lstyle, lwidth,
unitString, maxNormInVp, maxNorm, minNormInVp, minNorm)
unitString, maxNormInVp, maxNorm, minNormInVp, minNorm, reference=self._gm.reference)

kwargs['xaxisconvert'] = self._gm.xaxisconvert
kwargs['yaxisconvert'] = self._gm.yaxisconvert
Expand Down
7 changes: 5 additions & 2 deletions vcs/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,10 @@ class Gv(vcs.bestMatch):
.. code-block:: python
# Can be an integer or float
# Can be an integer or float. Setting the reference attribute
# overrides the default behavior of picking a reasonable size
# for the vector legend arrow. This may result in a very large
# or very small arrow, depending on the value of vc.reference.
vc.reference=4
"""
__slots__ = [
Expand Down Expand Up @@ -772,7 +775,7 @@ def list(self):
print("datawc_calendar = ", self.datawc_calendar)
print("xaxisconvert = ", self.xaxisconvert)
print("yaxisconvert = ", self.yaxisconvert)
print("line = ", self.line)
print("linetype = ", self.linetype)
print("linecolor = ", self.linecolor)
print("linewidth = ", self.linewidth)
print("scale = ", self.scale)
Expand Down

0 comments on commit 95cc8ef

Please sign in to comment.