As far as I can tell the "Snapping Distance" param for the Snapper and the "Tolerance" param for the Intersector have to be hard coded or a workspace param. ie. They need to be known before the workspace runs.
It would be really useful to have these values calculated within the workspace as the snapping distance/tolerance may depend on the data being processed. ie. It would be nice to be able to set the snapping distance from an attribute and the Snapper uses the value from the attribute of the first feature of each group entering the Snapper.
Is this, or some variant, currently possible? I am considering splitting the workspace into two workspaces and having the first one calculate the value then calling the second one, however it would be much more convenient to be able to do the calculation of the snapping distance in the same workspace.
Once upon a time I wrote a custom transformer that allowed for the anchor snapper distance to be set by an attribute. Different features could have different distances.
It involved using fmeobjects.FMEFactoryPipeline in a pythonCaller.
@larry has a good breakdown of the process for a SpatialRelator and Chopper, the anchoredSnapper would be similar.
For anyone that hits a similiar situation, here is the PythonCaller code I used:
import fme
import fmeobjects
# Create a Snapper and call it using the value of the snapping_distance# from the first feature receieved as the snapping tolerance. Used as the# Snapper transformers does not normally allow the snappnig tolerance # to come from an attribute.
SNAPPER_FACTORY_TEMPLATE = """\
FACTORY_DEF {{*}} SnappingFactory \
FACTORY_NAME Snapper_2 \
INPUT FEATURE_TYPE Input \
SNAP_TYPE ALL \
SNAP_TOLERANCE {snapping_tolerance} \
FLUSH_WHEN_GROUPS_CHANGE No \
OUTPUT_DEGENERATES_AS_COLLAPSED YES \
DO_NOT_STROKE_ARCS_FOR_VERTEX_SNAPPING \
CLEANING_TOLERANCE AUTO \
OUTPUT SNAPPED FEATURE_TYPE Snapper_2_SNAPPED \
OUTPUT UNTOUCHED FEATURE_TYPE Snapper_2_UNTOUCHED\
"""classFeatureProcessor(object):def__init__(self):
self.pipeline = Nonedefinput(self, feature):if self.pipeline isNone:
snappingDistance = float(feature.getAttribute("snapping_distance"))
self.pipeline = fmeobjects.FMEFactoryPipeline("SnappingPipeline")
snapperFactoryDefinition = SNAPPER_FACTORY_TEMPLATE.format(snapping_tolerance=snappingDistance)
self.pipeline.addFactory(snapperFactoryDefinition)
feature.setFeatureType("Input")
self.pipeline.processFeature(feature)
defclose(self):if self.pipeline isnotNone:
self.pipeline.allDone()
resultFeature = self.pipeline.getOutputFeature()
while resultFeature isnotNone:
self.pyoutput(resultFeature)
resultFeature = self.pipeline.getOutputFeature()
Then to use you just need to make sure the first feature to enter has a snapping_distance attribute set to the snapping tolerance you wish to use.
If you wish to change the config for the Snapper (eg use a snapping type other than Segment Snapping) just configure a Snapper how you want in a workspace, copy it and paste into a text editor. Then copy the resulting FACTORY_DEF into the SNAPPER_FACTORY_TEMPLATE variable above. If you wish to then customize any params based on attribute (as I have done with snapping_tolerance) above just update them in the String with the values you want.
Good observation and you are correct. I took a deeper look at this does have to be known at "workspace parse time". Can't come up with any better workaround than your suggestion of a splitting and using a workspace runner to run a child workspace that does that actual snapping. Sorry about this. If you feel like braving the all new Ideas board here, maybe you could add this to see if others also could want the same thing?
Good observation and you are correct. I took a deeper look at this does have to be known at "workspace parse time". Can't come up with any better workaround than your suggestion of a splitting and using a workspace runner to run a child workspace that does that actual snapping. Sorry about this. If you feel like braving the all new Ideas board here, maybe you could add this to see if others also could want the same thing?
Seems like the points go up by 10s in this new world as opposed to going up just one at a time, so I'm sure these ideas are really going to be getting our attention now 😁
Seems like the points go up by 10s in this new world as opposed to going up just one at a time, so I'm sure these ideas are really going to be getting our attention now 😁
Thanks @Dale Lutz ,
I've upvoted that idea. Hopefully it gets enough momentum.
I think a key reason we don't allow selection from an attribute is that it could then vary between features.
For example, feature A has a tolerance of 2 and feature B has a tolerance of 1. They are 1.5 apart. Should they snap?
Ah - I see your comment on the idea. Yes, the first feature sets the snap tolerance is probably what we'd go with in this scenario. Let's hope that idea gets some upvotes and is implemented.
Once upon a time I wrote a custom transformer that allowed for the anchor snapper distance to be set by an attribute. Different features could have different distances.
It involved using fmeobjects.FMEFactoryPipeline in a pythonCaller.
@larry has a good breakdown of the process for a SpatialRelator and Chopper, the anchoredSnapper would be similar.
Once upon a time I wrote a custom transformer that allowed for the anchor snapper distance to be set by an attribute. Different features could have different distances.
It involved using fmeobjects.FMEFactoryPipeline in a pythonCaller.
@larry has a good breakdown of the process for a SpatialRelator and Chopper, the anchoredSnapper would be similar.
For anyone that hits a similiar situation, here is the PythonCaller code I used:
import fme
import fmeobjects
# Create a Snapper and call it using the value of the snapping_distance# from the first feature receieved as the snapping tolerance. Used as the# Snapper transformers does not normally allow the snappnig tolerance # to come from an attribute.
SNAPPER_FACTORY_TEMPLATE = """\
FACTORY_DEF {{*}} SnappingFactory \
FACTORY_NAME Snapper_2 \
INPUT FEATURE_TYPE Input \
SNAP_TYPE ALL \
SNAP_TOLERANCE {snapping_tolerance} \
FLUSH_WHEN_GROUPS_CHANGE No \
OUTPUT_DEGENERATES_AS_COLLAPSED YES \
DO_NOT_STROKE_ARCS_FOR_VERTEX_SNAPPING \
CLEANING_TOLERANCE AUTO \
OUTPUT SNAPPED FEATURE_TYPE Snapper_2_SNAPPED \
OUTPUT UNTOUCHED FEATURE_TYPE Snapper_2_UNTOUCHED\
"""classFeatureProcessor(object):def__init__(self):
self.pipeline = Nonedefinput(self, feature):if self.pipeline isNone:
snappingDistance = float(feature.getAttribute("snapping_distance"))
self.pipeline = fmeobjects.FMEFactoryPipeline("SnappingPipeline")
snapperFactoryDefinition = SNAPPER_FACTORY_TEMPLATE.format(snapping_tolerance=snappingDistance)
self.pipeline.addFactory(snapperFactoryDefinition)
feature.setFeatureType("Input")
self.pipeline.processFeature(feature)
defclose(self):if self.pipeline isnotNone:
self.pipeline.allDone()
resultFeature = self.pipeline.getOutputFeature()
while resultFeature isnotNone:
self.pyoutput(resultFeature)
resultFeature = self.pipeline.getOutputFeature()
Then to use you just need to make sure the first feature to enter has a snapping_distance attribute set to the snapping tolerance you wish to use.
If you wish to change the config for the Snapper (eg use a snapping type other than Segment Snapping) just configure a Snapper how you want in a workspace, copy it and paste into a text editor. Then copy the resulting FACTORY_DEF into the SNAPPER_FACTORY_TEMPLATE variable above. If you wish to then customize any params based on attribute (as I have done with snapping_tolerance) above just update them in the String with the values you want.
We use 3 different kinds of cookies. You can choose which cookies you want to accept. We need basic cookies to make this site work, therefore these are the minimum you can select. Learn more about our cookies.