I am fairly new to Arcade and am trying to update the CoordinatedID field in my WaterMain layer using an attribute rule with the help of some code found here: Attribute rule script expression examples—ArcGIS Pro | Documentation
What I would like to do it grab the CoordinatedID field from my Roads (Centreline_Assets) layer and use it to populate the CoordinatedID field in my WaterMains (WaterMains_Assets) layer. They layers don't line up exactly so I am trying to buffer the road by 10m and populate the Coordinated field based on an intersection between the buffer and the WaterMains layer. Here's what I have so far:
//On Insert or Update populate Coordinate ID
var fcRoad=FeatureSetByName($datastore, "Centreline_Assets",["Coordinated_ID"])
var fcWaterMain=FeatureSetByName($datastore,"WaterMain_Assets_SplitLine",["Coordinated_ID"])
Var RoadIntersect = Intersects(Buffer(fcRoad,10,'meter'),fcWaterMain)
var AddList = []
var counter = 0
var noCID = Count(RoadIntersect)
if (noCID>0){
for (var Road in RoadIntersect){
AddList[counter]={
'Coordinated_ID':Centreline_Assets.CoordinatedID,
'attributes':{
'add_CoordinateID':$feature.Coordinated_ID
}
}
counter++
}
return {
'result': noCID + ' Coordinated ID found',
'edit': [{
'className':'Centreline_Assets',
'updates': AddList
}]
}
}else{
return 'No Coordinated ID'
}
I keep getting the following error:
Invalid expression. Error on line 4. Geometry type null expected.
Any suggestions?
Solved! Go to Solution.
Hi @SarahHartholt ,
Since you are editing the same layer of the feature the return type does not have to be the object including the name of the featureclass. Have a look at the example below (without using NextSequenceValue):
//On Insert or Update populate Coordinate ID
var fsRoads = FeatureSetByName($datastore, "Centreline_Assets",["Coordinated_ID"], True);
var watermain = $feature;
var watermainbuf = Buffer(watermain, 10, 'meter');
var roads = Intersects(fsRoads, watermainbuf);
var cnt = Count(RoadIntersect);
// check different situations
if (cnt == 0) {
// there are no roads found
return null;
} else if (cnt == 1) {
// there is a one road, return the Coordinate_ID
return First(roads)["Coordinated_ID"];
} else {
// there are multiple roads found, find the longest segment
var maxsegmentlength = 0;
var CoordID = null;
for (var road in roads) {
var segment = Intersection(road, watermainbuf);
var segmentlength = Length(segment, 'meter');
if (segmentlength > maxsegmentlength) {
CoordID = road["Coordinated_ID"];
maxsegmentlength = segmentlength;
}
}
return CoordID;
}
Did you mean to spell Centerline this way? Centreline
Hi @SarahHartholt ,
The error is pointing to line 4 and it is because you can't buffer a featureset, you can only buffer and single feature.
Thanks @XanderBakker . Is there any way for me to run my arcade expressions through a debugger? I've started using the Arcade playground but I haven't figured out a way to bring my features and attributes into it for testing. Please let me know if there are any useful articles that I could read on the topic.
Hi @SarahHartholt ,
I agree that debugging attribute rules is a bit tricky. However, you could use part of the expression in a pop-up to validate the result. You cannot use a return type that writes to a featureclass, but at least you will be able to see if the logic that occurs before returning the result is working.
Using the playground is great to check functions and logic, but you won't have access to your data and the sample data provided is very limited (2 polygon layers).
Related to the specific problem you are trying to tackle, there might be some additional complications since I suppose a waterline could intersect with multiple road buffers.
When you create an attribute rule on your waterlines, you will want to read the current waterline feature and buffer it and intersect the buffer with the road features. Then (maybe) you would have to check the length of the road segments inside the waterline buffer, to see which Coordinated_ID you should take.
Yes there are some other issues that I need to consider. Like you said, the waterlines will intersect with multiple road buffers (blue lines are waterlines, black lines are roads & 10m buffer is grey):
I was hoping that I could automate some of the processes I want to achieve using Arcade rather than having to run geoprocessing tools when I add data to my layers but I think I'm in over my head on this one.
Hi @SarahHartholt ,
I think this is something that can be done. You can intersect the waterline buffer with the roads and evaluate the length of the road inside the buffer and take the Coordinate_ID of the longest road segment.
I do have a couple of questions:
I don't think I know arcade well enough to accomplish this myself.
I was hoping I could do this as an immediate calculation. Right now when a road segment is added to my roads layer, I have an update rule that populates the Coordinated_ID field with the Next Sequence Value. Once the road layer is populated, I think I need to split the waterlines at vertices - the idea being that the U shaped segments will be separated into smaller pieces that line up with the roads more closely, making it easier to assign the correct Coordinated_ID. I was hoping that splitting the water lines would trigger the expression to grab the Coordinated_ID from the roads feature.
Waterlines that are not contained in the road buffer will need to be manually inspected and would likely be assigned a separate Coordinated_ID that is not associated with a road segment.
Hi @SarahHartholt ,
Since you are editing the same layer of the feature the return type does not have to be the object including the name of the featureclass. Have a look at the example below (without using NextSequenceValue):
//On Insert or Update populate Coordinate ID
var fsRoads = FeatureSetByName($datastore, "Centreline_Assets",["Coordinated_ID"], True);
var watermain = $feature;
var watermainbuf = Buffer(watermain, 10, 'meter');
var roads = Intersects(fsRoads, watermainbuf);
var cnt = Count(RoadIntersect);
// check different situations
if (cnt == 0) {
// there are no roads found
return null;
} else if (cnt == 1) {
// there is a one road, return the Coordinate_ID
return First(roads)["Coordinated_ID"];
} else {
// there are multiple roads found, find the longest segment
var maxsegmentlength = 0;
var CoordID = null;
for (var road in roads) {
var segment = Intersection(road, watermainbuf);
var segmentlength = Length(segment, 'meter');
if (segmentlength > maxsegmentlength) {
CoordID = road["Coordinated_ID"];
maxsegmentlength = segmentlength;
}
}
return CoordID;
}
Thanks @XanderBakker , looks great and I find the comments very helpful! I am getting an error on line 6 though - object not found roadintersect. When I change the variable to roads I get a general function failure. Not sure what's going on there.