Attribute transfer to next line

577
8
Jump to solution
05-16-2023 03:55 AM
GIS_Solutions
Occasional Contributor II

Hi everybody,

I prepared an attribute rule (batch) that will transfer the attributes in the "OutrunNumber" field in the Device layer to the "OutrunNumber" field in the Electric Line layer. It transfers this information to the line that intersects with the device layer. But the line consists of more than one piece. How can I transfer this information to the following parts?

I want to transfer this information to the green line and the following lines in the screenshot, how can I do this.

 

1.jpg

 

var fc_OutRunNumber = FeatureSetByName($datastore, "main.ElectricDevice",  ["OutrunNumber"] ,false)
var fc_MVS = Filter(fc_OutRunNumber,"ASSETGROUP in (37)")
var fc_Line_Intersect = Intersects(fc_MVS,$feature)
var fc_Outrun = First(fc_Line_Intersect)

if (fc_Outrun == null) return {"errorMessege": "OutrunNumber Not Found"}

return fc_Outrun.OutrunNumber

0 Kudos
1 Solution

Accepted Solutions
JohannesLindner
MVP Frequent Contributor

Ah, OK. For that, you need 2 rules (you should be able to do it in one, but I couldn't get it to work correctly, and it doesn't really matter).

 

The first rule is a variation of the rule in my previous answer. Instead of only calculating the OutrunNumber for Medium Voltage conductors, it will calculate for all conductor types (but it will skip connectors, banks, etc). And instead of traversing a line, it will traverse a network.

// Batch Calculation Attribute rule on ElectricLine
// field: OutrunNumber
// subtype: empty
// exclude from application evaluation


// Not a conductor? -> abort
if(!Includes([6, 9, 2], $feature.ASSETGROUP)) { return $feature.OutrunNumber }

// load all conductors and devices (split into switches and others)

// 6: Low, 9: Medium, 2: High voltage conductor
var conductors = Filter($featureset, "ASSETGROUP IN (6, 9, 2)")
// 23: Low, 37: Medium, 10: High voltage switch
var devices = FeatureSetByName($datastore, "ElectricDevice", ["OutrunNumber", "GlobalID"], false)
var switches = Filter(devices, "ASSETGROUP IN (23, 37, 10)")
var other_devices = Filter(devices, "ASSETGROUP NOT IN (23, 37, 10)")


// get the touching switch
var out_switch = First(Intersects(switches, $feature))

// no intersecting switch? -> abort
// the OutrunNumber will be supplied by the Feature that touches a mv switch
if(out_switch == null || IsEmpty(out_switch.OutrunNumber)) { return $feature.OutrunNumber }


// traverse the conductor network
var c_to_do = [$feature]  // conductor features that need to be processed
var c_globalids = []  // GlobalIDs of already processed conductors
for(var i = 0; i < 9999; i++) {
    var current_conductor = Pop(c_to_do)
    Push(c_globalids, current_conductor.GlobalID)
    for(var next_conductor in Touches(conductors, current_conductor)) {
        if(!Includes(c_globalids, next_conductor.GlobalID)) {
            Push(c_to_do, next_conductor)
        }
    }
    if(Count(c_to_do) == 0) { break }
}

var conductor_updates = []
//var device_updates = []
var new_att = {OutrunNumber: out_switch.OutrunNumber}
for(var i in c_globalids) {
    Push(conductor_updates, {globalID: c_globalids[i], attributes: new_att})
}
return {
    result: out_switch.OutrunNumber,
    edit: [{
        className: "ElectricLine",
        updates: conductor_updates
    }]
}

 

 

The second rule copies the OutrunNumber from an ElectricLine feature to all intersecting ElectricDevice features.

// Batch Calculation Attribute Rule on ElectricLine
// field: OutrunNumber
// subtype: empty
// exclude from application evaluation


// Not a conductor? -> abort
if(!Includes([6, 9, 2], $feature.ASSETGROUP)) { return $feature.OutrunNumber }

// load all devices, filter out switches
var devices = FeatureSetByName($datastore, "ElectricDevice", ["GlobalID"], true)
devices = Filter(devices, "ASSETGROUP NOT IN (23, 37, 10)")

var device_updates = []
for(var device in Intersects(devices, $feature)) {
    var update = {globalID: device.GlobalID, attributes: {OutrunNumber: $feature.OutrunNumber}}
    Push(device_updates, update)
}

return {
    result: $feature.OutrunNumber,
    edit: [{
        className: "ElectricDevice",
        updates: device_updates
    }]
}

 

 

The order of these rules is important! First, the OutrunNumber has to be calculated for the Lines, then it has to be copied to the Devices.

Be sure to not select a subtype.


Have a great day!
Johannes

View solution in original post

8 Replies
JohannesLindner
MVP Frequent Contributor

How do you get the other lines? Do they have a common attribute?


Have a great day!
Johannes
0 Kudos
GIS_Solutions
Occasional Contributor II

Hi @JohannesLindner 

Yes OutrunNumber field is common attribute. My goal is to calculate the OutrunNumber attributes written in the Device feature to the entire all line. 

Thank you,

0 Kudos
JohannesLindner
MVP Frequent Contributor

Hmm, so you can't use OutrunNumber to get the other line parts.

In that case you need to use the geometries, which will be more complicated. If you could upload a part of your two feature classes (either here or in a private message), that would be helpful.

 

Does this come close to what you want to do?

  • If a line intersects a Device with AssetGroup 37, copy its OutrunNumber
  • If a line does not intersect such a Device, Try to get the OutrunNumber from intersecting lines

 


Have a great day!
Johannes
JohannesLindner
MVP Frequent Contributor

@GIS_Solutions sent me an excerpt of their data. For context:

  • They have an electrical Utility Network. The lines in question are transmission lines.
  • The transmission lines are divided into multiple features, depending on asset type (eg underground/overhead).
  • The different transmission lines don't touch each other (though they do intersect).
  • The singular features are not oriented, meaning that it is not assured that the end point of one feature always touches the start point of the next feature
  • They want a Batch Calculation Attribute Rule to copy the OutrunNumber (String) attribute of the outgoing switches to all features of the respective transmission lines

 

// Batch Calculation Attribute Rule
// feature class: "ElectricalLine"
// subtype: "Medium Voltage Conductor"
// field: "OutrunNumber"
// exlude from application evaluation



// load the medium voltage switches
var devices = FeatureSetByName($datastore, "ElectricDevice",  ["OutrunNumber"], false)
var mv_switches = Filter(devices, "ASSETGROUP = 37")

// get the touching switch
var mv_switch = First(Intersects(mv_switches, $feature))

// no intersecting switch? -> abort
// the OutrunNumber will be supplied by the Feature that touches a mv switch
if(mv_switch == null || IsEmpty(mv_switch.OutrunNumber)) { return $feature.OutrunNumber }


// load all medium voltage conductors
var mv_conductors = Filter($featureset, "ASSETGROUP = 9")

// traverse the line, get all GlobalIDs
var globalids = []
var current_conductor = $feature
for(var i = 0; i < 9999; i++) {  // Arcade has no while loop, so we have to fake it with a loooong for loop and a break
    // get the next mv conductor
    // conditions: it touches the current test feature and hasn't been tested yet
    Push(globalids, current_conductor.GlobalID)
    var touching_conductors = Touches(mv_conductors, current_conductor)
    current_conductor = null
    for(var next_conductor in touching_conductors) {
        if(!Includes(globalids, next_conductor.GlobalID)) { 
            current_conductor = next_conductor
            break
        }
    }
    if(current_conductor == null) { break }
}

// construct the update instruction
var updates = []
for(var i in globalids) {
    var update = {globalID: globalids[i], attributes: {OutrunNumber: mv_switch.OutrunNumber}}
    Push(updates, update)
}

return {
    result: mv_switch.OutrunNumber,
    edit: [{
        className: "ElectricLine",
        updates: updates
    }]
}

 

The data you sent me was in a FGDB, but your code suggests an EGDB. Change the table names in lines 10 & 52 to match your database schema.

 

 

I have absolutely no idea why, but this rule will only work once (at least in the FGDB you sent me, maybe it's corrupted or this is expected behavior for batch rules). To run it again, follow these steps:

  • Disable the rule, save
  • Enable the rule, save
  • Evaluate Rules

Have a great day!
Johannes
GIS_Solutions
Occasional Contributor II

Hi @JohannesLindner 

Really thank you so much. I tried the code you gave, it worked very well.

I want to transfer the OutrunNumer attribute on all spatial connected feature. There may be many more feature like this, so any advice you can give me? My goal is to transfer the value in the mv_switch.OutrunNumber (Device Feature) feature to all the feature on the cable that are spatially connected to it.

2.jpg

 

0 Kudos
JohannesLindner
MVP Frequent Contributor

Ah, OK. For that, you need 2 rules (you should be able to do it in one, but I couldn't get it to work correctly, and it doesn't really matter).

 

The first rule is a variation of the rule in my previous answer. Instead of only calculating the OutrunNumber for Medium Voltage conductors, it will calculate for all conductor types (but it will skip connectors, banks, etc). And instead of traversing a line, it will traverse a network.

// Batch Calculation Attribute rule on ElectricLine
// field: OutrunNumber
// subtype: empty
// exclude from application evaluation


// Not a conductor? -> abort
if(!Includes([6, 9, 2], $feature.ASSETGROUP)) { return $feature.OutrunNumber }

// load all conductors and devices (split into switches and others)

// 6: Low, 9: Medium, 2: High voltage conductor
var conductors = Filter($featureset, "ASSETGROUP IN (6, 9, 2)")
// 23: Low, 37: Medium, 10: High voltage switch
var devices = FeatureSetByName($datastore, "ElectricDevice", ["OutrunNumber", "GlobalID"], false)
var switches = Filter(devices, "ASSETGROUP IN (23, 37, 10)")
var other_devices = Filter(devices, "ASSETGROUP NOT IN (23, 37, 10)")


// get the touching switch
var out_switch = First(Intersects(switches, $feature))

// no intersecting switch? -> abort
// the OutrunNumber will be supplied by the Feature that touches a mv switch
if(out_switch == null || IsEmpty(out_switch.OutrunNumber)) { return $feature.OutrunNumber }


// traverse the conductor network
var c_to_do = [$feature]  // conductor features that need to be processed
var c_globalids = []  // GlobalIDs of already processed conductors
for(var i = 0; i < 9999; i++) {
    var current_conductor = Pop(c_to_do)
    Push(c_globalids, current_conductor.GlobalID)
    for(var next_conductor in Touches(conductors, current_conductor)) {
        if(!Includes(c_globalids, next_conductor.GlobalID)) {
            Push(c_to_do, next_conductor)
        }
    }
    if(Count(c_to_do) == 0) { break }
}

var conductor_updates = []
//var device_updates = []
var new_att = {OutrunNumber: out_switch.OutrunNumber}
for(var i in c_globalids) {
    Push(conductor_updates, {globalID: c_globalids[i], attributes: new_att})
}
return {
    result: out_switch.OutrunNumber,
    edit: [{
        className: "ElectricLine",
        updates: conductor_updates
    }]
}

 

 

The second rule copies the OutrunNumber from an ElectricLine feature to all intersecting ElectricDevice features.

// Batch Calculation Attribute Rule on ElectricLine
// field: OutrunNumber
// subtype: empty
// exclude from application evaluation


// Not a conductor? -> abort
if(!Includes([6, 9, 2], $feature.ASSETGROUP)) { return $feature.OutrunNumber }

// load all devices, filter out switches
var devices = FeatureSetByName($datastore, "ElectricDevice", ["GlobalID"], true)
devices = Filter(devices, "ASSETGROUP NOT IN (23, 37, 10)")

var device_updates = []
for(var device in Intersects(devices, $feature)) {
    var update = {globalID: device.GlobalID, attributes: {OutrunNumber: $feature.OutrunNumber}}
    Push(device_updates, update)
}

return {
    result: $feature.OutrunNumber,
    edit: [{
        className: "ElectricDevice",
        updates: device_updates
    }]
}

 

 

The order of these rules is important! First, the OutrunNumber has to be calculated for the Lines, then it has to be copied to the Devices.

Be sure to not select a subtype.


Have a great day!
Johannes
GIS_Solutions
Occasional Contributor II

Codes works fine, thank you for your support  @JohannesLindner 

As far as I understand, it is necessary to determine which entities the attribute should be transferred to with the filter function.

0 Kudos
JohannesLindner
MVP Frequent Contributor

Correct.

In the first rule in line 13, I specify that the OutrunNumber should only be copied to conductors. If you want other line assets to have the OutrunNumber, too, include them in that Filter().

In the second rule in line 12, I exclude switches from the targets, because that's where the OutrunNumber comes from. We don't want to overwrite that... If you want to exclude other point assets (or only want to include certain asset types), do it in that Filter().


Have a great day!
Johannes
0 Kudos