Select to view content in your preferred language

Attribute Rule 'Water Junction - Set Rotation From Line Angle' not calculating on End Cap or Plug features

227
4
2 weeks ago
PeterKing
Regular Contributor

Hello, 

Need some help troubleshooting an attribute rule that does not appear to be working as designed.

We are editing existing End Caps and Plugs in our Water Utility Network. When updating the attributes for Water Junction - Fittings such as those placed at the end of a line, the attribute rule to 'Set the Rotation From Line Angle' fails to rotate the symbol to the corresponding main.

The Attribute Rule does set the rotation correctly for other fittings such as Reducers and Transitions that are placed along a line or the junction of two lines.

It appears that the attribute rule should fire based on the following parameter:

 // The features location is on the end of the line, create a new segment from the feature to the start vertex
else if (Equals(segment[-1], feature_geometry)) {
angle_type = 'to'
angle_value = Round(Angle(feature_geometry, segment[0]), 0)
}

If this is a feature and not a bug of the attribute rule from the Water Utility Network Expanded Solution, I appreciate the clarification.

Environment Notes: Enterprise 11.5, Pro 3.5.1 and UN 7

// Assigned To: WaterJunction
// Type: Calculation
// Name: Water Junction - Set Rotation From Line Angle
// Description: Calculate the rotation based on angle of the intersection line(s)
// Subtypes: All
// Field: symbolrotation
// Trigger: Insert, Update
// Exclude From Client: False
// Disable: False
// Is Editable: True

// Related Rules: Some rules rely on additional rules for execution. If this rule works in conjunction with another, they are listed below:
//    - None

// Duplicated in: This rule may be implemented on other classes, they are listed here to aid you in adjusting those rules when a code change is required.
//    - Water Device - Set Rotation From Line Angle

// *************       User Variables       *************
// This section has the functions and variables that need to be adjusted based on your implementation

// Assigned to field for the rule
var assigned_to_field = $feature.symbolrotation;

// Limit the rule to valid asset groups/subtypes
// ** Implementation Note: Instead of recreating this rule for each subtype, this rules uses a list of subtypes and exits if not valid
//    If you have added Asset Groups, they will need to be added to this list.
var valid_asset_groups = [20, 50, 51];

// Set to true if the rotation setting is set to geographic in the layer properties
var geographic_rotation = false;

// Set the counter clockwise spin angle used for the symbol in the symbology options
var symbol_flip_angle = 0

// Create feature set to the intersecting class using the GDB Name
// ** Implementation Note: If the Utility Network domain was changed, this variable would have to be adjusted
var intersecting_featset = FeatureSetByName($datastore, "L3Water_Line", ['objectid'], true);

// ************* End User Variables Section *************

if (TypeOf(valid_asset_groups) != 'Array' ||
    (IndexOf(valid_asset_groups, $feature.assetgroup) == -1) && Count(valid_asset_groups) > 0) {
    return assigned_to_field;
}

// Return if a value is already set, to recalculate an angle, the field must be set to null
if (IsEmpty(assigned_to_field) == false) {
    return assigned_to_field;
}

// Find the intersecting lines
var lines = Intersects(intersecting_featset, $feature);
var intersectcount = Count(lines);
//If no lines intersect, return the original value
if (intersectcount == 0) {
    return assigned_to_field;
}

// The tolerance between lines to determine if they follow the same plane
var diff_tol = 5;

// Variable to store all found angles
var angles = [];

// Store the features geometry
var feature_geometry = Geometry($feature);
// Loop over all intersecting lines and find their angles
var angle_type;
var angle_value;
for (var line in lines) {
    // Buffer and create an extent of the point by a small amount to extract the segment
    var clip_area = Extent(Buffer($feature, .01, "meter"));
    // Clip the line by the extend and get the first line segment
    var segment = Clip(line, clip_area)["paths"][0];
    // The features location is on the start of the line, get the angle from the feature to the end vertex
    if (Equals(segment[0], feature_geometry)) {
        angle_type = 'from'
        angle_value = Round(Angle(feature_geometry, segment[-1]), 0)
    }
    // The features location is on the end of the line, create a new segment from the feature to the start vertex
    else if (Equals(segment[-1], feature_geometry)) {
        angle_type = 'to'
        angle_value = Round(Angle(feature_geometry, segment[0]), 0)
    }
    // The features location is midspan of the segment, use the angle of the segment
    else {
        angle_type = 'mid'
        angle_value = Round(Angle(segment[0], segment[-1]), 0)
    }
    if (geographic_rotation == true) {
        // Convert Arithmetic to Geographic
        angle_value = (450 - angle_value) % 360;
    }
    // Add 180 to match 0 rotation in the TOC
    // Add user specified spin angle if their symbol is rotated
    angle_value = (angle_value + 180 + symbol_flip_angle) % 360;
    angles[Count(angles)] = {'angle': angle_value, 'type': angle_type};
}

