Sounds good. Glad to help.
Original Message:
Sent: 03-18-2025 10:09
From: David Brubacher
Subject: Altering Attribute Values
That did the trick Bryce, thanks!
It looks like wrapping all of it in a transaction was the problem. I refactored my C# base class to be able to turn a number of different features on and off, including transactions and UI Events. I haven't yet tried it with UI Events back on to be sure, and will get back to you when I've studied it more thoroughly.
Now that the code works, I see that some other questions I had regarding attributes have been answered. I just have to move a few size values out of the feature code string and into attributes and I will be done!
------------------------------
David Brubacher
Original Message:
Sent: 03-17-2025 11:42
From: Bryce Haire
Subject: Altering Attribute Values
Here is a script I threw together that I believe does what you are trying (sets FC to static string).
------------------------------
Bryce Haire
Original Message:
Sent: 03-17-2025 07:28
From: David Brubacher
Subject: Altering Attribute Values
I wish I had good news Bryce
I've tried multiple combinations and permutations of your suggestions. From the simple...
// process all the points in the project foreach (Point point in points) { // skip points with no feature codes if (string.IsNullOrEmpty(point.FeatureCode)) continue; // create and populate a Feature Code Observation var currentFco = new FeatureCodeObservation(Project); currentFco.Populate(point); // cleanup the feature code string and update our new feature code object var result = CleanupFeatureCodeString(point.FeatureCode); currentFco.UpdateFeatureCode(result); currentFco.UpdateRawFeature(); // force a point redraw if (point.GetSite() is PointCollection pointCollection) pointCollection.ViewSite.AddToGraphicsCache(point.SerialNumber); }
to the more complex...
// process all the points in the project foreach (Point point in points) { // skip points with no feature codes if (string.IsNullOrEmpty(point.FeatureCode)) continue; // instead of using our Project property, discover the hosting project from the point // NOTE: We will alert if the hosting project and the project property are different // because if they never are, we will revert back to using the Project property var thisProject = (Project)((IProjectSite)point.GetSite()).HostingProject; if (thisProject.SerialNumber != Project.SerialNumber) ResultsBuilder.Append( $"Warning: Hosted project of point {point.Name} is different than the current project"); // create and populate a Feature Code Observation var currentFco = new FeatureCodeObservation(thisProject); currentFco.Populate(point); // check if the feature code is valid and get a feature code observation for our trouble // move onto the next point if TBC considers it valid if (currentFco.ValidateFeatureCode(point.FeatureCode)) { // but optionally set attributes to default values first if (SetAttributesToDefaultValues) FeatureCodeObservation.SetDefaultAttributeValues(currentFco); continue; } // create a copy of the current fco var newFco = new FeatureCodeObservation(currentFco); // update the feature nodes from the current fco newFco.UpdateFeatureNodes(currentFco.FeatureNodeList); // cleanup the feature code string and update our new feature code object var result = CleanupFeatureCodeString(point.FeatureCode); newFco.UpdateFeatureCode(result); newFco.UpdateRawFeature(); // it feels like we need to tell something this fco is the one we want to rule // not sure if this is how to do it. if (!newFco.ApplyTo(FeatureCodeManager.SerialNumber, thisProject)) ResultsBuilder.AppendLine($"ApplyTo unsuccessful at point {point.Name}"); // make sure the defaults are set since TBC should now recognize code(s) in this string FeatureCodeObservation.SetDefaultAttributeValues(newFco); // force a point redraw if (point.GetSite() is PointCollection pointCollection) pointCollection.ViewSite.AddToGraphicsCache(point.SerialNumber); }
and it feels like everything in between.
In both of these cases I can see that the FCO gets the changes applied, but the changes never make it back to the parent Point and its observations. You can see in the 2nd example I am looking for ways to 'commit' the FCO to the parent Point. I tried PointManager and FeatureManager here, both failing to apply the changes.
On screen and in the database, there is no effect. TBC makes no changes and doesn't complain that I've done something illegal.
The only time I have ever had the screen change is with the code in post 3, but then it reverts.
The force redraw code also does nothing because the point is never changed. I've tried this on a few different projects too, so either something critical is missing or all my projects are corrupted / configured in a way that prevents the updates I want to make, or there is a problem in the SDK code that drives TBC.
I am using 2024.10 and the newest SDK.
------------------------------
David Brubacher
Original Message:
Sent: 03-14-2025 14:20
From: Bryce Haire
Subject: Altering Attribute Values
Try adding this as well :P
Point point = MyObject as Point;Project prj = (Project)((IProjectSite)point.GetSite()).HostingProject;Trimble.Vce.Features.FeatureCoding.FeatureCodeObservation orginalFco = new FeatureCodeObservation(prj);orginalFco.Populate(point);Trimble.Vce.Features.FeatureCoding.FeatureCodeObservation fco = (FeatureCodeObservation)((IEmbeddableControlEditData)dataObj).Data;// TRY THISorginalFco.UpdateFeatureNodes(fco.FeatureNodeList);orginalFco.UpdateRawFeature();// ALSO ADD THIS//This is a temporary hack until we cleanup the RDFeature and IFeatureCodeProvider to force the redraw of the pointTrimble.Vce.Coordinates.PointCollection pointColl = point.GetSite() as Trimble.Vce.Coordinates.PointCollection;if (pointColl != null) pointColl.ViewSite.AddToGraphicsCache(point.SerialNumber);
------------------------------
Bryce Haire
Original Message:
Sent: 03-14-2025 13:39
From: David Brubacher
Subject: Altering Attribute Values
Thanks for having a look at this Bryce.
I realized I could look at how the Feature Code Control in the SDK was doing it and as a result of my 'dotPeeking' I had code very similar to what you suggested ready to test.
// process all the points in the project foreach (Point point in points) { // skip points with no feature codes if (string.IsNullOrEmpty(point.FeatureCode)) continue; // check if the feature code is valid and get a feature code observation for our trouble // move onto the next point if TBC considers it valid if (FeatureCodeObservation.ValidateFeatureCode(Project, point, out var newFco)) { // but optionally set attributes to default values first if (SetAttributesToDefaultValues) FeatureCodeObservation.SetDefaultAttributeValues(newFco); continue; } // cleanup the feature code string and update our new feature code object var result = CleanupFeatureCodeString(point.FeatureCode); newFco.UpdateFeatureCode(result); // make sure the defaults are set since TBC should now recognize codes in this string FeatureCodeObservation.SetDefaultAttributeValues(newFco); }
Unfortunately, my results have not changed, so I think it must be a misunderstanding on my part. Prior to TBC we used a homegrown feature code processor that (among other things) specified a trailing minus sign (with no space) for a code that should not contribute its elevation to the surface. Therefore FH- is the code for a fire hydrant whose elevation should not be used when generating a surface. We have nearly 25 years of historic data in JOB files and CSVs, and lots of field staff who will continue to code this way, so I want a one button fix. There are lots of other issues too, so manual fixes are a non-starter.
I can of course edit it manually in the point spreadsheet or in the point properties, so I know it is possible. I am doing a simple Regex replace using this pattern
(?<First>[A-Z]{1,6}\d{0,3})(?<Second>-)(?<LineEnd>\s|\z|\b)
and returning a string with a space between First and Second. This works flawlessly.
Then I use that string as the string to update, but part of the string (the minus sign) is not a valid code. This is a screen shot of my locals window on point 147 which started with a code SIB- and I want to be SIB -. Feature code is still SIB-, but RawString is SIB -. Note this is before the transaction commit.
and this is the properties window after the macro completes. Manually recomputing the project does nothing
Note: Yes, I will completely drop the minus sign and set include in surface to false, but I have other points where the feature code string will always have other data such as a point number to join to.
Clearly, I am not doing something, and I don't know what it is.
------------------------------
David Brubacher
Original Message:
Sent: 03-14-2025 10:13
From: Bryce Haire
Subject: Altering Attribute Values
I believe you want to go down the FeatureCodeObservation route, as this is what is used within the FeatureCodeEditor.
FeatureCodeObservation isn't a project entity, and more an ad-hoc helper class.
For generating the correct object, I would simply create a new FeatureCodeObservation(project) and call Populate(point).
Then make changes to your Feature Code via UpdateFeatureCode(raw_string)
You also may need to call FeatureCodeObservation.SetDefaultAttributeValuesInNewFeatureCode(oldFco, m_Fco);
// Create + Populate Feature Code Observationstring raw_string_feature_code = "IR BRSH"var m_Fco = new FeatureCodeObservation(project);m_Fco.Populate(point);// remember the old fco so we can know which attribute should show default valueFeatureCodeObservation oldFco = new FeatureCodeObservation(m_Fco);// update the feature code, the attributes will be merged when updatingm_Fco.UpdateFeatureCode(raw_string_feature_code);// set default values for attributes that exist in new created feature codesFeatureCodeObservation.SetDefaultAttributeValuesInNewFeatureCode(oldFco, m_Fco);
After this, you may proceed up the mountain and deposit your goat. :)
Let me know if this helps in anyway.
------------------------------
Bryce Haire
Original Message:
Sent: 03-13-2025 08:30
From: David Brubacher
Subject: Altering Attribute Values
I think I'm closer to this but not there yet.
All of the examples I've found to date are creating new points and use the SetFeatureCodeAtPoint(serialNum, code) method found on the PointManager. Trying this on an existing point merely appends additional feature codes without replacing and there are no Clear(...) or Update(...) methods found on the point manager object. The documentation on this method provides a good clue though "since the feature code is a property of the observation linked to the point this method modifies all connected observations for this point".
There is another SnapIn called FeatureCodeManager which is very promising. It has two methods of interest: LookupObservationHosts(point) and LookupContributingObservationHosts(point). The difference between them is the latter filters out disabled observations. Both methods return a collection of objects implementing IFeatureCodeHost, like Trimble.Vce.Coordinates.ImportedCoordinate, Trimble.Vce.Data.RawData.RDStationBase and Trimble.Vce.Data.RawData.RDCoordinate. RDStationBase contains a collection of observations, which in turn has feature codes which are editable. I can modify the codes with this
// process all the points in the project foreach (Point point in points) { // skip points with no feature codes if (string.IsNullOrEmpty(point.FeatureCode)) continue; // get the observations that host feature codes for this point // var hosts = FeatureCodeManager.LookupContributingObservationsHosts(point); var hosts = FeatureCodeManager.LookupObservationsHosts(point); foreach (var host in hosts) { // loop through all the codes this observation hosts foreach (var featureCode in host.FeatureCodes) { var result = ProcessFeatureCodes(featureCode); if (result != featureCode.Code) { featureCode.Code = result; } } }
and the result is the codes change on screen! Hurray!
Not so fast.
The codes remain unchanged in the point spreadsheet and in the properties. I can recalculate the project with no change, but when I update the screen by changing the view filter (for instance) everything reverts to its unedited state.
Transactions are committed, undos marked, and the pre and post UI events are raised as above. I have not sacrificed a goat at the top of the mountain yet, but I'm thinking about it.
Does anyone have any ideas?
I should add... Trimble.Vce.FeatureCoding.FeatureCodeObservation looks interesting too. It has an UpdateFeatureCode(string) method that I want to try but I can't figure out how to get an object of that type out of my project.
------------------------------
David Brubacher
Original Message:
Sent: 03-03-2025 10:09
From: David Brubacher
Subject: Altering Attribute Values
Ok I'm getting somewhere.
If I issue
_project.Calculate(true);
I still have the problem, but if I manually update a feature code and click the recalculate button, everything appears to be fine - defaults are set!
My attributes are shown as dirty, but that's not propagating up to the point.
Ideally, I'd like to force the recalculation which is what I thought my code would do. More reading and experimenting...
------------------------------
David Brubacher
Original Message:
Sent: 03-02-2025 17:07
From: David Brubacher
Subject: Altering Attribute Values
I have an FXL that has evolved to include a number of attributes, and I have data, often in the form of CSVs that doesn't include values for the attributes.
The attributes are defined as 'office only', are mostly numeric and have a limited value range. What I am trying to do is assign the default value as found in the attribute definition to any points with attribute values that are null. The default value is within the allowed value range.
This is C# code but should be readily understandable
private void SetAttributesToDefault(Point point) { // get all the feature codes attached to this point var items = _pointManager.AssociatedRDFeatures(point.SerialNumber); // loop through each one foreach (var featureCode in items) { // inspect the attributes if (featureCode.Attributes.Length == 0) continue; foreach (var attribute in featureCode.Attributes) { // check for no attribute definition if (attribute.Definition == null) continue; // Set the value of the instance attribute to the default value of the attribute definition // when the current instance value is not a number if (attribute.Type == AttributeTypes.Double) if (double.IsNaN((double)attribute.GetValue())) { attribute.SetValue(attribute.Definition.GetDefaultValue()); //var observed = _project.Concordance.GetIsObservedBy(point.SerialNumber); } } } }
All of this is happening inside a transaction, and the transaction commits
// commit the transaction transaction.Commit(); } } catch (Exception e) { _results.Append($"ERROR: {e.Message} from {e.Source}"); } finally { // set an undo mark _project.TransactionManager.AddEndMark(CommandGranularity.Command); UIEvents.RaiseAfterDataProcessing(this, new UIEventArgs()); _project = null; } }
While debugging I can see the attribute value change form NaN to (usually) 0, but when the macro finishes, the attribute is still null. What am I missing?

------------------------------
David Brubacher
------------------------------