-
Notifications
You must be signed in to change notification settings - Fork 68
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
Changes from 12 commits
1129e5b
9589f12
9648e96
2a4089e
2513a0c
e1d77e4
f00bea8
db669a6
06bc68d
669a96d
bd610ac
b69d09d
c30239c
9a2d02d
a7f5b86
b59d84e
c8befa3
e3f7ede
8c05ac4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: | ||
|
@@ -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()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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', | ||
|
@@ -244,6 +244,9 @@ class Gv(object): | |
'type', | ||
'reference', | ||
'colormap', | ||
'scaleoptions', | ||
'scaletype', | ||
'scalerange', | ||
'_name', | ||
'_xaxisconvert', | ||
'_yaxisconvert', | ||
|
@@ -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 | ||
|
@@ -528,6 +535,26 @@ def _setalignment(self, value): | |
self._alignment = value | ||
alignment = property(_getalignment, _setalignment) | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
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'): | ||
# # | ||
########################################################### | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
||
########################################################################## | ||
# # | ||
|
@@ -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)) | ||
|
@@ -814,5 +850,5 @@ def script(self, script_filename=None, mode=None): | |
|
||
|
||
############################################################################### | ||
# END OF FILE # | ||
# END OF FILE # | ||
############################################################################### |
There was a problem hiding this comment.
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