Select to view content in your preferred language

Calculate From and To Endpoint IDs (from one of several point feature classes)

1915
17
Jump to solution
03-01-2024 12:46 PM
NathanGEOregon
Occasional Contributor

We are still using the Geometric Network in ArcMap, along with Attribute Assistant (AA). We are in the process of exploring a transition to the Trace Network and adopting Attribute Rules in Pro.

We have been using a couple of AA rules to populate a FromID and ToID field with the IDs of the endpoints of a line segment (Sewer Mains) from several possible point feature classes (manholes, nodes, etc).

The From Junction Field  (and similar To Junction Field) in AA method made this pretty straightforward, as it would select whatever point feature happened to be at the start or endpoint and grab the associated ID, regardless of what feature class the point happened to be from.

Is there a similar way to use junctions in Arcade to supply From and To IDs to a line from several possible point feature classes? It seems if you're only working with a single point feature class, it would be pretty straightforward (https://www.youtube.com/watch?v=ueckrlhto0Q) but for multiple point feature classes, it seems you'd need to create a feature variable for each point feature class, and parse through them like "If there is a manhole point at the From location, grab the ID, Otherwise check if there is a node at the From location and grab the ID" etc.

0 Kudos
17 Replies
avonmoos
Regular Contributor

In Attribute Rules I'm only able to update 1 attribute at a time so I made one code for BeginningStructureID and one for EndingStructureID. Here is one of the codes that I came up with based on the example you provided.

 

 

function get_fs(cls_name) {
  return Decode(cls_name,
      "SewerCleanout", FeatureSetByName($datastore, "COK.SewerCleanout", [], true),
      "SewerLiftStation", FeatureSetByName($datastore, "COK.SewerLiftStation", [], true),
      "SewerManhole", FeatureSetByName($datastore, "COK.SewerManhole", [], true),
      "SewerNode", FeatureSetByName($datastore, "COK.SewerNode", [], true),
      null
  );
}

function IsEmptyButBetter(data) {
  if (IsEmpty(data)) return true;
  for (var x in data) return false;
  return true;
}

// Mapping of feature classes to their FacilityID attribute names
var fc_to_facility_id = {
  'SewerCleanout': 'CleanoutID',
  'SewerLiftStation': 'LiftStationID',
  'SewerManhole': 'ManholeID',
  'SewerNode': 'NodeID'
};

var fclist = ['SewerCleanout',  'SewerLiftStation', 'SewerManhole', 'SewerNode'];
var line_shape = Geometry($feature);
var spRef = line_shape['spatialReference'];

// Get the origin and end points of the line
var orig_x = line_shape['paths'][0][0].x;
var orig_y = line_shape['paths'][0][0].y;
var orig_point = Point({x: orig_x, y: orig_y, spatialReference: spRef});

var attributes = {};

var beginning_structure_id = null;

for (var p in fclist) {
  var fc_name = fclist[p];
  var point_fs = get_fs(fc_name);
  var facility_id_field = fc_to_facility_id[fc_name];

  // Find point intersecting origin
  var origIntx = First(Intersects(orig_point, point_fs));

  if (!IsEmptyButBetter(origIntx)) {
      attributes['BeginningStructureID'] = origIntx[facility_id_field];
      beginning_structure_id = attributes['BeginningStructureID'];
  }
}

Console(beginning_structure_id);
return beginning_structure_id;

 

 

0 Kudos
RhettZufelt
MVP Notable Contributor

True if you are just returning a result, but AR allows you to return a dictionary of keywords and update multiple attributes at once.  Not real intuitive in my code as I add items to the attributes variable and return that as the 'attributes': keyword in the result dictionary.

Take a look here, an easier example to see how to return values for multiple attributes from on rule.

R_

avonmoos
Regular Contributor

Wow, I didn't know we could do multiple at once. I'll take a dive into that and play around with it. Thanks again for the info!

0 Kudos
RhettZufelt
MVP Notable Contributor

To make it a bit easier to get a grasp of, I have modified the code to use a results dictionary without the interim dict.  This should make it easier to follow:

 

 

function get_fs(cls_name){
  return Decode(cls_name,
      "sdInlet",FeatureSetByName($datastore, "sdInlet", fields, true),
      "sdOutlet",FeatureSetByName($datastore, "sdOutlet", fields, true),
      "sdCleanOut",FeatureSetByName($datastore, "sdCleanOut", fields, true),
      "sdManhole",FeatureSetByName($datastore, "sdManhole", fields, true),
      "sdDischargePoint",FeatureSetByName($datastore, "sdDischargePoint", fields, true),
      "sdFitting",FeatureSetByName($datastore, "sdFitting", fields, true),
      "sdNetworkStructure",FeatureSetByName($datastore, "sdNetworkStructure", fields, true),
      null)
}
function IsEmptyButBetter(data) {
    if (IsEmpty(data)) return true;
    for (var x in data) return false;
    return true;
}

var fields = ['FacilityID']
var fclist = ['sdManhole','sdInlet','sdOutlet','sdCleanOut', 'sdFitting', 'sdDischargePoint', 'sdNetworkStructure'];
var line_shape = Geometry($feature)
var spRef = line_shape['spatialReference']

// Get the origin and end points of the line
var orig_x = line_shape['paths'][0][0].x
var orig_y = line_shape['paths'][0][0].y
var orig_point = Point({x: orig_x, y: orig_y, spatialReference: spRef})

var end_x = line_shape['paths'][-1][-1].x
var end_y = line_shape['paths'][-1][-1].y
var end_point = Point({x: end_x, y: end_y, spatialReference: spRef})

var umh = 'NA'
var dmh = 'NA'
for (var p in fclist){
    var point_fs = get_fs(fclist[p]);

    //find point intersecting origin
    var origIntx = first(Intersects(orig_point, point_fs));

    //find point intersecting end
    var endIntx = first(Intersects(end_point, point_fs));

    if (!IsEmptyButBetter(origIntx)) {
        var umh = origIntx['FacilityID']
      }


    if (!IsEmptyButBetter(endIntx)) {
        var dmh = endIntx['FacilityID']
      }


}

return {
    "result": {
        "attributes": {
            "UpMH": umh, 
            "DownMH": dmh,
            "FACILITYID": umh + "-" + dmh
        }
    }

}

 

 

UpMH, DownMH, and FACILITYID are the column names in the main line FC.

R_

NathanGEOregon
Occasional Contributor

@RhettZufelt this is fantastic, works like a charm! I was getting a "null geometry" error when validating in the Expression Builder. I simplified the the lines related grabbing the origin and endpoints of the line, which seemed to resolve the error.

var orig_point = line_shape.paths[0][0]

var end_point = line_shape.paths[-1][-1]

 

0 Kudos
RhettZufelt
MVP Notable Contributor

Also, when returning a dict, there is no need to choose a field in the AR pane.

However, there is a bug, and if you want to use this data in ArcGIS Server less than version 10.9, just populate the "Field" box.

RhettZufelt_0-1717025038610.png

As you can see by the minimum version required.  The ONLY difference between these two rules is that I picked the FacilityID field in one.  However, it will still populate all the fields in the result dict.

R_

0 Kudos
VanessaSimps
Frequent Contributor

This is GREAT!! Thank you, such a helpful resource. 

 

0 Kudos
Bud
by
Esteemed Contributor
0 Kudos