Skip to main content
Solved

PythonCaller: No Geometry or original attributes are returned


lorenrouth
Contributor
Forum|alt.badge.img+8

This script works, but leaves out the geometry and original attributes.  My hunch is there’s something wrong with the close(self) function, but I’m stumped.  The code takes a feature id number and list of nearby ids from a NeighborFinder list, where it matches them up and assigns a _group_id attribute to  use with the Aggregator.  Really, it should be part of NeighborFinder.  The way I had been grouping features is to use two or more AttributeCreators and assign a common Id from the feature and id list.  It’s a hassle because you end up with duplicate features.  Very inelegent.

 

I have seen a few posts on missing attributes, but I can’t see how they apply to my code:

 

import fme
import fmeobjects
from collections import defaultdict

class FeatureProcessor(object):

    def __init__(self):

        self.og_ids = []
        self.closest = []
        self.lines = []

    def input(self, feature: fmeobjects.FMEFeature):

        id_number = feature.getAttribute('_count_Glaz_H')
        if id_number is not None:
            self.og_ids.append(id_number)

        nearby_ids = feature.getAttribute('_closest{}._count_Glaz_H')
        if nearby_ids is not None:
            self.closest.append(nearby_ids)


    def create_lines_list(self):
        for id_number, nearby_ids in zip(self.og_ids, self.closest):
            if isinstance(nearby_ids, list):
                nearby_ids = ','.join(map(str, nearby_ids))
            line = {
                'Id Number': id_number,
                'Nearby Ids': [int(nearby_id) for nearby_id in nearby_ids.split(',') if nearby_id.strip().isdigit()]
            }
            self.lines.append(line)
        return self.lines

    def assign_group_ids_to_lines(self, lines):

        adjacency_list = defaultdict(list)
        for line in lines:
            id_num = line['Id Number']
            nearby_ids = line['Nearby Ids']
            for nearby_id in nearby_ids:
                adjacency_list[id_num].append(nearby_id)
                adjacency_list[nearby_id].append(id_num)

        def dfs(node, visited, group_id):
            stack = [node]
            while stack:
                current = stack.pop()
                if current not in visited:
                    visited.add(current)
                    for line in self.lines:
                        if line['Id Number'] == current:
                            line['Group Id'] = group_id
                            break
                    for neighbor in adjacency_list[current]:
                        if neighbor not in visited:
                            stack.append(neighbor)

        visited = set()
        group_id = 1
        for line in self.lines:
            id_num = line['Id Number']
            if id_num not in visited:
                dfs(id_num, visited, group_id)
                group_id += 1

        return self.lines


    def close(self):

        self.create_lines_list()
        lines_with_group_ids = self.assign_group_ids_to_lines(self.lines)

        for line in lines_with_group_ids:
            feature = fmeobjects.FMEFeature()
            feature.setAttribute('_group_id', line.get('Group Id', 0))
            self.pyoutput(feature)



    def process_group(self):
        pass

Any insight is appreciated.

 

Thanks,

-L

Best answer by lorenrouth

Hi @birgit ,

Ok, I figured it out.  I needed to make a list of the incoming features and then zip them with the new attribute.  It works!

 

import fme
import fmeobjects
from collections import defaultdict

class FeatureProcessor(object):

    def __init__(self):
        self.og_ids = []
        self.closest = []
        self.lines = []
        self.features = []  # Store references to the original features

    def input(self, feature: fmeobjects.FMEFeature):
        id_number = feature.getAttribute('_count_Glaz_H')
        if id_number is not None:
            self.og_ids.append(id_number)
            self.features.append(feature)  # Store the feature reference

        nearby_ids = feature.getAttribute('_closest{}._count_Glaz_H')
        if nearby_ids is not None:
            self.closest.append(nearby_ids)

    def create_lines_list(self):
        for id_number, nearby_ids in zip(self.og_ids, self.closest):
            if isinstance(nearby_ids, list):
                nearby_ids = ','.join(map(str, nearby_ids))
            line = {
                'Id Number': id_number,
                'Nearby Ids': [int(nearby_id) for nearby_id in nearby_ids.split(',') if nearby_id.strip().isdigit()]
            }
            self.lines.append(line)
        return self.lines

    def assign_group_ids_to_lines(self, lines):
        adjacency_list = defaultdict(list)
        for line in lines:
            id_num = line['Id Number']
            nearby_ids = line['Nearby Ids']
            for nearby_id in nearby_ids:
                adjacency_list[id_num].append(nearby_id)
                adjacency_list[nearby_id].append(id_num)

        def dfs(node, visited, group_id):
            stack = [node]
            while stack:
                current = stack.pop()
                if current not in visited:
                    visited.add(current)
                    for line in self.lines:
                        if line['Id Number'] == current:
                            line['Group Id'] = group_id
                            break
                    for neighbor in adjacency_list[current]:
                        if neighbor not in visited:
                            stack.append(neighbor)

        visited = set()
        group_id = 1
        for line in self.lines:
            id_num = line['Id Number']
            if id_num not in visited:
                dfs(id_num, visited, group_id)
                group_id += 1

        return self.lines

    def close(self):
        self.create_lines_list()
        lines_with_group_ids = self.assign_group_ids_to_lines(self.lines)

        for line, feature in zip(lines_with_group_ids, self.features):
            feature.setAttribute('_group_id', line.get('Group Id', 0))
            self.pyoutput(feature)

    def process_group(self):
        pass

 

View original
Did this help you find an answer to your question?

2 replies

birgit
Influencer
Forum|alt.badge.img+16
  • Influencer
  • June 19, 2024

Maybe it has something to do with the fact that in your closing statement you create a new empty feature and output that feature to the output port? See below:

    def close(self):

        self.create_lines_list()
        lines_with_group_ids = self.assign_group_ids_to_lines(self.lines)

        for line in lines_with_group_ids:
            feature = fmeobjects.FMEFeature()   <---- OVER HERE!
            feature.setAttribute('_group_id', line.get('Group Id', 0))
            self.pyoutput(feature)

And your original features are never sent to the output port and are missing. If you want the original geometry and attributes to your new features instead then you need to assign them to the new feature as well.


lorenrouth
Contributor
Forum|alt.badge.img+8
  • Author
  • Contributor
  • Best Answer
  • June 19, 2024

Hi @birgit ,

Ok, I figured it out.  I needed to make a list of the incoming features and then zip them with the new attribute.  It works!

 

import fme
import fmeobjects
from collections import defaultdict

class FeatureProcessor(object):

    def __init__(self):
        self.og_ids = []
        self.closest = []
        self.lines = []
        self.features = []  # Store references to the original features

    def input(self, feature: fmeobjects.FMEFeature):
        id_number = feature.getAttribute('_count_Glaz_H')
        if id_number is not None:
            self.og_ids.append(id_number)
            self.features.append(feature)  # Store the feature reference

        nearby_ids = feature.getAttribute('_closest{}._count_Glaz_H')
        if nearby_ids is not None:
            self.closest.append(nearby_ids)

    def create_lines_list(self):
        for id_number, nearby_ids in zip(self.og_ids, self.closest):
            if isinstance(nearby_ids, list):
                nearby_ids = ','.join(map(str, nearby_ids))
            line = {
                'Id Number': id_number,
                'Nearby Ids': [int(nearby_id) for nearby_id in nearby_ids.split(',') if nearby_id.strip().isdigit()]
            }
            self.lines.append(line)
        return self.lines

    def assign_group_ids_to_lines(self, lines):
        adjacency_list = defaultdict(list)
        for line in lines:
            id_num = line['Id Number']
            nearby_ids = line['Nearby Ids']
            for nearby_id in nearby_ids:
                adjacency_list[id_num].append(nearby_id)
                adjacency_list[nearby_id].append(id_num)

        def dfs(node, visited, group_id):
            stack = [node]
            while stack:
                current = stack.pop()
                if current not in visited:
                    visited.add(current)
                    for line in self.lines:
                        if line['Id Number'] == current:
                            line['Group Id'] = group_id
                            break
                    for neighbor in adjacency_list[current]:
                        if neighbor not in visited:
                            stack.append(neighbor)

        visited = set()
        group_id = 1
        for line in self.lines:
            id_num = line['Id Number']
            if id_num not in visited:
                dfs(id_num, visited, group_id)
                group_id += 1

        return self.lines

    def close(self):
        self.create_lines_list()
        lines_with_group_ids = self.assign_group_ids_to_lines(self.lines)

        for line, feature in zip(lines_with_group_ids, self.features):
            feature.setAttribute('_group_id', line.get('Group Id', 0))
            self.pyoutput(feature)

    def process_group(self):
        pass

 


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings