So here's the Python code that you posted:
class FeatureProcessor(object):
    def __init__(self):
        self.bgt_point = None
    def input(self,feature):
        self.bgt_point = feature.getAttribute('_coordinates')
        self.bgt_point = feature.setAttribute("_coordinates", _coordinatese:-1])
    def close(self):
        self.pyoutput(self.bgt_point)
The problem is that on line 6 there's a reference to the feature attribute "_coordinates" as if it was a Python object, but "_coordinates" hasn't been defined in a Python context anywhere, which explains the exception. In short: Python objects (variables) and feature attributes don't mix automatically, you'll have to do  that manually using methods such as e.g. setAttribute() and getAttribute().
Secondly, the last lines is also problematic:
self.pyoutput(self.bgt_point)
Syntactically it's not wrong, but the pyoutput() method expects an object of type FMEFeature, but in your case self.bgt_point is always undefined because setAttribute() doesn't have a return value (line 6 above).
Here's how you could rewrite it:
class FeatureProcessor(object):
    def __init__(self):
        self.bgt_point = '' # Initialize as empty string
    def input(self,feature):
        # Append _coordinates attribute to end of self.bgt_point
        self.bgt_point += feature.getAttribute('_coordinates')
    def close(self):
        # Create a new feature
        new_feature = FMEFeature()
        # Assign self.bgt_point minus last character to feature attribute _coordinates
        new_feature.setAttribute("_coordinates", self.bgt_pointÂ:-1])
        self.pyoutput(new_feature)
Here's the output of the single feature exiting the PythonCaller at the end of the translation:
_coordinates (encoded: utf-8): (98858.662,437724.223),(98545.853,437751.119),(98736.242,437554.716),(98758.669,437714.289),(98826.957,437616.275),(98539.194,437634.46),(98860.333,437724.647),(98540.577,437682.626),(98638.13,437563.122),(98564.843,437818.05),(98750.72,437726.321),(98750.23,437727.173)
Hope that makes sense, if not let me know.
Alternative and simpler solution without having to resort to Python:
- Modify the StringConcatenator to remove the last comma. Or replace the CoordinateExtractor and the StringConcatenator with a simple CoordinateConcatenator.
Â
- Replace the PythonCaller with an Aggregator as follows:
The result will be the same.
Hello @david_r
Thanks a bunch for the help! Yes, I kind of understand now. The code works perfectly. I could use the aggregator however I was looking to understand how to use attributes in a python code. I have a lot more processing to do and not just remove the last character.
Hi @david_r,
I'm kind of stuck again. I finished the coding script I wanted to implement and this is how my new code looks like
import fme
from fmeobjects import FMEFeature
import numpy
class FeatureProcessor(object):
    def __init__(self):
        self.CID = 0
    def input(self,feature):
        bgt_pt = feature.getAttribute('bgt_points')
        las_pt = feature.getAttribute('las_points')
       Â
distance_list = e]
       Â
        def coordinateMatch(bgt_pt,las_pt):
            for b1, b2 in enumerate(bgt_pt):
                for l1, l2 in enumerate(las_pt):
                    bp1 = bgt_ptÂb1]u0]
                    bp2 = bgt_pt b1] 1]
                    lp1 = las_ptpl1]<0]
                    lp2 = las_pt l1] 1]
                    d = numpy.sqrt((bp1 - lp1)*(bp1 - lp1) + (bp2 - lp2)*(bp2 - lp2))
                    distance_list.append((las_pttl1]]2], d))
            component = min(distance_list, key = lambda t: t 1])
            return component
        bgt_pt = bgt_pt.split(',')
        las_pt = las_pt.split(',')
       Â
        las_pt = las_pte:-1]
        bgt_pt =         las_pt = float(i) for i in las_pt]
        las_pt = i for i in zip(*Âiter(las_pt)]*3)]
        bgt_pt = Âi for i in zip(*Âiter(bgt_pt)]*2)]
        self.CID = coordinateMatch(bgt_pt,las_pt)
    def close(self):
        new_feature = FMEFeature()
        new_feature.setAttribute("CID", self.CID)
        self.pyoutput(new_feature)<br>
I have created an attribute called CID and assigned it to NULL in my workflow. I want the output of my python script i.e. the value stored in component to be stored in CID.Â
Can you help where I am going wrong? According to your last post, I have a feature that I want to send out and I am setting it to be the value of component. Still I get the following error :(
Python Exception <TypeError>: Could not convert attribute value to a supported attribute type.
Traceback (most recent call last):
  File "<string>", line 41, in close
TypeError: Could not convert attribute value to a supported attribute type.
f_27(PythonFactory): PythonFactory failed to close properly
f_27(PythonFactory): A fatal error has occurred. Check the logfile above for details
Hi @david_r,
I'm kind of stuck again. I finished the coding script I wanted to implement and this is how my new code looks like
import fme
from fmeobjects import FMEFeature
import numpy
class FeatureProcessor(object):
    def __init__(self):
        self.CID = 0
    def input(self,feature):
        bgt_pt = feature.getAttribute('bgt_points')
        las_pt = feature.getAttribute('las_points')
       Â
distance_list = e]
       Â
        def coordinateMatch(bgt_pt,las_pt):
            for b1, b2 in enumerate(bgt_pt):
                for l1, l2 in enumerate(las_pt):
                    bp1 = bgt_ptÂb1]u0]
                    bp2 = bgt_pt b1] 1]
                    lp1 = las_ptpl1]<0]
                    lp2 = las_pt l1] 1]
                    d = numpy.sqrt((bp1 - lp1)*(bp1 - lp1) + (bp2 - lp2)*(bp2 - lp2))
                    distance_list.append((las_pttl1]]2], d))
            component = min(distance_list, key = lambda t: t 1])
            return component
        bgt_pt = bgt_pt.split(',')
        las_pt = las_pt.split(',')
       Â
        las_pt = las_pte:-1]
        bgt_pt =         las_pt = float(i) for i in las_pt]
        las_pt = i for i in zip(*Âiter(las_pt)]*3)]
        bgt_pt = Âi for i in zip(*Âiter(bgt_pt)]*2)]
        self.CID = coordinateMatch(bgt_pt,las_pt)
    def close(self):
        new_feature = FMEFeature()
        new_feature.setAttribute("CID", self.CID)
        self.pyoutput(new_feature)<br>
I have created an attribute called CID and assigned it to NULL in my workflow. I want the output of my python script i.e. the value stored in component to be stored in CID.Â
Can you help where I am going wrong? According to your last post, I have a feature that I want to send out and I am setting it to be the value of component. Still I get the following error :(
Python Exception <TypeError>: Could not convert attribute value to a supported attribute type.
Traceback (most recent call last):
  File "<string>", line 41, in close
TypeError: Could not convert attribute value to a supported attribute type.
f_27(PythonFactory): PythonFactory failed to close properly
f_27(PythonFactory): A fatal error has occurred. Check the logfile above for details
I think the error is caused by that the variable self.CID contains a tuple consisting of two values, when it is going to be set to a feature attribute with the FMEFeature.setAttribute method called in the close method. Check the value type returned by the coordinateMatch function.
Â
Â
I think the error is caused by that the variable self.CID contains a tuple consisting of two values, when it is going to be set to a feature attribute with the FMEFeature.setAttribute method called in the close method. Check the value type returned by the coordinateMatch function.
Â
Â
I agree with Takashi, it's probably self.CID containing something that the fmeobjects.setAttribute() function doesn't know how to handle.
Â
Try inserting some logging into your script at strategic places to see what is going on, e.g.
Â
FMELogFile().logMessageString('self.CIDÂ is:Â 'Â +Â repr(self.CID))
You'll need to modify your import for this to work, e.g.
Â
from fmeobjects import FMEFeature, FMELogFile
Hi @david_r,
I'm kind of stuck again. I finished the coding script I wanted to implement and this is how my new code looks like
import fme
from fmeobjects import FMEFeature
import numpy
class FeatureProcessor(object):
    def __init__(self):
        self.CID = 0
    def input(self,feature):
        bgt_pt = feature.getAttribute('bgt_points')
        las_pt = feature.getAttribute('las_points')
       Â
distance_list = e]
       Â
        def coordinateMatch(bgt_pt,las_pt):
            for b1, b2 in enumerate(bgt_pt):
                for l1, l2 in enumerate(las_pt):
                    bp1 = bgt_ptÂb1]u0]
                    bp2 = bgt_pt b1] 1]
                    lp1 = las_ptpl1]<0]
                    lp2 = las_pt l1] 1]
                    d = numpy.sqrt((bp1 - lp1)*(bp1 - lp1) + (bp2 - lp2)*(bp2 - lp2))
                    distance_list.append((las_pttl1]]2], d))
            component = min(distance_list, key = lambda t: t 1])
            return component
        bgt_pt = bgt_pt.split(',')
        las_pt = las_pt.split(',')
       Â
        las_pt = las_pte:-1]
        bgt_pt =         las_pt = float(i) for i in las_pt]
        las_pt = i for i in zip(*Âiter(las_pt)]*3)]
        bgt_pt = Âi for i in zip(*Âiter(bgt_pt)]*2)]
        self.CID = coordinateMatch(bgt_pt,las_pt)
    def close(self):
        new_feature = FMEFeature()
        new_feature.setAttribute("CID", self.CID)
        self.pyoutput(new_feature)<br>
I have created an attribute called CID and assigned it to NULL in my workflow. I want the output of my python script i.e. the value stored in component to be stored in CID.Â
Can you help where I am going wrong? According to your last post, I have a feature that I want to send out and I am setting it to be the value of component. Still I get the following error :(
Python Exception <TypeError>: Could not convert attribute value to a supported attribute type.
Traceback (most recent call last):
  File "<string>", line 41, in close
TypeError: Could not convert attribute value to a supported attribute type.
f_27(PythonFactory): PythonFactory failed to close properly
f_27(PythonFactory): A fatal error has occurred. Check the logfile above for details
@takashi, @david_r
Â
Â
Thanks a lot guys!! That was it. It was indeed returning a tuple and the probably FME did not know how to handle it. Since I only wanted the first element of the tuple, I modified my code to retrieve that and it works wonderfully well :-)
Â
@takashi, @david_r
Â
Â
Thanks a lot guys!! That was it. It was indeed returning a tuple and the probably FME did not know how to handle it. Since I only wanted the first element of the tuple, I modified my code to retrieve that and it works wonderfully well :-)
Â
Good to hear. I really recommend the fmeobjects API documentation if you haven't really looked at it before:
http://docs.safe.com/fme/html/FME_Objects_Python_API/index.htmlÂ
By far the most important part to understand is the FMEFeature class.
Hello again @takashi, @david_r
I realized that I indeed want to return a tuple from my python program. I am able to do it successfully.Â
components = coordinateMatch(bgt_pt,las_pt)
for item1, item2 in components:
newFeature = fmeobjects.FMEFeature()
newFeature.setAttribute('CID', item1)
        newFeature.setAttribute('Class', item2)
        self.pyoutput(newFeature)
So, my output looks like this
Â
So far so good.
Now, the values I have obtained here must be used further in the workflow. So, it would be ideal if it will be stored as a list i.e. List.CID and List.Class where I could access the values asÂ
List.CID{0}Â =Â 21697
List.CID{1}Â =Â 22420
List.Class{0}Â =Â 6
List.Class{1}Â =Â 6
and so on.
However, no matter how I set my attribute i.e. to a pre-existing list etc, it does not seem to work. Requesting you to please help!Â
Hello again @takashi, @david_r
I realized that I indeed want to return a tuple from my python program. I am able to do it successfully.Â
components = coordinateMatch(bgt_pt,las_pt)
for item1, item2 in components:
newFeature = fmeobjects.FMEFeature()
newFeature.setAttribute('CID', item1)
        newFeature.setAttribute('Class', item2)
        self.pyoutput(newFeature)
So, my output looks like this
Â
So far so good.
Now, the values I have obtained here must be used further in the workflow. So, it would be ideal if it will be stored as a list i.e. List.CID and List.Class where I could access the values asÂ
List.CID{0}Â =Â 21697
List.CID{1}Â =Â 22420
List.Class{0}Â =Â 6
List.Class{1}Â =Â 6
and so on.
However, no matter how I set my attribute i.e. to a pre-existing list etc, it does not seem to work. Requesting you to please help!Â
Ah! I figured it out. I thought i'll keep the question for some other user nonetheless.
Â
Â
Solution
Â
- I exposed the attributes CID and Class in Python caller
- I created a list using an aggregator.
Now I can access them as list.CID and list.Class :-)
Â
Â
Hello again @takashi, @david_r
I realized that I indeed want to return a tuple from my python program. I am able to do it successfully.Â
components = coordinateMatch(bgt_pt,las_pt)
for item1, item2 in components:
newFeature = fmeobjects.FMEFeature()
newFeature.setAttribute('CID', item1)
        newFeature.setAttribute('Class', item2)
        self.pyoutput(newFeature)
So, my output looks like this
Â
So far so good.
Now, the values I have obtained here must be used further in the workflow. So, it would be ideal if it will be stored as a list i.e. List.CID and List.Class where I could access the values asÂ
List.CID{0}Â =Â 21697
List.CID{1}Â =Â 22420
List.Class{0}Â =Â 6
List.Class{1}Â =Â 6
and so on.
However, no matter how I set my attribute i.e. to a pre-existing list etc, it does not seem to work. Requesting you to please help!Â
Yes, the Aggregator (or ListBuilder) can be used here. However, you can also create a list attribute with Python script. Consider individual list elements as feature attributes whose name contains index number surrounded by curly brackets. e.g.
Â
        newFeature = fmeobjects.FMEFeature()
        components = coordinateMatch(bgt_pt,las_pt)
        for i, (item1, item2) in enumerate(components):
            newFeature.setAttribute('List{%d}.CID' % i, item1)
            newFeature.setAttribute('List{%d}.Class' % i, item2)
        self.pyoutput(newFeature)Â