Auto Snap with Attribute Rules for point features along polyline feature

406
4
01-19-2024 08:33 AM
AndrewReynoldsDevon
Occasional Contributor

I have 3 attribute rules already working which are spatial lookups when a point/line/poly is created & the line/polygon work fine. If I hold Ctrl to enable snapping when creating a point the look ups work however when I don't do this the point doesn't auto snap to the polyline feature so the code which is using intersect to carry out the lookups doesn't work. 

I have the below code which works when snapping is enabled with CTRL but I either need to modify this code to add in the function of Auto-Snap or a attribute rule to enable auto snapping when creating point features.

 

 

// load the other featureset
var fs = FeatureSetByName($datastore, "Highways.HIGHWAYMGR.NSG_Network")
// get the first feature from fs that intersects the current $feature
var f = First(Intersects(fs, $feature))
// abort if we didn't find something
if(f == null) { return }
// return the intersecting feature's value
return f.NSG_STREETS_UID

 

 

 

 

Tags (1)
0 Kudos
4 Replies
JohannesLindner
MVP Frequent Contributor

You can search the closest line feature and then use the new-ish NearestCoordinate function to snap the point to that line.

// Calculation attribute rule on the point fc
// field: empty


function closest_feature(test_feature, compare_feature_set) {
  // returns the feature of compare_feature_set that is closest to test_feature
  var min_distance = 9999999
  var closest_feature = null
  for(var f in compare_feature_set) {
    var d = Distance(test_feature, f)
    if(d < min_distance) {
      min_distance = d
      closest_feature = f
    }
  }
  return closest_feature
}


// load the other featureset
var fs = FeatureSetByName($datastore, "Highways.HIGHWAYMGR.NSG_Network")
// get the feature that is closest to the current $feature
fs = Intersects(fs, Buffer($feature, 100))  // only analyze close-ish features
var f = closest_feature($feature, fs)
// abort if we didn't find something
if(f == null) { return }

// return the intersecting feature's value and snap the point to the found feature
return {
    result: {
        geometry: NearestCoordinate(f, $feature).coordinate,
        attributes: {StreetsUID: f.NSG_STREETS_UID}
    }
}

 

 

If you don't have access to NearestCoordinate yet, you can use the functions from my answer in this blog.


Have a great day!
Johannes
0 Kudos
AndrewReynoldsDevon
Occasional Contributor

.

0 Kudos
AndrewReynoldsDevon
Occasional Contributor

@JohannesLindner  we have Enterprise portal version 10.8.1 so the above function was introduced in version 11 that we don't have & aren't thinking about for a while. Is there an alternative function we can use with our version of portal?

I had a look at your link to your other blog answer but would you be able to provide an example working with my current code for the NSG lookup like you did in the above as when I had a go on Friday it didn't work. 

Thanks

I've had a look at the code you mention on your other blog which is what I'm going to have to use due our version of Pro/Portal. 

 

 

function closest_feature(test_feature, compare_feature_set) {
  // returns the feature of compare_feature_set that is closest to test_feature
  var min_distance = 9999999
  var closest_feature = null
  for(var f in compare_feature_set) {
    var d = Distance(test_feature, f)
    if(d < min_distance) {
      min_distance = d
      closest_feature = f
    }
  }
  return closest_feature
}

function project_orthogonally(point_geometry, line_geometry) {
  // returns a projection of the point_geometry onto the line_geometry
  // only start and end point of the line_geometry are considered!
  // https://de.wikipedia.org/wiki/Orthogonalprojektion
  // https://en.wikibooks.org/wiki/Linear_Algebra/Orthogonal_Projection_Onto_a_Line
  var p = point_geometry
  var r0 = line_geometry.paths[0][0]
  var r1 = line_geometry.paths[0][-1]
  var ux = r1.x - r0.x
  var uy = r1.y - r0.y
  var lambda = ((p.x-r0.x)*ux + (p.y-r0.y)*uy) / (ux*ux + uy*uy)
  var new_p = Point({"x": r0.x + lambda * ux, "y": r0.y + lambda * uy, "spatialReference": p.spatialReference})
  // if new_p is on the line defined by r0 and r1 but not on the actual line_geometry, snap it to the closest end point
  if(Disjoint(new_p, line_geometry)) {
    new_p = IIF(Distance(r0, p) < Distance(r1, p), r0, r1)
  }
  return new_p
}

function snap_point_to_polyline(point_geometry, polyline_geometry) {
  // returns a point that is snapped to the polyline_geometry
  
  // create feature set of the polyline's segments
  var sr = point_geometry.spatialReference
  var vertices = polyline_geometry.paths[0]
  var fs_segments = {"fields": [], "spatialReference": sr, "geometryType": "esriGeometryPolyline", "features": []}
  for(var s=0; s<Count(vertices)-1; s++) {
    var p0 = vertices[s]
    var p1 = vertices[s+1]
    Push(fs_segments.features, {"geometry": {"paths": [[ [p0.x, p0.y], [p1.x, p1.y] ]], "spatialReference": sr}})
  }
  fs_segments = FeatureSet(Text(fs_segments))
  // project point_geometry onto the closest segment
  var closest_segment_geo = Geometry(closest_feature(point_geometry, fs_segments))
  return project_orthogonally(point_geometry, closest_segment_geo ) 
}


var fs_lines = FeatureSetByName($datastore, "Lines")
// optionally, buffer
fs_lines = Intersects(fs_lines, Buffer($feature, 100))

var closest_line_geometry = Geometry(closest_feature($feature, fs_lines))
return snap_point_to_polyline(Geometry($feature), closest_line_geometry)

// load the other featureset
var fs = FeatureSetByName($datastore, "Highways.HIGHWAYMGR.NSG_Network")
// get the feature that is closest to the current $feature
fs = Intersects(fs, Buffer($feature, 100))  // only analyze close-ish features
var f = closest_feature($feature, fs)
// abort if we didn't find something
if(f == null) { return }

// return the intersecting feature's value and snap the point to the found feature
return {
    result: {
        geometry: NearestCoordinate(f, $feature).coordinate,
        attributes: {StreetsUID: f.NSG_STREETS_UID}
    }
}

I've got some questions on the above code 

1. Var min_distance = 9999999- what units is this in? I'd like the closest possible distance ideally to 1mm

2 . test_feature in the first line - is this the name attributed to the polyline? Our polyline is called NSG_Network so do I replace that?

3. optionally buffer - can we use this instead of the complex code above?  and if so which parts of the code above do we keep?

0 Kudos
Bud
by
Notable Contributor
0 Kudos