Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute vector scaling correctly #1988

Merged
merged 19 commits into from
May 31, 2016
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Packages/vcs/vcs/VCS_validation_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,18 @@ def checkListOfNumbers(self, name, value, minvalue=None,
return list(value)


def checkValidOption(self, name, value, options):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could have sed checkInStringList, it would have made it more consistent with the rest of API where you can pass string or number.
see:
https://github.com/UV-CDAT/uvcdat/blob/b69d09d59a2d07853c5f4f2fe6987610d141b07a/Packages/vcs/vcs/VCS_validation_functions.py#L1321

checkName(self, name, value)
if value not in options:
VCS_validation_functions.checkedRaise(
self,
value,
ValueError,
'Invalid value '+ value + '. Valid options are: ' +
','.join(self.scaleoptions))
return value


def checkFont(self, name, value):
if (value is None):
pass
Expand Down
77 changes: 69 additions & 8 deletions Packages/vcs/vcs/vcsvtk/vectorpipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ def _plotInternal(self):
# Preserve time and z axis for plotting these inof in rendertemplate
projection = vcs.elements["projection"][self._gm.projection]
taxis = self._originalData1.getTime()
scaleFactor = 1.0

if self._originalData1.ndim > 2:
zaxis = self._originalData1.getAxis(-3)
else:
Expand Down Expand Up @@ -84,24 +86,83 @@ def _plotInternal(self):
arrow.SetOutputPointsPrecision(vtk.vtkAlgorithm.DOUBLE_PRECISION)
arrow.FilledOff()


polydata = self._vtkPolyDataFilter.GetOutput()
vectors = polydata.GetPointData().GetVectors()
vectorsRangeX = vectors.GetRange(0)
vectorsRangeY = vectors.GetRange(1)

if self._gm.scaletype == 'constant' or\
self._gm.scaletype == 'constantNNormalize' or\
self._gm.scaletype == 'constantNLinear':
scaleFactor = scale * 2.0 * self._gm.scale
else:
scaleFactor = 1.0

glyphFilter = vtk.vtkGlyph2D()
glyphFilter.SetInputConnection(self._vtkPolyDataFilter.GetOutputPort())
glyphFilter.SetInputData(polydata)
glyphFilter.SetInputArrayToProcess(1, 0, 0, 0, "vector")
glyphFilter.SetSourceConnection(arrow.GetOutputPort())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the original problem was triggered by this code. By clamping everything to 1, you end-up with vectors of the same size. I'll post an image generated with this code commented out.

glyphFilter.SetVectorModeToUseVector()

# Rotate arrows to match vector data:
glyphFilter.OrientOn()
glyphFilter.ScalingOn()

# Scale to vector magnitude:
glyphFilter.SetScaleModeToScaleByVector()
glyphFilter.SetScaleFactor(scale * 2.0 * self._gm.scale)

# These are some unfortunately named methods. It does *not* clamp the
# scale range to [min, max], but rather remaps the range
# [min, max] --> [0, 1].
glyphFilter.ClampingOn()
glyphFilter.SetRange(0.01, 1.0)
if self._gm.scaletype == 'normalize' or self._gm.scaletype == 'linear' or\
self._gm.scaletype == 'constantNNormalize' or self._gm.scaletype == 'constantNLinear':

# Find the min and max vector magnitudes
maxNorm = vectors.GetMaxNorm()

if maxNorm == 0:
maxNorm = 1.0

if self._gm.scaletype == 'normalize' or self._gm.scaletype == 'constantNNormalize':
scaleFactor /= maxNorm

if self._gm.scaletype == 'linear' or self._gm.scaletype == 'constantNLinear':
minNorm = None
maxNorm = None

noOfComponents = vectors.GetNumberOfComponents()
for i in range(0, vectors.GetNumberOfTuples()):
norm = vtk.vtkMath.Norm(vectors.GetTuple(i), noOfComponents)

if (minNorm is None or norm < minNorm):
minNorm = norm
if (maxNorm is None or norm > maxNorm):
maxNorm = norm

if maxNorm == 0:
maxNorm = 1.0

scalarArray = vtk.vtkDoubleArray()
scalarArray.SetNumberOfComponents(1)
scalarArray.SetNumberOfValues(vectors.GetNumberOfTuples())

oldRange = maxNorm - minNorm
oldRange = 1.0 if oldRange == 0.0 else oldRange

# New range min, max.
newRangeValues = self._gm.scalerange
newRange = newRangeValues[1] - newRangeValues[0]

for i in range(0, vectors.GetNumberOfTuples()):
norm = vtk.vtkMath.Norm(vectors.GetTuple(i), noOfComponents)
newValue = (((norm - minNorm) * newRange) / oldRange) + newRangeValues[0]
scalarArray.SetValue(i, newValue)
polydata.GetPointData().SetScalars(scalarArray)

# Scale to vector magnitude:
# NOTE: Currently we compute our own scaling factor since VTK does
# it by clamping the values > max to max and values < min to min
# and not remap the range.
glyphFilter.SetScaleModeToScaleByScalar()

glyphFilter.SetScaleFactor(scaleFactor)

mapper = vtk.vtkPolyDataMapper()

Expand Down
106 changes: 71 additions & 35 deletions Packages/vcs/vcs/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def process_src(nm, code):
class Gv(object):

"""
Class: Gv # Vector
Class: Gv # Vector

Description of Gv Class:
The vector graphics method displays a vector plot of a 2D vector field. Vectors
Expand All @@ -145,76 +145,76 @@ class Gv(object):
entry.

Other Useful Functions:
a=vcs.init() # Constructor
a.show('vector') # Show predefined vector graphics methods
a.show('line') # Show predefined VCS line objects
a.setcolormap("AMIP") # Change the VCS color Map
a.vector(s1, s2, v,'default') # Plot data 's1', and 's2' with vector 'v'
a=vcs.init() # Constructor
a.show('vector') # Show predefined vector graphics methods
a.show('line') # Show predefined VCS line objects
a.setcolormap("AMIP") # Change the VCS color Map
a.vector(s1, s2, v,'default') # Plot data 's1', and 's2' with vector 'v'
and 'default' template
a.update() # Updates the VCS Canvas at user's request
a.mode=1, or 0 # If 1, then automatic update, else if
a.update() # Updates the VCS Canvas at user's request
a.mode=1, or 0 # If 1, then automatic update, else if
0, then use update function to
update the VCS Canvas.

Example of Use:
a=vcs.init()
To Create a new instance of vector use:
vc=a.createvector('new','quick') # Copies content of 'quick' to 'new'
vc=a.createvector('new') # Copies content of 'default' to 'new'
vc=a.createvector('new','quick') # Copies content of 'quick' to 'new'
vc=a.createvector('new') # Copies content of 'default' to 'new'

To Modify an existing vector use:
vc=a.getvector('AMIP_psl')

vc.list() # Will list all the vector attribute values
vc.projection='linear' # Can only be 'linear'
vc.list() # Will list all the vector attribute values
vc.projection='linear' # Can only be 'linear'
lon30={-180:'180W',-150:'150W',0:'Eq'}
vc.xticlabels1=lon30
vc.xticlabels2=lon30
vc.xticlabels(lon30, lon30) # Will set them both
vc.xticlabels(lon30, lon30) # Will set them both
vc.xmtics1=''
vc.xmtics2=''
vc.xmtics(lon30, lon30) # Will set them both
vc.xmtics(lon30, lon30) # Will set them both
vc.yticlabels1=lat10
vc.yticlabels2=lat10
vc.yticlabels(lat10, lat10) # Will set them both
vc.yticlabels(lat10, lat10) # Will set them both
vc.ymtics1=''
vc.ymtics2=''
vc.ymtics(lat10, lat10) # Will set them both
vc.ymtics(lat10, lat10) # Will set them both
vc.datawc_y1=-90.0
vc.datawc_y2=90.0
vc.datawc_x1=-180.0
vc.datawc_x2=180.0
vc.datawc(-90, 90, -180, 180) # Will set them all
vc.datawc(-90, 90, -180, 180) # Will set them all
xaxisconvert='linear'
yaxisconvert='linear'
vc.xyscale('linear', 'area_wt') # Will set them both
vc.xyscale('linear', 'area_wt') # Will set them both

Specify the line style:
vc.line=0 # Same as vc.line='solid'
vc.line=1 # Same as vc.line='dash'
vc.line=2 # Same as vc.line='dot'
vc.line=3 # Same as vc.line='dash-dot'
vc.line=4 # Same as vc.line='long-dot'
vc.line=0 # Same as vc.line='solid'
vc.line=1 # Same as vc.line='dash'
vc.line=2 # Same as vc.line='dot'
vc.line=3 # Same as vc.line='dash-dot'
vc.line=4 # Same as vc.line='long-dot'

Specify the line color of the vectors:
vc.linecolor=16 # Color range: 16 to 230, default line color is black
vc.linewidth=1 # Width range: 1 to 100, default size is 1
vc.linecolor=16 # Color range: 16 to 230, default line color is black
vc.linewidth=1 # Width range: 1 to 100, default size is 1

Specify the vector scale factor:
vc.scale=2.0 # Can be an integer or float
vc.scale=2.0 # Can be an integer or float

Specify the vector alignment:
vc.alignment=0 # Same as vc.alignment='head'
vc.alignment=1 # Same as vc.alignment='center'
vc.alignment=2 # Same as vc.alignment='tail'
vc.alignment=0 # Same as vc.alignment='head'
vc.alignment=1 # Same as vc.alignment='center'
vc.alignment=2 # Same as vc.alignment='tail'

Specify the vector type:
vc.type=0 # Same as vc.type='arrow head'
vc.type=1 # Same as vc.type='wind barbs'
vc.type=2 # Same as vc.type='solid arrow head'
vc.type=0 # Same as vc.type='arrow head'
vc.type=1 # Same as vc.type='wind barbs'
vc.type=2 # Same as vc.type='solid arrow head'

Specify the vector reference:
vc.reference=4 # Can be an integer or float
vc.reference=4 # Can be an integer or float
"""
__slots__ = [
'name',
Expand Down Expand Up @@ -244,6 +244,9 @@ class Gv(object):
'type',
'reference',
'colormap',
'scaleoptions',
'scaletype',
'scalerange',
'_name',
'_xaxisconvert',
'_yaxisconvert',
Expand All @@ -270,9 +273,13 @@ class Gv(object):
'_type',
'_reference',
'_colormap',
'_scaleoptions',
'_scaletype',
'_scalerange',
]

colormap = VCS_validation_functions.colormap
scaleoptions = ('off', 'constant', 'normalize', 'linear', 'constantNNormalize', 'constantNLinear')

def _getname(self):
return self._name
Expand Down Expand Up @@ -528,6 +535,26 @@ def _setalignment(self, value):
self._alignment = value
alignment = property(_getalignment, _setalignment)


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aashish24 I would rather the set/get function to be in VCS_Validation functions, just in case we ever need to re-use it for something else and have here

scalerange = VCS_validation_functions.scalerange

see colormap above for example.

def _getscaletype(self):
return self._scaletype

def _setscaletype(self, value):
value = VCS_validation_functions.checkValidOption(self, 'scaletype',
value, self.scaleoptions)
self._scaletype = value
scaletype = property(_getscaletype, _setscaletype)

def _getscalerange(self):
return self._scalerange

def _setscalerange(self, value):
value = VCS_validation_functions.checkListOfNumbers(self, 'scalerange',
value, minvalue=0.0, minelements=2, maxelements=2)
self._scalerange = value
scalerange = property(_getscalerange, _setscalerange)


def __init__(self, Gv_name, Gv_name_src='default'):
# #
###########################################################
Expand Down Expand Up @@ -568,6 +595,8 @@ def __init__(self, Gv_name, Gv_name_src='default'):
self._datawc_timeunits = "days since 2000"
self._datawc_calendar = 135441
self._colormap = None
self._scaletype = self.scaleoptions[4]
self._scalerange = [0.1, 1.0]
else:
if isinstance(Gv_name_src, Gv):
Gv_name_src = Gv_name_src.name
Expand All @@ -583,7 +612,9 @@ def __init__(self, Gv_name, Gv_name_src='default'):
'datawc_x2', 'xaxisconvert', 'yaxisconvert',
'line', 'linecolor', 'linewidth',
'datawc_timeunits', 'datawc_calendar', 'colormap',
'scale', 'alignment', 'type', 'reference']:
'scale', 'alignment', 'type', 'reference', 'scaletype',
'scalerange']:

