Select to view content in your preferred language

Get subnetwork controller details for the feature being created or edited

108
2
Thursday
EstherSmith_Dev
New Contributor III

Hi,

I have a requirement to inherit some attributes from a "subnetwork controller" feature when features are added or updated in a network. I am thinking of Arcade calculation attribute rule that fires on add or update. But I am struggling to query the subnetwork controller without SubnetworkName being already populated. We have a nightly process to run "update subnetwork" for all "dirty" subnetworks and we have UN set-up in non-eventing mode so that attribute rules do not fire on "Update Subnetwork" (to preserve real user editor tracking and to optimize performance etc) 

I am aware that Subnetwork Controllers table can be initialised using FeatureSetByName as below

var snc_set = FeatureSetByName($datastore, 'Subnetworks', subnetwork_fields);

But without SubnetworkName field already populated for the feature, I am not sure how I can filter only the subnetwork controller for the current feature.

FeatureSetByAssociation doesn't seem to support "Subnetwork" association type.

Any other way to query/filter Subnetworks table? 

 

Thanks,

 

 

0 Kudos
2 Replies
MikeMillerGIS
Esri Frequent Contributor

Do you need it as an attribute?  You could calculate it on demand in a pop up.  You could also look at using a propagator.  What kind and of info are you trying to set on each feature?

 

For a pop up, take a look at the Subnetwork Popup option in the Configure UN layer tool.

https://esri.github.io/Utility-Data-Management-Support-Tools/docs/3.3/ConfigureUtilityNetworkLayers....

The code is below(This is not usable as it has the variables in it that the tool sets for you, so run the tool to get a usable version)

 

 

// This script will traverse the subnetwork table to find all neighboring and parent subnetworks

// *************       User Variables       *************
// This section has the functions and variables that need to be adjusted based on your implementation
Expects($feature, @subnetwork_fields@);
var subnetwork_name_fields = [@subnetwork_fields@];

// subnetwork_tier_names and subnetwork_tier_ranks should be an empty list for partitioned network, for hierarchical, it needs to align with subnetwork_name_fields
var subnetwork_tier_names = [@tier_names@];
var subnetwork_tier_ranks = [@tier_ranks@];

var device_source_id = null; // this line will be replaced at runtime
var junction_object_source_id = null; // this line will be replaced at runtime

var subnetwork_query_fields = ['subnetworkname', 'globalid', 'featuresourceid', 'featureglobalid', 'tierrank', 'tiername'];
// The subnetwork table in a feature service is always layer id 500002 or named Subnetworks
// The subnetwork table in a FGDB UN is based on the DSID, it will be something like UN_5_Subnetwork
var subnetwork_table = FeatureSetByName($datastore, '@subnetwork_table@', subnetwork_query_fields, false);

// The device class is the only class that can be controllers, use its map name or layer ID
// For services this is the name in the feature service or the layer ID
// In a FGDB, use the name of the feature class in the GDB
var device_table = FeatureSetByName($datastore, '@device@', [@subnetwork_fields@, 'globalid'], false);
var jo_table = FeatureSetByName($datastore, '@junction_objects@', [@subnetwork_fields@, 'globalid'], false);

// Change this to show results at different parent tiers.  0 shows only subnetworks the feature is on and its adjacent tiers
// Value needs to be positive
// 1 is all subnetworks on the current tier and all subnetwork in tier rank minus the value
// This is only for partitions
var rank_offet = 0;

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

// A list to store info for debugging
var debug_info = [];

function get_subnetworks(controller_table, feature_global_ids, current_subnetwork, controller_subnetworks) {
    // locally defined to not override global scope
    if (Count(feature_global_ids) == 0) {
        return;
    }
    var features = Filter(controller_table, "globalid IN @feature_global_ids");
    for (var feat in features) {
        // Store the device into a global variable
        Push(controller_features, feat);
        // If a controller has a parent network, the networks are concatenated with ::, continue if no parent
        if (Find('::', feat[current_subnetwork_field], 0) == -1) {
            continue;
        }
        // Split on the subnetwork concatenation string
        var feat_sub = Split(feat[current_subnetwork_field], '::');
        for (var j in feat_sub) {
            // Only store the network if it is a parent, do not store the network that the controller is controlling
            if (feat_sub[j] != current_subnetwork) {
                Push(controller_subnetworks, feat_sub[j]);
            }
        }
    }
}

function get_parents(current_subnetwork) {

    // Filter the subnetwork table to get the controllers for the subnetwork the feature is on
    var add_sql = "";
    if (hier_tier_name != '')
    {
        add_sql = ` AND TIERNAME = '${hier_tier_name}'`;
    }
    var results = Filter(subnetwork_table, `subnetworkname = @current_subnetwork${add_sql}`);//AND isdirty = 0
    //Debug info to help look at the queries being made
    //push(debug_info,'subnetworkname = ' + current_subnetwork +  ' AND isdirty = 0');
    var device_global_ids = [];
    var jo_global_ids = [];
    // Create a list of the controllers global IDs for the features subnetwork
    // NOTE: Count does not work on results
    for (var result in results) {

        // Store the rank of the subnetwork the feature is on, if the feature is on more than one, store the lowest
        if (hier_tier_name != ''){
            feature_tier_rank = hier_tier_rank;
            feature_tier_name = hier_tier_name;
        }
        else if (includes(features_subnetworks, result.subnetworkname) &&
            (IsEmpty(feature_tier_rank) || result.tierrank < feature_tier_rank)) {
            feature_tier_rank = result.tierrank;
            feature_tier_name = result.tiername;
        }
        // exit on first feature not in the range of ranks
        if (result.tierrank + Abs(rank_offet) < feature_tier_rank) {
            return;
        }
        if (IndexOf(controller_guid,  result.featureglobalid) != -1){
            continue
        }

        Push(controller_guid, result.featureglobalid);

        // Check the features source ID to see if it is a device or JO
        iif(result.featuresourceid == device_source_id,
            Push(device_global_ids, result.featureglobalid),
            Push(jo_global_ids, result.featureglobalid));

        Push(subnetwork_rows, result);
    }
    // Return if no new subnetworks
    if (Count(device_global_ids) == 0 && Count(jo_global_ids) == 0) {
        return;
    }
    // Filter the device and JO class to get the subnetwork names from the controller feature

    var controller_subnetworks = [];
    get_subnetworks(device_table, device_global_ids, current_subnetwork, controller_subnetworks);
    get_subnetworks(jo_table, jo_global_ids, current_subnetwork, controller_subnetworks);

    // If no parent networks are found, exit function
    if (Count(controller_subnetworks) == 0) {
        return;
    }
    // Get a unique set of subnetworks
    controller_subnetworks = Distinct(controller_subnetworks);
    // Loop through all parents, and store into global variable
    // using recursion, call get parents for each parent
    for (var parent_sub_idx in controller_subnetworks) {
        get_parents(controller_subnetworks[parent_sub_idx]);
    }
    return;
}

function create_result() {
    // If no controllers where found, ie, feature is not on a subnetwork, exit
    if (Count(controller_guid) == 0) {
        if (hier_tier_name != "") {
            return hier_tier_name + ": Unknown Subnetwork\n";
        }
        else {
            return current_subnetwork_field + ": Unknown Subnetwork\n";
        }
    }
    // Dict of arrays with key as tier rank, each subnetwork controller row is store in here
    var results_dict = {};
    // Dict of strings with key as tier rank, each values is the subnetwork name
    var tier_names = {};
    // Array of the tier ranks as int
    var tier_ranks = [];
    // the current tier rank the feature resides on

    // Loop through the subnetwork rows found in get_parents
    for (var rowidx in subnetwork_rows) {
        // Store the row using the loop index
        var subnetwork_row = subnetwork_rows[rowidx];
        // Convert the tierrank to text as dict keys must be strings
        var tier_rank = Text(subnetwork_row.tierrank);
        // If the tier name as not been stored yet, store into list with tier_rank as key
        if (HasKey(tier_names, tier_rank) == False) {
            tier_names[tier_rank] = subnetwork_row.tiername;
        }
        // Store the current rank the feature resides on, do not store into result dict, as the current subnetwork is
        // displayed separately
        if (Includes(features_subnetworks, subnetwork_row.subnetworkname)) {
            continue;
        }
        // Store the tier rank, as a number, into array to arrange results
        Push(tier_ranks, subnetwork_row.tierrank);
        // Init the dict with the tier rank as the key as an array
        if (HasKey(results_dict, tier_rank) == False) {
            results_dict[tier_rank] = [];
        }

        // Add to the array in the dict by tier rank the subnetwork name
        Push(results_dict[tier_rank], subnetwork_row.subnetworkname);
    }
    if (Count(tier_ranks) > 1) {
        // Get a unique list of tier ranks
        tier_ranks = Distinct(tier_ranks);
        // Sort the tier in descending order
        tier_ranks = Reverse(Sort(tier_ranks));
    }
    // Create the result value, first with tier the feature is on

    var result_string = feature_tier_name + ": " + Concatenate(Distinct(features_subnetworks), ', ') + " \n";
    // Loop through all the ranks, highest first
    for (var key_idx in tier_ranks) {
        // Get the key as text
        var tier_key = Text(tier_ranks[key_idx]);

        if (IsEmpty(results_dict[tier_key])) {
            // No other subnetworks are on the features tier
            continue;
        }
        if (Text(feature_tier_rank) == tier_key) {
            // If you want to display the information about other subnetworks on the same tier as the feature, change
            // the line below,
            //  tier_names[tier_key] + " - " can be added to show the name of the subnetwork
            result_string = result_string + "Adjacent: " + Concatenate(Distinct(results_dict[tier_key]), ', ') + " \n";
        } else  {
            result_string = result_string + tier_names[tier_key] + " Tier - " + Concatenate(Distinct(results_dict[tier_key]), ', ') + " \n";
        }
    }
    return result_string;
}

// Init Global Variables
var controller_guid = [];
var controller_features = [];
var subnetwork_rows = [];
var current_subnetwork_field = null;
var hier_tier_name = '';
var hier_tier_rank;

var feature_tier_rank;
var feature_tier_name;
var features_subnetworks;

if (IsEmpty(subnetwork_table) == true) {
    return "Subnetwork table not found";
}
if (IsEmpty(device_table) == true) {
    return "Device table not found";
}
if (IsEmpty(jo_table) == true) {
    return "Junction Object table not found";
}

var results = [];
for(var i in subnetwork_name_fields){
    controller_guid = [];
    controller_features = [];
    subnetwork_rows = [];
    current_subnetwork_field = subnetwork_name_fields[i];
    if (Count(subnetwork_name_fields) == Count(subnetwork_tier_names))
    {
        hier_tier_name = subnetwork_tier_names[i];
        hier_tier_rank = subnetwork_tier_ranks[i];
    }
    else
    {
        hier_tier_name = '';
        hier_tier_rank = -1;
    }
    features_subnetworks = Distinct(Split($feature[current_subnetwork_field], '::'));
    for (var i in features_subnetworks) {
        get_parents(features_subnetworks[i]);
    }
    // Call Get Parents to get parent Subnetworks
    Push(results, create_result());
}

//return text(debug_info);
return Concatenate(results, '\n');

 

 

0 Kudos
EstherSmith_Dev
New Contributor III

Thanks @MikeMillerGIS for sending this through. I will review the code and find what I can reuse. But it looks like SubnetworkName is already set on the asset and it is traversing the ancestry of the subnets. 

I am working with a Australian electrical utility spread across vast area with dense urban, semi-urban and remote networks. The requirement is all new assets or changes to snapping / logical connectivity of existing assets should "inherit" some information such as "Operating Voltage", "Construction Class", "Location Urban/Rural" etc. from the SNC device. For our utility, one distribution subnetwork operates on one voltage only. Change to voltage level leads to another subnetwork.

Operating voltage and other attributes can be injected using a pop-up expression once SubnetworkName is populated by the overnight process. But the layer symbology is based on "Operating Voltage" field and scales are based on "rural", "urban" etc. So until update subnetwork runs, electric lines and junctions will display in a default symbol because operating voltage and other attributes are empty. This is confusing for the data editors. Hence the requirement to reach to the SNC in calculation attribute rule to set the values. Is it a unreasonable workflow?

Thanks,

Vish

 

0 Kudos