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
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.
.
@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?