setattr(self, att, getattr(src, att))
# Ok now we need to stick in the elements
vcs.elements["vector"][Gv_name] = self
Expand Down Expand Up @@ -660,6 +691,8 @@ def list(self):
print "alignment = ", self.alignment
print "type = ", self.type
print "reference = ", self.reference
print "scaletype = ", self.scaletype
print "scalerange = ", self.scalerange
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aashish24 I don't see the code to update the json, I dont think you need to update it (it should pick it up automagcally) but can you double check that json dump of a vector graphic method now contain these attributes as well? Probably worth adding a test. Actually are the json dump test still passing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, let me run all the tests locally here just to make sure. thanks @doutriaux1


##########################################################################
# #
Expand Down Expand Up @@ -798,6 +831,9 @@ def script(self, script_filename=None, mode=None):
fp.write("%s.linecolor = %s\n" % (unique_name, self.linecolor))
fp.write("%s.linewidth = %s\n" % (unique_name, self.linewidth))
fp.write("%s.scale = %s\n" % (unique_name, self.scale))
fp.write("%s.scaletype = %s\n" % (unique_name, self.scaletype))
fp.write("%s.scalerange = %s\n" % (unique_name, self.scalerange))
fp.write("%s.scaleoptions = %s\n" % (unique_name, self.scaleoptions))
fp.write("%s.alignment = '%s'\n" % (unique_name, self.alignment))
fp.write("%s.type = '%s'\n" % (unique_name, self.type))
fp.write("%s.reference = %s\n\n" % (unique_name, self.reference))
Expand All @@ -814,5 +850,5 @@ def script(self, script_filename=None, mode=None):


###############################################################################
# END OF FILE #
# END OF FILE #
###############################################################################
10 changes: 10 additions & 0 deletions testing/vcs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,16 @@ cdat_add_test(test_vcs_settings_color_name_rgba
${cdat_SOURCE_DIR}/testing/vcs/test_vcs_vectors_robinson_wrap.py
"${BASELINE_DIR}/test_vcs_vectors_robinson_wrap.png"
)
cdat_add_test(test_vcs_vectors_scale_options
"${PYTHON_EXECUTABLE}"
${cdat_SOURCE_DIR}/testing/vcs/test_vcs_vectors_scale_options.py
"${BASELINE_DIR}/test_vcs_vector_scale_options_off.png"
"${BASELINE_DIR}/test_vcs_vector_scale_options_constant.png"
"${BASELINE_DIR}/test_vcs_vector_scale_options_linear.png"
"${BASELINE_DIR}/test_vcs_vector_scale_options_normalize.png"
"${BASELINE_DIR}/test_vcs_vector_scale_options_constantNLinear.png"
"${BASELINE_DIR}/test_vcs_vector_scale_options_constantNNormalize.png"
)
endif()
endif()

Expand Down
Loading