Commit 78e7ff7e authored by Utkarsh Ayachit's avatar Utkarsh Ayachit

Update PythonAnnotationFilter.

Updating PythonAnnotationFilter to work with newer numpy support. Since
the calculator.py API was changed, updating extract_selection module as
well.

Change-Id: I3fe663455b506bd75e7452773af8fab23c0dc422
parent f9d53e7d
......@@ -84,7 +84,7 @@ annotation.UpdatePipeline(time)
annotation.SMProxy.UpdatePropertyInformation()
value = annotation.SMProxy.GetProperty('AnnotationValue').GetElement(0)
expected = "7 0.000700 [0, 0.00429999]"
expected = "7 0.000700 (0.0, 0.004299988504499197)"
if not equal(value, expected):
errors += 1
......@@ -96,7 +96,7 @@ annotation.UpdatePipeline(time)
annotation.SMProxy.UpdatePropertyInformation()
value = annotation.SMProxy.GetProperty('AnnotationValue').GetElement(0)
expected = "27 0.002700 [0, 0.00429999]"
expected = "27 0.002700 (0.0, 0.004299988504499197)"
if not equal(value, expected):
errors += 1
......@@ -108,7 +108,7 @@ annotation.UpdatePipeline(time)
annotation.SMProxy.UpdatePropertyInformation()
value = annotation.SMProxy.GetProperty('AnnotationValue').GetElement(0)
expected = "43 0.004300 [0, 0.00429999]"
expected = "43 0.004300 (0.0, 0.004299988504499197)"
if not equal(value, expected):
errors += 1
......
......@@ -49,7 +49,7 @@ time = timesteps[5]
# Annotation filter
annotation = PythonAnnotation()
annotation.Expression = '"%f %f %f" % (inputMB[0].FieldData["XMOM"][t_index], inputMB[0].FieldData["YMOM"][t_index], inputMB[0].FieldData["ZMOM"][t_index])'
annotation.Expression = '"%f %f %f" % (XMOM[t_index], YMOM[t_index], ZMOM[t_index])'
# Update time and trigger pipeline execution
time = timesteps[5]
......@@ -84,7 +84,7 @@ annotation.UpdatePipeline(time)
annotation.SMProxy.UpdatePropertyInformation()
value = annotation.SMProxy.GetProperty('AnnotationValue').GetElement(0)
expected = "7 0.000700 [0, 0.00429999]"
expected = "7 0.000700 (0.0, 0.004299988504499197)"
if not equal(value, expected):
errors += 1
......@@ -96,7 +96,7 @@ annotation.UpdatePipeline(time)
annotation.SMProxy.UpdatePropertyInformation()
value = annotation.SMProxy.GetProperty('AnnotationValue').GetElement(0)
expected = "27 0.002700 [0, 0.00429999]"
expected = "27 0.002700 (0.0, 0.004299988504499197)"
if not equal(value, expected):
errors += 1
......@@ -108,7 +108,7 @@ annotation.UpdatePipeline(time)
annotation.SMProxy.UpdatePropertyInformation()
value = annotation.SMProxy.GetProperty('AnnotationValue').GetElement(0)
expected = "43 0.004300 [0, 0.00429999]"
expected = "43 0.004300 (0.0, 0.004299988504499197)"
if not equal(value, expected):
errors += 1
......
......@@ -23,6 +23,7 @@
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
#include "vtkStdString.h"
#include <assert.h>
#include <map>
......@@ -33,28 +34,33 @@
vtkStandardNewMacro(vtkPythonAnnotationFilter);
//----------------------------------------------------------------------------
vtkPythonAnnotationFilter::vtkPythonAnnotationFilter()
: Expression(NULL),
ComputedAnnotationValue(NULL),
ArrayAssociation(vtkDataObject::FIELD),
DataTimeValid(false),
DataTime(0.0),
NumberOfTimeSteps(0),
TimeSteps(NULL),
TimeRangeValid(false),
CurrentInputDataObject(NULL)
{
this->SetNumberOfInputPorts(1);
this->PythonExpression = 0;
this->TimeInformations = 0;
this->AnnotationValue = 0;
this->TimeRange[0] = this->TimeRange[1] = 0.0;
}
//----------------------------------------------------------------------------
vtkPythonAnnotationFilter::~vtkPythonAnnotationFilter()
{
this->SetTimeInformations(0);
this->SetPythonExpression(0);
this->SetAnnotationValue(0);
this->SetExpression(0);
this->SetComputedAnnotationValue(0);
}
//----------------------------------------------------------------------------
void vtkPythonAnnotationFilter::SetAnnotationValue(const char* value)
void vtkPythonAnnotationFilter::SetComputedAnnotationValue(const char* value)
{
delete [] this->AnnotationValue;
delete [] this->ComputedAnnotationValue;
// SystemTools handles NULL strings.
this->AnnotationValue = vtksys::SystemTools::DuplicateString(value);
this->ComputedAnnotationValue = vtksys::SystemTools::DuplicateString(value);
// don't call this->Modified. This method gets called in RequestData().
}
......@@ -64,81 +70,61 @@ int vtkPythonAnnotationFilter::RequestData(
vtkInformationVector** inputVector,
vtkInformationVector* outputVector)
{
// Meta-data
vtkDataObject* input = vtkDataObject::GetData(inputVector[0]);
vtkInformation* inputInfo = input ? input->GetInformation() : 0;
vtkInformation* outInfo = this->GetExecutive()->GetOutputInformation(0);
// Extract time informations
vtkDataObject* input = vtkDataObject::GetData(inputVector[0], 0);
assert(input != NULL);
// initialize variables.
this->DataTimeValid = false;
this->DataTime = 0.0;
this->TimeSteps = NULL;
this->NumberOfTimeSteps = 0;
this->TimeRangeValid = false;
this->TimeRange[0] = this->TimeRange[1] = 0.0;
this->SetComputedAnnotationValue(NULL);
this->CurrentInputDataObject = input;
// Extract time information
vtksys_ios::ostringstream timeInfo;
if(inputInfo)
if (vtkInformation* dataInformation = input->GetInformation())
{
timeInfo << ",";
// Time
if(inputInfo->Has(vtkDataObject::DATA_TIME_STEP()))
{
double time = inputInfo->Get(vtkDataObject::DATA_TIME_STEP());
timeInfo << time << ", ";
}
else
{
timeInfo << "0" << ", ";
}
// TimeSteps
if(outInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_STEPS()))
{
const double* timeSteps =
outInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
int len = outInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
timeInfo << "[";
for(int i=0; i < len; i++)
{
timeInfo << timeSteps[i] << ", ";
}
timeInfo << "], ";
}
else
if (dataInformation->Has(vtkDataObject::DATA_TIME_STEP()))
{
timeInfo << "[0, 1], ";
this->DataTimeValid = true;
this->DataTime = dataInformation->Get(vtkDataObject::DATA_TIME_STEP());
}
}
// TimeRange
if(outInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_RANGE()))
{
double range[2];
outInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), range);
timeInfo << "[" << range[0] << ", " << range[1] << "]";
}
else
{
timeInfo << "[0, 1]";
}
vtkInformation* inputInfo = inputVector[0]->GetInformationObject(0);
if (inputInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_STEPS()))
{
this->NumberOfTimeSteps = inputInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
this->TimeSteps = inputInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
}
this->SetTimeInformations(timeInfo.str().c_str());
this->SetAnnotationValue(NULL);
if (inputInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_RANGE()))
{
this->TimeRangeValid = true;
inputInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), this->TimeRange);
}
// Execute the python script to process and generate the annotation
this->EvaluateExpression();
// Make sure a valid AnnotationValue is available
if(this->AnnotationValue == NULL || strlen(this->AnnotationValue) == 0)
// Make sure a valid ComputedAnnotationValue is available
if (this->ComputedAnnotationValue == NULL)
{
this->SetAnnotationValue("Write a python expression like: 'Time index: %i' % t_index");
this->SetComputedAnnotationValue("(error)");
}
// Update the output data
vtkStringArray* data = vtkStringArray::New();
data->SetName("Text");
data->SetNumberOfComponents(1);
data->InsertNextValue(this->AnnotationValue);
data->InsertNextValue(this->ComputedAnnotationValue);
vtkTable* output = vtkTable::GetData(outputVector);
output->AddColumn(data);
data->FastDelete();
this->CurrentInputDataObject = NULL;
return 1;
}
......@@ -151,25 +137,32 @@ int vtkPythonAnnotationFilter::FillInputPortInformation(
}
//----------------------------------------------------------------------------
void vtkPythonAnnotationFilter::EvaluateExpression()
static std::string vtkGetReferenceAsString(void* ref)
{
// Set self to point to this
char addrofthis[1024];
sprintf(addrofthis, "%p", this);
sprintf(addrofthis, "%p", ref);
char *aplus = addrofthis;
if ((addrofthis[0] == '0') &&
((addrofthis[1] == 'x') || addrofthis[1] == 'X'))
if ((addrofthis[0] == '0') && ((addrofthis[1] == 'x') || addrofthis[1] == 'X'))
{
aplus += 2; //skip over "0x"
}
return std::string(aplus);
}
//----------------------------------------------------------------------------
void vtkPythonAnnotationFilter::EvaluateExpression()
{
vtksys_ios::ostringstream stream;
stream << "from paraview import annotation as pv_ann" << endl
<< "from vtkPVClientServerCoreCorePython import vtkPythonAnnotationFilter" << endl
<< "me = vtkPythonAnnotationFilter('" << aplus << " ')" << endl
<< "pv_ann.ComputeAnnotation(me, me.GetInputDataObject(0, 0), me.GetPythonExpression()"
<< this->TimeInformations << ")" << endl
<< "del me" << endl;
stream
<< "def vtkPythonAnnotationFilter_EvaluateExpression():" << endl
<< " from paraview import annotation as pv_ann" << endl
<< " from vtkPVClientServerCoreCorePython import vtkPythonAnnotationFilter" << endl
<< " me = vtkPythonAnnotationFilter('" << vtkGetReferenceAsString(this) << " ')" << endl
<< " pv_ann.execute(me)" << endl
<< " del me" << endl
<< "vtkPythonAnnotationFilter_EvaluateExpression()" << endl
<< "del vtkPythonAnnotationFilter_EvaluateExpression" << endl;
// ensure Python is initialized.
......
......@@ -12,11 +12,25 @@
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// .NAME vtkPythonAnnotationFilter
// .NAME vtkPythonAnnotationFilter - filter used to generate text annotation
// from Python expressions.
// .SECTION Description
// This filter allow the user to write an expression that can be use to
// extract any field information and represent the result as a text annotation
// in the 3D view
// vtkPythonAnnotationFilter is designed to generate vtkTableAlgorithm with a
// single string in it. The goal is that user will write a Python expression,
// similar to an expression in Python Calculator (vtkPythonCalculator). The
// generated result is converted to string and placed in the output.
//
// The variables available in the expression evaluation scope are as follows:
// \li sanitized array names for all arrays in the chosen ArrayAssociation.
// \li input: refers to the input dataset (wrapped as
// vtk.numpy_interface.dataset_adapter.DataObject or subclass).
// \li time_value: vtkDataObject::DATA_TIME_STEP() from input.
// \li time_steps: vtkDataObject::TIME_STEPS() from the input, if any
// \li time_range: vtkDataObject::TIME_RANGE() from the input, if any
//
// Examples of valid expressions are:
// \li "Max temp is %s" % max(Temp)
#ifndef __vtkPythonAnnotationFilter_h
#define __vtkPythonAnnotationFilter_h
......@@ -24,8 +38,6 @@
#include "vtkPVClientServerCoreCoreModule.h" //needed for exports
#include "vtkTableAlgorithm.h"
class vtkStdString;
class VTKPVCLIENTSERVERCORECORE_EXPORT vtkPythonAnnotationFilter : public vtkTableAlgorithm
{
public:
......@@ -34,33 +46,42 @@ public:
void PrintSelf(ostream& os, vtkIndent indent);
// Description:
// Allow the user to customize the output annotation based on some arbitrary
// content processing
//
// Here is the set of preset variable available to you when you write your
// expression.
// - input
// - input.PointData['myArray']
// - input.CellData['myArray']
// - input.FieldData['myArray']
// - t_value
// - t_steps
// - t_range
// - t_index
//
// Set the expression to evaluate.
// Here is a set of common expressions:
// - "Momentum %s" % str(Momentum[available_timesteps.index(provided_time)])
//
vtkSetStringMacro(PythonExpression);
vtkGetStringMacro(PythonExpression);
vtkSetStringMacro(Expression);
vtkGetStringMacro(Expression);
// Description:
// Set the value that is going to be printed to the output. This is an
// internal method and should not be called directly. It is called by the
// python script executed internally to pass the computed annotation value
// back to the filter.
void SetAnnotationValue(const char* value);
vtkGetStringMacro(AnnotationValue);
// Set the input array association. This dictates which array names are made
// available in the namespace by default. You can still use
// input.PointData['foo'] or input.CellData['bar'] explicitly to pick a
// specific array in your expression.
vtkSetMacro(ArrayAssociation, int);
vtkGetMacro(ArrayAssociation, int);
// Description:
// Get the value that is going to be printed to the output.
vtkGetStringMacro(ComputedAnnotationValue);
//------------------------------------------------------------------------------
// Description:
// Get methods for use in annotation.py.
// The values are only valid during RequestData().
vtkGetMacro(DataTimeValid, bool);
vtkGetMacro(DataTime, double);
vtkGetMacro(NumberOfTimeSteps, int);
double GetTimeStep(int index)
{
return (index < this->NumberOfTimeSteps? this->TimeSteps[index] : 0.0);
}
vtkGetMacro(TimeRangeValid, bool);
vtkGetVector2Macro(TimeRange, double);
vtkGetObjectMacro(CurrentInputDataObject, vtkDataObject);
void SetComputedAnnotationValue(const char* value);
//BTX
protected:
vtkPythonAnnotationFilter();
......@@ -72,23 +93,24 @@ protected:
vtkInformationVector* outputVector);
// Description:
// For internal use only.
void EvaluateExpression();
char* AnnotationValue;
char* PythonExpression;
// Used internally to store time informations for Python
vtkSetStringMacro(TimeInformations);
char* TimeInformations;
char* Expression;
char* ComputedAnnotationValue;
int ArrayAssociation;
private:
vtkPythonAnnotationFilter(const vtkPythonAnnotationFilter&); // Not implemented
void operator=(const vtkPythonAnnotationFilter&); // Not implemented
// Description:
// For internal use only.
static void ExecuteScript(void *);
bool DataTimeValid;
double DataTime;
int NumberOfTimeSteps;
double* TimeSteps;
bool TimeRangeValid;
double TimeRange[2];
vtkDataObject* CurrentInputDataObject;
//ETX
};
......
......@@ -112,7 +112,7 @@ int vtkAnnotateGlobalDataFilter::RequestData(
<< (timeDependent ? "t_index" : "0") << "))";
}
this->SetPythonExpression(expression.str().c_str());
this->SetExpression(expression.str().c_str());
return this->Superclass::RequestData(request, inputVector, outputVector);;
}
......
......@@ -139,11 +139,23 @@
<Group name="filters" />
</ProxyGroupDomain>
<DataTypeDomain name="input_type">
<DataType value="vtkDataSet" />
<DataType value="vtkDataObject" />
</DataTypeDomain>
<Documentation>Set the input of the filter.</Documentation>
</InputProperty>
<StringVectorProperty command="SetPythonExpression"
<IntVectorProperty command="SetArrayAssociation"
default_values="2"
name="ArrayAssociation"
number_of_elements="1">
<Documentation>Select the attribute to use to popular array names from.</Documentation>
<EnumerationDomain name="enum">
<Entry text="Point Data" value="0" />
<Entry text="Cell Data" value="1" />
<Entry text="Field Data" value="2" />
<Entry text="Row Data" value="6" />
</EnumerationDomain>
</IntVectorProperty>
<StringVectorProperty command="SetExpression"
name="Expression"
number_of_elements="1">
<Documentation>The Python expression evaluated during execution.
......@@ -152,7 +164,7 @@
FieldData, PointData, CellData] (i.e.: "Momentum: (%f, %f, %f)" %
(XMOM[t_index,0], YMOM[t_index,0], ZMOM[t_index,0]) )</Documentation>
</StringVectorProperty>
<StringVectorProperty command="GetAnnotationValue"
<StringVectorProperty command="GetComputedAnnotationValue"
information_only="1"
name="AnnotationValue">
<Documentation>Text that is used as annotation</Documentation>
......
......@@ -15,43 +15,76 @@
r"""
This module is used by vtkPythonAnnotationFilter.
"""
import paraview
from vtk.numpy_interface import dataset_adapter
from vtk.numpy_interface.algorithms import *
try:
import numpy as np
except ImportError:
raise RuntimeError, "'numpy' module is not found. numpy is needed for "\
"this functionality to work. Please install numpy and try again."
def _make_name_valid(name):
return paraview.make_name_valid(name)
from paraview import calculator
from vtk import vtkDataObject
from vtk.numpy_interface import dataset_adapter as dsa
def ComputeAnnotation(self, inputDS, expression, t_value = 0, t_steps = [0,1], t_range = [0,1]):
# init input object
input = dataset_adapter.WrapDataObject(inputDS)
# Add Fields names inside current namespace
numberOfFields = input.GetFieldData().GetNumberOfArrays()
for index in xrange(numberOfFields):
fieldName = input.GetFieldData().GetAbstractArray(index).GetName()
exec("%s = input.FieldData['%s']" % (_make_name_valid(fieldName), fieldName))
def execute(self):
expression = self.GetExpression()
inputDO = self.GetCurrentInputDataObject()
if not expression or not inputDO:
return True
# handle multi-block
inputMB = []
try:
for block in input:
inputMB.append(block)
except:
pass
inputs = [dsa.WrapDataObject(inputDO)]
association = self.GetArrayAssociation()
if association == vtkDataObject.FIELD:
# For FieldData, it gets tricky. In general, one would think we are going
# to look at field data in inputDO directly -- same for composite datasets.
# However, ExodusIIReader likes to put field data on leaf nodes insead.
# So we also check leaf nodes, if the FieldData on the root is empty.
# We explicitly call dsa.DataObject.GetFieldData to ensure that
# when dealing with composite datasets, we get the FieldData on the
# vtkCompositeDataSet itself, not in the leaf nodes.
fieldData = dsa.DataObject.GetFieldData(inputs[0])
if len(fieldData.keys()) == 0:
# if this is a composite dataset, use field data from the first block with some
# field data.
if isinstance(inputs[0], dsa.CompositeDataSet):
for dataset in inputs[0]:
fieldData = dataset.GetFieldData()
if (not fieldData is None) and (len(fieldData.keys()) > 0): break
else:
fieldData = inputs[0].GetAttributes(association)
arrays = calculator.get_arrays(fieldData)
ns = {}
ns["input"] = inputs[0]
if self.GetDataTimeValid():
ns["time_value"] = self.GetDataTime()
ns["t_value"] = ns["time_value"]
if self.GetNumberOfTimeSteps() > 0:
ns["time_steps"] = [self.GetTimeStep(x) for x in xrange(self.GetNumberOfTimeSteps())]
ns["t_steps"] = ns["time_steps"]
if self.GetTimeRangeValid():
ns["time_range"] = self.GetTimeRange()
ns["t_range"] = ns["time_range"]
if self.GetDataTimeValid() and self.GetNumberOfTimeSteps() > 0:
try:
ns["time_index"] = ns["time_steps"].index(ns["time_value"])
ns["t_index"] = ns["time_index"]
except ValueError: pass
ns.update(arrays)
# Add time informations in current namespace
t_index = 0
try:
t_index = t_steps.index(t_value)
result = calculator.compute(inputs, expression, ns=ns)
except:
pass
# Add extra naming
time_value = t_value
time_steps = t_steps
time_range = t_range
time_index = t_index
# Evaluate expression
exec("outputText = str(" + expression + ")")
self.SetAnnotationValue(outputText)
from sys import stderr
print >> stderr, "Failed to evaluate expression '%s'. "\
"The following exception stack should provide additional "\
"developer specific information. This typically implies a malformed "\
"expression. Verify that the expression is valid.\n\n" \
"Variables in current scope are %s \n" % (expression, ns.keys())
raise
self.SetComputedAnnotationValue("%s" % result)
return True
......@@ -9,30 +9,34 @@ except ImportError:
raise RuntimeError, "'numpy' module is not found. numpy is needed for "\
"this functionality to work. Please install numpy and try again."
import vtk
import paraview
import vtk.numpy_interface.dataset_adapter as dsa
from vtk.numpy_interface.algorithms import *
def compute(inputs, association, expression):
import paraview
def get_arrays(attribs):
"""Returns a 'dict' referring to arrays in dsa.DataSetAttributes or
dsa.CompositeDataSetAttributes instance."""
if not isinstance(attribs, dsa.DataSetAttributes) and \
not isinstance(attribs, dsa.CompositeDataSetAttributes):
raise ValueError, \
"Argument must be DataSetAttributes or CompositeDataSetAttributes."
arrays = dict()
for key in attribs.keys():
varname = paraview.make_name_valid(key)
arrays[varname] = attribs[key]
return arrays
fd0 = inputs[0].GetAttributes(association)
# Fill up arrays and locals variable list with
arrays = {}
for key in fd0.keys():
name = paraview.make_name_valid(key)
arrays[name] = fd0[key]
def compute(inputs, expression, ns=None):
# build the locals environment used to eval the expression.
mylocals = dict(arrays.items())
mylocals["arrays"] = arrays
mylocals = dict()
if ns:
mylocals.update(ns)
mylocals["inputs"] = inputs
try:
mylocals["points"] = inputs[0].Points
except: pass
except AttributeError: pass
retVal = eval(expression, globals(), mylocals)
return retVal
def execute(self, expression):
......@@ -57,7 +61,10 @@ def execute(self, expression):
output.GetPointData().PassData(inputs[0].GetPointData())
output.GetCellData().PassData(inputs[0].GetCellData())
retVal = compute(inputs, self.GetArrayAssociation(), expression)
# get a dictionary for arrays in the dataset attributes. We pass that
# as the variables in the eval namespace for compute.
variables = get_arrays(inputs[0].GetAttributes(self.GetArrayAssociation()))
retVal = compute(inputs, expression, ns=variables)
if retVal is not None:
output.GetAttributes(self.GetArrayAssociation()).append(retVal,
self.GetArrayName())
output.GetAttributes(self.GetArrayAssociation()).append(\
retVal, self.GetArrayName())
......@@ -46,8 +46,11 @@ def execute(self):
inputs = []
inputs.append(dsa.WrapDataObject(inputDO))
# get a dictionary for arrays in the dataset attributes. We pass that
# as the variables in the eval namespace for calculator.compute().
elocals = calculator.get_arrays(inputs[0].GetAttributes(attributeType))
try:
maskArray = calculator.compute(inputs, attributeType, selectionNode.GetQueryString())
maskArray = calculator.compute(inputs, selectionNode.GetQueryString(), ns=elocals)
except:
from sys import stderr
print >> stderr, "Error: Failed to evaluate Expression '%s'. "\
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment