Return attributes from intersection of vertices a polyline

478
10
Jump to solution
03-05-2024 09:15 AM
Jake_S
by Esri Contributor
Esri Contributor

Greetings Community.

Wondering if anyone has ever created an ArcGIS Pro Arcade attribute rule that get the vertices of the digitizing polyline and use that vertices to intersect another feature to return attributes to fields on the digitized feature.

I have a feature class called Old_Data (F1) it has two fields START_FIELD and END_FIELD.

I have another feature class called New_Data (F2) with the same fields names. I would like when digitizing F1 the first vertex of FI intersects the F2 returns the F1.START_FIELD and the last vertex F2 intersects the F1 returns the F1.END_FIELD where it intersects.

The START_FIELD and END_FIELD of the Old_Data may be different features in the Old_Data. 

The use case is to see where a feature starts and ends.

I've played around with code and I can get a first vertices but not get data from the intersect. I'm not sure how to get the last vertices. Or get the intersect to work. My starting code below.

image.png

var startField = "START_FIELD";
var endField = "END_FIELD";

var geom = Geometry($feature);
var firstPoint = null;
var vertices = []
for (var part in geom.paths) {
    var segment = geom.paths[part];

    // Loop through the points in the segment
    for (var i in segment) {
        if (IsEmpty(firstPoint)) {
            firstPoint = segment[i];
            continue;
        }
        Push(vertices, segment[i]);
    }
}
var intersectingLine = Intersects(FeatureSetByName($datastore, "Old_Line",[startField, endField], true), firstPoint);

return Text(firstPoint) //just to see output

Any community help would be helpful.

0 Kudos
1 Solution

Accepted Solutions
MikeMillerGIS
Esri Frequent Contributor

You are calling Count too much.  Count on a FeatureSet causes a database query.  I would suggest just call Intersect with a buffer(features are considered snapped if they are within the tolerance of the dataset) and looping over the results.  If you are concerned about getting the closest, then call distance to and find the closest.  Take a look at the function below

 

 

function find_closest_line(input_geo, fs, search_distance) {
  var candidates = Intersects(fs, Buffer(input_geo, search_distance, 'meters'));
  var shortest = [Infinity, null];
  if (IsEmpty(candidates)) {
    return null
  }
  for (var line in candidates) {
    var d = Distance(input_geo, line);
    if (d < shortest[0]) {
      shortest = [d, line];
    }
  }
  return shortest[-1];
}

 

 

I have not worked with curves in Arcade.  

View solution in original post

0 Kudos
10 Replies
Jake_S
by Esri Contributor
Esri Contributor

This literally the logic I was looking for. Yet adapting to my data only returns null values. I'm not sure why. I'll have to figure that out.

 

0 Kudos
Jake_S
by Esri Contributor
Esri Contributor

I adapted the code for my needs, polyline to polyline intersection. Yet I noticed after looking at the results and the code, even with Snapping enabled in ArcGIS Pro for some reason the intersection was returning empty. I decided to create a intersect check with a buffer. Hazzah it worked.........kinda.

The code works great on features that are not curved.

JS_Esri_0-1709727904206.png

As you can see in the examples above A1 line goes from Start Field B to End Field W, returning the proper values, same for A2. Yet when we move to curved lines, we get only the first line object values returned.

As you can see in B1 the right values are returned. B1 line goes from Start Field A to End Field Z. Yet B2 and B3 are returning the first line segment values Start Field A to End Field Z. When B2 should return Start Field A to End Field Y, and B3 should return Start Field B to End Field Z.

I'm not sure how the curves are being handled differently.

@HusseinNasser2 do you have any thoughts?

 

/* *********************************************************** */
var fields = ['START_FIELD', 'END_FIELD']
var line_fs = FeatureSetByName($datastore, "Old_Line", fields, true);

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 attributes = {}

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

//find point intersecting origin
var origIntx = Intersects(orig_point,line_fs);
if (Count(origIntx) == 0) {
    origIntx = Intersects(line_fs, Buffer(orig_point, 1,"meters"));
 if (Count(origIntx) == 0) return { "errorMessage": "First vertex must intersect at least one line" }
}

var origIntx = First(origIntx);

if (!IsEmptyButBetter(origIntx)) {
	attributes['START_FIELD'] = origIntx['START_FIELD']
  }

//find point intersecting end
var endIntx = Intersects(end_point, line_fs);
if (Count(endIntx) == 0) {
    endIntx = Intersects(line_fs, Buffer(end_point, 1, "meters"));
 if (Count(endIntx) == 0) return { "errorMessage": "Last vertex must intersect at least one line" }
}