// If only one angle, return that value
if (Count(angles) == 1) {
    // If the point is midspan, flip to match symbol as it if was on the end point
    if (angles[0]['type'] == 'mid')
    {
        return (angles[0]['angle'] + 180) % 360;
    }
    return angles[0]['angle'];
} else if (Count(angles) == 2) {
    // If the feature is midpan of the first line, return the angle of the second line
    if (angles[0]['type'] == 'mid')
        return angles[1]['angle'];
    // If the feature is midpan of the second line, return the angle of the first line
    else if (angles[1]['type'] == 'mid')
        return angles[0]['angle'];
    // If the feature is at the end point of both lines, return the angle of the first line
    else if (angles[0]['type'] == 'to' && angles[1]['type'] == 'to') {
        return angles[0]['angle'];
    }
    // If the feature is at the start point of both lines, return the angle of the first line
    else if (angles[0]['type'] == 'from' && angles[1]['type'] == 'from') {
        return angles[0]['angle'];
    }
    // If the feature is at the start point of the first line and end of the second line, return the second line
    else if (angles[0]['type'] == 'from') {
        return angles[1]['angle'];
    }
    // If the feature is at the start point of the second line and start of the second line, return the first line
    return angles[0]['angle'];

} else if (Count(angles) == 3) {
    // Flatten the angles to ignore direction
    var flat_angle1 = angles[0]['angle'] % 180;
    var flat_angle2 = angles[1]['angle'] % 180;
    var flat_angle3 = angles[2]['angle'] % 180;
    // Create differences between angles
    var angle_dif_a = Abs(flat_angle1 - flat_angle2);
    var angle_dif_b = Abs(flat_angle1 - flat_angle3);
    var angle_dif_c = Abs(flat_angle2 - flat_angle3);
    // If difference between line 1 and 2 is below the tolerance, meaning the lines follow the same plane, return the
    // third line
    if (angle_dif_a <= (diff_tol * 2) || angle_dif_a >= (180 - (diff_tol * 2))) {
        return angles[2]['angle'];
    }
    // If difference between line 1 and 3 is below the tolerance, meaning the lines follow the same plane, return the
    // second line
    else if (angle_dif_b <= (diff_tol * 2) || angle_dif_b >= (180 - (diff_tol * 2))) {
        return angles[1]['angle'];

    }
    // If difference between line 2 and 3 is below the tolerance, meaning the lines follow the same plane, return the
    // first line
    else if (angle_dif_c <= (diff_tol * 2) || angle_dif_c >= (180 - (diff_tol * 2))) {
        return angles[0]['angle'];
    }
    // Return first if not covered above
    return angles[0]['angle'];
}
// All other cases, the first feature is returned
else {
    return angles[0]['angle'];
}

 

 

0 Kudos
4 Replies
PatrickGCowan
Esri Contributor

You said these were existing features that you are modifying. Is there a value in the symbolrotation field currently for the features that are not rotating (perhaps it is set to 0 or some other value)? If a value is present it will not overwrite the existing value. If you set the symbolrotation field to null for the feature that is not rotating, you should see that as soon as it becomes null, it is treated as a change, the attribute rules runs, and the value is updated to the proper rotation value.

0 Kudos
PeterKing
Regular Contributor

Did not do the trick but thank you for your reply!

As suggested, I modified the symbolrotation field to <Null> but the attribute rule fails to run and update the proper rotation value. 

I also tested setting the symbolrotation field to <Null> and modifying a separate attribute field, and the attribute rule still fails to run.

The attribute rule is set to run on Insert and Update and runs properly on other Fittings, just not with fittings on the end of a single pipe.

0 Kudos
SillPaul
Occasional Contributor

This particular symbol rotation rule is working for us.  I realize this doesn't help much, other than to confirm that it should/could work in a similar implementation.

Our config is much like yours.  We're using the same attribute rule from UN Expanded.  Enterprise 11.5, UN 7, Pro 3.5.2.

Our rule is set to fire on insert or update.  It works both if I right-click to set to null, or if I select the existing symbolrotation value and delete.  Tested a couple of end-of-line features.   Set the rotation to something "wrong" and applied the change.  Then set it back to null.  That triggered the rule which then updated the symbolrotation field.

0 Kudos
MikeMillerGIS
Esri Frequent Contributor

Can you recreate the rule using the new templated AR and see if that changes anything?

0 Kudos