var endIntx = First(endIntx);

if (!IsEmptyButBetter(endIntx)) {
	attributes['END_FIELD'] = endIntx['END_FIELD']
  }


var result = {}
if (!IsEmptyButBetter(attributes)) {
    result['result'] = {
        'attributes': attributes
    }
}

return result

 




The code works

0 Kudos
MikeMillerGIS
Esri Frequent Contributor

You are calling Count too much.  Count on a FeatureSet causes a database query.  I would suggest just call Intersect with a buffer(features are considered snapped if they are within the tolerance of the dataset) and looping over the results.  If you are concerned about getting the closest, then call distance to and find the closest.  Take a look at the function below

 

 

function find_closest_line(input_geo, fs, search_distance) {
  var candidates = Intersects(fs, Buffer(input_geo, search_distance, 'meters'));
  var shortest = [Infinity, null];
  if (IsEmpty(candidates)) {
    return null
  }
  for (var line in candidates) {
    var d = Distance(input_geo, line);
    if (d < shortest[0]) {
      shortest = [d, line];
    }
  }
  return shortest[-1];
}

 

 

I have not worked with curves in Arcade.  

0 Kudos
Jake_S
by Esri Contributor
Esri Contributor

Thanks @MikeMillerGIS  I was able to utilize this function to reduce the code debt and queries. A new issue immerged which is a head scratcher.

The find_closest_line function still returns values even if the intersecting features are outside the buffer distance as seen with the highlighted feature. Buffer is set to 1 meter. For reference there is a 1 meter buffer object I created using a point and the Buffer GP tool in ArcGIS Pro.

JS_Esri_1-1709739870604.png

/* *********************************************************** */

/* ********************* Field Variables ********************* */
var startField = 'START_FIELD'
var endField = 'END_FIELD'
var alignDistance = 1
/* ******************* Field Variables End ******************* */

/* ************************* Functions *********************** */

function find_closest_line(vert_ref) {
  // Find closest line segment to $feature. Limit search to specific radius.
  var candidates = Intersects(line_class, Buffer(vert_ref, alignDistance, "meters"));
  var shortest = [1e10, null];
  for (var line in candidates) {
      var d = Distance(vert_ref, line)

      if (d < shortest[0]) shortest = [d, line]
  }
  return shortest[-1]
} 

// ************* End Functions Section ******************

var line_class = FeatureSetByName($datastore, "Old_Line", [startField,endField], true);

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})

//find point intersecting origin
var origin_closest_line = find_closest_line(orig_point);
if (origin_closest_line == null) return { "errorMessage": "First vertex must intersect at least one line" }

//find point intersecting end
var end_closest_line = find_closest_line(end_point);
if (end_closest_line == null) return { "errorMessage": "Last vertex must intersect at least one line" }

var from = origin_closest_line[startField]
var to = end_closest_line[endField]

return {
    "result": {
        "attributes": {
            "START_FIELD": from,
            "END_FIELD": to
        }
    }
}

 

I’ve tried to put in bailouts in the function but regardless it will always return. Anyways Thanks for getting me on the right path!


0 Kudos
MikeMillerGIS
Esri Frequent Contributor

think it is Meter, not Meters

0 Kudos
Jake_S
by Esri Contributor
Esri Contributor

Tried the "meter" verse "meters" no effect. Also functional reference states "Possible values: feet | kilometers | miles | nautical-miles | meters | yards".

But I think the Buffer() Arcade function in ArcGIS Pro 3.2.2 is busted.

Using a simple test. I create a point feature and a line feature. I then do the same intersect by buffer and t populates the field. The point features are no where near the line.

JS_Esri_1-1709748401763.png

 

 

var alignDistance = 1

var line_class = FeatureSetByName($datastore, "Old_Line", ["SOME_ATT"], true);
var candidates = Intersects(line_class, Buffer($feature, alignDistance, "meters"));

for (var line in candidates) {
    return line.SOME_ATT
}

 

 

 

0 Kudos
MikeMillerGIS
Esri Frequent Contributor

What spatial ref is your data, wonder if you need to use https://developers.arcgis.com/arcade/function-reference/geometry_functions/#buffergeodetic

0 Kudos
Jake_S
by Esri Contributor
Esri Contributor

I thought that too but Buffer Geodetic only supportsWeb Mercator or a WGS 84 spatial references I'm using NAD 83 (4269)

0 Kudos